开启辅助访问
 找回密码
 注册帐号

扫一扫,访问微社区

开发者专栏

关注:2103

当前位置:游戏蛮牛 技术专区 开发者专栏

__________________________________________________________________________________
开发者干货区版块规则:

  1、文章必须是图文形式。(至少2幅图)
      2、文章字数必须保持在1500字节以上。(编辑器右下角有字数检查)
      3、本版块只支持在游戏蛮牛原创首发,不支持转载。
      4、本版块回复不得无意义,如:顶、呵呵、不错......【真的会扣分的哦】
      5、......
__________________________________________________________________________________
查看: 2647|回复: 22

[碧俐千仞] 《球球大作战》源码解析—— (10)分布式服务端

[复制链接]  [移动端链接]
排名
18578
昨日变化
4

30

主题

186

帖子

737

积分

Rank: 9Rank: 9Rank: 9

UID
53741
好友
36
蛮牛币
1975
威望
0
注册时间
2014-11-6
在线时间
127 小时
最后登录
2018-1-28

专栏作家

发表于 2018-1-17 22:28:16 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有帐号?注册帐号

x

知乎@罗培羽

这套源码采用单进程单线程架构,受限于cpu一个核心的运算能力,该套源码也就能够撑几十人同时在线(估算),再多服务端会很卡,帧率不能保证。那么,怎样才能撑起更多人呢,必须采用分布式架构。源码作者给出了一套聊天系统分布式方案,试图整合到球球大作战源码中(至今未完成),作者开了个分支存放该聊天代码(https://github.com/huytd/agar.io-clone/tree/multi-server),该分布式采用了redis作为消息转发的载体,实现进程间通信。下文前半部分翻译自github中wiki的内容,后半部分为笔者的评价。

Wiki翻译
为什么用分支
随着在线人数的增长,网络延迟变得越来越严重。于是添加使用服务端集群(服务器代码在gateway.js),再使用redis实现消息转发。

服务端架构
聊天例子启用了4个服务端进程,再启用1个redis进程,通过它的订阅/发布功能来转发消息。
======================================================
|                       REDIS                        |
======================================================
||                ||                ||              ||
||                ||                ||              ||
||                ||                ||              ||     
Server #1       Server #2        Server #3       Server #4

在此结构下,我们可以用多个进程分担计算压力,然后通过进程间通信传输信息(如食物、玩家、聊天信息等)。

如何运行
1、配置环境
安装并运行redis服务端(redis-server)
2、启动服务器
下载源码的multi-server分支,使用npm run cluster命令运行服务器。程序会开启4个服务端进程,分别监听http://localhost:3000, http://localhost:3001,http://localhost:3002和http://localhost:3003
3、测试
用浏览器打开http://localhost:3000,登陆进去
用浏览器打开http://localhost:3001,登陆进去
在其中一个客户端中发送聊天信息,另一个客户端将会看到该信息。

运行起来
由于作者的wiki只有上面几句,笔者稍作整理,写出更详细一些的运行流程。

1、下载源码,切换到源码目录,安装库文件:npm install
2、安装redis: sudo apt-get install redis-server
3、启动redis:redis-server
4、运行服务端: npm run cluster

此时可以看到服务端的输出消息,没有报错,开启成功
球球大作战 源码解析101371.png



如果执行ps -ef | grep node查看进程,可以看到有4个进程集群进程。
球球大作战 源码解析101417.png



用浏览器打开客户端,如下图中,两个客户端连接着不同的服务器进程,但它们依然可以相互聊天。
球球大作战 源码解析101466.png


代码解析
项目源码可在下面的地址中下载,主要目录结构如下图所示、
https://github.com/huytd/agar.io-clone/tree/multi-server
球球大作战 源码解析101632.png




先看启动进程的gateway.js,它的功能是fork出另外几个进程,代码如下。

[AppleScript] 纯文本查看 复制代码
var SERVER_COUNT = process.env.SERVER_COUNT || 2;
var cluster = require('cluster');
var Server = require('./server');

if (cluster.isMaster) {
    // Spawn servers here
    for (var i = 0; i < SERVER_COUNT; i++) {
        var worker = cluster.fork();
        worker.send('index:' + i.toString());
        console.log('Spawned #' + i);
    }
} else {
    // Get the index from master to spawn new process
    process.on('message', function(msg) {
        var match = msg.match(/index:(\d+)/);
        if (match !== null) {
            var idx = parseInt(match[1]);
            var gameServer = new Server();
            gameServer.start(idx);
        }
    });
}



process.on(XXX):表示收到信号的时候执行某个方法,在上述代码中,如果该进程是fork出来的子进程,那么让它监听message,当收到message时,执行gameServer.start,并传入自身id。如果是父进程,它会计算每个子进程的id,然后给子进程发送message。关于process的介绍可以参见下面的文章:http://blog.csdn.net/xufeng0991/article/details/45395251

cluster:代码中用到isMaster和fork。通过isMaster判断该进程是否为父进程,通过fork建立子进程。父进程会调用send方法给子进程发送信号,子进程收到信号后新建Server,然后启动它,也即是执行server.js进程。关于cluster可以参见下面的文章:http://nodejs.org/api/cluster.html

server.js是服务端的核心代码,聊天逻辑全在里面。当服务端发送聊天时,会有如下输出。
球球大作战 源码解析102915.png


聊天时,客户端会发送playerChat协议,服务端收到后调用handlePlayerChat。

[AppleScript] 纯文本查看 复制代码
            socket.on('playerChat', function(data) {
                handlePlayerChat(data, true);
            });


handlePlayerChat的代码如下所示。核心代码为if (withRedis) {……}里面的语句,它先广播serverSendPlayerChat协议,即向本服的玩家发送聊天信息。然后使用redisPublish.publish向redis发送数据。

[AppleScript] 纯文本查看 复制代码
            function handlePlayerChat(data, withRedis) {
                var _sender = data.sender.replace(/(<([^>]+)>)/ig, '');
                var _message = data.message.replace(/(<([^>]+)>)/ig, '');
                var chat = {sender: _sender, message: _message.substring(0,35)};
                ……
                if (withRedis) {
                    socket.broadcast.emit('serverSendPlayerChat', chat);
                    // Publish chat to redis
                    console.log("SERVER #" + self.serverIndex + " published chat");
                    redisPublish.publish('server:' + self.serverIndex + ':SharePlayerChat', JSON.stringify(chat));
                } else {
                    socket.emit('serverSendPlayerChat', chat);
                }
            }


关于redis的订阅/发布功能,可以参考下面的文章:
http://redis.readthedocs.io/en/2.4/pub_sub.html
psubscribe:http://www.runoob.com/redis/pub-sub-psubscribe.html
publish:http://www.runoob.com/redis/pub-sub-publish.html


在Server启动时候就订阅了所有频道,所以当某个客户端publish数据时,redis将向订阅该频道的所有server发送数据,代码如下。Redis只是一个转发器。

[AppleScript] 纯文本查看 复制代码
    function Server() {
        ……
        redisClient.psubscribe('*');



当收到redis转发的数据时,执行下面的代码。先通过match解析字符串获取发出聊天的服务器id,即emittedServer。因为发送聊天时已经给本服玩家广播了聊天消息,所以只对非本服玩家做处理,即调用handlePlayerChat(chat, false),注意第二个参数是false,这时会进入handlePlayerChat方法中if(withRedis)的else部分,即向客户端发送协议。

[AppleScript] 纯文本查看 复制代码
            redisClient.on('pmessage', function(pattern, channel, data){
                //if (pattern == 'server:*:serverSendPlayerChat') {
                    console.log("Server " + self.serverIndex + " received");
                    var match = channel.match(/server:(\d+):SharePlayerChat/);
                    var chat = JSON.parse(data);
                    if (match !== null) {
                        var emittedServer = parseInt(match[1]);
                        console.log("I am #" + self.serverIndex + " received from #" + emittedServer + " with data: ");
                        if (emittedServer != self.serverIndex) {
                            console.log(chat);
                            handlePlayerChat(chat, false);
                        }
                    }
                //}
            });


如此便完成了消息转发,笔者认为这个方案做做简单聊天就好了,做球球大作战的分布式开发是远远不够的,因为仅有的消息转发并不能让游戏撑起更多的玩家。要让游戏撑起更多玩家,必须要分房间,一个进程负责一定数量的房间逻辑。再有一个匹配服务器,负责全服的战斗匹配。
球球大作战 源码解析105725.png


还是放个广告吧,笔者出版的一本书《网络游戏实战》充分的讲解怎样开发一款网络游戏,特别对网络框架设计、网络协议、数据处理等方面都有详细的描述,相信会是一本好书的。目前已经在规划第二版,相信是一本好书。
球球大作战 源码解析105819.png



回复

使用道具 举报

7日久生情
2520/5000
排名
3670
昨日变化
21

2

主题

1840

帖子

2520

积分

Rank: 7Rank: 7Rank: 7Rank: 7

UID
241666
好友
0
蛮牛币
10036
威望
0
注册时间
2017-9-6
在线时间
342 小时
最后登录
2018-2-22
发表于 2018-1-18 07:19:55 来自Mobile--- | 显示全部楼层
感谢分享

回复

使用道具 举报

6蛮牛粉丝
1058/1500
排名
5784
昨日变化
2

6

主题

638

帖子

1058

积分

Rank: 6Rank: 6Rank: 6

UID
236677
好友
0
蛮牛币
1693
威望
0
注册时间
2017-8-9
在线时间
218 小时
最后登录
2018-2-22
发表于 2018-1-18 09:05:54 | 显示全部楼层
感谢分享
[发帖际遇]: 一个袋子砸在了 lidong91 头上,lidong91 赚了 1 蛮牛币. 幸运榜 / 衰神榜

回复

使用道具 举报

7日久生情
1907/5000
排名
3785
昨日变化

7

主题

1025

帖子

1907

积分

Rank: 7Rank: 7Rank: 7Rank: 7

UID
168159
好友
3
蛮牛币
5650
威望
0
注册时间
2016-9-12
在线时间
557 小时
最后登录
2018-2-9

锦衣玉食

发表于 2018-1-18 09:31:33 | 显示全部楼层

回复

使用道具 举报

7日久生情
1610/5000
排名
4822
昨日变化
46

0

主题

1162

帖子

1610

积分

Rank: 7Rank: 7Rank: 7Rank: 7

UID
219676
好友
0
蛮牛币
1474
威望
0
注册时间
2017-7-12
在线时间
198 小时
最后登录
2018-2-22
发表于 2018-1-18 09:48:33 | 显示全部楼层
谢谢分享

回复

使用道具 举报

6蛮牛粉丝
1212/1500
排名
2363
昨日变化

3

主题

333

帖子

1212

积分

Rank: 6Rank: 6Rank: 6

UID
159631
好友
1
蛮牛币
2479
威望
0
注册时间
2016-7-30
在线时间
364 小时
最后登录
2018-2-22
发表于 2018-1-19 09:20:54 | 显示全部楼层
谢谢分享

回复

使用道具 举报

4四处流浪
323/500
排名
11951
昨日变化
2

3

主题

121

帖子

323

积分

Rank: 4

UID
216830
好友
1
蛮牛币
221
威望
0
注册时间
2017-4-9
在线时间
137 小时
最后登录
2018-2-10
发表于 2018-1-19 09:24:17 | 显示全部楼层

回复

使用道具 举报

3偶尔光临
163/300
排名
9784
昨日变化
3

0

主题

47

帖子

163

积分

Rank: 3Rank: 3Rank: 3

UID
248343
好友
0
蛮牛币
323
威望
0
注册时间
2017-10-12
在线时间
26 小时
最后登录
2018-2-11
发表于 2018-1-19 10:06:44 | 显示全部楼层
v5v5v5v5v5

回复

使用道具 举报

5熟悉之中
746/1000
排名
3275
昨日变化

1

主题

137

帖子

746

积分

Rank: 5Rank: 5

UID
57980
好友
0
蛮牛币
379
威望
0
注册时间
2014-11-27
在线时间
234 小时
最后登录
2018-2-8
QQ
发表于 2018-1-19 11:44:24 | 显示全部楼层
谢谢分享

回复

使用道具 举报

5熟悉之中
572/1000
排名
7639
昨日变化
1

0

主题

317

帖子

572

积分

Rank: 5Rank: 5

UID
146677
好友
9
蛮牛币
2226
威望
0
注册时间
2016-4-25
在线时间
121 小时
最后登录
2018-2-5
QQ
发表于 2018-1-19 13:48:40 | 显示全部楼层
谢谢分享,楼主加油

回复 支持 反对

使用道具 举报

5熟悉之中
767/1000
排名
4067
昨日变化

0

主题

222

帖子

767

积分

Rank: 5Rank: 5

UID
227843
好友
1
蛮牛币
2116
威望
0
注册时间
2017-6-20
在线时间
249 小时
最后登录
2018-2-3
发表于 2018-1-19 14:25:05 | 显示全部楼层
{:107:}{:107:}{:107:}

回复 支持 反对

使用道具 举报

排名
20087
昨日变化
2

0

主题

8

帖子

35

积分

Rank: 1

UID
265289
好友
0
蛮牛币
70
威望
0
注册时间
2018-1-19
在线时间
9 小时
最后登录
2018-2-5
发表于 2018-1-19 15:18:16 | 显示全部楼层
占楼,以后仔细研究研究

回复 支持 反对

使用道具 举报

5熟悉之中
785/1000
排名
4293
昨日变化
3

4

主题

234

帖子

785

积分

Rank: 5Rank: 5

UID
160770
好友
9
蛮牛币
503
威望
0
注册时间
2016-8-5
在线时间
263 小时
最后登录
2018-2-22
发表于 2018-1-22 08:55:56 | 显示全部楼层
学习了,感谢分享

回复

使用道具 举报

4四处流浪
388/500
排名
10592
昨日变化

3

主题

80

帖子

388

积分

Rank: 4

UID
190325
好友
1
蛮牛币
324
威望
0
注册时间
2016-12-7
在线时间
227 小时
最后登录
2018-2-22
发表于 2018-1-22 09:13:52 | 显示全部楼层
非常期待第二部书啊,网络游戏实战让我学到了太多了,非常好的一本书!

回复 支持 反对

使用道具 举报

3偶尔光临
233/300
排名
9094
昨日变化

0

主题

81

帖子

233

积分

Rank: 3Rank: 3Rank: 3

UID
251059
好友
1
蛮牛币
121
威望
0
注册时间
2017-10-27
在线时间
50 小时
最后登录
2018-2-7
发表于 2018-1-22 09:14:00 | 显示全部楼层
感谢分享

回复

使用道具 举报

您需要登录后才可以回帖 登录 | 注册帐号

本版积分规则

关闭

站长推荐 上一条 /1 下一条

快速回复 返回顶部 返回列表