Pomelo分布式游戏服务器框架
Pomelo介绍&入门
目录
前言&介绍
Pomelo:一个快速、可扩展、Node.js分布式游戏服务器框架
从三四年前接触Node.js开始就接触到了Pomelo,从Pomelo最初的版本到现在,总的来说网易出品还算不错,但是发展不算快;用它做过一些项目和小游戏表现还不错。
用它的主要好处:
1. 入门简单,有比较丰富的文档和示例(虽然现在看版本也比较老了,但是入门没什么问题)
2.分布式多进程且扩展简单(单进程多线程,每个服务器都是一个Node进程,通过配置文件就可以管理集群)
3.可以不去关注底层和网络相关逻辑,聚焦业务逻辑的处理,对于有Web服务器开发经验却没有游戏服务器开发经验来说还是比较友好的
4.提供了很多工具和客户端支持(像IOS、Android & Java、Javascript、C、Cocos2d-x、U3D等)
......
入门参考链接
https://github.com/NetEase/pomelo/wiki/Home-in-Chinese
其它链接:
https://github.com/NetEase/pomelo
https://www.npmjs.com/package/pomelo
安装Pomelo
安装要求
Windows下安装要求环境
Python (2.5 < 版本 < 3)
VC++编译器
PS: Windows新环境自已检查一下,我本机环境已经装好python2.7,Visaul Studio也安装了所以也有VC++编译器
其它操作系统应该问题不大
官方安装介绍文档:https://github.com/NetEase/pomelo/wiki/%E5%AE%89%E8%A3%85pomelo
全局安装Pomelo
npm install pomelo -g
安装成功后如下图,可以看到现在最新版本为2.2.5
说明:Pomelo光是安装可能出现各种失败
1. 回头去检查一下,Python和VC++编辑器是否有问题
2.如果以前全局安装过Pomelo,最好删除掉 “C:\Users\当前用户\AppData\Roaming\npm\node_modules”目录下Pomelo文件夹和“C:\Users\当前用户\AppData\Roaming\npm-cache”目录下Pomelo开头的文件夹
3.如果并不报错,npm卡住不动,多数是网络原因,重复多安几次;或者打开FQ工具试试;也可以用淘宝镜像 cnpm 安装
创建项目并启动
安装好pomelo之后,开始创建项目并安装依赖项
pomelo init 项目名
执行创建项目命令后,出现如下图选择项(Please select underly connector, 1 for websocket(native socket), 2 for socket.io, 3 for wss, 4 for socket.io(wss), 5 for udp, 6 for mqtt: [1])
这是让你选择connector的协议,除了5 for udp,其它都是长连接,我们接下来选择 2 for socket.io
在上图cmd中输入2,并回车,选择socket.io继续安装
这里connector协议可以通过app.js配置进行修改// app configuration
app.configure('production|development', 'connector', function(){
app.set('connectorConfig',
{
connector : pomelo.connectors.sioconnector,
...
});
});
成功后,转到项目根目录,执行安装项目执行 npm-install.bat 依赖项 (其它平台执行npm-install.sh)
cd 项目目录
npm-install.bat
项目创建完成后,目录如下图
项目结构说明
game-server : 游戏服务器,所有游戏服务器功能和逻辑都在此目录下
game-server/app.js:入口文件
game-server/app: 存放游戏逻辑和功能相关代码都这个子目录下,servers目录下可以新建多个目录来创建不同类型的服务器,在pomelo中,使用路径来区分服务器类型
game-server/config:存放游戏服务器配置文件目录,像日志、服务器、数据库等几乎所有配置文件都可以存放到此目录下
game-server/logs:日志目录,存放游戏服务器所有日志文件
web-server: web服务器(如果你是个H5游戏,这里就是Web客户端,如果是IOS、Andriod客户端,这目录就没什么用)
shared:公共代码存放处,这里要以放一些共用代码
所有依赖项安装成功后,开始启动项目
启动game-server
cd game-server
pomelo start
启动命令执行成功后,出现如下图错误提示
[2017-11-23 11:54:42.226] [ERROR] console - Option path is not valid. Please refer to the README.
[2017-11-23 11:54:42.226] [ERROR] console - Option close timeout is not valid. Please refer to the README.
[2017-11-23 11:54:42.226] [ERROR] console - Option heartbeats is not valid. Please refer to the README.
[2017-11-23 11:54:42.226] [ERROR] console - Option log level is not valid. Please refer to the README.
问题原因和解决方式
原因:新版的socket.io用法不正确的导致的,官方早已修复,就是没有publish到npm包中
修复方式:把node_modules目录下的pomelo中sioconnector.js(../game-server/node_modules/pomelo/lib/connectors/sioconnector.js)
替换为 https://github.com/NetEase/pomelo/blob/master/lib/connectors/sioconnector.js
替换后再启动game-server,就没有这些错误提示了^_^!
测试连接
1.启动web-server
cd web-server
node app
启动后如下图
会发些有一些提示,这是express写法问题,可以打开web-server根目录下app.js,按如下修改
//var app = express.createServer(); 注释掉这一行代码,替换为下面这一行代码
var app = express();
再启动时无express用法提示^_^!
2.打开http://localhost:3001
如上图,点击“Test Game Server”按钮,提示“game server is ok.”,则此项目game-server可用^_^!
聊天服务器
上面大体了解了pomelo,要入门还是以一个聊天服务器为入门示例最好,其它逻辑相对简单,入门学习不会因其它游戏逻辑影响。
官方有个非常好的示例:https://github.com/NetEase/chatofpomelo 官方也有很多说明
网上也有很多文章分析讲解这项目,我就不完全解释些项目了,接下来我就在上面新建的好的“PomeloDemo”的基础上改成一个聊天服务器
1.新建gate和chat服务器
在app/servers目录下新建gate和chat服务器,新建好后目录如下
gate服务器:
在一般情况下用户量一台机器就可以支撑,但用户量多了就得扩充服务器,gate服务器的作用就相当于前端负载均衡服务器;
客户端向gate服务器发出请求,gate服务器会给客户端分配一个connector服务器;
分配策略是根据客户端的某一个key做hash得到connector的id,这样就可以实现各个connector服务器的负载均衡。这个一会儿会实现
connector服务器:
接受客户端请求,并将其路由到chat
服务器,以及维护客户端的链接;
同时,接收客户端对后端服务器的请求,按照用户配置的路由策略,将请求路由给具体的后端服务器。当后端服务器处理完请求或者需要给客户端推送消息的时候,connector服务器同样会扮演一个中间角色,完成对客户端的消息发送;
connector服务器会同时拥有clientPort和port,其中clientPort用来监听客户端的连接,port端口用来给后端提供服务;
chat服务器:
handler和remote决定了服务器的行为;
handler接收用户发送过来的send请求,remote由connector RPC发起远程调用时调用;
在remote里由于涉及到用户的加入和退出,所以会有对channel的操作。
其实也可以提前了解一些Pomelo中的术语,不分别解释,可以提前看看:https://github.com/NetEase/pomelo/wiki/%E6%9C%AF%E8%AF%AD%E8%A7%A3%E9%87%8A
2.配置master.json
3.配置servers.json
打开config目录下servers.json文件,配置好各种 type 的服务器,配置如下
解释一下配置中的各字段:
id: 字符串类型的应用服务器ID
host:应用服务器的IP或者域名
port:RPC请求监听的端口
clientPort: 前端服务器的客户端请求的监听端口
frontend:bool类型,是否是前端服务器,默认: false
可选参数:
max-connections:前端服务器最大客户连接数
args: node/v8
配置,如配置为"args": "--debug=5858 "
这样就可以启用项目调试(没用过,临时问了一下谷歌,看别人是这么解释的^_^!)
4.配置adminServer.json
打开config目录下adminServer.json文件,配置好各种 type 的服务器,配置如下
它有什么作用,可以看一下以下2个链接
http://blog.csdn.net/nynyvkhhiiii/article/details/49249915
https://github.com/NetEase/pomelo-admin#server-master-auth
5.解决服务器分配问题
从上面的servers.json配置的修改可以看出与最开始创建出来的项目一个服务器相比,connector和chat我都配置了三个服务器
这就要解决客户端请求服务器分配问题
解决思路:用户访问gate服务器,使用用户的uid的crc32的校验码与connector服务器的个数取余,从而得到一个connector服务器,把这个connector服务器分配给请求用户
在app目录下新建util目录,目录下新建“dispatcher.js”和 “routeUtil.js”文件,处理此服务器分配逻辑
准备好这些文件后,在game-server服务器入口文件app.js中添加配配置
var pomelo = require('pomelo');
var routeUtil = require('./app/util/routeUtil'); /**
* Init app for client.
*/
var app = pomelo.createApp();
app.set('name', 'PomeloDemo'); // app configuration
// app.configure('production|development', 'connector', function(){
app.configure('production|development', function(){
// route configures
app.route('chat', routeUtil.chat);
app.set('connectorConfig',
{
connector : pomelo.connectors.sioconnector,
// 'websocket', 'polling-xhr', 'polling-jsonp', 'polling'
transports : ['websocket', 'polling'],
heartbeats : true,
closeTimeout : 60 * 1000,
heartbeatTimeout : 60 * 1000,
heartbeatInterval : 25 * 1000
});
// filter configures
app.filter(pomelo.timeout());
}); // start app
app.start(); process.on('uncaughtException', function (err) {
console.error(' Caught exception: ' + err.stack);
});
注意:
app.configure('production|development', 'connector', function(){
修改为
app.configure('production|development', function(){
这个如果不修改,在启动调用时会遇到 engine.io 中报错 TypeError: Cannot read property 'indexOf' of undefined at Server.verify !
6.实现 gate.gateHandler.queryEntry
作用:用户连接gate服务器,返回分配的connector
在gate目录下handler下新建gateHandler.js,代码如下
7.实现chat服务器chatRemote.js
chat服务器会接受connector的远程调用,完成channel维护中的用户的加入以及离开
可以看到上面代码中的add和kick分别对应着加入和离开channel
8.实现chat服务器chatHandler.js
chat服务器执行聊天逻辑,维护channel信息,一个房间就是一个channel,一个channel里有多个用户,当有用户发起聊天的时候,就会将其内容广播到整个channel。
这里面是发送消息(给房间内所有人和指定用户)
9.实现connector中entryHandler.js
主要完成接受客户端的请求,维护与客户端的连接,路由客户端的请求到chat服务器;
这里完成的主要就是RPC远程调用chat服务器chatRemote中的实现
10.运行
到此这个聊天服务器实现就完成, 打开命令行工具,执行没有错误信息,基本就成功了!
cd game-server目录
pomelo start
编写web聊天客户端测试
我就在web-server目录中写了个测试客户端
把结构改了一下,换成了ejs模版,代码如下
routes中index.js文件代码
views中index.ejs文件代码
<html>
<head><title><%= title %></title></head>
<body>
<div id="tipMsg" style="color:red; height:30px;"></div>
<div id="pnlLogin">
<h1>1.登录(连接Gate服务器)</h1>
用户名:<input id="txtUserName" type="text" maxlength="20" ></br>
房间号:<input id="txtRoomId" type="text" maxlength="8" >
<input id="btnLogin" type="button" value="点击登录" />
<br/>
</div>
<div id="pnlChat" style="display:none;">
<h1>2.聊天室</h1>
用户名:<span id="spUserName" style="color:blue;padding-right:50px;"></span>
房间号:<span id="spRoomId" style="color:blue;padding-right:50px;"></span>
<div id="txtMessage" style="width:800px; height:400px;border:1px solid #000; overflow-y:auto; overflow-x:hidden; "></div>
<br/>
发送给:<select id="selUserList" style="width:200px;">
<option value="*">所有人</option>
</select>
<br/>
<br/>
<textarea id="txtSendMessage" type="text" style="width:690px;height:80px; overflow:auto;float:left;" ></textarea>
<input id="btnSend" type="button" value="发送" style="margin-left:10px; height:80px; width:100px;float:left;" />
</div>
</body>
</html>
<script src="js/socket.io.js"></script>
<script src="js/pomeloclient.js"></script>
<script src="js/jquery-1.11.2.min.js"></script>
<script type="text/javascript"> $(function(){ var rid = '';
var uname = ''; //监听"onAdd", 当有新用户加入时触发
pomelo.on('onAdd', function(data) {
var user = data.user;
$('#txtMessage').append('<span style="color:green;">[上线提醒]:欢迎 ' + user + ' 加入聊天室<span><br/>'); //添加到用户列表
$('#selUserList').append('<option value="' + user + '">' + user + '</option>');
}); //监听"onLeave", 当有用户离开聊天室时触发
pomelo.on('onLeave', function(data) {
var user = data.user;
$('#txtMessage').append('<span style="color:green;">[离线提醒]: ' + user + ' 离开聊天室<span><br/>'); //从用户列表移除
$('#selUserList option[value="' + user + '"]').remove();
}); // 监听"onChat", 接收消息
pomelo.on('onChat', function(data) {
var from = data.from,
target = data.target,
msg = data.msg; if(msg === null) return; var name = (target == '*' ? '所有人' : target);
var time = getNowFormatDate(); $("#txtMessage").append('<span style="color:blue;">[' + time +'][' + from + '] 对 [' + name + '] 说: ' + msg + '<span><br/>');
}); //当从聊天断开时
pomelo.on('disconnect', function(reason) {
$('#pnlLogin').show();
$('#pnlChat').hide();
}); //登录
$('#btnLogin').on('click', function(){
var userNameReg = /^[a-zA-Z0-9]+$/,
roomIdReg = /^[0-9]+$/; uname = $.trim($('#txtUserName').val()),
rid = $.trim($('#txtRoomId').val()); if(uname.length == 0){
alert('请输入登录名');
return false;
} if(!userNameReg.test(uname)) {
alert('登录名只能由字母或数字组成');
return false;
} if(rid.length == 0){
alert('请输入房间号');
return false;
} if(!roomIdReg.test(rid)) {
alert('房间号只能是数字');
return false;
} queryEntry(uname, function(host, port){
$('#tipMsg').append('Gate 连接成功! host:' + host + ' port:' + port + '<br/>'); //连接聊天服务器
pomelo.init({
host: host,
port: port,
log: true
}, function() {
var route = "connector.entryHandler.enter";
pomelo.request(route, {
username: uname,
rid: rid
}, function(data) {
if(data.error) {
$('#tipMsg').append('Chat 连接失败!<br/>');
return;
} $('#tipMsg').append('Chat 连接成功!<br/>'); $('#pnlLogin').hide();
$('#pnlChat').show(); $('#spUserName').text(uname);
$('#spRoomId').text(rid); //加载当前聊天室 已在线用户列表
$.each(data.users, function(i, item){
if(item != uname){
$('#selUserList').append('<option value="' + item + '">' + item + '</option>');
}
}); });
});
});
}); //发送消息
$('#btnSend').on('click', function(){
var route = "chat.chatHandler.send",
target = $("#selUserList").val(),
msg = $.trim($("#txtSendMessage").val()); if(msg.length == 0){
alert('不能发送空消息!');
return;
} pomelo.request(route, {
rid: rid,
content: msg,
from: uname,
target: target
}, function(data) { $("#txtSendMessage").val(''); if(from == uname) {
var name = (target == '*' ? '所有人' : target);
var time = getNowFormatDate(); $("#txtMessage").append('<span style="color:blue;">[' + time +'][' + from + '] 对 [' + name + '] 说: ' + msg + '<span><br/>');
} }); }); }) //连接Gate服务器
function queryEntry(uid, callback) {
var route = 'gate.gateHandler.queryEntry';
pomelo.init({
host: '127.0.0.1',
port: 15014,
log: true
}, function() {
pomelo.request(route, {
uid: uid
}, function(data) {
pomelo.disconnect();
if(data.code === 500) {
alert('用户名在此房间中已存在,请重新输入新的用户名!');
return;
}
callback(data.host, data.port);
});
});
}; function getNowFormatDate() {
var date = new Date();
var seperator1 = "-";
var seperator2 = ":";
var month = date.getMonth() + 1;
var strDate = date.getDate();
if (month >= 1 && month <= 9) {
month = "0" + month;
}
if (strDate >= 0 && strDate <= 9) {
strDate = "0" + strDate;
}
var currentdate = date.getFullYear() + seperator1 + month + seperator1 + strDate
+ " " + date.getHours() + seperator2 + date.getMinutes()
+ seperator2 + date.getSeconds();
return currentdate;
} </script>
app.js代码如下:
运行起来后,测试结果如下图:
写在之后
Pomelo学习入门不算复杂,写一篇感觉讲不全,写多篇感觉太散,大家将就着看,有些东西不认识的还是去看一下API文档 http://pomelo.netease.com/api.html (也是低水准的官方API^_^!)
或者问一下google啥的...
可以参考这两个例子来学习:
https://github.com/NetEase/chatofpomelo
https://github.com/NetEase/lordofpomelo
入门建议从chatofpomelo开始
看之前可以提前看看一些pomelo术语,有个大体了解,再边看代码边理解:https://github.com/NetEase/pomelo/wiki/%E6%9C%AF%E8%AF%AD%E8%A7%A3%E9%87%8A
主要参考资料:
https://github.com/NetEase/pomelo/wiki/Home-in-Chinese (比较杂乱,可能官方大神都忙着搞赚钱的项目,将就着看,有很多东西对入门来说还是很有用的)
如果有些问题解决不了,可以去社区问一下:http://nodejs.netease.com/tag/pomelo (感觉现在活跃度也比较低^_^!)
Pomelo分布式游戏服务器框架的更多相关文章
- Unity3d&C#分布式游戏服务器ET框架介绍-组件式设计
前几天写了<开源分享 Unity3d客户端与C#分布式服务端游戏框架>,受到很多人关注,QQ群几天就加了80多个人.开源这个框架的主要目的也是分享自己设计ET的一些想法,所以我准备写一系列 ...
- Leaf - 一个由 Go 语言编写的开发效率和执行效率并重的开源游戏服务器框架
转自:https://toutiao.io/posts/0l7l7n/preview Leaf 游戏服务器框架简介 Leaf 是一个由 Go 语言(golang)编写的开发效率和执行效率并重的开源游戏 ...
- 游戏服务器框架:Leaf/go
Leaf 是一个使用 Go 语言开发的开源游戏服务器框架,注重运行效率并追求极致的开发效率.Leaf 适用于几乎所有的游戏类型.其主要的特性: 良好的使用体验.Leaf 总是尽可能的提供简洁和易用的接 ...
- skynet游戏服务器框架分享
分享下我之前做的服务器框架; 游戏在线最高3万; 物理机I7的3台阿里云分服;性能及其强劲; 框架: 底层基于比较流行的skynet,基础采用c语言,脚本lua,部分服务golang; Skyne ...
- 基于Golang的游戏服务器框架cellnet开发日记(二)
看官们肯定还有大部分不是很熟悉Actor模型. 我这里基于Erlang, Skynet等语言和框架库来实战型解释下Actor模型. Actor概念 Actor模型和OO类似, 都是符合人的思维模式进 ...
- C# 游戏服务器框架
http://www.supersocket.net/ http://blog.csdn.net/zhuweisky/article/details/9055989 http://blog.csdn. ...
- 深入浅出node.js游戏服务器开发1——基础架构与框架介绍
2013年04月19日 14:09:37 MJiao 阅读数:4614 深入浅出node.js游戏服务器开发1——基础架构与框架介绍 游戏服务器概述 没开发过游戏的人会觉得游戏服务器是很神秘的 ...
- GoWorld – 用Golang写一个分布式可扩展、可热更的游戏服务器
GoWorld代码:https://github.com/xiaonanln/goworld Golang具有运行效率高.内存安全等优良特性,因此是非常适合用来进行服务器开发.使用Golang开发游戏 ...
- 使用 Go 语言开发大型 MMORPG 游戏服务器怎么样?(非常稳定、捕获所有异常、非常适合从头开始,但大公司已经有现成的C++框架、所以不会使用)
使用 Go 语言开发大型 MMORPG 游戏服务器怎么样?和C Socket服务器比起来有什么优劣?可行性怎么样? 从2013年起,经朋友推荐开始用Golang编写游戏登陆服务器, 配合C++做第三方 ...
随机推荐
- Django_博客_XSS 攻击防范
背景: 博客项目中用户后台添加文章时,若通过富文本编辑器输入 标签内容或者 js 指令会导致文章排版错乱,甚至进行XSS攻击 攻击现象: 文本内容输入 js 指令 文章描述时正确显示其文本内容 但在打 ...
- 纯CSS3实现打火机火焰动画
HTML5已经越来越流行起来了,尤其是移动互联网的发展,更是带动了HTML5的迅猛发展,我们也是时候学习HTML5了,以防到时候落伍.今天给大家介绍10款效果惊艳的HTML5应用,方便大家学习,也将应 ...
- 手速太慢QAQ
显然D是个细节题,但是还剩1h时看眼榜还没人过EF,只好冷静写D,大概思路是任何时候如果min(n,m)<=2,max(n,m)<=4暴搜,否则直接贪心是很对的,即第一步让S.T长度平均化 ...
- NOIP2018备考——DP专题练习
P4095 [HEOI2013]Eden 的新背包问题 P2657 [SCOI2009]windy数 P3413 SAC#1 - 萌数 P3205 [HNOI2010]合唱队 P476 ...
- 【转】Example of using the --info linker option
5.3 Example of using the --info linker option This is an example of the output generated by the --in ...
- 遭遇:“传入的表格格式数据流(TDS)远程过程调用(RPC)协议流不正确” 错误
http://www.cnblogs.com/delphinet/archive/2010/03/09/1681777.html 正在写一个类似文章的发表系统.其中记录文章内容的字段Contents设 ...
- 螺旋队列和hiho1525逃离迷宫3
我是真调不出错误了! hiho1525逃离迷宫3 #include <stdio.h> #include <stdlib.h> #include <math.h> ...
- JavaScript事件代理和委托
在javasript中,代理.委托经常出现. 那么它究竟在什么样的情况下使用?它的原理又是什么? 这里介绍一下javascript delegate的用法和原理,以及Dojo,jQuery等框架中de ...
- DNA序列编码中Hairpin的定义和计算
DNA序列编码中Hairpin的定义和计算 觉得有用的话,欢迎一起讨论相互学习~Follow Me 参考文献 [1] 张凯. DNA计算核酸编码优化及算法设计[D]. 2008. [2] Shin, ...
- Codeforces 835E. The penguin's game
http://codeforces.com/problemset/problem/835/E 题意: 这是一道交互题 有n个数,其中有2个y,n-2个x 每次你可以询问若干个数的异或和,从而得出y的位 ...