workerman README

The project's README from github.com/walkor/workerman. As one markdown file: README.md.

Workerman

Gitter Latest Stable Version Total Downloads Monthly Downloads Daily Downloads License

What is it

Workerman is an asynchronous event-driven PHP framework with high performance to build fast and scalable network applications. It supports HTTP, WebSocket, custom protocols, coroutines, and connection pools, making it ideal for handling high-concurrency scenarios efficiently.

Requires

A POSIX compatible operating system (Linux, OSX, BSD)
POSIX and PCNTL extensions required
Event/Swoole/Swow extension recommended for better performance

Installation

composer require workerman/workerman

Documentation

https://manual.workerman.net

Basic Usage

A websocket server

<?php

use Workerman\Worker;

require_once __DIR__ . '/vendor/autoload.php';

// Create a Websocket server
$ws_worker = new Worker('websocket://0.0.0.0:2346');

// 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

use Workerman\Worker;

require_once __DIR__ . '/vendor/autoload.php';

// #### 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, $request) {
    //$request->get();
    //$request->post();
    //$request->header();
    //$request->cookie();
    //$request->session();
    //$request->uri();
    //$request->path();
    //$request->method();

    // Send data to client
    $connection->send("Hello World");
};

// Run all workers
Worker::runAll();

A tcp server

use Workerman\Worker;

require_once __DIR__ . '/vendor/autoload.php';

// #### 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 connection is closed
$tcp_worker->onClose = function ($connection) {
    echo "Connection closed\n";
};

Worker::runAll();

Enable SSL

<?php

use Workerman\Worker;

require_once __DIR__ . '/vendor/autoload.php';

// SSL context.
$context = [
    'ssl' => [
        '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();

AsyncTcpConnection (tcp/ws/text/frame etc...)


use Workerman\Worker;
use Workerman\Connection\AsyncTcpConnection;

require_once __DIR__ . '/vendor/autoload.php';

$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();

Coroutine

Coroutine is used to create coroutines, enabling the execution of asynchronous tasks to improve concurrency performance.

<?php
use Workerman\Connection\TcpConnection;
use Workerman\Coroutine;
use Workerman\Events\Swoole;
use Workerman\Protocols\Http\Request;
use Workerman\Worker;
require_once __DIR__ . '/vendor/autoload.php';

$worker = new Worker('http://0.0.0.0:8001');

$worker->eventLoop = Swoole::class; // Or Swow::class or Fiber::class

$worker->onMessage = function (TcpConnection $connection, Request $request) {
    Coroutine::create(function () {
        echo file_get_contents("http://www.example.com/event/notify");
    });
    $connection->send('ok');
};

Worker::runAll();

Note: Coroutine require Swoole extension or Swow extension or Fiber revolt/event-loop, and the same applies below

Barrier

Barrier is used to manage concurrency and synchronization in coroutines. It allows tasks to run concurrently and waits until all tasks are completed, ensuring process synchronization.

<?php
use Workerman\Connection\TcpConnection;
use Workerman\Coroutine\Barrier;
use Workerman\Coroutine;
use Workerman\Events\Swoole;
use Workerman\Protocols\Http\Request;
use Workerman\Worker;
require_once __DIR__ . '/vendor/autoload.php';

// Http Server
$worker = new Worker('http://0.0.0.0:8001');
$worker->eventLoop = Swoole::class; // Or Swow::class or Fiber::class
$worker->onMessage = function (TcpConnection $connection, Request $request) {
    $barrier = Barrier::create();
    for ($i=1; $i<5; $i++) {
        Coroutine::create(function () use ($barrier, $i) {
            file_get_contents("http://127.0.0.1:8002?task_id=$i");
        });
    }
    // Wait all coroutine done
    Barrier::wait($barrier);
    $connection->send('All Task Done');
};

// Task Server
$task = new Worker('http://0.0.0.0:8002');
$task->onMessage = function (TcpConnection $connection, Request $request) {
    $task_id = $request->get('task_id');
    $message = "Task $task_id Done";
    echo $message . PHP_EOL;
    $connection->close($message);
};

Worker::runAll();

Parallel

Parallel executes multiple tasks concurrently and collects results. Use add to add tasks and wait to wait for completion and get results. Unlike Barrier, Parallel directly returns the results of each task.

<?php
use Workerman\Connection\TcpConnection;
use Workerman\Coroutine\Parallel;
use Workerman\Events\Swoole;
use Workerman\Protocols\Http\Request;
use Workerman\Worker;
require_once __DIR__ . '/vendor/autoload.php';

// Http Server
$worker = new Worker('http://0.0.0.0:8001');
$worker->eventLoop = Swoole::class; // Or Swow::class or Fiber::class
$worker->onMessage = function (TcpConnection $connection, Request $request) {
    $parallel = new Parallel();
    for ($i=1; $i<5; $i++) {
        $parallel->add(function () use ($i) {
            return file_get_contents("http://127.0.0.1:8002?task_id=$i");
        });
    }
    $results = $parallel->wait();
    $connection->send(json_encode($results)); // Response: ["Task 1 Done","Task 2 Done","Task 3 Done","Task 4 Done"]
};

// Task Server
$task = new Worker('http://0.0.0.0:8002');
$task->onMessage = function (TcpConnection $connection, Request $request) {
    $task_id = $request->get('task_id');
    $message = "Task $task_id Done";
    $connection->close($message);
};

Worker::runAll();

Channel

Channel is a mechanism for communication between coroutines. One coroutine can push data into the channel, while another can pop data from it, enabling synchronization and data sharing between coroutines.

<?php
use Workerman\Connection\TcpConnection;
use Workerman\Coroutine\Channel;
use Workerman\Coroutine;
use Workerman\Events\Swoole;
use Workerman\Protocols\Http\Request;
use Workerman\Worker;
require_once __DIR__ . '/vendor/autoload.php';

// Http Server
$worker = new Worker('http://0.0.0.0:8001');
$worker->eventLoop = Swoole::class; // Or Swow::class or Fiber::class
$worker->onMessage = function (TcpConnection $connection, Request $request) {
    $channel = new Channel(2);
    Coroutine::create(function () use ($channel) {
        $channel->push('Task 1 Done');
    });
    Coroutine::create(function () use ($channel) {
        $channel->push('Task 2 Done');
    });
    $result = [];
    for ($i = 0; $i < 2; $i++) {
        $result[] = $channel->pop();
    }
    $connection->send(json_encode($result)); // Response: ["Task 1 Done","Task 2 Done"]
};
Worker::runAll();

Pool

Pool is used to manage connection or resource pools, improving performance by reusing resources (e.g., database connections). It supports acquiring, returning, creating, and destroying resources.

<?php
use Workerman\Connection\TcpConnection;
use Workerman\Coroutine\Pool;
use Workerman\Events\Swoole;
use Workerman\Protocols\Http\Request;
use Workerman\Worker;
require_once __DIR__ . '/vendor/autoload.php';

class RedisPool
{
    private Pool $pool;
    public function __construct($host, $port, $max_connections = 10)
    {
        $pool = new Pool($max_connections);
        $pool->setConnectionCreator(function () use ($host, $port) {
            $redis = new \Redis();
            $redis->connect($host, $port);
            return $redis;
        });
        $pool->setConnectionCloser(function ($redis) {
            $redis->close();
        });
        $pool->setHeartbeatChecker(function ($redis) {
            $redis->ping();
        });
        $this->pool = $pool;
    }
    public function get(): \Redis
    {
        return $this->pool->get();
    }
    public function put($redis): void
    {
        $this->pool->put($redis);
    }
}

// Http Server
$worker = new Worker('http://0.0.0.0:8001');
$worker->eventLoop = Swoole::class; // Or Swow::class or Fiber::class
$worker->onMessage = function (TcpConnection $connection, Request $request) {
    static $pool;
    if (!$pool) {
        $pool = new RedisPool('127.0.0.1', 6379, 10);
    }
    $redis = $pool->get();
    $redis->set('key', 'hello');
    $value = $redis->get('key');
    $pool->put($redis);
    $connection->send($value);
};

Worker::runAll();

Pool for automatic acquisition and release

<?php
use Workerman\Connection\TcpConnection;
use Workerman\Coroutine\Context;
use Workerman\Coroutine;
use Workerman\Coroutine\Pool;
use Workerman\Events\Swoole;
use Workerman\Protocols\Http\Request;
use Workerman\Worker;
require_once __DIR__ . '/vendor/autoload.php';

class Db
{
    private static ?Pool $pool = null;
    public static function __callStatic($name, $arguments)
    {
        if (self::$pool === null) {
            self::initializePool();
        }
        // Get the connection from the coroutine context
        // to ensure the same connection is used within the same coroutine
        $pdo = Context::get('pdo');
        if (!$pdo) {
            // If no connection is retrieved, get one from the connection pool
            $pdo = self::$pool->get();
            Context::set('pdo', $pdo);
            // When the coroutine is destroyed, return the connection to the pool
            Coroutine::defer(function () use ($pdo) {
                self::$pool->put($pdo);
            });
        }
        return call_user_func_array([$pdo, $name], $arguments);
    }
    private static function initializePool(): void
    {
        self::$pool = new Pool(10);
        self::$pool->setConnectionCreator(function () {
            return new \PDO('mysql:host=127.0.0.1;dbname=your_database', 'your_username', 'your_password');
        });
        self::$pool->setConnectionCloser(function ($pdo) {
            $pdo = null;
        });
        self::$pool->setHeartbeatChecker(function ($pdo) {
            $pdo->query('SELECT 1');
        });
    }
}

// Http Server
$worker = new Worker('http://0.0.0.0:8001');
$worker->eventLoop = Swoole::class; // Or Swow::class or Fiber::class
$worker->onMessage = function (TcpConnection $connection, Request $request) {
    $value = Db::query('SELECT NOW() as now')->fetchAll();
    $connection->send(json_encode($value));
};

Worker::runAll();

Available commands

php start.php start
php start.php start -d
php start.php status
php start.php status -d
php start.php connections
php start.php stop
php start.php stop -g
php start.php restart
php start.php reload
php start.php reload -g

Benchmarks

https://www.techempower.com/benchmarks/#section=data-r19&hw=ph&test=plaintext&l=zik073-1r

Supported by

JetBrains logo.

webman
AdapterMan

Donate

PayPal

LICENSE

Workerman is released under the MIT license.

workerman (walkor/workerman)

Searching Database…

11.5K ·GitHub
这是什么?Workerman 是一个异步事件驱动的 PHP 框架,用于构建快速、可扩展的网络应用,内置对 HTTP、WebSocket、TCP 和自定义协议的支持。
它是框架,还是只针对某一种协议的库?它是完整的套接字服务器框架:你编写一个 Worker 类,它会处理事件循环、连接,以及 HTTP、WebSocket、TCP 和 SSL 的协议解析,另外还提供协程和连接池。
我要怎样才能跑起一个服务器?通过 Composer 安装,并编写一个简短的 PHP 脚本来创建 Worker(例如 websocket、http 或 tcp 服务器),然后用 Workerman 的命令行界面运行它。
我的服务器运行它需要什么?需要兼容 POSIX 的操作系统(Linux、macOS、BSD),并安装 POSIX 和 PCNTL PHP 扩展;推荐安装 Event、Swoole 或 Swow 扩展以获得更好的性能,协程功能则必须依赖其中之一。
它能在 Windows 上运行吗?文档没有直接说明,但它要求兼容 POSIX 的操作系统(Linux、macOS、BSD),因此原生 Windows 不在支持列表中。
支持哪些平台?Linux、macOS 和 BSD,即任何兼容 POSIX 的操作系统。
Workerman 提供哪些协程原语?它提供用于运行异步任务的 Coroutine、用于等待多个并发任务的 Barrier、用于运行任务并收集各自结果的 Parallel,以及用于在协程之间传递数据的 Channel。
Pool 有什么用?Pool 用于管理可复用的资源,例如数据库连接,支持获取、归还、创建和销毁操作,还包括方便使用的自动获取与释放模式。
有哪些相关项目基于 Workerman 构建?生态系统包括 webman(一个完整的 Web 框架)和 AdapterMan,二者都基于 Workerman 的网络核心构建。
Workerman 要获得最佳性能,需要额外的扩展吗?需要。虽然它仅用 PHP 原生的 select 事件循环也能运行,但推荐安装 Event、Swoole 或 Swow 扩展以提升性能,协程支持则必须依赖其中之一。
What are similar projects?gnet, tornado, webserver, muduo, netty, vert.x, CocoaAsyncSocket, fasthttp
Creator
walkor
Size
3.9 MB
Stars
11.5K
Age
12y
Commits · 90 Days