HTTP://LOCALHOST
PALESTRA INTERATIVA!
SMARTPHONE / TABLET / NOTEBOOK
AUMENTE O VOLUME!
ASYNC E WEBSOCKETS COM PHP
MUITO PRAZER, PODE ME CHAMAR DE BOB!
Evangelista PHP - VIVA PHP SP CAMPINAS! VIVA PHP!
Já fiz mais de 100 sites! o/
Trabalho na Memed, fazendo uma plataforma de prescrição
médica
Sou fução desde quando nasci...
GOSTO DO PHP PORQUE
NÓS SOMOS PIRATAS!
Num belo dia, precisei fazer algo assim:
Buscando usuários:
//Vou querer X usuários que encaixam nessa busca,
//batata grande e uma coca-cola
$usuarios = Usuarios::where('nome', $q)->get();
//Processa a resposta
$usuarios = $this->processarResposta($usuarios);
//Vai que é tua View!
return view('resultado', ['usuarios' => $usuarios]);
Você faz isso?
Sim (0)
Não (0)
$usuarios = Usuarios::where('nome', $q)->get();
//Só será executado daqui para frente após
//a resposta do banco de dados (IO) chegar
$usuarios = $this->processarResposta($usuarios);
//Aqui após um processo intensivo de CPU
return view('resultado', ['usuarios' => $usuarios]);
BLOCKING IO
PRECISO SER MAIS RÁPIDO!
Vou processar metade dos usuários em paralelo com a outra metade
NODE.JS
Usuarios.find({
where: {nome: q},
limit: 5000
}).then(function(usuarios) {
//Só executa aqui quando chegar o resultado
processarResultado(usuarios);
});
Usuarios.find({
where: {nome: q},
offset: 5000,
limit: 5000
}).then(function(usuarios) {
//Só executa aqui quando chegar o resultado
processarResultado(usuarios);
});
E NO PHP?
Você sabe como resolver isso?
Sim (0)
Não (0)
THREADING
FORK
O que um fork faz?
Cria novo processo, do zero! (0)
Cria um clone e copia a memória (0)
Cria um clone e usa a mesma memória (0)
$pid = pcntl_fork();
if ($pid == -1) {
die('could not fork =(');
} else if ($pid) {
// I'm your father!
DB::reconnect('mysql');
$users = $this->getUsers($totalUsers / 2, 0);
//Aguarda o processo filho terminar
pcntl_wait($status);
//Faz um merge do que o processo pai e filho processaram
//Os dados do processo filho estão no cache
$users = array_merge($users, Cache::pull($cacheKey));
}
} else {
// we are the child
DB::reconnect('mysql');
$users = $this->getUsers($totalUsers / 2, $totalUsers / 2);
//Armazena os usuários processados no cache
Cache::forever($cacheKey, $users);
die();
}
BUSCANDO 10.000 USUÁRIOS (COM ORM)
FORMA TRADICIONAL: 3063,82MS
FORK: 2029,68MS
O FORK TEM UM PROBLEMA:
NÃO FUNCIONA QUANDO O PHP É EXECUTADO
ATRAVÉS DO APACHE E DO NGINX
NON BLOCKING IO
//cria o main loop
$loop = ReactEventLoopFactory::create();
//cria a conexão com o MySQL
$connection = new ReactMySQLConnection($loop, array(
'dbname' => $_ENV['DB_DATABASE'],
'user' => $_ENV['DB_USERNAME'],
'passwd' => $_ENV['DB_PASSWORD'],
));
$connection->connect(function () {});
$connection->query($query, function ($command, $conn) use ($loop) {
if ($command->hasError()) {
$error = $command->getError();
} else {
$results = $command->resultRows;
$this->users = array_merge($this->users, $this->processUsers($results));
$this->queries--;
if ($this->queries == 0) {
$loop->stop();
}
}
});
Quem ganhou?
Forma tradicional (0)
React (0)
BUSCANDO 10.000 USUÁRIOS (SEM ORM)
FORMA TRADICIONAL: 659,02MS
REACT MYSQL: 1123,89MS
POR QUE COM REACT NÃO FOI MELHOR?
IO ­ CPU ­ CPU
REACT/MYSQL V0.2.0
CALMA, O REACT NOS DEU SUPER PODERES!
EXEMPLO DE SERVIDOR
$loop = ReactEventLoopFactory::create();
$socket = new ReactSocketServer($loop);
$socket->on('connection', function ($conn) use ($totalUsers) {
echo 'Enviando mensagem...' . PHP_EOL;
$users = $this->getUsers($totalUsers / 2, $totalUsers / 2);
$conn->end(serialize($users));
echo 'Enviada' . PHP_EOL;
});
$socket->listen(1337);
$loop->run();
EXEMPLO DE CLIENTE
$loop = ReactEventLoopFactory::create();
$dnsResolverFactory = new ReactDnsResolverFactory();
$dns = $dnsResolverFactory->createCached('8.8.8.8', $loop);
$connector = new ReactSocketClientConnector($loop, $dns);
$connector->create('127.0.0.1', 1337)->then(function (ReactStreamStream $stream) use
$stream->on('data', function ($data, $stream) use (&$buffer) {
$buffer .= $data;
});
});
$loop->run();
Quem ganhou?
Forma tradicional (0)
React (0)
BUSCANDO 10.000 USUÁRIOS (COM ORM)
FORMA TRADICIONAL: 3063,82MS
FORK: 2029,68MS
REACT SOCKET: 1910MS
EU QUERO MAIS!
RFC 6455 ­ DEZEMBRO DE 2011
Em 12 de Setembro de 2011 o Google Chrome 15 já implementava
The WebSocket Protocol enables two-way communication between a
client running untrusted code in a controlled environment to a remote
host that has opted-in to communications from that code.
Já usou Websockets?
Sim (0)
Não (0)
VANTAGEM DE USAR WEBSOCKETS:
O servidor websocket pode ficar na mesma porta que o HTTP padrão, ele
usa campos do cabeçalho HTTP distintos
Suporta um servidor rodando múltiplos domínios
Suporta proxies HTTP
Só envia cabeçalhos HTTP no ato da conexão, depois é dado puro (em
UTF-8 ou binário)
SOCKET.IO
BIBLIOTECA PARA NODEJS DE SERVIDOR DE
WEBSOCKETS
Funciona bem, mas não é mais Websocket puro, vai muito além!
SERVIDOR
$controller = new Controller();
$server = IoServer::factory(
new HttpServer(
new WsServer(
$controller
)
),
777
);
$loop = $server->loop;
$loop->addPeriodicTimer(5, function () use ($controller) {
$controller->sendCounterMessage();
});
$server->run();
use RatchetMessageComponentInterface;
use RatchetConnectionInterface;
class App implements MessageComponentInterface
{
public static $connections;
public static $lastMessage;
public function __construct()
{
self::$connections = new SplObjectStorage;
}
public function onOpen(ConnectionInterface $connection)
{
self::$connections->attach($connection);
if (isset(App::$lastMessage)) {
Sender::send(App::$lastMessage, $connection);
}
Log::d('Espectador conectado: ' . $connection->resourceId);
}
public function onMessage(ConnectionInterface $connection, $message)
{
App::$lastMessage = $message;
foreach (self::$connections as $anotherConnection) {
if ($anotherConnection !== $connection) {
Sender::send($message, $anotherConnection);
}
}
Log::d($message);
}
PADRONIZE SEUS OBJETOS
class HornMessage extends Message
{
protected $type = 'horn';
//getters and setters
}
class SlideMessage extends Message
{
protected $type = 'slide';
protected $indexh;
protected $indexv;
protected $indexf;
//getters and setters
}
ESCALABILIDADE
SEU SERVIDOR WEBSOCKET SOMENTE CUIDA DOS
WEBSOCKETS
TCHAU PESSOAL, ATÉ A PRÓXIMA!
Avalie minha palestra! CLIQUE AQUI
@gabrielrcouto
PHPSP
PHPSP CAMPINAS
Essa palestra está no GitHub
0 / 0
Buzinar

React e Ratchet, async e websockets com PHP, por Gabriel Couto