构建 ActionScript 游戏服务器,
 支持超过 15000 并发连接
Building an ActionScript Game Server with over
        15,000 Concurrent Connections


             Renaun Erickson

                                       1
基本 MMO 游戏架构




        •
        •
        •
        •
        联 数 服 客
        网
          据 务 户
            器 端
          库




              2
MMO 网络注意事项




 •
 •
 •
 •
 •
 数 数 延 防 协
         议
 据 据 迟 火




           (TCP, UDP, HTTP)
 架 包   墙
 构 的
  大
  小
                              3
必须能够创造出大量的连接,
实现在一个合理的速度和带宽内发
      送数据包




            4
套接字实现
• 检查每个文件描述符的每一帧
 – select, poll
    • 大量的连接慢
• 操作系统优化
 – epoll, kqueue, /dev/poll, pollset, I/O Completion
   Ports
    • 快速操作系统的具体实施
• 基于事件库
 – Libevent and libev
    • 快速大量的连接
    • 可移植的代码 , 隐藏操作系统的具体实施


                                            5
服务器配置

• 配置操作系统允许大量文件描述符
• 配置操作系统为每个 file descriptor 设
  置的 RAM 大小
• 配置操作系统网络协议的缓冲区大小
• 配置操作系统用什么范围的端口 , 每个
  IP 地址有 65000 个端口


                        6
客户端和服务器上的 Flash

• 在中国,许多 MMO 游戏使用 Flash 作为客户
  端
• 服务器自然有 MMO 游戏逻辑
• 出于安全的考虑,把游戏逻辑放在服务器端
• 客户端和服务器上使用同一种语言的许多好处


服务器端使用 ActionScript 可能吗?



                        7
服务器上的 ActionScript

• Flash Player 解剖
  – Core: 虚拟机 , 原语 , ActionScript 句法
  – APIs: C/C++ 实现丰富的 Flash Player API 和
    功能的类
• Tamarin
  – 开源的 Flash Player 器的核心部件
  – 没有丰富的 API 或功能,你必须自己编写



                                   8
Tamarin 的基础工程

• Thane (part of Whirled SDK by ThreeRings)
  – http://wiki.whirled.com/Whirled_SDK
• Redtamarin (by zwetan and others)
  – http://code.google.com/p/redtamarin/
• PushButton Engines Network component by
  Ben Garney
  – https://github.com/PushButtonLabs/PBNetwork
    ing


                                           9
案例研究 : SpellTraction




设计通过 : Garth Braithwaite http://www.garthdb.com/ @garthdb
                                                  10
现场演示和源代码


• http://renaun.com/serveras/test
• http://renaun.com/serveras/spelltraction/
• https://github.com/renaun/ActionScriptGa
  meServerExamples




                                    11
服务器设置
• 游戏服务器
 – Amazon EC2 Medium 服务器
 – 64 bit Ubuntu OS
 – 运行两个游戏实例
   • 主要的游戏
   • 负载测试游戏
• 负载测试服务器
 – Amazon EC2 Medium 服务器
 – 自定义测试脚本


                           12
服务器代码


• Redtamarin
  – 创建 ServerSocket.as – libev 套接字实现
  – ActionScript 文件编译成 ActionScript 字节
    码( ABC 文件)
  – 命令行 redtamarin 外壳运行 ABC 文件




                                 13
服务器 ActionScript 代码
function loopHandler():void
{
        if (!ss.listening)
                ss.listen("10.111.33.190", 12122);
        else
                serverBrain.beat(); // Game Tick
}

var serverDispatcher:SocketServerNerveDispatcher
        = new SocketServerNerveDispatcher();
var serverBrain:ServerBrain = new ServerBrain();
var serverNerve:ServerNerveSystem
         = new ServerNerveSystem(serverBrain, serverDispatcher);
serverBrain.addNerve(serverNerve);
var ss:ServerSocket
         = serverDispatcher.createServerSocket(serverNerve);
ss.loop.add(loopHandler);
ss.start(250);




                                                        14
libev 套接字实现
bool ServerSocketObject::_listen(Stringp host, const int port)
{
    struct socket_io w_accept;

    StUTF8String hostUTF8(host);
    const char* hostAddr = hostUTF8.c_str();

    if( (w_accept.fd = socket(PF_INET, SOCK_STREAM, 0)) < 0 ) return false;
    int flags = fcntl(w_accept.fd, F_GETFL, 0);
    fcntl(w_accept.fd, F_SETFL, (flags > 0 ? flags : 0) | O_NONBLOCK);

    sockaddr_in addr;
    memset(&addr, 0, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = inet_addr(hostAddr);
    addr.sin_port = htons(port);
    int status = bind(w_accept.fd, reinterpret_cast<struct sockaddr *>(&addr), sizeof(addr));
    if (status != 0) return false;

    _socket = w_accept.fd;
    if (listen(w_accept.fd, 2) < 0) return false;
    w_accept.socketObject = this;

    // Initialize and start a watcher to accepts client requests
    ev_io_init(&w_accept.io, accept_cb, w_accept.fd, EV_READ);
    ev_io_start(loop_ev, &w_accept.io);
    ev_loop(loop_ev, 0);
    return true;
}



                                                                                 15
Linux 内核配置
/etc/sysctl.conf
net.core.rmem_max = 33554432
net.core.wmem_max = 33554432
net.core.rmem_default = 1048576
net.core.wmem_default = 1048576
net.core.optmem_max = 33554432
net.ipv4.tcp_rmem = 4096 4096 33554432
net.ipv4.tcp_wmem = 4096 4096 33554432
net.ipv4.tcp_mem = 786432 1048576 26777216
net.ipv4.tcp_max_tw_buckets = 360000
net.core.netdev_max_backlog = 30000
net.ipv4.ip_local_port_range = 2048 65535
net.ipv4.tcp_window_scaling = 1
net.ipv4.tcp_no_metrics_save = 1
net.core.somaxconn = 131072
fs.file-max = 131072

/usr/include/linux/limits.h
NR_OPEN = 65536
/etc/security/limits.conf
*                soft     nofile        65535
*                hard     nofile        65535




                                                16
客户端代码
 客户




              共享代码




服务器



                 17
Q&A


  renaun@adobe.com

  http://github.com/renaun

  @renaun

  http://renaun.com/blog




                       18

构建ActionScript游戏服务器,支持超过15000并发连接

  • 1.
    构建 ActionScript 游戏服务器, 支持超过 15000 并发连接 Building an ActionScript Game Server with over 15,000 Concurrent Connections Renaun Erickson 1
  • 2.
    基本 MMO 游戏架构 • • • • 联 数 服 客 网 据 务 户 器 端 库 2
  • 3.
    MMO 网络注意事项 • • • • • 数 数 延 防 协 议 据 据 迟 火 (TCP, UDP, HTTP) 架 包 墙 构 的 大 小 3
  • 4.
  • 5.
    套接字实现 • 检查每个文件描述符的每一帧 –select, poll • 大量的连接慢 • 操作系统优化 – epoll, kqueue, /dev/poll, pollset, I/O Completion Ports • 快速操作系统的具体实施 • 基于事件库 – Libevent and libev • 快速大量的连接 • 可移植的代码 , 隐藏操作系统的具体实施 5
  • 6.
    服务器配置 • 配置操作系统允许大量文件描述符 • 配置操作系统为每个file descriptor 设 置的 RAM 大小 • 配置操作系统网络协议的缓冲区大小 • 配置操作系统用什么范围的端口 , 每个 IP 地址有 65000 个端口 6
  • 7.
    客户端和服务器上的 Flash • 在中国,许多MMO 游戏使用 Flash 作为客户 端 • 服务器自然有 MMO 游戏逻辑 • 出于安全的考虑,把游戏逻辑放在服务器端 • 客户端和服务器上使用同一种语言的许多好处 服务器端使用 ActionScript 可能吗? 7
  • 8.
    服务器上的 ActionScript • FlashPlayer 解剖 – Core: 虚拟机 , 原语 , ActionScript 句法 – APIs: C/C++ 实现丰富的 Flash Player API 和 功能的类 • Tamarin – 开源的 Flash Player 器的核心部件 – 没有丰富的 API 或功能,你必须自己编写 8
  • 9.
    Tamarin 的基础工程 • Thane(part of Whirled SDK by ThreeRings) – http://wiki.whirled.com/Whirled_SDK • Redtamarin (by zwetan and others) – http://code.google.com/p/redtamarin/ • PushButton Engines Network component by Ben Garney – https://github.com/PushButtonLabs/PBNetwork ing 9
  • 10.
    案例研究 : SpellTraction 设计通过: Garth Braithwaite http://www.garthdb.com/ @garthdb 10
  • 11.
  • 12.
    服务器设置 • 游戏服务器 –Amazon EC2 Medium 服务器 – 64 bit Ubuntu OS – 运行两个游戏实例 • 主要的游戏 • 负载测试游戏 • 负载测试服务器 – Amazon EC2 Medium 服务器 – 自定义测试脚本 12
  • 13.
    服务器代码 • Redtamarin – 创建 ServerSocket.as – libev 套接字实现 – ActionScript 文件编译成 ActionScript 字节 码( ABC 文件) – 命令行 redtamarin 外壳运行 ABC 文件 13
  • 14.
    服务器 ActionScript 代码 functionloopHandler():void { if (!ss.listening) ss.listen("10.111.33.190", 12122); else serverBrain.beat(); // Game Tick } var serverDispatcher:SocketServerNerveDispatcher = new SocketServerNerveDispatcher(); var serverBrain:ServerBrain = new ServerBrain(); var serverNerve:ServerNerveSystem = new ServerNerveSystem(serverBrain, serverDispatcher); serverBrain.addNerve(serverNerve); var ss:ServerSocket = serverDispatcher.createServerSocket(serverNerve); ss.loop.add(loopHandler); ss.start(250); 14
  • 15.
    libev 套接字实现 bool ServerSocketObject::_listen(Stringphost, const int port) { struct socket_io w_accept; StUTF8String hostUTF8(host); const char* hostAddr = hostUTF8.c_str(); if( (w_accept.fd = socket(PF_INET, SOCK_STREAM, 0)) < 0 ) return false; int flags = fcntl(w_accept.fd, F_GETFL, 0); fcntl(w_accept.fd, F_SETFL, (flags > 0 ? flags : 0) | O_NONBLOCK); sockaddr_in addr; memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_addr.s_addr = inet_addr(hostAddr); addr.sin_port = htons(port); int status = bind(w_accept.fd, reinterpret_cast<struct sockaddr *>(&addr), sizeof(addr)); if (status != 0) return false; _socket = w_accept.fd; if (listen(w_accept.fd, 2) < 0) return false; w_accept.socketObject = this; // Initialize and start a watcher to accepts client requests ev_io_init(&w_accept.io, accept_cb, w_accept.fd, EV_READ); ev_io_start(loop_ev, &w_accept.io); ev_loop(loop_ev, 0); return true; } 15
  • 16.
    Linux 内核配置 /etc/sysctl.conf net.core.rmem_max =33554432 net.core.wmem_max = 33554432 net.core.rmem_default = 1048576 net.core.wmem_default = 1048576 net.core.optmem_max = 33554432 net.ipv4.tcp_rmem = 4096 4096 33554432 net.ipv4.tcp_wmem = 4096 4096 33554432 net.ipv4.tcp_mem = 786432 1048576 26777216 net.ipv4.tcp_max_tw_buckets = 360000 net.core.netdev_max_backlog = 30000 net.ipv4.ip_local_port_range = 2048 65535 net.ipv4.tcp_window_scaling = 1 net.ipv4.tcp_no_metrics_save = 1 net.core.somaxconn = 131072 fs.file-max = 131072 /usr/include/linux/limits.h NR_OPEN = 65536 /etc/security/limits.conf * soft nofile 65535 * hard nofile 65535 16
  • 17.
    客户端代码 客户 共享代码 服务器 17
  • 18.
    Q&A renaun@adobe.com http://github.com/renaun @renaun http://renaun.com/blog 18

Editor's Notes

  • #2 Gòujiàn ActionScript yóuxì fúwùqì, zhīchí chāoguò 15000 bìngfā liánjiē? 15000 = yì wàn wǔ qiān
  • #3 Jīběn MMO yóuxì jiàgòu Kèhù duān Fúwùqì Shùjùkù Liánwǎng
  • #4 MMO Wǎngluò zhùyì shìxiàng Xiéyì Fánghuǒqiáng Yánchí Shùjù bāo de dàxiǎo Shùjù jiàgòu
  • #5 Bìxū nénggòu chuàngzào chū dàliàng de liánjiē, Shíxiàn zài yīgè hélǐ de sùdù hé dàikuān nèi fāsòng shùjù bāo
  • #6 Tào jiē zì shíxiàn Jiǎnchá měi gè wénjiàn miáoshù fú de měi yī zhèng Dàliàng de liánjiē màn Cāozuò xìtǒng Yōuhuà Kuàisù cāozuò xìtǒng de jùtǐ shíshī Jīyú shìjiàn kù Kuàisù dàliàng de liánjiē Kě yízhí de dàimǎ, Yǐncáng cāozuò xìtǒng de jùtǐ shíshī
  • #7 Fúwùqì pèizhì Pèizhì cāozuò xìtǒng yǔnxǔ dàliàng wénjiàn miáoshù fú Pèizhì cāozuò xìtǒng duōshǎo RAM wèi měi gè file descriptor Pèizhì cāozuò xìtǒng wèi měi gè file descriptor shèzhì de RAM dàxiǎo Měi gè IP dìzhǐ yǒu 65000 gè duānkǒu
  • #8 Kèhù duān hé fúwùqì shàng de Flash Zài zhōngguó, xǔduō MMO yóuxì shǐyòng Flash zuòwéi kèhù duān Fúwùqì zìrán yǒu MMO yóuxì luójí Chū yú ānquán de kǎolǜ, bǎ yóuxì luójí fàng zài fúwùqì duān Kèhù duān hé fúwùqì shàng shǐyòng tóngyī zhǒng yǔyán de xǔduō hǎochù Fúwùqì duān shǐyòng ActionScript kěnéng ma?
  • #9 Fúwùqì shàng de ActionScript Flash Player Jiěpōu Core: Xūnǐ jī, Yuán yǔ, Jùfǎ APIs: C/C++ Shíxiàn fēngfù de Flash Player API hé gōngnéng de lèi Tamarin Kāi yuán de Flash Player de héxīn bùjiàn Méiyǒu fēngfù de API huò gōngnéng, nǐ bìxū zìjǐ biānxiě
  • #10 Tamarin de jīchǔ gōngchéng -
  • #11 Ànlì yánjiū
  • #12 Xiànchǎng yǎnshì hé yuán dàimǎ
  • #13 Fúwùqì shèzhì Yóuxì fúwùqì Amazon EC2 Medium fúwùqì 64 bit Ubuntu OS Yùn háng liǎng gè yóuxì shílì Zhǔyào de yóuxì Fùzǎi cèshì yóuxì Fùzǎi cèshì fúwùqì Amazon EC2 Medium fúwùqì Zì dìngyì cèshì jiǎoběn
  • #14 Fúwùqì dàimǎ Redtamarin Chuàngjiàn ServerSocket.as – libev Tào jiē zì shíxiàn ActionScript wénjiàn biānyì chéng ActionScript zì jié mǎ (ABC wénjiàn) Mìnglìng xíng redtamarin wàiké yùnxíng ABC wénjiàn
  • #15 Fúwùqì ActionScript dàimǎ
  • #16 libev Tào jiē zì shíxiàn
  • #17 Linux nèihé pèizhì
  • #18 Kèhù duān dàimǎ Kèhù Gòngxiǎng dàimǎ Fúwùqì
  • #19 Wèntí hé dá&apos;àn