欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Python 于 webgame 的应用(上)

程序员文章站 2023-11-19 12:38:04
注:本文根据 2011 年 12 月 4 日我在上海 pycon china 上的报告的录像整理而来,有较多口语,但废话不多。  (前面约四分半钟的暖场与自我介绍,略)...

注:本文根据 2011 年 12 月 4 日我在上海 pycon china 上的报告的录像整理而来,有较多口语,但废话不多。

 (前面约四分半钟的暖场与自我介绍,略)今天的主题演讲其实受到两个人的很大的启发,一个是洪强宁洪教授,他在 2010 的时候做过一个叫《python 于 web2.0 网站的应用》,大家可以看到我今天这个标题就是直接从他那里借鉴过来的,连微创新都没有。这个 slide 是很好的,所以我有给出网址,大家可以去看一下。然后还有沈崴,沈崴在 2010 年的时候也写了一个 slide,就是《python 艺术》。这个 slide 对我的 python 代码的风格影响非常大,他主要是讲到了应用 python 时应该遵循怎样的编程哲学,也就是说怎样更加 pythonic。这两个 slide 我曾经在珠三角技术沙龙的某次活动上说是我在 2010 年遇到的最好的两个幻灯片。

今天我以我经历的项目为蓝本,向大家介绍一下 webgame 服务器端开发这一块的技术和工具。大部分内容都是我在过去或现在的项目中使用的,有小部分内容是我以后才会在项目中用的,大家可以只当我是在做一个介绍,不要当我在做一个。如果你选择了这些技术,我不负责任,风险自担。所以这是一个源于项目、高于项目的(分享),因为我在做项目的过程当中会做一些思考和实验,然后这些思考和实验的结果就反映在这个演讲里面。

(开始项目介绍)嗯,这是刚才跟大家提到过的《天下盛境》,它是一个横版卷轴的动作类网页游戏,可以看到它的地图是从左到右移动的,可以在主城里交友、跟网友互动,或是在副本打怪。它现在 host 在 0505u.com 这个运营平台,服务器端完全使用 python 开发,没有一行 c 代码,从去年(2010 年)的 8 月份开发到现在(2011 年 12 月初)。这是我最近做的一个项目,因为演讲主题会讲到前做的一些东西,所以再介绍另一个项目——web 版的棋牌。它其实就是 qq 游戏的一个仿制品,在我自己的网站上有提供给大家访问,它的服务器端也是完全用 python 来开发的,时间大概是用了两三个月的时间来开发,然后后面有一些零星的维护。它是一个半成品,不是一个可以商用的成品。

接下来从库,也就是 library 的角度来介绍一下相关的开发。首先,对于 python 在项目中的“位置”大家是怎么看的呢?游戏里面,经常用到脚本语言,比如最常用的一个脚本语言 lua,一般来说网游会用 c++ 写一个 host,由它调用多个 lua 脚本来完成一个项目(的业务逻辑)。这就是 lua 项目常见的(脚本)存在形式,而 python 的话稍有不同,python 本身就是主体来的,有一些 c/c++ 写的扩展来解决某些特定的问题,也有一些用 python 来写的业务逻辑,通常 python 的网游就是这样的一个结构(见下图)。

 Python 于 webgame 的应用(上)

大型的 python 项目大家看看是不是这样的:首先有一个入口的主文件 main.py,然后有几个业务逻辑的文件(file1.py、file2.py),然后同层有一些自制或公司用的库(lib1/lib2/lib3),大家的项目是否都是这样的结构?

 Python 于 webgame 的应用(上)

这样做是不对的。但是我之前经历的几个项目、包括我之前看到过的几个项目,都是这样做。然后我认为,大中型的 python 项目的结构应该是这样的:

 Python 于 webgame 的应用(上)

它就是一个入口文件,然后写了一些业务逻辑(file1.py/file2.py),就是这样。大家可能会觉得有一些奇怪,那不是更小了吗?是的,其实我主张 lib 要放在 site-packages 里面,也就是做的时候,库就是库,你要分开,不要跟业务逻辑混在一起。但是大家觉得有必要搞那么复杂吗?用刚才的方式也赚到钱了呀。项目也上线了。其实呢,这么做是有一些好处的,而且最重要的是它不复杂。

 Python 于 webgame 的应用(上)

其实就只是写一个 setup.py,就是用 distutils 写一个 setup.py 然后你再把它打包、安装过去就可以了。setup.py 有两个比较关键的地方,第一个就是如何避免手写 setup.py,第二个就是怎么建立命名空间包。所谓命名空间包就就是类似这种先有一个 abu 的前缀,后面才是 rpc 的包名,这样我们就可以建立专门用来做的 abu.db,包括我们自己的业务逻辑,比如 abu.qipai。这个在 zope 项目是比较常见的。为了构建这样的结构,我给大家介绍一个东西——paster。因为它已经放上 pypi 所以大家可以使用 pip/easy_install 来安装它。它提供了创建项目、安装、测试、部署和运行的全栈式的支持。

 Python 于 webgame 的应用(上)

大家可以帮看一下它的帮助,它后面可以加很多命令,比如创建项目、运行项目,还有产生配置文件之类的。有很多项目都使用它来构建自己的功能,比如说像 pylons、turbogears、zopeskel 等。如果要通过 paster 来运行服务的话,像今天大妈(zoomquiet)说到 supervisor 是吧,其实是我比较鄙视的一个东西来的,它不好用,可以试一下这个,另外我也比较推崇 start-stop-daemon,无论如何,我觉得不需要再手写守护进程,没有必要。通过 pastedeploy 可以把自己的应用以守护进程的方式或其它方式运行起来。以上是对 paster 的简单介绍,接下来看一下它的基本用法。

要创建一个应用或一个库,首先要有一个模板。可以通过(create 子命令的 --list-template 参数来查看当前环境可用的模板,比如在这里有一安装就有的 basic_package,然后可以用 -t 参数指定模板,后续可跟项目名,即可创建(项目)包。paster 会询问一些问题,比如版本,只需要填入或采用默认值即可,等询问完成,就获得了 setup.py 和相应的目录结构及相关文件了,马上即可使用,无需手写,因为 setup.py 里的 setup 函数调用有许多参数,而且这些参数还支持多种形式,要了解清楚也是非常困难的事情(,所以能不手写就不手写吧)。以上讲述的是如何避免手写 setup.py,接下来聊一下如何创建命名空间包。创建命名空间包,可以先通过 pypi 安装 pbp.skels。pbp.skels 带有许多模板,可以加速创建命名空间包之类的应用,节省宝贵时间。安装以后,可以看到多了一个 pbp_package:

 Python 于 webgame 的应用(上)

然后可以通过 -t 参数指定使用这个模板来创建带点的命名空间包了。通过命名空间,可以有效地把自己的代码与别人的代码分隔开来。接下来是一个深入的主题,我不在此展开,有兴趣的朋友可以去读一下这篇 wiki(http://lucasmanual.com/mywiki/pythonpaste),它讲述的是怎么样针对 paster 编写自己的横板、扩展它的命令,因为 paster 甚至可以让你自己添加扩展自己的命令。

(再回到 setup.py 上来),通过它可以做到项目生命周期的全系列支持。比如在开发时使用 develop 子命名,可以避免每一次改动都要 install 一次。还可以用 test 进行测试,bdist/sdist 打发布包,register 在 pypi server 注册,用 upload 把发布包上传到 pypi server 等。

所以通过这些工具的支持,大家可以很方便地把代码以库的形式分隔开来,放到库应该在的地方,而不是跟业务逻辑代码混在一起,这也有利于在产生服务器上部署代码。甚至可以自建 cheese shop,也就是 pypi server,可以建立一个公司内部使用 cheese shop,就能方便同事使用你的项目。特别是像我前东家网易这样的大公司,有时候想推广一些东西给同事用,同事说我很难用上你的东西啊,比如要穿越内网隔离之类的很麻烦,那就可以通过自建 cheese shop 来解决。

(还有一个最佳实践就是)每一次开发软件包的时候,都应该有一个干净的、纯洁的环境。virtualenv 可以帮助大家建立一个纯净的环境。在项目发布的时候,不要使用的那个 python 环境,而是应该针对每一个项目建立相应的 virtualenv 的目录,用 virtualenv 里的 python 来运行它。去年洪教授已经在它的幻灯片里介绍过了,大家可以找来看一下,他的幻灯片写的非常详尽、严谨,而且把一个 pythoner 应该要了解的东西他基本都有介绍到,我从他那里学习到很多。

接下来讲一下插件,首先,插件跟库有什么不同呢?为了讲好这个话题,我曾特意搜索了一下,结果看到 * 上有一个很好的解释(http://*.com/a/2792342):插件扩展了大性应用的能力;而库则是一系列的子程序或 class 来帮助你的开发。所以库和插件是两个有较大区别的概念,所以我今天是分开来介绍的。(举个生活中的例子),伞就是一个插件,当手握一把伞的时候,人就有了“防雨”的能力了。(回到软件开发中),我们以棋牌项目为例,如图,大家可以看到有多种游戏在其中:

 Python 于 webgame 的应用(上)

有斗地主、五子棋和象棋等,这些可以看作是(软件的)“功能”。接下来转到后台看一下,可能会稍有不同的感觉:

 Python 于 webgame 的应用(上)

这个棋牌的项目是每一张桌子是一条独立的进程来运行的,比如我和另一个人下棋,那们就会有一条单独的进程为服务我们。进程以 desk/main.py 作为入口点,它后面可以跟不同的参数,如果跟的是 xiangqi,那么它就会加载象棋业务逻辑的插件,成为一个象棋服务器;参数是 doudizhu,则会加载斗地主的插件,成为一个斗地主服务器。这就给开发业务逻辑程序员提供了很好的扩展性,而且可以匹配权限隔离,也可以通过封装降低业务逻辑的开发技术,可以让相对初级的程序去开发业务逻辑,甚至干脆外包也不会暴露太多细节。以这个棋牌项目为例,开发业务逻辑(游戏玩法)的人不需要了解网络编辑,不需要了解数据库,不需要了解多线程,因为它开发的时候不需要调用原始的网络、数据库、多线程的接口,因为它们都是 host 提供的功能,他们只是实现 host 定义好的接口(或协议)。这其中也是通过 setuptools 来做的:

 Python 于 webgame 的应用(上)

大家看这个 doudizhu 目录,它里面有个 setup.py,还有一个很重要的是 game_impl.py 文件。在 game_impl.py 文件中,它实现了业务逻辑,也就是实现了 host 定义的接口。接下来我们先看看 setup.py 文件,大家可以看到其中最重的是这两行:可以看到定义了一个 qipaionweb.games 的节,节里面有个叫 doudizhu 的配置项,它的值是 doudizhu.game_impl:gameimpl。这个值是一个 class,它也可以是一个 function。

 Python 于 webgame 的应用(上)

接下来讲一下如何加载插件,大家可以看以下代码:

 Python 于 webgame 的应用(上)

大家可以看到 get_game_impl_class 函数接受一个 game_name 参数,这个 game_name 参数其实就是前面讲到的命令行传入的参数,比如 xiangqi、doudizhu 之类的。然后通过 pkg_resources.load_entry_point 把 setup.py 里通过 entry_points 参数定义的内容取出来,在这里也就是 class gameimpl 啦,所以接下来把它实例化后就可用了。在 host 中,我们除却定义接口,还做了一些业务封装和通用功能,比如与上一级进程(也就是房间进程通信),比如每一个小游戏都有的踢人,统一管理业务相关的计时器等。游戏的实现首先是根据定义实现接口,也就是实现业务逻辑。业务程序员不接触网络、不接触数据库、面对的也是单线程的编程环境,可以大大地降低开发难度。对于插件的话题,大家还可以参考一下 trac 的架构,喜欢 ood 的朋友应该能从中借鉴不少东西,我自己也从中学习到许多,比如它的组件管理器、组件和扩展点,以及接口的声明等。trac 的架构与我刚才讲的稍有不同,我讲的是一个很简单的版本,因为棋牌并不需要像 trac 项目那么高的扩展性。

 Python 于 webgame 的应用(上)

这是 trac 文档里的两张图,可以看到 trac.core 里面有一个组件管理器,它对应很多个组件,而组件就有很多个扩展点,每一个扩展点都会实现某一个接口,大概就是这样的架构。它的 wiki 里面有详细的文档,大家可以在线访问。插件机制不只有这一种,甚至可以自己设计和实现,比如今天上午演讲的 limodou 写的 ulipad 里面就自己实现了一套插件机制。它是一套借助 mixin 来实现的插件机制。

游戏(服务器)是一件 cpu 密集、i/o 密集的应用……(待续)