# 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资源不能充分使用。要根据负载量设计合理方案，避免过度设计和简单问题复杂化。

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