赖勇浩(http://laiyonghao.com)

(续上)

游戏(服务器)是一种 CPU 密集、I/O 密集的应用,但是因为 GIL 的原因,Python 不能充分利用多核,所以一般都采用分布式的方案,那么 CPU 方面就没有太多好讲的了,不过 I/O 方面蛮有意思,可以讲一下。这里有没有 node.js 社区的朋友?(有人举手)。这句话你熟悉吗?(幻灯片上是一句话:I/O needs to be done differently.)这句话是 node.js 的作者说的,他说 I/O 该用不同的方法来实现啦。我觉得他说得很对,……后来他也做了 node.js。这里有一个 node.js 操作 DB 的例子,DB 操作必须是有 I/O,有 I/O 就有阻塞,有阻塞就并发性较差。node.js 是这样解决的:

在操作数据库的时候,指定一个回调函数,在操作结束的时候,再由 node.js 把结果推送给你。借助 javascript 强大的闭包语法,可以写出“很漂亮”的带有回调的程序,而且看起来好像阻塞程序一样简单,又带有很高的并发性能。这就是 node.js 认为的 I/O 该有的样子。但是我不认同这句话。我认为 I/O 应该这样做,以下举个页游编程中常见的例子:

上面是玩家输入用户名、密码后按下登陆键,从账号验证到进入游戏的流程。当用户名、密码发送到专门用以登陆的 signin 服务器,signin 需要先查询数据库验证用户名、密码。在此我们只考虑用户名、密码无误的情况,signin 知道用户名、密码无误之后,就得先告知 game 服务器,game 服务器会返回一个令牌给 signin,后面客户端可以凭此令牌登陆 game 服务器开始游戏之旅。这么复杂的流程,涉及到多条进程之间的通信,也就是有许多的 I/O。这种应用使用 node.js 来写的话,可能需要写上两三个回调,个人觉得是比较麻烦的。那么我觉得 I/O 最好能够像上图的代码中那样,……但这不就是传统的阻塞式的 I/O 吗?对的!我觉得 I/O 的接口应该跟之前无二,但是底层的实现需要改变;而不是像 node.js 一样,都带一毛 callback 的尾巴。那么怎么做到这一点呢?

解决方案就是协程,协程才是未来。接下来介绍一下 gevent,gevent 能够让上图的代码运行起来。gevent 就是 libevent 加上 greenlet,简单介绍一下这两个库。libevent 提供指定的文件描述符事件发生时调用回调函数的机制,当然 timeout、signals 等也会调用回调。所以底层其实跟 node.js 是一样的,是有一个回调函数的,但是可以通过 greenlet 来封装出同步的 API,将丑陋的回调隐藏起来。greenlet 是一种 green thread……即一种用户空间线程,提供伪并发机制,所谓伪并发就是它并不能让你拥有充分利用多核 CPU 的性能,但他的好处是它的调度是虚拟机层面的,确切地说就是可以由程序员自己来进行调度。greenlet 是 Python 界对 green thread 的一种比较好的实现。

如果使用 gevent 写一个简单的 echo 服务器,大概是这样子的:

可以看到有一个 echo 函数,它处理每一个客户端连接,它读一行、写一行的方式来实现 echo 业务。大家可以看到代码还是有点长。类似 gevent 的项目还有沈崴的 eurasia,http://code.google.com/p/eurasia 。

谈完了 I/O,接下来有必要看一下协议方面,因为页游对网络协议的处理还是颇有些要求的。这方面我比较推荐 google protobuf,这是一门协议描述语言,官方支持生成 C++/java/Python 的代码,有许多第三方插件可以生成其它语言的代码。通过 protobuf 可以很方便地描述业务协议,比如登陆的时候需要有 username 和 password,以及可选的 timestamp 之类的,protobuf 能够帮助你去做序列化和反序列化的工作。protobuf 还支持声明 RPC,也就是 service,这个特性能让大家方便地实现 RPC。我们也做了一套,就是 abu.rpc,它是基于 gevent 加 protobuf 来实现的。得益于 gevent,它提供了同步的 API,即当调用 RPC 的时候,就像调用普通函数一样,等待返回就可以了,无需回调。得益于 protobuf,它是一个二进制协议,所以每个数据包都有较小的尺寸。libevent 是一个高效的异步 I/O 库,所以 abu.rpc 也很快。不过 abu.rpc 最重要的两个特点是并行管线和双向调用。所谓双向调用就是指客户端可以调用服务器端,而服务器端也可以调用客户端提供的服务;也就是说客户端也是可以有服务的,它可以在创建的连接上绑定自己的服务。所谓的并行管线是这样的:

有时候客户端会发起一个比较重量级的、比较耗时的请求,而后又发起一个较轻量的请求。如果没有并行管线的支持,那么虽然轻量级的请求很快就处理完了,但客户端也只能等到重量级的请求完成以后才能收到轻量级的请求的处理结果,如上图左。这样轻量级的请求就为别的请求所累,响应时间就变长了。如果有并行管线的机制,当轻量级的请求发过来时,经过简单的计算,马上就能够返回结果,就有更快的响应,更好的实时性。大家再来看一下使用 abu.rpc 的 echo 服务器:

可以看到代码比直接使用 gevent 还是变短了不少的,复杂的地方可能是需要声明 service 吧。

今天上午周琦(ZoomQuiet)提到使用 rabbitMQ 来解耦,的确,MQ 挺适合在应用(进程)间的,他提到的面向消息编程其实大概可以说就是发布订阅模式:我对什么东西感兴趣,到时候你就推送给我。现在大家都比较关注进程之间的 MQ,但其实在进程之内、模块之间,也是极其需要“MQ”的,所以我们实现了一个叫 message 的模块。可以订阅感兴趣的主题,当有这样的主题发布时,订阅的回调就会被调用到了。

上图中右侧就是输出。通过 context 可以对消息处理流程做简单的干涉,比如某个订阅者处理完了认为别的函数都没有必要再调用之类,那么可以使用 context 来终止它。主要是受到 falcon 语言的启发而编写的,falcon 有丰富的语言特性,比如面向消息编程这个词,我第一次见就是在 falcon 编程语言的手册里看到的。关于这个库,我之前有写过一个 slide,放在这里:http://www.slideshare.net/laiyonghao/pythonmessage010 。在我们的《天下盛境》项目中,我们将其应用于任务、邮件以及好友等子系统中。举个任务子系统的例子,比如完成任务需要杀死 5 只怪物,针对这么多玩家做轮询的话是比较麻烦的,但通过订阅“怪物死亡”的主题,就可以在合适的时机去判定任务是否已经完成了,从而达到模块与模块间比较好的解耦的效果。

最后,给大家介绍一个简单但又很难归类的库。在游戏中,需要大量处理二进制的数据,这些数据通常由不同的平台、操作系统生成或存储,比如在 32 位机上进行开发,但运营部署是在 64 位机器上,在 py2.6 的环境开发,在 py2.7 的环境部署,等。这时候往往产生一些兼容性的问题,这类问题很难查出真正的原因。比如内置函数 hash() 在 32 位机器上返回的是 32 位的有符号整数,在 64 位机器上返回 64 位的有符号数,如果两台机器需要比对哈希结果,稍加不注意就可能会出问题,解决问题分分钟需要两个晚上都很常见,因为代码没有任务地方出错,但逻辑全乱了套。因为我们在做客户端与服务器端的通信加密时也是使用 Python 去实现,所以遇到了不少类似的问题。后来我们总结出需要一系列绝对地返回 32 位带符号整数的函数,所以我们编写了这个 absolute32 程序库。它很简单,只是对标准库的几个函数进行了封装,提供了 hash/add/crc/adler 等函数。以 add 为例,它对溢出的的处理是与 C 语言一样的,而不是像 Python 那样自动转换为 long 类型。这个库的几个函数我们在 py2.6/py3.1 和 32bit/64bit ubuntu 是进行了交叉测试,可以很好的简单我们的兼容性问题。

以上就是我今天要介绍的内容,谢谢大家。

Python 于 webgame 的应用(下)相关推荐

  1. python 在无网络环境下安装包

    python 在无网络环境下安装包 1 找到python包路径 C:\Users\admin\AppData\Local\Programs\Python\Python36-32\Lib\site-pa ...

  2. python线下培训-天津python培训,0基础学python线上还是线下好?

    0基础开始选择学习python的时候就注定要纠结于学习方式是线上学习好呢还是线下学习好呢?其实两者都各有各的优势,线上课程便宜,时间自由灵活,不过需要我们强大的自学能力以及自控能力:线下的话学习氛围. ...

  3. python下载文件到指定目录-Python获取指定文件夹下的文件名的方法

    本文采用os.walk()和os.listdir()两种方法,获取指定文件夹下的文件名. 一.os.walk() 模块os中的walk()函数可以遍历文件夹下所有的文件. os.walk(top, t ...

  4. python怎么读文件夹下的文件夹-python如何获取当前文件夹下所有文件名详解

    前言 本文主要给大家介绍了关于python获取当前文件夹下所有文件名的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧 os 模块下有两个函数: os.walk() os.li ...

  5. 线上学python哪家好-Python线上和线下培训哪个好?老男孩Python

    学习Python对于很多人来说都是非常不错的选择,也是最合适的选择.想必不少人都知道,学习Python分为线上和线下两种模式,那么这两种模式有什么区别呢?我们简单的了解一下. 先来为大家介绍一下线上培 ...

  6. python 在内网windows环境下pip三方包

    我没用过Linux环境. 一般情况下,内网安装三方包,只需要在pypi找到对应python版本(2.7,3.6,...),系统位数(32位,64位)的whl包,cmd命令行cd进入相关目录,pip i ...

  7. Python+Django+Eclipse 在Windows下快速开发自己的网站

    Python+Django+Eclipse 在Windows下快速开发自己的网站 一.配置开发环境 我的开发环境是:Python3.3.2 + Django1.5.2 + Eclipse 1.安装Py ...

  8. Python语言学习之双下划线那些事:python和双下划线使用方法之详细攻略

    Python语言学习之双下划线那些事:python和双下划线使用方法之详细攻略 目录 双下划线介绍 1.关于双下划线的函数或方法或属性 双下划线介绍 1.关于双下划线的函数或方法或属性 __name_ ...

  9. python删除指定文件夹下文件和文件夹的方法

    python删除指定文件夹下的文件,是一个常用的功能.我找了不少地方,一直没有找到合适的模版,那只好自己倒腾一个比较实用的模版了. 基本模块 这里面会用到几个模块,一个是目录下所有文件的的函数:lis ...

  10. python如何删除文件夹下文件和文件夹?

    def del_file(filepath):"""删除某一目录下的所有文件或文件夹:param filepath: 路径:return:""&quo ...

最新文章

  1. 《互联网人求职图鉴》:这类人才“最吃香”,最高薪编程语言出炉!
  2. LVS负载均衡之持久性连接介绍(session篇)
  3. php和python写爬虫-一个简单的Python写的XML爬虫
  4. 《编码的奥秘》---学习编程一年半的体会
  5. Hibernate 4.3 ORM工具
  6. 简述python的优点_Python是什么及Python的优点和缺点
  7. 转:zTree树控件入门之checkbox:如何动态设置节点的checkbox选择框启用与禁用状态(chkDisabled)...
  8. 嵌入式linux ucgui,Helper2416开发板移植ucgui(嵌入式linux运行ucgui)
  9. 远程IT运维的升级,“团队协作”
  10. 计算机编程小学生有必要学吗,小学生有必要学少儿编程吗
  11. Unity-tweak-tool插件
  12. 计算机毕业设计ssm基于b_s架构的实习管理系统
  13. ps插件摹客iDoc使用技巧
  14. Linux下Oracle 11g安装(3)—— Oracle安装篇
  15. MySQL查询最近7天数据
  16. 微博5亿用户数据泄露:通讯录匹配机制是罪魁祸首!
  17. linux更改终端颜色_如何更改Linux终端的颜色
  18. 安装sql server 2000时又出现:安装程序配置服务器失败。参考服务器错误日志和 C:/WINNT/sqlstp.log 了解更多信息。
  19. ad20如何画出pcb板大小_资深大咖SMT硬件工程师手把手教你分清多层板及PCB各层中的含义,给力!...
  20. Web3艺术策展平台分析报告:模式、市场表现、发展分析

热门文章

  1. linux:root密码错误的解决办法
  2. batch size 代码
  3. 计算机伦理学论文,WebQuest在社会与职业道德(计算机伦理学)课程中的教学应用研究...
  4. 谈微商那点事,如何避开红海
  5. 三年开发经验, 字节跳动抖音组离职后, 一口气拿到 15 家公司 Offer
  6. 关于“如何提高写作能力”,收集了一些信息
  7. 利用SpaceVim打造完美Go IDE
  8. Python笔记:数据集拼接(数据匹配)
  9. python 使用字节流bytes格式读取文件转为int格式,再转为0,1字符串格式
  10. 自定义比例的GS噪声和椒盐噪声的加入和阿尔法修正的均值滤波的实现