Loïc d'Anterroches
Novembre 2011
Tout ce que Node.JS, RoR, Django, la dernière techno à la mode peuvent faire, PHP le peut aussi.
Donc ne succombez pas aux cris des sirènes.
Les polymères de l'Aeron sont produits par DSM.
Besoin de propriétés comme la pression de vapeur, densité, capacité thermique, enthalpy de formation, la viscosité, le point de fusion etc.
Recherche : serveur Nginx, PHP-fastcgi avec un framework léger, MongoDB..
Science : Python, OpenBabel, R et même du Fortran pour prédire les propriétés de nouvelles molécules.
Problème
Passage du desktop au client web, calcul immédiat au batch... pas possible.
Chercher une solution...
Et toujours au niveau serveur
Comment mettre à disposition facilement un service codé en Python à mon application PHP sans faire un fork ?
Buzzword: Realtime
import zmq
import science
context = zmq.Context()
socket = context.socket(zmq.REP)
socket.bind("tcp://localhost:5560")
while True:
message = socket.recv()
result = science.predict(message)
socket.send(result)
C'est du Python, c'est un serveur robuste, c'est rapide comme pas deux.
<?php
$context = new ZMQContext();
// Socket pour se connecter au serveur
$requester = new ZMQSocket($context, ZMQ::SOCKET_REQ);
$requester->connect("tcp://localhost:5560");
$requester->send("Nc1ccccc1O");
printf("La réponse est : %s.", $requester->recv());
C'est du PHP, c'est aussi robuste et rapide comme pas deux.
Un serveur, un client, deux langages, haute performance et asynchrone.
Le squelette de votre système. Les primitives de ØMQ sont très bien pensées.
$context = new ZMQContext();
$sub = new ZMQSocket($context, ZMQ::SOCKET_SUB);
$sub->setSockOpt(ZMQ::SOCKOPT_SUBSCRIBE, 'CHOCOLAT');
$sub->connect("tcp://localhost:5560");
while (true) {
printf("Reçu : %s.", $sub->recv());
}
$context = new ZMQContext();
$sub = new ZMQSocket($context, ZMQ::SOCKET_PUB);
$sub->bind("tcp://localhost:5560");
$quoi = array('CHOCOLAT', 'FRAISE', 'BANANE');
for ($i=0; $i<100000; $i++) {
$sub->send(array_rand($quoi) . ' ' . microtime()); usleep(1000);
}
Pour PUSH/PULL pas d'abonnement côté client.
Le lien entre les éléments.
Un blob c'est du JSON, BSON, un jpg, de l'utf-8 ou pas.
Les enveloppes pour les abonnements (SUB) ou rassembler des messages en un gros message (multipart).
Rappel du problème
Mongrel2 peut faire du PULL/PUSH, pour différents protocoles — HTTP, JsSocket, Websocket, XML — sur le même port, sur un unique serveur.
Buzz words: asynchrone, coroutines, event driven, ZeroMQ, performance, you name it.
from mongrel2 import handler
import json
import science
from uuid import uuid4
conn = handler.Connection(uuid4().hex,
"tcp://127.0.0.1:9997",
"tcp://127.0.0.1:9996")
while True:
req = conn.recv()
if req.is_disconnect():
continue
result = science.predict(req.body)
conn.reply_http(req, result)
Le même service fournit à un client web. science peut lui même être livré via ØMQ... le marteau.
Le génie de ce design : toutes les étapes sont asynchrones, seule la connexion avec le client ne l'est pas.
Requête : UUID ID PATH SIZE:HEADERS,SIZE:BODY,
34f9ceee-cd52-4b7f-b197-88bf2f0ec378 # Mongrel2 id
6 # Client id
/path/foo # HTTP Path
422:{"PATH":"/path/foo","PATTERN":"/path","user-agent":"curl/7.19.7..."},\
7:"HELLO" # PayloadRéponse : UUID SIZE:ID ID ID, BODY
34f9ceee-cd52-4b7f-b197-88bf2f0ec378 # Mongrel2 id 3:6 9, # Clients id <html ...
Une réponse, plusieurs clients
$_XXXPHP plus rapide que PHP : Hello World! plus rapide avec Photon qu'avec Apache/mod_php
Toute votre infrastructure peut communiquer de manière simple et homogène.
class MailSender extends \photon\task\AsyncTask
{
public $name = 'mailsender';
public function work($socket)
{
list($taskname, $client, $payload) = explode(' ', $socket->recv(), 3);
$payload = json_decode($payload);
$emails = $payload['emails'];
$subject = $payload['subject'];
$body = $payload['body'];
$headers = 'From: ' . Conf::f('from_email') . "\r\n" .
'X-Mailer: Photon/' . \photon\VERSION;
foreach ($payload['emails'] as $to) {
mail($to, $payload['subject'], $payload['body'], $headers);
}
}
}Réception en PULL d'un travail émis en asychrone.
class MyViews
{
public function updateTicket($request, $match)
{
// Do a lot of work to update the ticket
$payload = array(
'emails' => array('foo1@example.com',
'foo2@example.com',
'foo3@example.com',
'foo4@example.com'),
'subject' => 'Ticket 123 has been updated.',
'body' => 'Hello, Photon is great and you too.');
$runner = new \photon\task\Runner();
$runner->run('mailsender', $payload);
return new \photon\http\Response('Ticket updated!', 'text/plain');
}
}
Le run() sans avoir besoin d'une queue pour les emails en sortie. Tout se fait dans le thread I/O de ØMQ.
$context = new ZMQContext($io_threads=1, $is_persistent=true); $sub = new ZMQSocket($context, ZMQ::SOCKET_PUB);
$context par process
ga.js
Système complexe mais simple à développer puis déployer
Mais les signaux vont dans le bon sens...
Page statique, le livre d'or modifiait une page statique.
Framework, base de données, beaucoup de "boilerplate" mais pourquoi ?
Il y a dedans et dehors
ØMQ's error handling philosophy is a mix of fail-fast and resilience. Processes, we believe, should be as vulnerable as possible to internal errors, and as robust as possible against external attacks and errors.
Simplement lancer de nouveaux handlers
$ php monprojet.phar serve
les .phar c'est chouette