Skip to content

Latest commit

 

History

History
189 lines (147 loc) · 4.4 KB

File metadata and controls

189 lines (147 loc) · 4.4 KB

Custom Processes, WebSocket/TCP, and Timers

All processes — including the built-in HTTP process — are defined in config/process.php. After changing this file you must restart (reload is not enough).

Defining a custom process

// config/process.php
return [
    // ... default webman (HTTP), monitor, etc. ...

    // Resident worker with no listen socket
    'my-task' => [
        'handler' => process\MyTask::class,
        'count'   => 1,                      // process count
        'constructor' => ['param1' => 'xx'], // constructor args
    ],

    // WebSocket service
    'ws' => [
        'handler' => process\Ws::class,
        'listen'  => 'websocket://0.0.0.0:8888',
        'count'   => 4,
        'reusePort' => true,
    ],

    // TCP service (text protocol, framed by \n)
    'tcp' => [
        'handler' => process\Tcp::class,
        'listen'  => 'text://0.0.0.0:9000',
        'count'   => 2,
    ],
];

Process classes receive workerman callbacks via onXxx methods:

<?php

declare(strict_types=1);

namespace process;

use Workerman\Worker;
use Workerman\Connection\TcpConnection;
use Workerman\Protocols\Http\Request;

class Ws
{
    public function onWorkerStart(Worker $worker): void
    {
        // Process start: init connections, load data, register timers
    }

    public function onConnect(TcpConnection $connection): void {}

    public function onWebSocketConnect(TcpConnection $connection, Request $request): void
    {
        // Auth from GET params during handshake
        $token = $request->get('token');
        if (!$token) {
            $connection->close();
        }
    }

    public function onMessage(TcpConnection $connection, $data): void
    {
        $connection->send('received: ' . $data);
    }

    public function onClose(TcpConnection $connection): void {}
}

Timers (Workerman\Timer)

use Workerman\Timer;

public function onWorkerStart(Worker $worker): void
{
    // Every 10 seconds
    Timer::add(10, function () {
        // Always catch exceptions or the timer may stop
        try {
            OrderService::closeExpired();
        } catch (\Throwable $e) {
            \support\Log::error('timer error: ' . $e->getMessage());
        }
    });

    // One-shot delayed task
    Timer::add(5, fn () => something(), [], false);
}

Rules:

  • Timer process count must be 1, otherwise every process runs the job (duplicates).
  • Do not Timer::add inside controllers/middleware — each request leaks a timer.

Crontab (workerman/crontab)

composer require workerman/crontab
<?php

declare(strict_types=1);

namespace process;

use Workerman\Crontab\Crontab;

class CronTask
{
    public function onWorkerStart(): void
    {
        // second minute hour day month weekday (6 fields, second-level)
        new Crontab('0 */5 * * * *', function () {   // every 5 minutes
            ReportService::flush();
        });

        new Crontab('0 30 2 * * *', function () {    // daily 02:30
            CleanupService::run();
        });
    }
}

Also register in config/process.php with count => 1. On multi-host deployments, use a Redis lock so only one machine runs the job.

IPC / pushing to clients

HTTP and WebSocket run in different processes; connection objects are not directly shared. Push options:

  • webman/push: pusher-protocol plugin, private channels, JS SDK; trigger from HTTP via API.
  • Redis pub/sub or queues: HTTP publishes; WebSocket process subscribes and pushes.
  • workerman/channel: lightweight cross-process broadcast.
// webman/push example from an HTTP controller
$api = new \Webman\Push\Api(
    'http://127.0.0.1:3232',
    config('plugin.webman.push.app.app_key'),
    config('plugin.webman.push.app.app_secret')
);
$api->trigger('user-1', 'message', ['content' => 'hello']);

Queue consumers (webman/redis-queue)

composer require webman/redis-queue
// Produce (from a controller)
use Webman\RedisQueue\Client;
Client::send('send-mail', ['to' => 'a@b.c'], delay: 60);

// Consumer: app/queue/redis/SendMail.php (auto-registered)
namespace app\queue\redis;

use Webman\RedisQueue\Consumer;

class SendMail implements Consumer
{
    public $queue = 'send-mail';
    public $connection = 'default';

    public function consume($data): void
    {
        Mailer::send($data['to']);
        // Throwing retries per config (retry_seconds, max_attempts)
    }
}