长连接分布式解决方案

    目录

    imi v2.0 开始,新增了长连接分布式解决方案。

    该方案支持在 TCP、WebSocket 长连接服务中,轻松实现原生分布式推送功能。

    使用分布式解决方案时,你对连接的绑定、分组、推送等操作,底层都会自动实现逻辑,极大地降低了心智负担,可以说是一把梭!

    组件引入:composer require imiphp/imi-workerman-gateway

    Demo: composer create-project imiphp/project-websocket:~2.0

    模式

    消息队列模式

    AMQP

    imi v2.0 开始,新增了支持 RabbitMQ 或其他支持 AMQP 协议的消息队列。

    使用发布订阅模式,每个消费者(Worker 进程)都是一个绑定到交换机的独立队列。

    需要注意的是,无法准确判断指定连接是否存在于其他服务,需要业务层面自行实现。

    此外,所有模式都不推荐使用持久化特性,因为没有实际意义。

    此模式仅支持 Swoole
    AMQP 一把梭模式

    会忽略 routingKey,将所有队列都接收所有指令,使用起来简单易用,但是性能相对较差。

    用法:

    项目配置文件:

    'imi' => [
        'beans' => [
            'ServerUtil' => 'AmqpServerUtil',
        ],
    ],
    'beans' => [
        'AmqpServerUtil' => [
            // 'amqpName' => null, // amqp 连接名称
            // 交换机配置,同 AMQP 组件的 @Exchange 注解参数
            'exchangeConfig' => [
                'name' => 'imi_server_util_test', // 交换机名
                'type' => \PhpAmqpLib\Exchange\AMQPExchangeType::FANOUT, // fanout 模式
            ],
            // 队列配置,同 AMQP 组件的 @Queue 注解参数
            'queueConfig' => [
                'name'    => 'imi_server_util_', // 每个进程中的队列名前缀,如果是多实例部署,请设为不同的
                'durable' => false, // 非持久化
            ],
            // 'consumerClass' => 'AmqpServerConsumer', // 消费者类,如有需要可以覆盖自己实现
            // 'publisherClass' => 'AmqpServerPublisher', // 发布者类,如有需要可以覆盖自己实现
        ],
    ],
    AMQP 路由模式

    在路由模式下,队列会根据指定的 routing key 接收指定的消息。

    使用路由模式需要配置对应的 GroupHandler 和 ConnectionContextHandler。

    当绑定、解绑 Group 或者更改 flag 时,对应的交换机、队列、routing key 也会被绑定和解绑。

    因此,在消费者接收消息时,只会接收与自己相关的消息。

    用法:

    项目配置文件:

    'imi' => [
        'beans' => [
            'ServerUtil' => 'AmqpServerUtil',
        ],
    ],
    'beans' => [
        'ServerGroup' => [
            'groupHandler' => 'GroupAmqp', // 配置对应的 GroupHandler
        ],
        'ConnectionContextStore'   => [
            'handlerClass'  => 'ConnectionContextAmqp', // 配置对应的 ConnectionContextHandler
        ],
        'AmqpServerUtil' => [
            // 'amqpName' => null, // amqp 连接名称
            // 交换机配置,同 AMQP 组件的 @Exchange 注解参数
            'exchangeConfig' => [
                'name' => 'imi_server_util_test', // 交换机名
                'type' => \PhpAmqpLib\Exchange\AMQPExchangeType::DIRECT, // direct 模式
            ],
            // 队列配置,同 AMQP 组件的 @Queue 注解参数
            'queueConfig' => [
                'name'    => 'imi_server_util_', // 每个进程中的队列名前缀,如果是多实例部署,请设为不同的
                'durable' => false, // 非持久化
            ],
            // 'consumerClass' => 'AmqpServerConsumer', // 消费者类,如有需要可以覆盖自己实现
            // 'publisherClass' => 'AmqpServerPublisher', // 发布者类,如有需要可以覆盖自己实现
        ],
    ],

    Redis

    该方案采用 Redis 发布订阅实现,每个运行实例都连接到 Redis 并订阅,当有消息发布时,Redis 会发送给所有订阅的服务器,服务器再进行消息的推送,从而实现了原生分布式推送功能。

    这个解决方案是基于 Redis 的发布订阅机制实现的,可以轻松地在多个服务器之间共享消息。因为 Redis 是一种高性能的内存数据库,所以它可以轻松处理大量消息,从而确保在分布式环境下推送消息的效率和可靠性。

    此模式仅支持 Swoole

    用法:

    项目配置文件:

    'imi' => [
        'beans' => [
            'ServerUtil' => 'RedisServerUtil',
        ],
    ],
    'beans' => [
        'RedisServerUtil' => [
            'redisName' => null, // 配置的 Redis 连接名称,为 null 则使用默认
            'channel' => 'imi:RedisServerUtil:channel', // 发布订阅的频道名,不同服务请设为不同的,以防冲突
        ],
    ],

    网关模式

    Workerman Gateway

    Workerman Gateway 是一个成熟的实现方案,可以实现分布式消息推送,同时也支持不断线更新业务代码。这使得它特别适合处理海量设备的物联网项目。

    在使用 Workerman Gateway 的过程中,需要注意编写的代码运行在 Worker 进程中,因此需要单独配置 Worker 类型的服务器配置。这与纯粹的 Swoole、Workerman 服务配置略有不同。

    如果您想了解更多关于 Workerman Gateway 的知识,可以参考其官方文档:http://doc4.workerman.net/

    此模式支持 Swoole、Workerman

    Swoole 用法:

    项目配置文件:

    [
        // 主服务器配置(子服务器配置同理)
        'mainServer'    => defined('SWOOLE_VERSION') ? [
            'namespace'    => '服务命名空间',
            'type'         => \Imi\WorkermanGateway\Swoole\Server\Type::BUSINESS_WEBSOCKET, // WebSocket 业务服务器
            // 'type'         => \Imi\WorkermanGateway\Swoole\Server\Type::BUSINESS_TCP, // TCP 业务服务器
            'mode'         => \SWOOLE_BASE,
            //网关配置
            'workermanGateway' => [
                // worker 名称,在不同的 worker 实例中必须不同,一般推荐环境变量来修改
                'workerName'           => 'websocketWorker',
                'registerAddress'      => '127.0.0.1:13004', // 注册中心地址
                'worker_coroutine_num' => swoole_cpu_num(), // 每个 Worker 进程中的工作协程数量
                // 待处理任务通道长度
                'channel'              => [
                    'size' => 1024,
                ],
            ],
        ] : [],
        'swoole' => [
            'imi' => [
                'beans' => [
                    'ServerUtil' => Imi\WorkermanGateway\Swoole\Server\Util\GatewayServerUtil::class,
                ],
            ],
        ],
    ]

    配置好后用 vendor/bin/imi-swoole swoole/start 启动服务即可。

    Workerman 用法:

    项目配置文件:

    [
        // Workerman 服务器配置
        'workermanServer' => [
            // Worker 配置
            // worker 名称 websocket,在不同的 worker 实例中必须不同,一般推荐环境变量来修改
            'websocket' => [
                'namespace'   => '服务命名空间',
                'type'        => Imi\WorkermanGateway\Workerman\Server\Type::BUSINESS_WEBSOCKET, // WebSocket 业务服务器
                // 'type'        => Imi\WorkermanGateway\Workerman\Server\Type::BUSINESS_TCP, // TCP 业务服务器
                'configs'     => [
                    'registerAddress' => '127.0.0.1:13004', // 注册中心地址
                    'count'           => 2,
                ],
            ],
            // 其它监听端口的服务,可以不要
            'http' => [
                'namespace' => 'Imi\WorkermanGateway\Test\AppServer\ApiServer',
                'type'      => Imi\Workerman\Server\Type::HTTP,
                'host'      => \Imi\env('SERVER_HOST', '127.0.0.1'),
                'port'      => 13000,
                'configs'   => [
                    'registerAddress' => '127.0.0.1:13004',
                ],
            ],
        ],
        'workerman' => [
            'imi' => [
                'beans' => [
                    'ServerUtil' => Imi\WorkermanGateway\Workerman\Server\Util\GatewayServerUtil::class,
                ],
            ],
        ],
    ]

    按上面的配置好后,如果你的网关和注册中心是单独运行的,那么已经可以跑了。

    如果你希望用 imi 来启动网关和注册中心,可以参考如下配置。

    @app.workermanServer 配置中加入:

    [
        // 注册中心服务
        'register' => [
            'namespace'   => 'Imi\WorkermanGateway\Test\AppServer\Register',
            'type'        => Imi\WorkermanGateway\Workerman\Server\Type::REGISTER,
            'host'        => '0.0.0.0',
            'port'        => 13004,
            'configs'     => [
            ],
        ],
        // 网关服务
        'gateway' => [
            'namespace'   => 'Imi\WorkermanGateway\Test\AppServer\Gateway',
            'type'        => Imi\WorkermanGateway\Workerman\Server\Type::GATEWAY,
            'socketName'  => 'websocket://0.0.0.0:13002',
            'nonControlFrameType' => \Imi\Server\WebSocket\Enum\NonControlFrameType::TEXT, // 配置 WebSocket 纯文本通信协议
            // 'nonControlFrameType' => \Imi\Server\WebSocket\Enum\NonControlFrameType::BINARY, // 配置 WebSocket 二进制通信协议
            'configs'     => [
                'lanIp'           => '127.0.0.1',
                'startPort'       => 12900,
                'registerAddress' => '127.0.0.1:13004',
            ],
        ],
    ]

    配置好后用 vendor/bin/imi-workerman workerman/start 启动服务即可。