Oracle Connection Pooling, ovvero come gestire gli accessi concorrenti a database con una coda.

Oracle connection pooling
Oracle connection pooling

Negoziare una connessione con un database è una operazione non banale che richiede una consistente attività per la realizzazione del collegamento sicuro, autenticato e la messa a disposizione delle risorse del database all’applicazione che ne fa uso. Ho esperienza di questo fatto soprattutto in ambito Java e Oracle ma le considerazioni da fare sono tutto sommato agnostiche e quindi indipendenti dalle piattaforme utilizzate. Tuttavia gli esempi sono in Java.

In ambito concorrente, come una applicazione web, questa operazione di negoziazione rischia presto di diventare un collo di bottiglia se si rimane nello schema 1 richiesta = 1 nuova connessione a DB.

Per risolvere questo problema è stato messa a punto la tecninca Oracle connection pooling (ovvero qui vediamo quella di Oracle, ma in realtà è una tecnica che si può applicare anche ad altri DBMS).

L’idea è quella di utilizzare una coda (in questo ambito definita come pool) nella quale mantenere un numero finito di connessioni riutilizzabili che vengono in continuazione prelevate dalla coda quando servono e rimesse nella coda al termine del loro utilizzo.

Oracle Connection Pooling: il caso d’uso

Supponiamo che arrivino due richieste quasi simultanee:

  1. Richiesta 1 (es. https://mysite.com/authenticate) la quale richiede collegamento al db per effettuare una autenticazione utente: in questa richiesta viene aperta una nuova connessione al database e viene lanciata la query sulla tabella utenti. Dopo aver acqusito il recordset, l’applicazione può dismettere la connessione invocando il metodo close()
  2. Richiesta 2 (es. https://mysite.com/articles/get/AB123) che deve eseguire una lettura di una tabella. L’applicazione apre una connessione ex novo (con il conseguente impegno di risorse e di tempo), lancia l’interrogazione e recupera il recordset.

Non sarebbe più efficiente per la Richiesta 2, qualora arrivasse dopo la conclusione della prima richiesta, riutilizzare la connessione aperta dalla Richiesta 1? Se la richiesta evitasse di chiamare la close() e invece “mettesse via” la connessione da qualche parte, la richiesta 2 potrebbe riutilizzarla senza dover negoziare un’altra volta con il DBMS username/password e caricamento driver.

Oracle connection Pooling: funzionamento

Una struttura dati efficiente per mettere via dati che si ritiene di usare di nuovo a breve, può essere una coda, in particolare una coda FIFO. Che nel gergo dei database si chiama Connection Pool. Questa coda conterrà descrittori di risorse DBMS.

Il funzionamento di questa coda è il seguente. Partiamo da una coda vuota.

Arriva il primo processo (una richesta HTTP: per esempio una login) che richiede all’applicazione di collegarsi al database. Siccome la coda è vuota, viene giocoforza negoziata una connessione nuova con il database, chiamiamola Connessione 1. Quando l’interrogazione è stata fatta e quindi la login è eseguita, la connessione non viene chiusa, bensì viene salvato il descrittore con un push() nella coda, che finora era ancora vuota.

Arriva il secondo processo che necessita di una connessione al DBMS e quello che fa prima di tutto è controllare se nella coda ci sono connessioni disponibili. Supponiamo che la login sia già stata fatta e quindi nella coda ci sia la connessione 1 rilasciata dal primo processo. Tutto ciò che deve fare il processo 2 è lanciare la query senza autenticarsi di nuovo; quindi serviamo due processi con una sola connessione.

Se, viceversa, il processo 2 arriva quando la login è ancora in corso, quindi la connessione 1 è ancora impegnata, in questo caso il processo 2 dovrà aprire una connessione 2.

Ma questo meccanismo fa sì che, dopo un po’ di tempo, la probabilità di trovare una connessione libera nella coda sia molto maggiore e quindi il numero di negoziazioni dirette è destinato a calare drasticamente.

Una strategia ancora migliore è questa: se all’avvio dell’applicazione server inizializzaziamo il pool costruendo e mettendo già via un certo numero di connessioni nella coda, risparmiamo tempo ad applicazione avviata.

Ogni volta che un processo preleva una connessione dalla coda, fa un pull(), ovvero toglie la connessione dalla coda; quando ha terminato, opera un push() e la rimette nella coda. Se il numero di processi concorrenti è maggiore della dimensione della coda fino a quel momento, viene negoziata una nuova connessione, altrimenti si fa più velocemente un pull().

Questo meccanismo deve essere gestito; ossia si deve sollevare l’applicazione dal negoziare nuove connessioni, perché dovrà essere tutto supervisionato dal processo di gestione della coda, che è un oggetto della classe ConnectionPool. L’applicazione dovrà solo limitarsi a chiedere una connessione al pool, e il pool in ogni caso gliela concederà (sia essa una connessione riciclata o una nuova di zecca), senza che l’applicazione debba interloquire direttamente con il database.

Per rendere poi più efficiente l’uso della memoria, si può fare a meno di allocare una coda dinamica (che implica continue chiamate alla JVM per farsi allocare memoria a runtime), ma utilizzzare invece un array di lunghezza fissa, in cui vengono impilate tante connessioni quante ne servono: all’inizio supponiamo che la coda sia vuota, viene fatto il push() di una nuova connessione, marcata con Active e consegnata al processo, che la potrà usare. Quando il processo che l’ha utilizzata la rilascia, il gestore del pool la dichiarerà Idle rimettendola a disposizione. Però non è che la inserisce nuovamente nel pool, semplicemente le cambia lo stato.

Tuttavia nell’implementazione di esempio che vedremo, viene utilizzata un’altra tecnica ancora: vengono impiegati due vettori; quello delle connessioni disponibili (o idle) e quello delle connessioni attive (active).

Il processo successivo che arriva a chiedere connessioni, si farà dare l’ultima connessione idle resasi disponibile (comportamento FIFO).

Oracle connection Pooling: esempio applicativo

Questo esempio è liberamente tratto (e semplificato) da Java Made So Easy.

Iniziamo dunque definendo la classe ConnectionPool:

package connectionPooling;
 
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Vector;
 
public class ConnectionPool implements Runnable {
    // i parametri di connessione
    private String driver, url, username, password;
    // la dimensione massima del pool
    private int maxConnections; 
    // le 2 code delle connessioni idle e active
    public Vector<Connection> idleConnections, activeConnections; 

    // il costruttore
    public ConnectionPool(
                  String driver, String url, String username,
                  String password, int maxConnections, int initialConnections
           ) throws SQLException {
           this.driver = driver;
           this.url = url;
           this.username = username;
           this.password = password;
           this.initialConnections = initialConnections;
           this.maxConnections = maxConnections;

           // in questo esempio il pool è mantenuto su due vettori:
           // - quello delle connessioni disponibili (o idle: pigre, nullafacenti);
           // - quello delle connessioni impegnate (o active);
           idleConnections = new Vector<Connection>(initialConnections);
           activeConnections = new Vector<Connection>();
           // qui opzionalmente preparo un certo numero di connessioni già pronte
           for (int i = 0; i < initialConnections; i++) {
                  idleConnections.addElement(makeNewConnection());
           }
    }

Il metodo fondamentale qui è makeNewConnection() con il quale costruiamo una nuova connessione “vera”; ovviamente lo dichiariamo private per impedire al programma chiamante di negoziare connessioni direttamente con il DBMS attraverso di essa:

private Connection makeNewConnection() throws SQLException {
           try {
                  // Carica il database driver
                  Class.forName(this.driver);
                  // Apri la connessione al db
                  Connection connection = DriverManager.getConnection(this.url, this.username,
                               this.password); 
                  return (connection);
           } catch (Exception cnfe) {
                  cnfe.printStackTrace();
                  throw new SQLException(
                               "ConnectionPool:: SQLException encountered:: "
                                             + cnfe.getMessage());
           }
    }

Come visto sopra questo metodo viene chiamato al deploy del connectioonPool per inizializzare un certo numero di connessioni già pronte per l’uso (che passo al costruttore).

Ora dobbiamo gestire il pool, ovvero consegnare al processo che ne fa richiesta una connessione idle, toglierla dal vettore delle idle e metterla in quello delle active:

/**
     * Method to return Connections
     */
    public synchronized Connection getConnection() throws SQLException {
           if (!idleConnections.isEmpty()) {
                  Connection existingConnection = (Connection) idleConnections
                               .lastElement();
                  int lastIndex = idleConnections.size() - 1;
                  // toglie la connessione dal vettore delle pigre:
                  idleConnections.removeElementAt(lastIndex);
                  // ... e la aggiunge al vettore delle attive:
                  activeConnections.addElement(existingConnection);
                  return (existingConnection);
           } else {
                  // se non ci sono più connessioni pigre, ne creo una di nuova: 
                  return makeNewConnection();
           }
    }

Poniamo attenzione sul fatto che il metodo getConnection() è definito come synchronized, per cui molte richieste pressoché simultanee verranno arbitrate in modo che le connessioni siano consegnate distintamente secondo il loro ordine di arrivo.

Può accadere, alla fine, che i processi esauriscano tutte le connessioni del pool: in questo caso il vettore delle connessioni active sarà pieno e quello delle idle sarà vuoto; si potrebbe gestire anche questa situazione ma, in questa fase in cui cerchiamo di capire come funziona il meccanismo, semplicemente notifichiamo che il pool è pieno.

                  if ((totalConnections() >= maxConnections) {
                        throw new SQLException("Connection limit reached");
                  }

dove totalConnection() è un count degli elementi del vettore activeConnections più il count degli elementi del vettore idleConnections.

Alla fine il programma chiamante utilizzerà il metodo getConnection() per comunicare con il pool, mentre la classe ConnectionPool utilizzerà il metodo (privato) makeConnection() per comunicare con il DBMS.

Così abbiamo operato la separazione tra applicazione e DBMS.

Ci manca solo un metodo per liberare una connessione (cioè toglierla dal vettore delle connesioni active e rimetterla in quello delle connessione idle), lo chiamiamo free():

  /**
     * Method to free the Connections
     */
    public synchronized void free(Connection connection) {
           activeConnections.removeElement(connection);
           idleConnections.addElement(connection);
    }

Alla fine siamo pronti ad utilizzare questa infrastruttura nell’applicazione; dobbiamo creare un oggetto istanza della classe ConnectionPool che mettterà a disposizione connessioni già pronte prelevabili con il metodo getConnection():

package connectionPooling;
 
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
 
/** Copyright (c), Ankit Mittal JavaMadeSoEasy.com */

public class PreparedStatementUseConnectionPooling {

    public static void main(String... arg) throws SQLException {
            ConnectionPool connectionPool = new ConnectionPool(
                        "oracle.jdbc.driver.OracleDriver",
                        "jdbc:oracle:thin:@localhost:1521:orcl", 
                        "foo", 
                        "bar",
                        10, 5);
 
           Connection dbConn = connectionPool.getConnection();
           System.out.println("Sono pronte le connessioni dalla classe ConnectionPool");
           
           PreparedStatement prepStmt = dbConn
                        .prepareStatement("select ID, NAME from EMPLOYEE");
           
           ResultSet rs = prepStmt.executeQuery();
           while (rs.next()) {
                  System.out.print(rs.getInt("ID") + " ");
                  System.out.println(rs.getString("NAME"));
           }
 
           if (rs != null)
                  rs.close(); // close resultSet
           if (prepStmt != null)
                  prepStmt.close(); // close PreparedStatement
 
           connectionPool.free(dbConn);
           System.out.println("Rilasciata la connessione dalla classe ConnectionPool");
    }
}
 

Si noti che il programma chiamante, dopo la connessione impartisce comandi direttamente al DBMS; il ConnectioPool si intromette tra di loro solo al momento dell’acquisizione di nuove connessioni (o della rimozione di un connessione che non serve più).

Per ulteriori dettagli implementativi si faccia riferimento all’articolo citato all’inizio.

Adesso il funzionamento di un pool dovrebbe essere almeno un pelino più chiaro. Almeno per me lo è stato.

Dati COVID-19 nuovamente online

Ho sistemato un errore che si era manifestato nel cruscotto dei dati relativi alla pandemia tuttora in corso.

L’errore si è verificato in seguito ad una modifica del tracciato dati messo online dalla Protezione Civile e aveva temporaneamente reso non operativo il cruscotto.

Potete nuovamente consultarli (dati nazionali e regionali) a questo link:

Python in pillole – parte 3: disegnare frecce

Python logo

Python programming skill terza parte: disegnamo in modalità testo come ultimo pattern quattro frecce:

right arrow
*                    
* *                  
* * *                
* * * *              
* * * * *            
* * * *              
* * *                
* *                  
*                    
                     
left arrow
                     
                  *  
                * *  
              * * *  
            * * * *  
          * * * * *  
            * * * *  
              * * *  
                * *  
                  *  
upward arrow
                     
                     
                     
                     
                     
        *            
      * * *          
    * * * * *        
  * * * * * * *      
* * * * * * * * *    
downward arrow
* * * * * * * * *    
  * * * * * * *    
    * * * * *    
      * * *    
        *    
           

Esercizio Python 7 – freccia verso destra (right arrow)

"""
print right arrow
*
* *
* * *
* *  
* 

e.g.
  0 1 2 3 4 5 6 7 8 9         i  test    N-1-i
0 *                           0, j<=0    9
1 * *                         1, j<=1    8
2 * * *                       2, j<=2    7
3 * * * *                     3, j<=3    6
4 * * * * *                   4, j <5    5
5 * * * *                     5, j <4    4
6 * * *                       6, j <3    3
7 * *                         7, j <2    2
8 *                           8, j <1    1
9                             9, j <0    0
"""
print("right arrow")
for i in range(N):
    for j in range(N):
        if j <= i and j < N - 1 - i:
            print("*"),
        else:
            print(" "),
    print(" ")  # a capo

Ho stampato un tracing e ho riportato tutti i valori delle variabili per le varie configurazioni disegnate. Come si vede, si tratta di combinare le condizioni che compaiono negli esercizi 3 e 6 (BLT e TLT), solo che sostituisco la condizione j <= N - 1 - i con j < N - 1 - i in modo da non disegnare la diagonale BLTR che risulterebbe col disegnare una freccia “spuntata”.

Esercizio Python 8 – freccia verso sinistra (left arrow)

"""
print left arrow
    *
  * *
* * *
  * *  
    * 
    
e.g.
  0 1 2 3 4 5 6 7 8 9      i  test    N-1-i
0                   *      0, j>=9    9*
1                 * *      1, j>=8    8*
2               * * *      2, j>=7    7*
3             * * * *      3, j>=6    6*
4           * * * * *      4, j>=5    5*
5             * * * *     *5, j> 5    4
6               * * *     *6, j> 6    3
7                 * *     *7, j> 7    2
8                   *     *8, j> 8    1
9                         *9, j> 9    0 
"""
print("left arrow")
for i in range(N):
    for j in range(N):
        if j >= i and j > N - 1 - i:
            print("*"),
        else:
            print(" "),
    print(" ")  # a capo

Per girare la freccia a sinistra è necessario ribaltare entrambe le condizioni di test:

  • da j<=i a j>=i
  • da j <= N - 1 - i a j > N - 1 - i

L’assenza dell’= sulla seconda condizione, al solito, evita il disegno della doppia punta.

Possiamo indovinare che anche per le frecce up e down si dovrà fare lo stesso…

Esercizio Python 9 – freccia verso l’alto (upward arrow)

"""
print upward arrow
    *  
  * * *
* * * * * 

e.g.
  0 1 2 3 4 5 6 7 8 9
0
1
2
3
4                       N-1-i  test  i    
5         *                 4 <= j < 5
6       * * *               3 <= j < 6
7     * * * * *             2 <= j < 7
8   * * * * * * *           1 <= j < 8
9 * * * * * * * * *         0 <= j < 9

"""
print("upward arrow")
for i in range(N):
    for j in range(N):
        if N - i - 1 <= j < i:
            print("*"),
        else:
            print(" "),
    print(" ")  # a capo

Qui è ormai evidente: è l’intersezione dei due esercizi BLT e BRT (esercizi 3 e 5). L’ultimo esercizio è la freccia verso il basso e, ormai, tutti gli arcani dovrebbero essere svelati e possiamo dire “com’era facile!”

Esercizio Python 10 – freccia verso il basso (downward arrow)

"""
print downward arrow
* * * * * 
  * * *
    *  

e.g.
  0 1 2 3 4 5 6 7 8 9     i   test  N-1-i
0 * * * * * * * * *       0   <=j<  9
1   * * * * * * *         1   <=j<  8
2     * * * * *           2   <=j<  7
3       * * *             3   <=j<  6
4         *               4   <=j<  5
5
6
7
8
9 
"""
print("downward arrow")
for i in range(N):
    for j in range(N - i):
        if i <= j < N - 1 - i:
            print("*"),
        else:
            print(" "),
    print(" ")  # a capo

Spero abbiate trovato divertenti questi piccoli esercizi (sì… anche se alla fine sono un po’ silly).

Alle prossime pillole!

Altre pillole Python sul disegno di frecce

Disegnare frecce 1Disegnare frecce 2

Link utili

Python.org

Python in pillole – parte 2: disegnare triangoli

Python logo
Python logo

Python programming skill seconda parte: un po’ più laborioso è disegnare questi quattro triangoli. Ma neanche tanto, vedrete.

Parte II – disegnare triangoli con Python

Bottom Left Triangle

BLT
*  
* *  
* * *  
* * * *  
* * * * *  
* * * * * *  
* * * * * * *  
* * * * * * * *  
* * * * * * * * *  
* * * * * * * * * *  

Top Right

TRT
* * * * * * * * * *  
  * * * * * * * * *  
    * * * * * * * *  
      * * * * * * *  
        * * * * * *  
          * * * * *  
            * * * *  
              * * *  
                * *  
                  * 

Bottom Right

BRT
                  *  
                * *  
              * * *  
            * * * *  
          * * * * *  
        * * * * * *  
      * * * * * * *  
    * * * * * * * *  
  * * * * * * * * *  
* * * * * * * * * *  

e infine Top Left

TLT
* * * * * * * * * *  
* * * * * * * * *    
* * * * * * * *      
* * * * * * *        
* * * * * *          
* * * * *            
* * * *              
* * *                
* *                  
*         

Li ho messi in quest’ordine perché sono due metà del quadrato diviso dalla stessa diagonale (TLBR nel primo, TRBL nel secondo).

Esercizio 3 – disegnare un triangolo BLT

print("BLT")
for i in range(N):
    for j in range(N):
        if i >= j:
            print("*"),
        else:
            print(" "),
    print(" ")  # a capo

Qui è evidente che l’esercizio 3 si ricava dall’esercizio 1 semplicemente sostituendo la condizione i=j con i>=j ma anche estendendo la scansione di j a tutto l’intervallo 0..N-1.

Esercizio 4 – disegnare un triangolo TRT

print("TRT")
for i in range(N):
    for j in range(N):
        if i <= j:
            print('*'),
        else:
            print(" "),
    print(" ")  # a capo

Anche qui è quasi ovvio, la condizione i <= j sostituisce la precedente i >= j (con la condizione = specifichiamo che va disegnata anche la diagonale).

Esercizio 5 – disegnare un triangolo BRT

print("BRT")
for i in range(N):
    for j in range(N):
        if j >= N - 1 - i:
            print("*"),
        else:
            print(" "),
    print(" ")  # a capo

Anche qui basta sostituire la condizione j = N - 1 - i con la relazione >=, a partire dall’esercizio 2.

Esercizio 6 – disegnare un triangolo TLT

print("TLT")
for i in range(N):
    for j in range(N):
        if j <= N - 1 - i:
            print("*"),
        else:
            print(" "),
    print(" ")

Ormai avete capito la regola, non serve spaccarsi la testa.

I pattern generati da questi programmi sono riportati in alto, all’inizio dell’articolo, nello stesso ordine.

Ok, abbiamo finito.! Con la prossima e ultima parte disegneremo frecce.

Altri articoli del blog

Vi lascio anche i riferimenti per le altre puntate:

Link utili Python

Python in pillole: esercizi su cicli annidati.

Python programming languagre
Python: un esercizio coi cicli

Oggi comincerò a pubblicare alcuni esercizi di Python sui cicli. Si assomigliano tutti, ognuno presenta una difficoltà leggeremente diversa, e sono basati sulla geometria. Fondamentalmente l’esercizio è indipendente dal linguaggio usato, è più un esercizio di algoritmi, ma per concisione e immediatezza d’uso ho preferito il Python,

Lo scopo, banale se volete, è quello di disegnare linee e triangoli in modalità testo, ma è un’esercizio che, a fronte di un risultato ludico – il vedere comparire un disegno -, permette di impratichirsi con la corretta valorizzazione delle variabili di ciclo.

È una pratica che fa anche un po’ anni ’80 (per chi ci è passato, e per chi non ci è passato potrà avere un’idea di quanto poco ci bastava per divertirci :-)).

Parte I – disegnare diagonali

I primi esercizi sono abbastanza semplici: tutti quanti hanno in comune la stessa struttura: due cicli for annidati e un test per stabiire se stampare un asterisco * o uno spazio. Per ognuno di questi ho riscritto più volte il codice in modo da approdare ad una forma il più simmetrica e concisa possibile (senza fare uso della funzione abs() che qui tornerebbe utile).

Esercizio 1 – disegnare una diagonale TLBR (Top Left Bottom Right)

Per prima cosa aggiungiamo ad ogni file l’intestazione

#!/usr/bin/python

N = 10

che dichiara l’interprete da usare per poter invocare il programma come un script da linea di comando:

$ ./diagonals.py

e inizializza a 10 l’ampiezza del pattern da visualizzare.

"""
print TLBR diagonal
*  
  *  
    *  
      *  
        * 
"""
print("TLBR")
# remember: range(N) goes form 0 to N-1
for i in range(N):
    for j in range(i + 1):
        if j == i:
            print('*'),
        else:
            print(" "),
    print(" ")  # a capo

Questo è abbastanza semplice: occorre un ciclo esterno per disegnare le righe (uso la variabile i) e uno per scandire le colonne (uso la variabile j)

La condizione che discrimina se stampare uno spazio o un asterisco è che ascisssa i e ordinata j siano uguali.

Finito. Una piccola ottimizzazione: la scansione delle colonne non viene fatta per tutta la riga ma solo fino al raggiungimento della condizione di plot (chiamiamola così).

L’ouput è il seguente

TLBR
*  
  *  
    *  
      *  
        *  
          *  
            *  
              *  
                *  
                  *  

Esercizio 2: – disegnare una diagonale TRBL (Top Right Bottom Left)

Si tratta semplicemente di disegnare l’atra diagonale del rettangolo:

"""
print TRBL diagonal
        *  
      *  
    *  
  *  
* 
"""
print("TRBL")
for i in range(N):
    for j in range(N - i):
        if j == N - 1 - i:
            print("*"),
        else:
            print(" "),
    print(" ")  # a capo

Il risultato è questo

TRBL
                  *  
                *  
              *  
            *  
          *  
        *  
      *  
    *  
  *  
*  

La condizione di plot è che l’indice di colonna sia uguale a N-1 (che è il massimo valore a cui arriva l’indice di riga) meno l’indice di colonna; è il numero di colonne che mancano da quella più a destra. È il duale del precedente in cui l’indice di riga era uguale all’indice di colonna (che è ovviamente il numero di colonne da quella più a sinistra). Quindi scritta così è evidente la simmetria che ci permette per sostituzione di passare da un programma all’altro. Ultima cosa, la scansione della riga non si estende a tutte le colonne ma solo fino alla condizione di plot, come nell’esercizio 1.

Dal punto di vista algebrico, nell’esecizio 1 si implementa il plot della funzione y=x (i = j nel nsotro programma), con il vincolo che l’asse delle y crescenti sia diretto verso il basso.

Nell’esercizio 2 si disegna la funzione y=N-x (i = N-j nel nostro codice), con la medesima convenzione.

Vi lascio anche i riferimenti per le puntate successive:

Fine! 🙂

Link utili Python

AdSense di Google: gli acronimi

Google AdSense
Google AdSense

Riporto per comodità mia ed eventualmente per piacere vostro, il significato degli acronimi utilizzzati in Google AdSense.

CTR pagina

Click-Through Rate = numero di click sui banner di una pagina / numero di visualizzazioni di quella pagina. Espresso in percentuale

Es: +4,02% significa che se la pagina è stata visualizzata 100 volte ha collezionato 4 click sui banner.

CPC

Cost Per Click = quanto si viene mediamente remunerati per un click su un Ad. Espresso in Euro.

Es: 0,13 € : ci sono iserzionisti che pagano pochi centesimi al click e chi anche 20 centesimi

RPM

Revenue Per Mile = Ricavo ogni 1000 impressioni per pagina. Espresso in Euro

Es: 5,20€ per 1000 impressioni/pagina

Impressioni

1 Impressione = il download di un banner sul dispositivo del navigante. Dipende da quanto il navigante si sofferma nella pagine. Se c’è un’alta frequenza di rimbalzo vuol dire che il navigante si ferma poco in un singola pagina e quindi non tutte gli Ad vengono scaricati e ho poche impressioni per pagina.

Link utili

Pillole Mirthconnect/NextGen: un semplice canale di echo

Costruiamo un semplice web service con MirthConnect che acquisice la request e ne ripropone il contenuto nella response (il comportamento del comando Unix echo)

Configuriamo per ingresso e uscita il tipo di file XML (oppure raw):

Impostazione dei tipi di dato del canale MirthConnect
Impostazione dei tipi di dato del canale MirthConnect

Definiamo come Source il componente Web Service Listener:

Source connector del canale Mirth HelloWorld
Source connector del canale Mirth HelloWorld

Ho scelto una porta 7100 per poi poterla collegare ad internet con un Reverse Proxy Apache.

È importante impostare la Response: si può fare anche dopo aver scritto la destination (anzi, così ci ritroviamo la viariabile che contine il valore da ritornare già nella combo, in questo caso response).

Per i rimanenti parametri lasciamo default. In particolare il nome del servizio da chiamare sarà MirthConnect. Quindi l’url di invocazione sarà qualcosa del tipo

https://betaingegneria.it/services/Mirth

Definiamo quandi una destination di tipo Javascript Writer e scriviamo questo programma:

Source connector del canale Mirth HelloWorld
Source connector del canale Mirth HelloWorld

Qui carichiamo una variabile javascript locale “text” con il contenuto grezzo del messaggio in entrata; lo scriviamo quindi nel logger (per controllare il traffico) e resitutiamo lo stesso valore alla mappa del canale nella variabile response.

Dopo aver scritto questo frammento potremo ritrovare la variabile response nella combo box di configurazione della Source – come visto prima.

A questo punto deployamo il canale e testiamo con SoapUI:

Test del canale Mirth HelloWorld con SoapUI
Test del canale Mirth HelloWorld con SoapUI

Ecco realizzato un webservice di echo ocn MirthConnect.

Link utili

Pillole Apache: configurazione di un virtualhost

Virtualhost con Apache: come evitare l'error 404
Apache httpd Virtualhost su localhost

Problema: definisco un virtualhost su un nuovo file di configurazione sotto la directory:

/etc/apache2/sites-available/newsite.conf

che contiene queste informazioni

<VirtualHost *:80>
        ProxyPreserveHost On
        ProxyPass /beta http://betaingegneria.it
        ProxyPassReverse /beta http://betaingegneria.it
        ServerName localhost
        ErrorLog ${APACHE_LOG_DIR}/error_newsite.log
        CustomLog ${APACHE_LOG_DIR}/access_newsite.log combined
</VirtualHost>

Lo abilito:

# a2ensite newsite
Enabling site newsite.
To activate the new configuration, you need to run:
systemctl reload apache2

Questo comando crea un link simbolico nella directory

/etc/apache2/sites-enabled/newsite.conf

Faccio ricaricare la configurazione ad Apache:

# systemctl reload apache2

Apache virtualhost: error 404

Il risultato è deludente: puntando a http://localhost/beta ho sempre un error 404 anziché vedere sul browser il sito betaingegneria.it.

In questo esempio non considero la gestione della crittografia, parliamo di http sulla porta standard 80 senza invio del certificato di chiave pubblica (server).

Però, se invece di creare un nuovo file scrivo la configurazione del proxy dentro al file 000-default.conf, rileggendo (reload) la configurazione nel browser, all’indirizzo http://localhost/beta vedo il mio sito. Questo era l’effetto che volevo ma creando un nuovo file di configurazione separato.

Per non farvela lunga (io invece un po’ di tempo ce l’ho perso) il problema è il “conflitto” tra il file 000.default.conf e il nuovo newsite.conf: infatti in questa directory ci dovrebbe esser un solo file per ogni nome di dominio; il file che avevo creato usava come ServerName il nome localhost, esattamente quello di default a cui si riferisce Apache quando ha esaurito i nomi di dominio definiti, gestito in 000.default.conf.

Quindi o disabilito 000-default.conf e scrivo la nuova configurazione nel nuovo file, oppure viceversa.

Disabilitare 000-default.conf a lungo andare non è la scelta più oculata perché in caso dismettessi il nuovo sito, se non mi ricordo questa cosa, lascio Apache senza un sito di default per localhost.

Link utili

Laravel bugfix: Errore 404 – not found ma la rotta esiste

Laravel bugfix  per un errore HTTP 404 su una rotta esistente
Laravel bugfix per un errore HTTP 404 su una rotta esistente

Laravel bugfix: sistemiamo questo bug: ho definito una rotta nel file routes/web.php:

Route::get('/activities/start', 'ActivityController@start');

e ho definito il metodo start() nell’ActivityController

    /**
     * start - start the day first activity
     * @return bool
     */
    public function start()
    {
        //
        $activity = new Activity;
        $activity->order_id = Config::get('constants.START');
        $activity->description = Config::get('constants.START_TEXT');
        $activity->notes = '';

        $activity->save();

        return true;
    }

Il risultato è deludente e alquanto irritante, visto che ho seguito questa prassi – che ritengo corretta – fin dall’inizio:

La soluzione è semplice: il metodo resource() nella definzione delle rotte va collocato dopo le definizioni delle singole rotte nel file routes/web.php:

...
Route::get('/activities/start', 'ActivityController@start');
Route::get('/activities/report', 'ActivityController@reportForm'); 
// at the end...
Route::resource('/activities', 'ActivityController');

In sostanza devo riorganizzare i metodi perché c’è qualche conflitto con le rotte RESTful generate dal resource()e quelle da me definite “a mano”.

Questa breve attività di Laravel bugfix è stata molto istruttiva.

A volte i file crescono senza troppi controlli, soprattutto mentre si impara un nuovo strumento coem Laravel o Grails, ma è bene fare spesso il punto dell’ordine e della pulizia all’interno dei file.

Altri articoli su Laravel bugfix

Problemi con la libreria Ixudra / Curl

#bugfix: Call to undefined function Ixudra\Curl\curl_init()

Ixudra curl - Cosa fare quando si verifica l'errore Call to undefined function Ixudra\Curl\curl_init()?
Cosa fare quando si verifica l’errore Call to undefined function Ixudra\Curl\curl_init()?

cURL è una nota libreria per trattare gli url e gestire chiamate http.

Utilizzo una libreria per Laravel che mi permette di fare delle richieste HTTP da server a sever che si chiama ixudra/curl.

La libreria funziona a meraviglia, se non che, per un problema di configurazione del mio SO, ogni volta che aggiorno la libreria, cURL smette di funzionare.

Il sintomo (undefined function Ixudra \ Curl\Curl_init())

Il sintomo del problema che Laravel esibisce è questo

Symfony\Component\Debug\Exception\FatalThrowableError
Call to undefined function Ixudra\Curl\curl_init()

In realtà il problema risiede ancora più in basso poiché nemmeno il client PHP è immune dagli effetti di questa errata configurazione; invocando semplicemente il client da riga di comando ho:

$ php -v
PHP Warning:  PHP Startup: Unable to load dynamic library 'curl.so' (tried: /usr/lib/php/20190902/curl.so (/usr/lib/php/20190902/curl.so: symbol curl_mime_addpart version CURL_OPENSSL_4 not defined in file libcurl.so.4 with link time reference), /usr/lib/php/20190902/curl.so.so (/usr/lib/php/20190902/curl.so.so: cannot open shared object file: No such file or directory)) in Unknown on line 0
PHP 7.4.6 (cli) (built: May 14 2020 10:02:44) ( NTS )
...

Il problema

La radice del problema si trova nella directory /usr/local/lib

$ cd /usr/local/lib/
$ ll
totale 12924
drwxr-xr-x  8 root root     4096 mag 27 08:48 ./
drwxr-xr-x 13 root root     4096 ott 17  2018 ../
-rw-r--r--  1 root root  1006444 mag 23  2017 libcurl.a
-rwxr-xr-x  1 root root     1042 mag 23  2017 libcurl.la*
lrwxrwxrwx  1 root root       16 mag 21 19:12 libcurl.so.4 -> libcurl.so.4.4.0*
-rwxr-xr-x  1 root root   542272 mag 23  2017 libcurl.so.4.4.0*
...

La “sconfigurazione” è dovuta alla presenza del link simbolico libcurl.so.4, la soluzione proposta è quella di interromperlo perché prenda il sopravvento la versione di sistema. Il fatto è che ogni volta che viene eseguito un aggiornamento di cURL, viene ripristinato anche questo link simbolico reintroducendo l’errore.

Una soluzione

Nell’attesa di escogitare una soluzione più definitiva ho seguito il consiglio che si dava nel forum:

$ sudo mv libcurl.so.4.4.0 libcurl.so.4.4.0.OLD

Così facendo non ho più il problema al client:

$ php -v
PHP 7.4.6 (cli) (built: May 14 2020 10:02:44) ( NTS )
Copyright (c) The PHP Group
Zend Engine v3.4.0, Copyright (c) Zend Technologies
    with Zend OPcache v7.4.6, Copyright (c), by Zend Technologies
    with Xdebug v2.9.5, Copyright (c) 2002-2020, by Derick Rethans

Come si vede, l’errore è scomparso. Affinche anche Apache possa beneficiare della soluzione del problema occorre riavviarlo:

$ sudo service apache2 restart

A questo punto anche l’applicazione Laravel torna a funzionare.

Altri articoli Laravel