You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

607 lines
15 KiB

4 years ago
# Workerman
[![Gitter](https://badges.gitter.im/walkor/Workerman.svg)](https://gitter.im/walkor/Workerman?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=body_badge)
[![Latest Stable Version](https://poser.pugx.org/workerman/workerman/v/stable)](https://packagist.org/packages/workerman/workerman)
[![Total Downloads](https://poser.pugx.org/workerman/workerman/downloads)](https://packagist.org/packages/workerman/workerman)
[![Monthly Downloads](https://poser.pugx.org/workerman/workerman/d/monthly)](https://packagist.org/packages/workerman/workerman)
[![Daily Downloads](https://poser.pugx.org/workerman/workerman/d/daily)](https://packagist.org/packages/workerman/workerman)
[![License](https://poser.pugx.org/workerman/workerman/license)](https://packagist.org/packages/workerman/workerman)
## What is it
Workerman is an asynchronous event-driven PHP framework with high performance to build fast and scalable network applications.
Workerman supports HTTP, Websocket, SSL and other custom protocols.
Workerman supports libevent/event extension, [HHVM](https://github.com/facebook/hhvm) , [ReactPHP](https://github.com/reactphp/react).
## Requires
PHP 5.3 or Higher
A POSIX compatible operating system (Linux, OSX, BSD)
POSIX and PCNTL extensions required
Event extension recommended for better performance
## Installation
```
composer require workerman/workerman
```
## Basic Usage
### A websocket server
```php
<?php
require_once __DIR__ . '/vendor/autoload.php';
use Workerman\Worker;
// Create a Websocket server
$ws_worker = new Worker("websocket://0.0.0.0:2346");
// 4 processes
$ws_worker->count = 4;
// Emitted when new connection come
$ws_worker->onConnect = function($connection)
{
echo "New connection\n";
};
// Emitted when data received
$ws_worker->onMessage = function($connection, $data)
{
// Send hello $data
$connection->send('hello ' . $data);
};
// Emitted when connection closed
$ws_worker->onClose = function($connection)
{
echo "Connection closed\n";
};
// Run worker
Worker::runAll();
```
### An http server
```php
require_once __DIR__ . '/vendor/autoload.php';
use Workerman\Worker;
// #### http worker ####
$http_worker = new Worker("http://0.0.0.0:2345");
// 4 processes
$http_worker->count = 4;
// Emitted when data received
$http_worker->onMessage = function($connection, $data)
{
// $_GET, $_POST, $_COOKIE, $_SESSION, $_SERVER, $_FILES are available
var_dump($_GET, $_POST, $_COOKIE, $_SESSION, $_SERVER, $_FILES);
// send data to client
$connection->send("hello world \n");
};
// run all workers
Worker::runAll();
```
### A WebServer
```php
require_once __DIR__ . '/vendor/autoload.php';
use Workerman\WebServer;
use Workerman\Worker;
// WebServer
$web = new WebServer("http://0.0.0.0:80");
// 4 processes
$web->count = 4;
// Set the root of domains
$web->addRoot('www.your_domain.com', '/your/path/Web');
$web->addRoot('www.another_domain.com', '/another/path/Web');
// run all workers
Worker::runAll();
```
### A tcp server
```php
require_once __DIR__ . '/vendor/autoload.php';
use Workerman\Worker;
// #### create socket and listen 1234 port ####
$tcp_worker = new Worker("tcp://0.0.0.0:1234");
// 4 processes
$tcp_worker->count = 4;
// Emitted when new connection come
$tcp_worker->onConnect = function($connection)
{
echo "New Connection\n";
};
// Emitted when data received
$tcp_worker->onMessage = function($connection, $data)
{
// send data to client
$connection->send("hello $data \n");
};
// Emitted when new connection come
$tcp_worker->onClose = function($connection)
{
echo "Connection closed\n";
};
Worker::runAll();
```
### Enable SSL
```php
<?php
require_once __DIR__ . '/vendor/autoload.php';
use Workerman\Worker;
// SSL context.
$context = array(
'ssl' => array(
'local_cert' => '/your/path/of/server.pem',
'local_pk' => '/your/path/of/server.key',
'verify_peer' => false,
)
);
// Create a Websocket server with ssl context.
$ws_worker = new Worker("websocket://0.0.0.0:2346", $context);
// Enable SSL. WebSocket+SSL means that Secure WebSocket (wss://).
// The similar approaches for Https etc.
$ws_worker->transport = 'ssl';
$ws_worker->onMessage = function($connection, $data)
{
// Send hello $data
$connection->send('hello ' . $data);
};
Worker::runAll();
```
### Custom protocol
Protocols/MyTextProtocol.php
```php
namespace Protocols;
/**
* User defined protocol
* Format Text+"\n"
*/
class MyTextProtocol
{
public static function input($recv_buffer)
{
// Find the position of the first occurrence of "\n"
$pos = strpos($recv_buffer, "\n");
// Not a complete package. Return 0 because the length of package can not be calculated
if($pos === false)
{
return 0;
}
// Return length of the package
return $pos+1;
}
public static function decode($recv_buffer)
{
return trim($recv_buffer);
}
public static function encode($data)
{
return $data."\n";
}
}
```
```php
require_once __DIR__ . '/vendor/autoload.php';
use Workerman\Worker;
// #### MyTextProtocol worker ####
$text_worker = new Worker("MyTextProtocol://0.0.0.0:5678");
$text_worker->onConnect = function($connection)
{
echo "New connection\n";
};
$text_worker->onMessage = function($connection, $data)
{
// send data to client
$connection->send("hello world \n");
};
$text_worker->onClose = function($connection)
{
echo "Connection closed\n";
};
// run all workers
Worker::runAll();
```
### Timer
```php
require_once __DIR__ . '/vendor/autoload.php';
use Workerman\Worker;
use Workerman\Lib\Timer;
$task = new Worker();
$task->onWorkerStart = function($task)
{
// 2.5 seconds
$time_interval = 2.5;
$timer_id = Timer::add($time_interval,
function()
{
echo "Timer run\n";
}
);
};
// run all workers
Worker::runAll();
```
### AsyncTcpConnection (tcp/ws/text/frame etc...)
```php
require_once __DIR__ . '/vendor/autoload.php';
use Workerman\Worker;
use Workerman\Connection\AsyncTcpConnection;
$worker = new Worker();
$worker->onWorkerStart = function()
{
// Websocket protocol for client.
$ws_connection = new AsyncTcpConnection("ws://echo.websocket.org:80");
$ws_connection->onConnect = function($connection){
$connection->send('hello');
};
$ws_connection->onMessage = function($connection, $data){
echo "recv: $data\n";
};
$ws_connection->onError = function($connection, $code, $msg){
echo "error: $msg\n";
};
$ws_connection->onClose = function($connection){
echo "connection closed\n";
};
$ws_connection->connect();
};
Worker::runAll();
```
### Async Mysql of ReactPHP
```
composer require react/mysql
```
```php
<?php
require_once __DIR__ . '/vendor/autoload.php';
use Workerman\Worker;
$worker = new Worker('tcp://0.0.0.0:6161');
$worker->onWorkerStart = function() {
global $mysql;
$loop = Worker::getEventLoop();
$mysql = new React\MySQL\Connection($loop, array(
'host' => '127.0.0.1',
'dbname' => 'dbname',
'user' => 'user',
'passwd' => 'passwd',
));
$mysql->on('error', function($e){
echo $e;
});
$mysql->connect(function ($e) {
if($e) {
echo $e;
} else {
echo "connect success\n";
}
});
};
$worker->onMessage = function($connection, $data) {
global $mysql;
$mysql->query('show databases' /*trim($data)*/, function ($command, $mysql) use ($connection) {
if ($command->hasError()) {
$error = $command->getError();
} else {
$results = $command->resultRows;
$fields = $command->resultFields;
$connection->send(json_encode($results));
}
});
};
Worker::runAll();
```
### Async Redis of ReactPHP
```
composer require clue/redis-react
```
```php
<?php
require_once __DIR__ . '/vendor/autoload.php';
use Clue\React\Redis\Factory;
use Clue\React\Redis\Client;
use Workerman\Worker;
$worker = new Worker('tcp://0.0.0.0:6161');
$worker->onWorkerStart = function() {
global $factory;
$loop = Worker::getEventLoop();
$factory = new Factory($loop);
};
$worker->onMessage = function($connection, $data) {
global $factory;
$factory->createClient('localhost:6379')->then(function (Client $client) use ($connection) {
$client->set('greeting', 'Hello world');
$client->append('greeting', '!');
$client->get('greeting')->then(function ($greeting) use ($connection){
// Hello world!
echo $greeting . PHP_EOL;
$connection->send($greeting);
});
$client->incr('invocation')->then(function ($n) use ($connection){
echo 'This is invocation #' . $n . PHP_EOL;
$connection->send($n);
});
});
};
Worker::runAll();
```
### Aysnc dns of ReactPHP
```
composer require react/dns
```
```php
require_once __DIR__ . '/vendor/autoload.php';
use Workerman\Worker;
$worker = new Worker('tcp://0.0.0.0:6161');
$worker->onWorkerStart = function() {
global $dns;
// Get event-loop.
$loop = Worker::getEventLoop();
$factory = new React\Dns\Resolver\Factory();
$dns = $factory->create('8.8.8.8', $loop);
};
$worker->onMessage = function($connection, $host) {
global $dns;
$host = trim($host);
$dns->resolve($host)->then(function($ip) use($host, $connection) {
$connection->send("$host: $ip");
},function($e) use($host, $connection){
$connection->send("$host: {$e->getMessage()}");
});
};
Worker::runAll();
```
### Http client of ReactPHP
```
composer require react/http-client
```
```php
<?php
require_once __DIR__ . '/vendor/autoload.php';
use Workerman\Worker;
$worker = new Worker('tcp://0.0.0.0:6161');
$worker->onMessage = function($connection, $host) {
$loop = Worker::getEventLoop();
$client = new \React\HttpClient\Client($loop);
$request = $client->request('GET', trim($host));
$request->on('error', function(Exception $e) use ($connection) {
$connection->send($e);
});
$request->on('response', function ($response) use ($connection) {
$response->on('data', function ($data) use ($connection) {
$connection->send($data);
});
});
$request->end();
};
Worker::runAll();
```
### ZMQ of ReactPHP
```
composer require react/zmq
```
```php
<?php
require_once __DIR__ . '/vendor/autoload.php';
use Workerman\Worker;
$worker = new Worker('text://0.0.0.0:6161');
$worker->onWorkerStart = function() {
global $pull;
$loop = Worker::getEventLoop();
$context = new React\ZMQ\Context($loop);
$pull = $context->getSocket(ZMQ::SOCKET_PULL);
$pull->bind('tcp://127.0.0.1:5555');
$pull->on('error', function ($e) {
var_dump($e->getMessage());
});
$pull->on('message', function ($msg) {
echo "Received: $msg\n";
});
};
Worker::runAll();
```
### STOMP of ReactPHP
```
composer require react/stomp
```
```php
<?php
require_once __DIR__ . '/vendor/autoload.php';
use Workerman\Worker;
$worker = new Worker('text://0.0.0.0:6161');
$worker->onWorkerStart = function() {
global $client;
$loop = Worker::getEventLoop();
$factory = new React\Stomp\Factory($loop);
$client = $factory->createClient(array('vhost' => '/', 'login' => 'guest', 'passcode' => 'guest'));
$client
->connect()
->then(function ($client) use ($loop) {
$client->subscribe('/topic/foo', function ($frame) {
echo "Message received: {$frame->body}\n";
});
});
};
Worker::runAll();
```
## Available commands
```php start.php start ```
```php start.php start -d ```
![workerman start](http://www.workerman.net/img/workerman-start.png)
```php start.php status ```
![workerman satus](http://www.workerman.net/img/workerman-status.png?a=123)
```php start.php connections```
```php start.php stop ```
```php start.php restart ```
```php start.php reload ```
## Documentation
中文主页:[http://www.workerman.net](http://www.workerman.net)
中文文档: [http://doc.workerman.net](http://doc.workerman.net)
Documentation:[https://github.com/walkor/workerman-manual](https://github.com/walkor/workerman-manual/blob/master/english/src/SUMMARY.md)
# Benchmarks
```
CPU: Intel(R) Core(TM) i3-3220 CPU @ 3.30GHz and 4 processors totally
Memory: 8G
OS: Ubuntu 14.04 LTS
Software: ab
PHP: 5.5.9
```
**Codes**
```php
<?php
use Workerman\Worker;
$worker = new Worker('tcp://0.0.0.0:1234');
$worker->count=3;
$worker->onMessage = function($connection, $data)
{
$connection->send("HTTP/1.1 200 OK\r\nConnection: keep-alive\r\nServer: workerman\r\nContent-Length: 5\r\n\r\nhello");
};
Worker::runAll();
```
**Result**
```shell
ab -n1000000 -c100 -k http://127.0.0.1:1234/
This is ApacheBench, Version 2.3 <$Revision: 1528965 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/
Benchmarking 127.0.0.1 (be patient)
Completed 100000 requests
Completed 200000 requests
Completed 300000 requests
Completed 400000 requests
Completed 500000 requests
Completed 600000 requests
Completed 700000 requests
Completed 800000 requests
Completed 900000 requests
Completed 1000000 requests
Finished 1000000 requests
Server Software: workerman/3.1.4
Server Hostname: 127.0.0.1
Server Port: 1234
Document Path: /
Document Length: 5 bytes
Concurrency Level: 100
Time taken for tests: 7.240 seconds
Complete requests: 1000000
Failed requests: 0
Keep-Alive requests: 1000000
Total transferred: 73000000 bytes
HTML transferred: 5000000 bytes
Requests per second: 138124.14 [#/sec] (mean)
Time per request: 0.724 [ms] (mean)
Time per request: 0.007 [ms] (mean, across all concurrent requests)
Transfer rate: 9846.74 [Kbytes/sec] received
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 0 0.0 0 5
Processing: 0 1 0.2 1 9
Waiting: 0 1 0.2 1 9
Total: 0 1 0.2 1 9
Percentage of the requests served within a certain time (ms)
50% 1
66% 1
75% 1
80% 1
90% 1
95% 1
98% 1
99% 1
100% 9 (longest request)
```
## Other links with workerman
[PHPSocket.IO](https://github.com/walkor/phpsocket.io)
[php-socks5](https://github.com/walkor/php-socks5)
[php-http-proxy](https://github.com/walkor/php-http-proxy)
## Donate
<a href="https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=UQGGS9UB35WWG"><img src="http://donate.workerman.net/img/donate.png"></a>
## LICENSE
Workerman is released under the [MIT license](https://github.com/walkor/workerman/blob/master/MIT-LICENSE.txt).