聊天服务器扩展
大家在上一篇文章里相信已经学会了pomelo框架的基本用法了,那么我们在上一篇文章的代码基础上继续扩展,丰富系统,另外也熟悉下他的更多的用法,这一节我将扩展它:增加一个机器人自动聊天的功能。
目的是让大家熟悉下定时器的用法,另外再熟悉下RPC方式。
rpc调用
pomelo中使用rpc调用进行进程间通信,在pomelo中rpc调用分为两大类,使用namespace进行区分,namespace为sys的为系统rpc调用,它对用户来说是透明的,目前pomelo中系统rpc调用有:
1.后端服务器向前端服务器请求session信息
2.后端服务器通过channel推送消息时对前端服务器发起的rpc调用
3.前端服务器将用户请求路由给后端服务器时也是sys rpc调用
除了系统rpc调用外,其余的由用户自定义的rpc调用属于user namespace的rpc调用,需要用户自己完成rpc服务端remote的handle代码,并由rpc客户端显式地发起调用.
服务器间RPC调用的抽象介绍
架构中各服务器之间的通讯主要是通过底层RPC框架来完成的,该RPC框架主要解决了进程间消息的路由和RPC底层通讯协议的选择两个问题。 服务器间的RPC调用也实现了零配置。实例如下图所示:
上图的remote目录里定义了一个RPC接口: chatRemote.js,它的接口定义如下:
chatRemote.kick = function(uid, player, cb) {}
其它服务器(RPC客户端)只要通过以下接口就可以实现RPC调用:
app.rpc.chat.chatRemote.kick(session, uid, player, function(data){});
这个调用会根据特定的路由规则转发到特定的服务器。(如场景服务的请求会根据玩家在哪个场景直接转发到对应的server)。
rpc的使用远比其它rpc框架简单好多,因为我们无需写任何配置文件,也无需生成stub。因为我们服务器抽象的实现的方式,使得rpc客户端可以在应用启动时扫描服务器目录自动生成stub对象。
完成了以上三个目标, 一个实时的分布式应用框架的轮廓就搭出来了。接下来我们在下一章节里说明下在当前demoserver的基础上不断地扩充,丰富它的功能。
拿起键盘就是干
我的思路是玩家在连接到服务器之后,在connector服务器创建一个定时器,每1秒向登录进来的客户端发送消息,当然发送的消息内容,你可以随机,也可以固定,推送给客户端消息的方式是通过调用一个time服务器的RPC方式pushmsg。
好了,拿起键盘开干:
由于增加了一个time服务器,所以第一步先往配置文件里server.json里配服务器:
{"development":{"connector":[{"id":"connector-server-1", "host":"127.0.0.1", "port":4050, "clientPort": 3050, "frontend": true},{"id":"connector-server-2", "host":"127.0.0.1", "port":4051, "clientPort": 3051, "frontend": true},{"id":"connector-server-3", "host":"127.0.0.1", "port":4052, "clientPort": 3052, "frontend": true}],"chat":[{"id":"chat-server-1", "host":"127.0.0.1", "port":6050},{"id":"chat-server-2", "host":"127.0.0.1", "port":6051},{"id":"chat-server-3", "host":"127.0.0.1", "port":6052}],"gate":[{"id": "gate-server-1", "host": "127.0.0.1", "clientPort": 3014, "frontend": true}],"time":[{"id": "time-server-1", "host": "127.0.0.1", "port": 7052}]},"production":{"connector":[{"id":"connector-server-1", "host":"127.0.0.1", "port":4050, "clientPort": 3050, "frontend": true},{"id":"connector-server-2", "host":"127.0.0.1", "port":4051, "clientPort": 3051, "frontend": true},{"id":"connector-server-3", "host":"127.0.0.1", "port":4052, "clientPort": 3052, "frontend": true}],"chat":[{"id":"chat-server-1", "host":"127.0.0.1", "port":6050},{"id":"chat-server-2", "host":"127.0.0.1", "port":6051},{"id":"chat-server-3", "host":"127.0.0.1", "port":6052}],"gate":[{"id": "gate-server-1", "host": "127.0.0.1", "clientPort": 3014, "frontend": true}],"time":[{"id": "time-server-1", "host": "127.0.0.1", "port": 7052}]
}
}
还有adminServer.json
[{"type": "connector","token": "agarxhqb98rpajloaxn34ga8xrunpagkjwlaw3ruxnpaagl29w4rxn"
}, {"type": "chat","token": "agarxhqb98rpajloaxn34ga8xrunpagkjwlaw3ruxnpaagl29w4rxn"
},{"type": "gate","token": "agarxhqb98rpajloaxn34ga8xrunpagkjwlaw3ruxnpaagl29w4rxn"
},{"type": "time","token": "agarxhqb98rpajloaxn34ga8xrunpagkjwlaw3ruxnpaagl29w4rxn"
}]
我在connector服务器增加一个定时器的回调tick,tick函数的功能很简单,通过RPC方式调用定时器远端的pushmsg方法,将自动聊天的内容发送给客户端
Handler.prototype.tick = function(session,uid,rid) {//run all the action//put user into channelconsole.log("定时器触发了");this.app.rpc.time.timeRemote.pushmsg(session, uid, this.app.get('serverId'), rid, true);
}
接下来我们在enter的路由下面做处理,向time服务器注册玩家的uid,rid信息,到时候time服务器在推送消息的时候就知道往哪里发送了。因此添加三行代码:
/*** New client entry chat server.** @param {Object} msg request message* @param {Object} session current session object* @param {Function} next next stemp callback* @return {Void}*/
Handler.prototype.enter = function(msg, session, next) {var self = this;var rid = msg.rid;var uid = msg.username + '*' + ridvar sessionService = self.app.get('sessionService');//duplicate log inif( !! sessionService.getByUid(uid)) {next(null, {code: 500,error: true});return;}session.bind(uid);session.set('rid', rid);session.push('rid', function(err) {if(err) {console.error('set rid for session service failed! error is : %j', err.stack);}});session.on('closed', onUserLeave.bind(null, self.app));//put user into channelself.app.rpc.chat.chatRemote.add(session, uid, self.app.get('serverId'), rid, true, function(users){next(null, {users:users});});this.app.rpc.time.timeRemote.add(session, uid, this.app.get('serverId'), rid, true);console.log("当前的connectorid:" + self.app.get('serverId')); setInterval(this.tick.bind(this), 1000,session,uid,rid);
};
到了我们的time服务器编写的时间了:创建time/remote目录,增加timeRemote.js的处理
module.exports = function(app) {return new TimeRemote(app);
};var TimeRemote = function(app) {this.app = app;this.channelService = app.get('channelService');
};
TimeRemote.prototype.add = function(uid, sid, name, flag) {var channel = this.channelService.getChannel(name, flag);var username = uid.split('*')[0]; if( !! channel) {channel.add(uid, sid);}
};
TimeRemote.prototype.pushmsg = function(uid, sid, name, flag) {var channel = this.channelService.getChannel(name, flag);var username = uid.split('*')[0];console.log("定时器收到通知============"+uid+"---------"+sid+"username:"+username);var param = {route: 'onChat',msg: "hello this is robot",from: "robot",target: username};channel.pushMessage(param);
};
到了我们的测试阶段:
浏览器打开http://localhost:3001,登录后看到机器人自动给客户端发送了消息
更多pomelo框架的开发使用,请关注我