route是用来标识一个具体服务或客户端接受服务端推送消息的位置,对服务器来说,其形式一般是:chat.chatHandler.send,其中chat是服务器类型,chatHandler是chat服务器中定义的一个Handler,send则是这个Handler中的一个handler方法。
对客户端来说,其路由形式一般为onXXX,当服务端请求到达后,前端服务器会将用户客户端请求派发到后端服务器,这种派发需要一个路由函数router,可以粗略地认为router是根据用户的session以及其请求内容做一些运算后,将其映射到一个具体的应用服务器ID。
可以通过application的route调用给某一类型的服务器配置其router。若不配置的话,Pomelo会使用一个默认的router。
Pomelo默认的路由函数是使用session里面的uid字段,计算uid字段的crc32校验码,然后用这个校验码作为key,跟同类应用服务器数目取余,得到要路由到的服务器编号。注意这里有一个陷进,如果session没有绑定uid的话,此时uid字段为undefined,可能会造成所有请求都路由到同一台服务器,所以在实际开发中还是需要自己来配置router。
当应用只有很少用户时只需一台服务器即可,但随着用户的增加,一台服务器无法承受同一时刻巨大的访问量,此时需要对服务器进行伸缩扩充。扩充服务器分布两部,首先需要修改服务器配置文件,添加服务器配置信息。其次,配置路由。
服务器配置
$ vim game-server/config/servers.json
例如:开发环境下配置一台网关服务器,配置三台前端服务器,配置三台聊天服务器。
{
"development":{
"gate": [
{"id": "gate-server-1", "host": "127.0.0.1", "clientPort": 3014, "frontend": true}
],
"connector": [
{"id": "connector-server-1", "host": "127.0.0.1", "port": 4050, "clientHost": "127.0.0.1", "clientPort": 3050, "frontend": true},
{"id": "connector-server-2", "host": "127.0.0.1", "port": 4051, "clientHost": "127.0.0.1", "clientPort": 3051, "frontend": true},
{"id": "connector-server-3", "host": "127.0.0.1", "port": 4052, "clientHost": "127.0.0.1", "clientPort": 3052, "frontend": true}
],
"chat": [
{"id":"chat-server-1", "host": "127.0.0.1", "port": 5050},
{"id":"chat-server-2", "host": "127.0.0.1", "port": 5051},
{"id":"chat-server-3", "host": "127.0.0.1", "port": 5052}
]
}
}
路由配置
当配置了不同类型的多台服务器时,此时就需要考虑用户请求的服务器分配问题。
对于网关服务器,由于存在多台前端服务器。所以需要从中选择一台服务器信息进行返回。可以使用用户的uid的crc32的校验码与前端服务器的个数取余,从而计算出一个前端服务器。
const index = Math.abs(crc.crc32(uid)) % connectors.length;
const connector = connectors[index];
当客户端请求到来时因为有多台聊天服务器,需要选择由哪台聊天服务器来服务,也就时前端服务器会将客户端请求路由到哪个后端服务器上。可使用同样的服务器分配策略。
const pomelo = require('pomelo');
const crc = require('crc');
const path = require('path');
/**初始化应用*/
const app = pomelo.createApp();
//上下文变量存取
app.set('name', 'test');
//全局配置
app.configure('production|development', function(){
//加载MySQL数据库
app.loadConfig("mysql", path.join(app.getBase(), "config/mysql.json"));
const host = app.get("mysql").host;//获取配置
console.log("mysql config: host = %s",host);
});
//网关服务器配置
app.configure('production|development', 'gate', function(){
//连接配置
app.set('connectorConfig',
{
connector : pomelo.connectors.hybridconnector,
heartbeat : 3,
useDict : true,
useProtobuf : true
});
});
//前端服务器配置
app.configure('production|development', 'connector', function(){
//连接配置
app.set('connectorConfig',
{
connector : pomelo.connectors.hybridconnector,
heartbeat : 3,
useDict : true,
useProtobuf : true
});
});
//聊天服务器配置
app.configure("production|development", "chat",function(){
//路由配置
app.route("chat", function(session, msg, app, cb){
const servers = app.getServersByType("chat");
if(!servers || servers.length===0){
cb(new Error("can not find chat servers"));
return;
}
const val = session.get("rid");
if(!val){
cb(new Error("session rid is not find"));
return;
}
const index = Math.abs(crc.crc32(val)) % servers.length;
const server = servers[index];
cb(null, server.id);
});
//过滤配置
app.filter(pomelo.timeout());
});
//开启应用
app.start();
process.on('uncaughtException', function (err) {
console.error(' Caught exception: ' + err.stack);
});
实际编程中,网络带宽的有效数据负载率是一个值得考虑的问题。对于移动客户端来说,网络资源往往并不丰富,为了尽可能地节省网络资源,往往需要尽大可能地增加数据包的有效数据率。