@yangfch3
2018-06-14T09:26:31.000000Z
字数 8662
阅读 3774
Pomelo
Pomelo
生态 pomelo-sync
component
构建) pomelo-jsclient-websocket
pomelo-jsclient-socket.io
pomelo-protocol
pomelo-protobuf
socket.io-client
chatofpomelo
chatofpomelo-websocket
- 聊天lordofpomelo
- MMO 中大型游戏treasures
- H5 游戏Node.js
版本推荐:6.x
pomelo init
后的 shared
目录:可以前后端共用的工具或者算法代码
TODO
web-server 存在的一个小问题:
node app.js
后,localhost:3001
能访问,但127.0.0.1:3001
无法访问
pomelo --help
pomelo xxx --help
查看 pomelo
及其子命令的帮助信息
使用 pomelo stop
,规避 pomelo kill
port
与 clientPort
port
是面向“后”的端口(即用于 rpc
调用的端口)clientPort
是面向“前(客户端)”的端口(即面向 client
的端口)gate
服务器和 connector
服务器又都被称作前端服务器
gate
服务器只有 clientPort
connector
服务器有 clientPort
和 port
后端服务器只有
port
connector
做为前端服务器
rpc
调用后端服务器,并获得 rpc
调用的结果前端服务器的职责:
session
信息后端服务器(backend)的职责:
remote
)以及处理前端请求(handler
)的逻辑 本质上前端服务器route
到后端的handler
也是 RPC
remote
与 handler
gate
服务器只有 handler
connector
服务器也只有 handler
后端服务器可以有 handler
和 remote
(且此两部分是同一个进程中),其中 handler
用于客户端对应路由消息的处理(由前端服务器 forward
),remote
用于前端服务器进行 RPC 调用
不管是前端服务器还是后端服务器,handler
必须调用 next
来传递 response
的消息(否则会报 request
超时未回复的错误)
服务器端路由是 前端服务器用以寻找客户端消息对应处理方式的框架设计,路由形式:<server>.<handlerFile>.<method>
客户端路由则可以 简单地理解为 pomelo client 订阅的事件,常见形式 onXXX
client ---request route-->
<server>.<handlerFile>.<method>
client <---response route---onXXX
同一个服务器下:
remote
与 handler
处于同进程,可互相调用(但一般不会发生调用)不同后端服务器之间:
remote
远程调用 后端服务器的 handler
与前端服务器的 handler
参数一致:(msg, session, next)
;
后端服务器 remote
的参数则一般自己定制,推荐:(params, cb)
,RPC 调用时 app.rpc...<remoteMethod>(session, params, cb)
后端服务器的 handler
与 remote
内可以 RPC 调用其他后端服务器的 remote
路由器:根据 route
的 session
/routeParam
决定指定处理该 route 的服务器
在 Pomelo 下,router
通常就是一个 dispatch
函数
exp.chat
即为路由器函数
路由器函数有四个参数位供 pomelo 传入:
1· routeParam
- 通常(但可以不)是 session
msg
app
- ctxcb
路由器使用 app.route
配置
sys namespace
session
信息(BackendSession
的 push
, pushAll
操作)channel
推送消息时对前端服务器发起的 rpc
调用<backendServer>.<handlerFile>.<method>
user namespace
:pomelo.app.rpc.xxx.xxxRemote...
前后端服务器之间的通信基本都是基于 RPC 调用,同时后端服务器之间也可以使用 RPC 进行通信
RPC 调用(例如:app.rpc...<remoteMethod>(session, params, cb)
)的第一个参数是用于 Pomelo
进行路由使用的(一般都是使用前端 session
),会传递给对应后端服务器使用 app.route
注册的路由函数(或 Pomelo 默认的路由策略函数),最后一个参数是 RPC 调用结果的回调函数,调用的错误或是结果全部通过该回调函数返回,且这个参数不能省略,中间有多少个参数一般没有限制(但推荐使用 params
对象的形式来批量传参)
proxy
与 remote
两个组件分别用于生成 RPC 通信的 client
与 server
对于后端服务器,remote
使用 servers.json
配置中的 port
创建 RPC 服务器
一个服务器(不论是 connector
还是后端服务器)都能与所有后端服务器进行 RPC 通信,所以 proxy
能够 懒创建 许多个 RPC Client,保持与 RPC Server 的长连接
三个 session
,两个 sessionService
session
- 底层、隐藏的,开发者不需要关注FrontendSession
SessionService
(前端服务器中 app.get('sessionService')
)BackendSession
BackendSessionService
(后端服务器中 app.get('backendSessionService')
)获取 session
的途径
handler
的 session
参数:FrontendSession
handler
的 session
参数:BackendSession
sessionService
的 get
与 getByUid
remote
中 BackendSessionService
的 get
与 getByUid
:BackendSession
FrontendSession
与 BackendSession
的属性与方法基本一致,但由于真实底层的 session
只在前端服务器,所以 BackendSession
的方法调用(push
, pushAll
等) 都是 RPC
调用(所以会有回调)
session
的 __socket__
是底层原生 socket
的引用
服务器端下发消息的几种方式
handler
的 next
- 做为 pomelo.request
的 response
channel.pushMessage
- channel
级 push
channelSessionService.pushMessageByUids
- channel
内选择性地 push
channelSessionService.broadcast
- 服务器级别的广播几种消息类型的消息头
消息体
消息头的 flag
字段
route 表示是否 route 压缩
route
压缩时的 flag 与 route 字段
在 pomelo 中,module 特指服务器监控管理模块,会有这四个钩子函数:masterHandler
monitorHandler
clientHandler
start
monitorHandler(agent, msg, cb)
- monitor 收到 master 的请求或者通知时的回调masterHandler(agent, msg)
- master 收到 monitor 的请求或者通知时回调clientHandler(agent, msg, cb)
- master 收到 client 的请求或通知时回调start(cb)
- admin module加载完成后,用来执行一些初始化监控时调用 pomelo 框架是由一些松散耦合的 component
组成的,每个 component
完成一些功能。整个 pomelo 框架可以看作是一个 component
容器,完成 component
的加载、增强 以及生命周期管理(start
, afterstart
, stop
)。
component 的生命周期函数能拿到 pomelo 的引用(
app
参数),所以我们可以在 component 对 pomelo 做任何处理和操作;所以 pomelo 的 plugin 也会有编写 component 来处理和增强 pomelo
对于已 app.load
的 component,pomelo 总是先调用其加载的每一个 component 提供的 start 函数(但 pomelo 内置的 component 排在前面),当各个组件 start
全部调用完后,才会去调用其加载的每一个 component 的 afterStart 方法,总是按顺序调用的。
一个 plugin 是由多个 component 以及一些事件响应处理器组成。
master、monitor 相关模块与组件以及 consoleService错综复杂的关系理清
admin-client
是指像 pomelo-cli
这样的服务器群管理客户端工具
master
moniter
Server
master
和 monitor
分别对应用于管理与监控的 Master
服务器与 Moniter
socket 客户端
app.configure(<env>[, <serverType>], cb)
app.set(<mountProp>, <value>)
enableForwardLog
- BooleanrpcDebugLog
- Boolean(同 app.enable('rpcDebugLog')
)app.route(<serverType>, <routerFn>)
app.before(<Filter>)
app.after(<Filter>)
app.filter(<Filter>)
app.rpcBefore(<Filter>)
app.rpcAfter(<Filter>)
app.rpcFilter(<Filter>)
用于注册 RPC 调用的 filter 链
app.enable(<feature>)
app.disable(<feature>)
app.set(<feature>, true)
app.enabled/disabled(<feature>)
来检测特性是否开启app.registerAdmin(<adminModule>, <opts>)
app.load(<component>, <opts>)
app.loadConfig(<configName>, <configPath>)
等价于:
app.set(<configName>, require(<configPath>))
app.use(<plugin>, opts)
connectorConfig
connector
- Connector hybridconnector
handshake
- Functionheartbeat
- NumberdistinctHost
- Booleansioconnector
heartbeats
- BooleancloseTimeout
- NumberheartbeatTimeout
- NumberheartbeatInterval
- Numbermqttconnector
publishRoute
subscribeRoute
udpconnector
disconnectOnTimeout
- Booleanheartbeat
- Numbertimeout
- NumberudpType
- String--- 上方每个 connector
下是该 connector
的特异可配置参数
--- 以下是几个 connector
的共同可配置参数
encode
- Functiondecode
- FunctionuseCrypto
- BooleanuseHostFilter
- BooleanblacklistFun
- FunctionuseDict
- BooleanuseProtobuf
- Booleanssl
- ObjectdisconnectOnTimeout
- BooleansessionConfig
singleSession
- Boolean 是否允许一个用户同时绑定到多个sessionproxyConfig
bufferMsg
- Boolean(旧版本使用 cacheMsg
)interval
- NumbermailBoxFactory
- Function pomelo-rpc
支持配置(但 pomelo
其实是没用到这些特性的)failMode
- String 'failfast'
- 快速失败'failover'
- 切换服务器尝试'failsafe'
- 自定制安全策略retryTimes
- Number 使用安全策略时的重试次数retryConnectTime
- Number 使用安全策略时的重连间隔时间routerType
- String replicas
- Number (ch)虚拟节点数量algorithm
- String (ch)hash 算法hashFieldIndex
- Number (ch)根据 rpc 参数列表中的具体(index)参数进行 hashremoteConfig
bufferMsg
- Boolean(旧版本使用 cacheMsg
)interval
- NumberacceptorFactory
- FunctionserverConfig
reloadHandlers
reloadRemotes
pushSchedulerConfig
scheduler
- Function/Function Array 具体调度策略flushInterval
- Number scheduler 为缓冲模式时可配置selector
dictionaryConfig
dict
- String 字典路径channelConfig
broadcastFilter
- Function 前端服务器上消息广播给每个 session 时的前处理app.enable('systemMonitor')
开启 pomelo 监控(信息可通过客户端 - cli/web 获取)
app.enable('rpcDebugLog')
开启 RPC 日志
对 filter
与 handler
的 next
处理流程发生的异常集中处理
app.set('errorHandler', function(err, msg, resp, session, next) {
// 记录、处理错误
next(null, resp);
});
pomelo.init(params)
params
:
host
port
encode
decode
encrypt
handshakeCallback
maxReconnectAttempts
reconnect
未预料的程序错误:
process.on('uncaughtException', function (err) {
console.error('进程级异常捕获: ' + err.stack);
});
handler
中 next
传递的错误
// 统一处理 handler 流程中 next(err) 的错误
app.set('errorHandler', function (err, msg, resp, session, next) {
// 此处进行错误记录、处理
next(null, resp);
});
handler
中对 remote
RPC 调用中传递给回调的错误
next(rpcError)
化归成 2 中对 handler
next
错误的处理pomelo-logger
基于 log4js
0.6.x
config/log4js.json
的特异配置:
replaceConsole
:决定是否调用 log4js.replaceConsole
lineDebug
rawMessage
reloadSecs
- 是否重新载入配置文件 config/log4js.json
filename 的模板写法:${opt:<prop>}/path/to/...
,pomelo
会在 app.init
流程中进行模板替换
pomelo-logger
也为我们提供了一种 log4js
在集群下的分进程日志写入思路:使用自己添加的 filename 模板特性(相当于每个进程加载的 log4js 都是不同的),达到不同的进程日志文件名区分的效果
4 中的实现在新版的 log4js
已经得到原生支持:multiFile
app.loadConfigBaseApp
- 第二个路径参数可以简写成基于 app.js
的路径,第三个布尔参数决定是否在配置变更时重载
app.transaction(name, conditions, handlers, retry)
- 模拟事务
conditions
可以是对象也可以是数组,只要组成元素是函数即可,并且conditions
内的元素(函数)都要能接收一个回调函数,condition
运行抛出的错误会传给回调函数的第一个参数 -- async 文档。
handlers
只在conditions
遍历检验完成且没有抛出错误时才会运行,如果handlers
遍历运行抛出了错误,则会按照retry
设置的次数重试。
app.addCrons
- 定时任务,用法见链接
app.globalFilter
消息处理链:globalBeforeFilter -> 前端服务器 -> beforeFilter -> (RPC Before Filter -> RPC 后端服务器 -> RPC After Filter) -> afterFilter -> globalAfterFilter
app.beforeStopHook
- 服务器停止前执行的操作
Pomelo
的请求 body 与响应 body 只要满足能被 JSON.stringify
即可以被发送
Pomelo
的 handler
中如果多次调用 next()
则会导致 后面传递的消息没有 msg.id 等元信息,因为这些信息在第一次调用 next 时已经被消耗了
官方文档的错误:当 masterha.json
被正确地放在 ./config
路径下时,pomelo masterha
即可(无需像文档那样添加配置路径)
pomelo start
与 pomelo masterha
需要保持一致才能开启高可用
pomelo
最新版中对非 development
环境下的非 master
进程做了 detached
设置,直接 ctrl + c
无法关闭进程,正确的方法是在 game-server 目录下执行 pomelo stop
pomelo-protocol
存在的问题:
Node.js 环境下会走这个分支,从而使用
new Buffer
来编码字符串,而浏览器不支持Buffer
,所以解码时会走pomelo-protocol
自创的解码方案这个分支,而new Buffer()
和Protocol.strdecode
在出现\uffff+
是有问题的
new Buffer('𠮷'); // <Buffer f0 a0 ae b7>
Protocol.strencode('𠮷'); // Uint8Array(6) [237, 161, 130, 237, 190, 183]
解决思路:new Buffer()
与 buffer.toString()
/TextDecoder.prototype.decode
是可逆的,Protocol.strencode()
与 Protocol.strdecode()
是可逆的;所以,要么让客户端(通过 polyfill 的方式)支持 Buffer
和 buffer.toString()
/TextDecoder.prototype.decode
,要么让服务器端所有的编码使用 Protocol.strencode()
(现在消息体的编码使用 Buffer
)