Server 服务器组件

server组件代表了beyod运行实例,它是整个网络框架的基础。server组件中又包含了一系列的Listeners组件配置。

可以从beyoioServer类继承,实现自定义server组件类,只要在在配置文件中指定class即可。

server对象引用

在任何位置,可以通过以下代码引用server组件对象:

\Yii::$app->server;
onWorkerStart回调
public function onWorkerStart($workerId, $GPID){
    
}

它运行在工作进程空间,当所有Listeners已经监听并开始准备接受客户端连接时,此方法被运行,你可以扩展自己的逻辑,实现个性化的功能(比如初始化操作,连接到订阅器,基于进程角色的任务调度等)。

server_id
只读,服务器实例号,取值为1~65535之间的整数,必须在配置文件中指定,集群环境中每个实例的server_id不能一致。获取当前server_id:
\Yii::$app->server->server_id;
workerId
workderid是非常重要的概念,它是指工作进程的顺序号(并不是pid),从1开始,当一个进程崩溃后,被主进程重新生成替补后,它的workerId是不变的。我们可以根据此值,实现进程功能区分,使不同的进程处理不同的任务。
可以在任何位置使用以下方式取得当前进程的Workerid:

\Yii::$app->server->getWorkerId();
GPID
全局进程唯一id(Global process unique identification), 在集群环境中,存在多台服务器的情况下,客户端可能连接到任何一台服务器的某个工作进程,

beyod诞生之初就考虑到分布式环境集群化部署,引入了GPID概念,它用来在集群环境中为每个工作进程分配全局唯一的id。

GPDI本质上是由 server_id和workerID组成,算法为:

$GPID = ($server_id << 16)+$workderId;

所以,集群环境要保证每台服务器的server_id不能一样。

显然,从理论上来说,单个beyod实例最多只能有65535个工作进程。

可以在任何位置得到当前进程的GPID:

\Yii::$app->server->GPID;

//或
\Yii::$app->server->getGPID();
工作进程之间的通讯

当客户端连接到不同的工作进程中时(或同一台机器的不同工作进程),它们属于不同的进程空间,无法直接通信。如果要互相通信,就需要一个共享通道。

典型的,如果我们要开发一个物联网应用,用户需要通过手机/网页向设备下发指令, 但架构是以多台服务器集群方式运行,这时就需要有GPID的帮助,以快速定位设备所在具体进程。原理如下:

  1. 工作进程启动后,即向订阅器(dispatcher/redis等)订阅频道,频道名称即为自己的GPID。
  2. 设备连接后,我们将device_id与GPID的映射结构保存到共享存储系统(如dispatcher/redis/memcache/database)中。
  3. 当需要向终端设置发送数据时,我们在第2步的共享存储系统中根据device_id找到GPID。
  4. 我们向订阅器(dispatcher/redis等)发布消息,频道号即为第3步找到的GPID,那么订阅了此频道的工作进程就会收到此消息,从而实现集群通信。

实现此种架构,最直接的有两种方式:

  1. 使用beyod自带的dispatcher实现
  2. 使用订阅器实现(如redis)

使用其它类似的方式也要完全可以的,但基本原理却是相通的。

如果你的终端设备较少,完全可以使用单机单进支持上万个连接还是没有问题的。

可以使用keeppalived/heartbeat等工具,实现主-备高可用自动切换。

使用单进程的一个弊端是,多核CPU资源不能充分使用。要根据负载量设计合理方案,避免过度设计和简单问题复杂化。

请参阅 集群架构:分布式进程通讯