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

Prossimo incontro: sabato 18 Luglio

In vista del periodo di ferie anticiperemo il nostro prossimo incontro a sabato 18 luglio, ore 14.00.

Ci incontreremo all’ ImpactHUB di Reggio Emilia.

Via dello Statuto 3,
42121 Reggio Emilia

Se il caldo non ci scioglie tutti i neuroni, faremo kata incrementali in TDD e chiacchiere sulle ultime tecnologie.

Non mancate!

Posted in Eventi | Tagged , | Leave a comment

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