Il gruppo PUG MoRe si riunisce (solitamente l'ultimo sabato di) ogni mese alle 14.00 qui; vi invitiamo a seguire i nostri aggiornamenti in mailing list, su questa pagina e su Twitter.

PHP User Group di Modena e Reggio Emilia

Resoconto dell’incontro del 20 giugno 2015

Questo mese ci siamo incontrati all ImpactHub di Reggio Emilia.

ImpactHub Reggio Emilia

Abbiamo trascorso una giornata dedicata alla presentazione di diverse tecnologie, tra cui lo stack ELK (Elastichsearch, Logstash, Kibana), Ansible, Symfony e dulcis in fundo una interessante sessione teorica e pratica di Growth Hacking.

June 20, 2015 at 1013AM

June 20, 2015 at 0957AM Noi ci siamo inseriti in questa giornata con una breve presentazione del PUG, vantandoci delle attività svolte sino ad oggi :-)

June 20, 2015 at 0959AM

CH8sHoeW8AArdUG Speriamo di avere incuriosito i presenti, le nuove iscrizioni sembrano confermarlo.

È emerso che siamo sempre più un Programmers User Group piuttosto che un PHP User Group. Tutti i linguaggi sono i benvenuti, anzi più linguaggi e metodologie diverse ci sono e più ci fa piacere! Dalla diversità si impara e non è necessario essere esperti, tutti sono i benvenuti!

June 20, 2015 at 0958AM

Posted in Eventi, Strumenti | Tagged , , , , , , | 3 Comments

Resoconto dell’incontro del 30 maggio 2015

Oggi abbiamo avuto il piacere di accogliere tre nuovi amici, Francisc, Daniele e Gianfranco.

Perciò, contrariamente a quanto avevamo programmato, abbiamo scelto di fare un coding dojo classico, sviluppando in TDD l’incremental kata EvilCorp.

May 30, 2015 at 0537PM

Come al solito ci siamo divisi in coppie e ciascuno è stato libero di sviluppare con il linguaggio che preferiva; le scelte sono ricadute su PHP, Python e Go.

Al termine ci siamo riuniti a far due chiacchiere per conoscere meglio i nuovi ragazzi ed è stato un pomeriggio piacevole come al solito.

May 30, 2015 at 0414PM

Probabilmente ci incontreremo nuovamente il 20 giugno presso il nuovissimo ImpactHub di Reggio Emilia in occasione di una giornata dedicata ad Ansible ed ElasticSearch.

Seguiteci qui e su Twitter per non perdervi gli aggiornamenti!

Posted in code kata, Eventi | Tagged , , , , , , , , , | Leave a comment

Prossimo incontro: sabato 30 maggio

Il nostro prossimo incontro si terrà sabato 30 maggio alle 14.00.

Reduci dal recente phpDay abbiamo deciso di sperimentare una formula nuova legata al TDD e al code review.

Se siete curiosi di saperne di più non mancate!

Posted in Eventi | Tagged , , , | Leave a comment

Quando c’e` comunicazione c’e` tutto

Il buon Roberto (a patto di lasciarlo in pace) se ne e` uscito alla grande con NetRobots, che e` un server in Python che simula una arena di combattimento, fra robot che dialogano attraverso messaggi in formato JSON, inviati su protocollo HTTP.

JSON e HTTP sono stati scelti per permettere a robot scritti in diversi linguaggi (un must per la natura eterogena del gruppo), di poter dialogare con il server. La sequenza di eventi e`:

  • un robot chiede al server di eseguire un comando, tipo “fai lo scan nella direzione 35, con ampiezza 90″, inviando il comando in JSON -> HTTP -> Socket
  • il server HTTP riceve il comando, lo esegue nell’arena di gioco virtuale aggiornando lo stato, e invia una risposta al robot tipo “c’e` un robot nemico alla distanza 20″, sempre in formato JSON -> HTTP -> Socket

Il server HTTP e` stato scritto in Flask, che e` un server HTTP minimale (nel senso buono del termine), che da` ampio controllo al programmatore. Il codice risultante e` compatto e leggibile, in perfetto stile Python. Per esempio questo e` il codice che riceve una richiesta JSON di scanner da un robot e risponde:

mod_robot.route('/<token>/scan', methods=['PUT'])
@check_token
def scan(robot):
    degree = int(float(request.form['degree']))
    resolution = int(float(request.form['resolution']))
    assert isinstance(robot, Robot)
    time.sleep(app.app.game_board_th.get_sleep_time())
    ret = robot.scan(degree, resolution)

    if ret is not None:
        resp = Response(response=json.dumps({'status': 'OK', 'distance': ret}),
                        status=200,
                        mimetype="application/json")
        return resp

    resp = Response(response=json.dumps({'status': 'KO', 'distance': None}),
                    status=406,
                    mimetype="application/json")
    return resp

Il problema di Flask, e che e` pensato (giustamente) per servire richieste HTTP in ordine FIFO, ma i robot dall’altra parte essendo in battaglia e in perenne pericolo di vita o di morte, non sono molto dell’idea di inviare educate richieste aspettando il loro turno, e sono in continuo bombardamento di messaggi HTTP.

Ogni thread Flask riceve la sua richiesta HTTP e se fosse fair, risponderebbe ai Robot dividendo i turni di gioco in intervalli regolari, per dare modo ad ogni robot, anche a quelli scritti in VisualBasic, di elaborare la contromossa. Inoltre le latenze della rete non sono note a priori e senza arrivare agli estremi del gioco per corrispondenza, sarebbe fair dare modo anche ai robot installati in zone colpite dal digital-divide, di poter dire la loro. Ci sono stati casi di Robot che tempo di inviare un messaggio di benvenuto al server, e una cartolina a casa dicendo che stavano bene ed erano pronti alla sfida, gia` erano stati scannerizzati e bombardati un migliaio di volte e il datagram TCP di risposta aveva trovato solo un profondo pozzo nero.

Ma in Flask non e` facile mettere in sospensione le risposte, o ordinarle per turno. Anche rispondendo con un messaggio tipo “non e` il tuo turno”, si rischia comunque di avere un poll continuo di richieste dal robot.

Ovviamente con un po’ di (in)sano hacking e` sicuramente possibile trovare una soluzione piu` o meno elegante per Flask, ma noi siamo il Team PUG MoRe e ci meritiamo di piu` :-)

ZeroMQ e` una libreria multilinguaggio (esistono versioni per quasi qualunque linguaggio di programmazione) che permette di scrivere facilmente un server che accetta comandi su connessioni in formato ZeroMQ, e inviare risposte ai robot scritti in altri linguaggi (un requirements del progetto NetRobots), ma che usano sempre la stessa libreria.

ZeroMQ puo` essere vista come una versione elegante (a livello di API) dei socket Linux. Le prestazioni sono di primissimo livello, tante` che e` stata scelta dal CERN per processare i dati dei suoi esperimenti scientifici, che non sono notoriamente parchi di banda o fiacchi nelle latenze, a meno che non si possano considerare le particelle sub-atomiche poche, lente e prevedibili :-)

Uno dei punti di forza di ZeroMQ e` che scala dal piccolo (due thread nello stesso processo che si scambiano messaggi), al medio (due processi sullo stesso host che si scambiano messaggi), al grande (due processi su host diversi che si scambiano messaggi). Per ogni situazione ZeroMQ usa una versione ad-hoc del protocollo cercando di minimizzare (o portare a 0) l’overhead dell’invio e ricezione del messaggio, ma con il vantaggio di avere una sola API di processing all’interno del programma. Quindi uno puo` scalare una applicazione, lasciando invariata la logica interna (la API e` sempre la stessa) ma cambiando solo la configurazione di deploy, facendo diventare ad esempio un thread un processo su una macchina esterna.

ZeroMQ puo` essere usata anche per gestire situazioni di multi-threading, dato che la logica a scambio di messaggi e` piu` intuitiva da capire rispetto a semafori o simili e allo stesso tempo ZeroMQ non e` molto piu` lenta di una chiamata di funzione, se usata all’interno dello stesso processo, dove basta passare un puntatore al messaggio da elaborare.

ZeroMQ si pone ad un livello piu` basso rispetto a Flask, dato che e` a livello di libreria di comunicazione (tipo Socket) e non di application-server. Pero`:

  • questo da` piu` liberta` di controllo, su come e quando ricevere, mettere in hold e inviare le risposte
  • la API molto elegante, non fa sentire troppo la mancanza di un prodotto piu` high-level come Flask

La filosofia di ZeroMQ per scrivere una applicazione e` questa:

  • io (ZeroMQ) ti do`:
    • una serie di protocolli di comunicazione
    • una serie di metodi di comunicazione fra socket (direct, publish-subscribe, broadcasting, etc…)
    • la certezza che qualunque combinazione (lecita) di protocollo e metodo di comunicazione, te la supporto in modo che piu` efficiente e robusto non si puo`
  • tu (programmatore) puoi:
    • combinare i protocolli e i metodi di comunicazione nel modo che preferisci (una esplosione combinatoria), per creare la piattaforma di messagging ad-hoc che ti serve
    • vedere l’applicazione come una serie di socket tra loro collegati, su cui leggono e scrivono processi consumer e producer
    • scalare l’applicazione facilmente

Al contrario, un prodotto di messagging completo e omnicomprensivo come RabbitMQ, tende ad essere da una parte completo (logging, persistenza dei messaggi, etc..), ma dall’altra ristretto alle funzionalita` che ha lui per gestire i messaggi. La sua filosofia e` questa:

  • queste sono le opzioni tipiche che ti offro per scambiare i messaggi
  • quasi di sicuro, sei in una di queste situazioni tipiche e quindi hai tutto gia` pronto
  • se non sei in una di queste situazioni, sono cavoli tuoi (che e` l’effetto Flask)

Evidentemente il CERN non era in una delle loro situazioni tipiche :-)

I robot si connettono tramite ZeroMQ al server

self.get_game_server_socket().send(c.SerializeToString())
status = RobotStatus()
status.ParseFromString(self.get_game_server_socket().recv())

e inviano una sequenza di:

  • send a command
  • recv the answer

Riceveranno la risposta solo quando e` il loro turno. Quindi il “recv” li mette in attesa della risposta (tipica pattern ZeroMQ dove il receive fa anche da synchro). Tutto il resto del codice e` per serializzare e deserializzare un messaggio usando Google ProtoBuf che e` una libreria multi linguaggio per la serializzazione/deserializzazione binaria efficiente dei messaggi. Quindi ProtoBuf prende il posto di JSON e ZeroMQ di HTTP.

Il server NetRobots e` decisamente piu` interessante:

  • apre un socket ZeroMQ esterno, da cui riceve le richieste dei robot
  • si mette in sleep una qualche frazione di secondo, in attesa dell’inizio del nuovo turno di gioco
  • nel mentre le richieste dei robot si accodano. ZeroMQ e` una libreria poco schizzinosa e ci pensa lei ad accodare lato server o nei casi piu` critici mettendo in hold i client
  • quando e` il momento del nuovo turno di simulazione, inizia a gestire le richieste dei clients
  • ZeroMQ e` multi threading e non e` un problema estrarre messaggi dalla coda (consumer), mentre altri thread spingono messaggi nella stessa coda (producer)

Il codice non e` di immediata comprensione dato che implementa una politica fair e tenta di evitare che uno stesso robot invii due richieste, senza aspettare la risposta, cercando di giocare quindi due o piu` volte nello stesso turno di gioco.

def process_robots_requests(self, client_socket):
    self._board.tick(self._deltatime)

    self.processed_robots.clear()

    # First process queued robots.
    queue = self.queued_robot_messages
    self.queued_robot_messages = {}
    # DEV-NOTE: these instructions are very important:
    # * the code must work assuming there are no any more queued robots
    # * a copy of the reference is maintaned for processing them

    for token, v in queue.iteritems():
        try:
            self.process_robot_command(v['command'], v['sender_address'], client_socket)
            self.processed_robots[token] = True
        except:
            self.debug_message("Unexpected error " + str(sys.exc_info()[0]))
            client_socket.send_multipart([v['sender_address'], b'', b''])
            # in ZMQ is mandatory sending an answer

    # Update the robots according the new requests from clients.
    again = True
    while again:
        sender_address = None
        try:
            sender_address, empty, binary_command = client_socket.recv_multipart(zmq.NOBLOCK)
            command = MainCommand()
            command.ParseFromString(binary_command)
            self.process_robot_command(command, sender_address, client_socket)

        except zmq.error.Again:
            # there are no more messages to process in the queue
            again = False

        except:
            # there is an error processing the command for this client.
            self.debug_message("Unexpected error " + str(sys.exc_info()[0]))
            if sender_address is not None:
                client_socket.send_multipart([sender_address, b'', b''])
                # in ZMQ is mandatory sending an answer


def process_robot_command(self, command, sender_address, client_socket):
    if command.HasField('createRobot'):
        self.process_create_robot_request(command.createRobot, sender_address, client_socket)

    elif command.HasField('deleteRobot'):
        self.process_delete_robot_request(command.deleteRobot, sender_address, client_socket)

    elif command.HasField('robotCommand'):
        token = command.robotCommand.token

        if token in self.banned_robots:
            # nothing to do, avoid to answer to these requests
            pass
        elif token in self.queued_robot_messages:
                self.banned_robots[token] = True
                robot = self._board.get_robot_by_token(token)
                self.debug_message("Banned " + token + ". Board time is " + str(self._board.global_time()) + ", robot time is " + str(robot.last_command_executed_at_global_time))
        else:
            if token in self.processed_robots:
                self.queued_robot_messages[token] = {'sender_address': sender_address, 'command': command }
                robot = self._board.get_robot_by_token(token)
            else:
                self.processed_robots[token] = True
                self.process_robot_request(command.robotCommand, sender_address, client_socket)

In questo codice la pattern di utilizzo delle ZeroMQ e` sinceramente un po’ astruso perche` e` stato utilizzato questo codice

sender_address, empty, binary_command = client_socket.recv_multipart(zmq.NOBLOCK)

che separa l’indirizzo del sender e il contenuto del messaggio ricevuto da un client Robot. Una volta processato il comando, la risposta viene inviata al client, usando

client_socket.send_multipart([sender_address, b'', status.SerializeToString()])

che in forma speculare invia il contenuto, rimettendo dentro al messaggio, l’indirizzo ricevente precedentemente estratto.

A differenza del codice Flask, in ZeroMQ i messaggi sono self-class-citzien, che possono essere manipolati esplicitamente:

  • separare contenuto di un messaggio, da indirizzo del sender
  • spostare un messaggio da un socket all’altro, anche di tipo diverso
  • etc.. etc..

La mia impressione finale e` che se avessi usato RabbitMQ avrei fatto prima:

  • non c’era bisogno di usare ProtoBuf
  • le pattern di utilizzo erano gia` codificate, e nel 90% dei casi vanno gia` bene per quello che uno vuole fare.
  • la curva di apprendimento e` minore

Pero` usare ZeroMQ e` stato veramente divertente:

  • dopo un po’ uno realizza che le combinazioni con cui usare e comporre la libreria sono infinite come il gioco dei Lego: ci puoi fare il castello, o l’aereo o …
  • e` un po’ la filosofia di Linux e delle pipes, dove uno puo` collegare ad un componente altri componenti, e cosi` via all’infinito
  • uno magari ci mette piu` tempo la prima volta a capire la filosofia della libreria, ma dopo ha in mano uno strumento veramente molto potente e versatile
  • con RabbitMQ avrei dovuto avere probabilmente un server separato, per la gestione dei messaggi, oltre al server Python. Un po’ come quando uno deve far girare un server MySQL insieme alla sua applicazione per avere le funzionalita` di database.. Le ZeroMQ sono come Sqlite, uno usa la libreria all’interno del server Python gia` scritto, e le funzionalita` che la fanno diventare una applicazione di messaging evoluta sono gia` tutte incluse nella libreria incorporata.

Inutile dire che mi aspetto che nel mondo commerciale prenderanno sempre piu` piede prodotti come RabbitMQ, dato che hanno la filosofia “tutto incluso”, vincente come tempi di apprendimento e utilizzo, rispetto al “tutto e` possibile” di ZeroMQ. Pero` lato divertimento la filosofia “tutto e` possibile” e` impagabile… :-)

Posted in code kata, progetti condivisi | Leave a comment

Power GO contro il povero Haskell

Non specifico i dettagli dell’esercizio, dando per scontato che c’eravate o abbiate letto i due articoli precedenti.

Haskell usato come lo uso io (senza lenses) e` particolarmente verboso nella specifica del cambiamento di stato di un oggetto, cosa che e` invece alla base di un linguaggio imperativo. Qua per esempio sto togliendo health ad un giocatore dell’arena

   attacked2 
     = attacked { combatant_health 
                    = max 0 ((combatant_health attacked) - attackStrenght) 
                }

Questa linea dice:

  • il nuovo oggetto “attacked2″, e` come l’oggetto “attacked” iniziale (di cui non posso cambiare niente, dato che in Haskell non posso modificare il valore di una variabile), tranne che ha il campo “health” diverso
  • il campo health e` uguale al massimo fra 0 (quindi non puo` diventare mai negativo) e la salute iniziale (“combatant_health attacked”) meno la potenza dell’attacco

In un linguaggio imperativo, sarebbe stato tutto molto piu` immediato e leggibile, tipo:

attacked.health -= attackStrenght;
if attacked.health < 0 then attacked.health = 0;

Quindi GO segna il primo GOL e si porta sul 1-0.

In Haskell pero` il codice e` piu` componibile, dato che essendoci meno side-effects ci sono anche meno sorprese quando si iniziano ad incastrare mattoncini su mattoncini. Coi pero` non si vincono le partite e quindi siamo ancora 1-0 per GO e il suo team.

Questo codice decide cosa fare, quando il giocatore “attacker” si scontra contro “attacked”

attack attacker attacked moves1 
  = case attacker == attacked of
      True
        -> moves1
           -- non fa niente e torna le mosse di prima
      False
        -> case combatant_isDead attacker of
             True
               -> (DeadMove attacker):moves1
                  -- un giocatore morto non puo` attaccare
             False
                -> case (mod tick (combatant_attackSpeed attacker) == 0) of
                    False
                      -> moves1
                         -- non e` ancora il turno di attacker e non puo` fare mosse
                    True
                      -> case combatant_isDead attacked of
                           True -> moves1
                                   -- un giocatore morto non puo` essere attaccato
                           False -> (AttackMove (combatant_name attacker) 
                                                (combatant_name attacked)
                                    ):moves1
                                   -- aggiunge una mossa di attacco alla lista di mosse fatte

In questo caso il codice funzionale ricorda un po’ quelle dimostrazioni matematiche in cui si analizzano le diverse path possibili, e terminano con un “come volevasi dimostrare”. E` elegante, ma di certo non posso segnare nessun gol a favore, dato che sono semplici azioni di contenimento a centro campo.

In Haskell si applica una mossa all’arena in questo modo:

arena_move :: Arena -> Move -> Arena 

cioe` si definisce una funzione che accetta un’arena iniziale, la mossa da applicare, e torna la nuova arena con la mossa applicata. Siccome non si puo’ cambiare il valore di una variabile, non si puo` che tornare un nuovo ogetto.

Perlomeno l’applicazione di piu` mosse ad una arena e` compatta come codice:

arena_moves :: Arena -> [Move] -> Arena
arena_moves arena1 moves = foldl' arena_move arena1 moves

e iniziamo a capire perche` le funzioni si compongono velocemente in Haskell. La “foldl” applica iterativamente una funzione di move all’arena precedente, passando come parametro la move successiva presa nella lista di mosse da applicare. Nel caso di due giocatori vive sono solo 2 mosse, o nessuna se sono tutti e due morti o non e` il loro turno, o piu` mosse se si stanno scontrando diversi giocatori.

I parser in Haskell si scrivono velocemente, dato che una delle librerie di Haskell (Parsec) definisce un interprete di un Domain Specific Language, adatto per descrivere i parser. E` tipico in Haskell descrivere computazioni complesse, usando una notazione ad-hoc (Domain Specific Language) interpretata da una libreria. Quindi Haskell da un linguaggio verboso, diventa un linguaggio compatto e ad-hoc per risolvere il problema di turno. Quello che in un linguaggio OO diventa una pattern (una serie di ogetti organizzati in un modo ricorrente), in Haskell diventa un interprete, e una rappresentazione piu` compatta del codice, usando un Domain Specific Language. Poi nella realta` gli interpreti hanno i loro limiti: meglio un aproccio a compilatore, dove e` possibile fare una pre-analisi del tutto, per esempio e` difficile in Haskell dire se la grammatica contiene errori di definizione o ricorsioni infinite, cose che invece riesce a fare un parser compilato con Bison/YACC & C.

parseLine :: Parser Combatant
parseLine = do 
   cn <- parseString
   sep
   speed <- parseInt
   sep
   health <- parseInt
   sep
   wn <- parseString
   sep
   wStrenght <- parseWeaponStrenght

   let w = Weapon {
             weapon_name = wn
           , weapon_strength = wStrenght
           }

   let c = Combatant {
             combatant_name = cn
           , combatant_attackSpeed = speed
           , combatant_health = health
           , combatant_weapon = w
           , combatant_order = 0          
           }

   return c

Qua` e` chiaramente un gol a favore di Haskell, ma peccato che sia stato annullato dall’arbitro per fuori gioco, dato che il team GO ha saggiamente optato per usare direttamente una libreria CSV del linguaggio, invece che scriversi il proprio parser.

Questo e` il codice per fare il parsing delle armi con potenza casuale all’interno di un range

parseWeaponStrenght :: Parser (Int, Int)
parseWeaponStrenght
  = P.try (do strictSpace
              P.char '('
              strictSpace
              i1 <- parseInt
              P.char ','
              strictSpace
              i2 <- parseInt
              P.char ')'
              strictSpace
            return (i1, i2)) <|> (do i <- parseInt
                                     return (i, i))

riconosce stringhe come “1” oppure “(1, 2)”.

Il simbolo “<|>” usato nel codice sopra dice: se il primo parser non e` riuscito a fare match, allora prova con questa alternativa. Il tutto codificato come libreria del linguaggio, non come built-in. Notevole direi. Quindi 1-1.

Ma purtroppo questo e` stato l’ultimo goal del team Haskell e segnato al limite del tempo regolamentare, quando oramai il team GO aveva dilagato segnando diversi goal e completando tutti gli esercizi del incremental Kata, e per di piu` giocando fuori casa, in completa discomfort zone, con un linguaggio nuovo sotto tutti i punti di vista. Praticamente la squadra primavera del GO ha annientato il blasonato ma viziato team Haskell.

L’allenatore del team Haskell ha provato a protestare affermando che il codice di Haskell supportava RPG fra 3 o piu` giocatori, e non solo 2, ma l’arbitro ha fatto notare che mai nel corso del Incremental Kata era stato chiesto questo e ha quindi ammonito il team Haskell, per mancanza di rispetto delle regole del TDD.

Il mio giudizio su Haskell: il linguaggio del futuro sara` un linguaggio con molte idee prese dai linguaggi funzionali (come gia` accade, vedi i Nullable types), una semantica/sintassi piu` ad oggetti tipo “subject.property” e la gestione dei domain-specific-languages attraveros una fase di analisi/compilazione, invece che di interpretazione. Quindi non sara` Haskell cosi` come e` ora a mio parere. Pero` molte delle idee dei linguaggi funzionali stanno diventando main-stream nei linguaggi imperativi tradizionali.

Ma voglio terminare “fantasticando” su cosa poteva fare l’accoppiata Roberto / Alessandro / Python: una manciata di chiamate di libreria, due piccole funzioni come collante e il pitone con le batterie termonucleari incluse, avrebbe strangolato nella sua morsa il povero Haskell ancor prima del fischio di inizio :-)

Posted in code kata | 3 Comments