烦恼一般都是想太多了。

0%

Pomelo内建组件及游戏需要实现的一些东西

先从用户接入游戏说起。

游戏运行

connector

负责 管理客户端的连接 ,创建端口监听,绑定事件响应。依赖 session, server, pushSchedule, connection 组件。

  1. 当有客户连接,请求 session 组件,获取当前连接的 session;若当前连接不存在 session,那么 session 组件会建立新的 session,并维护连接。

  2. 然后 connector 组件还会向 connection 组件上报连接信息,供统计使用。

  3. 最后,将拿到的 session 以及客户端的请求,一起抛给 server 组件,由 server 组件进行请求处理

  4. server 组件处理完请求后,又会通过 connector 组件将响应返回给客户端。在返回响应给客户端的时候, connector 组件做了一个缓存选择,这个缓存实现依赖于 pushScheduler 组件,也就是说 connector 组件并不是直接将响应发给客户端,而是将响应给 pushScheduler 组件。 pushScheduler 组件根据相应调度策略,可能不缓存直接通过 session 组件维护的连接,将响应发出去,也可能进行缓存,并按时 flush 。这是可以配置的。

支持的配置

  • connector: 底层使用的通信 connector,不配置的话,会默认使用sioconnector;
  • useProtobuf: 目前仅仅支持 connector 配置使用hybridconnector 的情况,配置其为 true ,将开启消息的 protobuf 功能;
  • useDict: 目前仅仅支持 connector 配置使用 hybridconnector 的情况,配置其为 true 时,将会开启基于字典的路由消息压缩;
  • useCrypto: 目前仅仅支持 connector 配置为 hybridconnector 的情况,配置其为 true 时,将会启用通信时的数字签名;
  • encode/decode: 消息的编码解码方式,如果不配置的话,将会默认使用 connector 配置中,底层 connector 提供的相应的编码解码函数。
  • transports:这个配置选项是用于 sioconnector 的,因为 socket.io 的通信方式可能会有多种,如 websocket,xhr-polling 等等。通过这个配置选项可以选择需要的方式。

通过类似代码配置:

app.set('connectorConfig', opts);

session

session 组件跟 connector 相关,也是仅仅被前端服务器加载,为 sessionService 提供一个组件包装, 加载 session 组件后,会在app的上下文中增加 sessionService ,可以通过app.get('sessionService')获取。

主要用来维护客户端的连接信息,以及生成 session 并维护 session

一个连接与一个 session 对应,同时 session 组件还维护具体登录用户与 session 的绑定信息。一个用户可以有多个客户端登录,对应于多个 session 。当需要给客户端推送消息或者给客户端返回响应的话,必须通过 session 组件拿到具体的客户端连接来进行。

支持的配置

singleSession: 如果这个配置项配置为true的话,那么将将不允许一个用户同时绑定到多个session,在绑定用户一次后,后面的绑定将会失败。

配置 session 组件,通过调用如下方式进行:

app.set('sessionConfig', opts);

connection

connection 组件是一个功能相对简单的组件,也是仅仅被前端服务器加载,为 connectionService 提供一个组件包装,他主要进行连接信息的统计, connector 组件接收到客户端连接请求以及有客户端离线时,以及用户登录下线等等情况,都会向其汇报。

connection 组件无配置项。

server

server 组件也是一个功能比较复杂的组件,它被除 master 外的服务器加载。server 组件会加载并维护自身的 Filter 信息和 Handler 信息。server 组件会从 connector 组件的回调里获得到相应的客户端请求或者通知,然后会使用自己的 before filters 对其消息进行过滤,再次调用自己的相应 Handler进行请求的逻辑处理,然后将响应通过回调的方式发给 connector 处理。最后调用after filters 进行一些清理处理。

当然,如果客户请求的服务本来就是前端服务器提供的话,会是上面的那种处理流程。

如果客户请求的服务是后端服务器提供的服务的话,则将不是上面的那种处理流程,此时会出现 sys rpc 调用。

前面那种前端服务器自己处理的情况具体调用为 doHandle ,而发起rpc调用的情况则为 doForward 。

这两种处理流程的不同点是,对于自身的请求,调用自己的filter-handler链进行处理,对于不是前端服务器自己提供的服务,则是发起一个sys rpc,然后将 rpc 调用的结果作为响应,发给 connector 进行处理。关于这个 rpc 调用则是 pomelo 内建的 msgRemote 实现的。

对于后端服务器来说,其客户请求不是直接来源于真实的客户端,而是来源于前端服务器对其发起的sys rpc调用,这个rpc调用的实现就是 pomelo 内建的msgRemote。在msgRemote的实现里,会将来自前端服务器的 sys rpc 调用请求派发给后端服务器的 server 组件,然后后端服务器会启用filter-handler 链对其进行处理,最后通过rpc调用的返回将具体的响应返回给前端服务器。

在前端服务器将客户端请求向后端服务器分派时,由于同类型的后端服务器往往有很多,因此需要一个路由策略 router ,一般情况下用户通过Application.route 调用为后端服务器配置router。

server 组件无配置项。

pushScheduler

pushScheduler 组件也是一个功能较为简单的组件,它仅仅被前端服务器加载,与 connector 组件的关系密切。

connector 组件收到 server 组件的对客户端请求的响应后,connector 并不直接将此响应返回给客户端,而是将这个给客户端发送响应的操作调度给 scheduler 组件。

pushScheduler 组件完成最后通过 session 组件拿到具体的客户端连接并将请求的响应发送给客户端的任务。因此,通过pushScheduler 组件可以对发给用户的响应进行缓冲,从而提高通信效率。pomelo 实现了两种调度策略,一种是不进行任何缓冲,直接将响应发送给客户端,一种是进行缓冲,并定时地将已缓冲的响应发送给对应的客户端。

配置项

schedulerscheduler 组件的具体调度策略配置,默认的是直接将响应发给客户端,同时pomelo还提供了有缓冲并且定时刷新的调度策略。用户也可以自定义自己的调度策略。

配置pushScheduler组件,通过调用如下:

app.set('pushSchedulerConfig', opts);

如果要启用使用缓冲的scheduler的话,可以在app.js中增加:

app.set('pushSchedulerConfig', {scheduler: pomelo.pushSchedulers.buffer, flushInterval: 20});

flushInterval是刷新周期,默认为20毫秒。

channel

channel 组件维护 channel 信息,可以被除了master之外的服务器加载。

channel组件可以看作是channelService的组件包装,加载该组件后,会在app上下文中加入channelService,可以通过app.get(‘channelService’)获取。

可以认为一个channel就是一个用户的集合,每一个用户大致对应于前端服务器中的一个session,用户可以通过channel组件向一个channel里面的所有用户推送消息。当然,由于后端服务器并不与客户端直接相连,故后端服务器会发起一个sys rpc来表示向客户端推送消息,接受这个远程调用的是pomelo已经实现的 ChannelRemote。

channel组件的配置项:

broadcastFilter, broadcast的过滤函数。会在执行channel的broadcast的时候,在前端服务器上,在消息发送给每个session之前,进行一个过滤。其函数签名为

broadcastFilter(session, msg, filterParam)

其中filterParam参数由在channelService的broadcast调用时传入,如下:

channelService.broadcast(type, route, {filterParam: param}, cb);

可以通过如下方式对Channel组件进行配置:

app.set('channelConfig', opts)

proxy

proxy 组件是一个重量级的组件,它被除 master 外的所有服务器加载。

proxy 组件会扫描具体应用服务器的目录,抽取其中的 remote 部分,由于javascript语言的动态性,可以很轻易地获得到 remote 中的关于远程调用的元信息,生成stub,并将这些调用都挂到app.rpc 下面。

当用户发起rpc调用时,proxy 组件会查看其扫描到的stub信息,以此决定此远程调用是否合法。同时,proxy又会创建一个 RpcClient,当发起远程调用时,负责与远端的remote进行通信,并得到远程调用的结果供调用者使用。当进行远程调用时,由于同类型的远程服务器可能有多个,所以这里同样需要配置相应的router。

配置项

proxy的配置项:

  • cacheMsg, 配置cacheMsg为true的话,将开启rpc调用时的对消息的缓冲,而不是直接一旦有rpc请求就发出。
  • interval, 与配置参数cacheMsg配合使用,设置flush缓存的周期
  • mailBoxFactory, rpc底层实现需要的,用户可以定义自己的mailBoxFactory,我们将在rpc原理里面详述。

另外,可以开启rpc的调用日志,通过如下的调用:

app.enable('rpcDebugLog');

配置proxy使用:

app.set('proxyConfig', opts);

remote

remote 组件是与 proxy 组件对等的组件,它用来提供rpc调用服务。rpc 组件完成对当前服务器的 remote 的加载,并开启监听端口,等待rpc客户端的连接及相应的rpc调用。当接收到具体的调用请求时,会根据调用请求中描述的调用请求信息,调用相应的 remote 中的相应方法。然后再将具体的处理结果返回给rpc客户端。rpc服务端还支持对调用请求的filter,也就是说跟server组件处理客户端请求一样,rpc服务端处理具体请求时也会使用filter-remote链进行处理。

remote组件配置项:

  • cacheMsg, 与proxy组件的含义相同
  • interval, 与proxy组件的含义相同
  • acceptorFactory, rpc底层实现需要的,可以认为跟proxy配置中的mailBoxFactory是对等的,我们将在rpc原理里面详述。
    跟proxy组件一样,用户可以开启rpcDebugLog来得到所有的rpc调用过程的日志。 配置remote组件使用:
app.set('remoteConfig', opts);

dictionary

dictionary组件是一个可选组件,不会被默认加载,只有当 connector组件的配置中开启了 useDict 的时候,此组件才会被加载。此组件会遍历所有 handler 的 route 字符串,还会从config/dictionary.json 中读取客户端的route字符串,然后对这些字符串进行编码,给予每一个路由赋予一个唯一的小整数,实现route信息压缩,当客户端与前端服务器通信时需要路由信息时,将不会再使用很长的那个字符串,而仅仅使用一个小整数。

dictionary的配置项:

dict, 客户端路由字符串文件的位置,默认使用的是config/dictionary.json 配置dictionary组件使用:

app.set('dictionaryConfig', opts);

protobuf

protobuf组件也是一个可选组件,不会被默认加载,只有当connector组件的配置中开启了useProtobuf的时候,此组件才会被加载。此组件会加载对应的proto文件,并完成消息的基于protobuf的编解码。默认的proto文件的配置信息在config/serverProtos.json和config/clientProtos.json中。具体会在详细介绍pomelo-protobuf中详细介绍。

protobuf组件无配置项。

backendSession

BackendSession组件可以看作是BackendSessionService的组件包装,加载该组件后,会在app的上下文中加入backendSessionService,可以通过app.get('backendSessionService')调用获取。可以被除了master之外的服务器加载。它主要为后端服务器提供BackendSession信息,并通过远程过程调用完成一些比如对原始session绑定uid等操作。

backendSession组件无配置项。

服务器管理

master

master组件仅仅由master服务器加载,它主要的功能包括启动所有的应用服务器、管理和监控所有的应用服务器和接受管理客户端的请求与响应。

在master组件的start方法里,会根据用户提供的服务器配置信息,启动用户配置的所有的具体应用服务器。

当master组件start结束后,他将开启一个socket监听端口,接受应用服务器和监控客户端的连接和注册,收集应用服务器上报的监控信息,给应用服务器推送一些消息,并对管理客户端发出的管理请求给予响应。管理客户端如pomelo-cli可能发出的请求包括查看某个服务器进程状态,增加一个服务器,停掉一个服务器等。以增加一个服务器为例,当管理客户端发出增加服务器请求时,会提供相应的服务器参数,如服务器类型,主机ip,开启的端口等。此时,master组件接受后,会启动相应的服务器,并将新增加的服务器信息广播通知给其他已经启动的服务器。

master组件无配置项。

monitor

monitor组件由所有的包括master服务器在内的服务器都会加载,它的主要功能就是与master建立连接进行通信,从而对整个应用服务器群进行管理和监控。master服务器本身也会加载monitor服务器,因为master服务器也会收集其本身自己的监控信息。

可以认为monitor服务器与master服务器是对等组件,monitor会通过master接受一些命令,比如关闭整个服务器等。对于一些周期性监控的信息,pomelo提供了两种收集方式,即pull方式和push方式。pull方式要求master周期地去与monitor通信,拉取相应的监控信息;push方式,则是由monitor周期地主动地向master报告其监控信息。

monitor组件无配置项。