GitHub上有哪些不错的开源游戏服务器框架架

微信(英文名:wechat)是腾讯公司于日推出的一个为智能终端提供即时通讯服务的免费应用程序,微信支持跨通信运营商、跨操作系统平台通过网络快速发送免费(需消耗少量网络流量)语音短信、视频、图片和文字,同时,也可以使用通过共享流媒体内容的资料和基于位置的社交插件“摇一摇”、“漂流瓶”、“朋友圈”、”公众平台“、”语音记事本“等服务插件。
微信提供公众平台、朋友圈、消息推送等功能,用户可以通过“摇一摇”、“搜索号码”、“附近的人”、扫二维码方式添加好友和关注公众平台,同时微信将内容分享给好友以及将用户看到的精彩内容分享到微信朋友圈。
截至2013年11月注册用户量已经突破6亿,是亚洲地区最大用户群体的移动即时通讯软件。基于这么庞大的用户群体,针对微信公众平台的营销也随之而来,水涨船高的公众平台的二次开发也愈发的热门起来,今天我们就来具体探讨下几款值得推荐的微信开源框架。
  1. Wechat-PHP-SDK &   微信公众平台 PHP 开发包,细化各项接口操作,支持链式调用。 &   Github托管地址:dodgepudding/wechat-php-sdk
  2. 微信公众平台 PHP SDK &   简单的微信公众平台 PHP SDK ,通过调用相应的接口,使你可以轻松地开发微信 App 。 &   Github托管地址:netputer/wechat-php-sdk
  3. Wechat-php &   本微信SDK实现了被动响应的官方 API 已经主动发送消息给订阅用户,主动批量发送消息给订阅用户。 &   Github托管地址:ligboy/Wechat-php &   非常强大的微信公众平台开发框架推荐中有官方和第三方开发者提供的丰富的插件,是免费并且成熟的框架,更多的信息可以点击这里:
&o微擎:http://www.we7.cc/ &o微笑:/ &oweiphp:/
多图详解Android的系统架构--值得收藏 近日,Google的一位工程师Sans Serif发布了一篇博文非常清楚的描述了Android系统架构,中国移动通信研究院院长黄晓庆在新浪微博上推荐了该文,并认为文中对Android的介绍很好,您可以看一下Google工程师眼中的Android系统架构是什么样的。以下为Sans Serif博文的译文:   Andriod是什么?   首先,就像Andr
Android实现类似微博的换肤功能 现在想做一个和新浪微博客户端一样的更换主题样式的功能,从网上搜了很多,多数都是说使用SharedUserid来控制,但是我不清楚,到底怎么使用,在主程序中如何来实现具体的更换操作;
其实我就是想要个这样的功能:
主程序一次性写好,默认自带有一套主题皮肤样式,之后的只需要像微博那样,下载新的皮肤apk安装即可更换;
即使客户端已经安装了很多个apk,主程
开发android项目设计思路 要练习做一个android项目,才开始就难住了我,现在自己已经想好基本有哪些功能。但是,下一步该怎么做呢?设计思路是什么样的?用什么数据库比较好?javawb里用过mvc,那么android也有类似的东西吗?也需要考虑框架之类的东西吗?求高人相助。
------解决方案-------------------- 数据库的话本地是内嵌的sqlite3,远端
为launcher添加仿HTC的preview(开源,附源码) 最近有时间了,顺便把之前做的一点东西慢慢整理出来,让大家一起研究,讨论下。
Android原生自带的preview不是很好看,很喜欢HTC的,之前看到ADW上也有那样的preview,所以将它提取出来,放入了原生的android2.2中。闲不说,直接上图,有图有真相:
原生的,未修改版本:(B
android ndk如何编译基于mips架构的开源C++库? Androidndkr8支持mips架构了,但是其中提供的交叉工具只有mipsel的,那我要编译mips的怎么办?,求高手指点迷津
------解决方案-------------------- mipsel表示小端编译
mips的编译器表示默认大端
sqlite3的外键约束不好使?
droptablet_ droptablet_
createtablet_group ( idinteger, primarykey(id) ); createtablet_user ( id
Maven开发Android指南 3 使用Android Maven Archetypes 创建新项目
在看完Maven开发Android指南 2 配置android-maven-plugin后,有人会疑惑怎么没有讲android-maven-plugin。这是Maven的特性之一,不需要手工下载plugin,配置pom.xml文件,只要是Maven Central中有的,mav
Android 开发时遇到的两个模拟器问题
开发环境: windows , JDK 7u10, ADTv21.0.1-543035 一.启动 Nexus 7 模拟器失败
模拟器启动出现如下错误: Starting emulator for AVD 'nexus7' Failed to allocate memory: 8 This application ha
android应用开发关于数据永久存储相关的问题? 在开发一个应用程序中,需对某一INT值进行保存和读取。同时,一旦保存后该值不受android系统中&设置——隐私权——恢复出厂设置&的影响。
目前想将该值保存到nvram中。在应用层中是否有直接的方法可以对NVRAM中的值进行读取和保存?
或者,是否在应用层中有存储空间可以对值进行保存,而不受and
Android开发22——广播接收者BroadcastReceiver的原理和注册方式
一、广播机制的基本概念 当某个事件产生时(如一条短信发来或一个电话打来),android操作系统会把这个事件广播给所有注册的广播接收者,需要处理这个事件的广播接收者进行处理。其实这就是日常生活中的广播。发生一个新闻后,广播电台会广播这个新闻给打开收音机的人,对这个新闻感兴趣的人会关注,可能会拿笔记下。新闻就
Android 近百个项目的源代码,覆盖Android开发的每个领域 AndroidPDF阅读器 http://sourceforge.net/projects/andpdf/files/
个人记账工具OnMyMeans http://sourceforge.net/projects/onmymeans/develop
Android电池监控An
Android开发学习之二——Android开发环境搭建
工欲善其事,必先利其器。 下面我们就开始搭建Android开发环境。 我采用的方式非常简单,采用的是Android官方提供的ADT Bundle,下载地址/sdk/index.html,如果不能下载可以到/data/607919下载。 下载后
按下返回键 不退出应用程序 从桌面点击再回来 类似腾讯微博 我知道重写onKeyDown这个方法监听按下返回键但是事件代码如何写呢?
------解决方案-------------------- switch(keyCode){
Android异步处理三:Handler+Looper+MessageQueue深入详解
在《Android异步处理一:使用Thread+Handler实现非UI线程更新UI界面》中,我们讲到使用Thread+Handler的方式来实现界面的更新,其实是在非UI线程发送消息到UI线程,通知UI线程进行界面更新,这一篇我们将深入学习Android线程间通讯的实现原理。 概述:Android使用
这样的效果怎样实现呢
如图,图中的列表可以滑动,点击按钮可以切换列表中显示的内容。
菜鸟求教,实现的思路呀,用什么控件的
------解决思路---------------------- TableLayoutListViewGridView都可以啊
要切换内容的话,推荐ListView吧
强烈建议CSDN开发一个android客户端? 强烈建议CSDN开发一个android客户端,求支持。
------解决方案-------------------- 帮顶一下!
------解决方案-------------------- 貌似没什么必要,研究技术的时候,都用电脑弄的
仿Siri的中文语音助理源码 之前闲暇之余借鉴Iris的框架,仿写了一个中文语音助理,效果如下:
语音接口使用的是科大讯飞的云语音接口,具体使用可以到科大讯飞的网站了解,智能语音处理部分使用的是小I聊天机器人的网络接口(/widget/1007/index.html)——用于聊
【Java 基础】想学习 Android 开发,需要多少Java基础呢? Java学到哪里好呢?
------解决方案-------------------- 1、选择快餐式的教育培训机构【简称速成,获得的多少与自身理解能力和动手逻辑能力有关】现在学java、一般都会讲解android应用层开发知识会涉及1到2个项目练手大概
Android开发,AVD无法启动 问题如题
一直卡在这个位置:
debug报错:
[2:03:40-hierarchyviewer]Unabletogetviewserverversionfromdeviceemulator-5554
Android蓝牙开发中,设备能用代码控制自动匹配吗? 用手机连接蓝牙设备的时候都会要求做一个匹配操作,双方输入一个匹配码,然后就可以匹配成功了。
但有的设备,如蓝牙耳机等,没有输入功能,一般这种设备的匹配码是固定,比如,我想问的是可不可以通过自己的程序来实现自动输入这种固定的匹配码,不要用户手动输入,比如针对某些第三方的蓝牙设备专门开发的软件。
我发现android在匹配
刚入门,请教一个功能的实现 想在软件里实现动态的加载图片的功能,图片是在数据库里保存着
请教如果实现,会用到哪些控件,有代码更好,多谢
------解决方案-------------------- 1.在服务器端准备好图片。
2.android端下载,存在本地
没有特别饶人的地方吧,一步步实现就行了。。。哪里不懂再问我
------解决方案------------------
现在是android的时代了吗? 一直做嵌入式的开发,越来越感觉现在是android的时代了,不管什么都跟android相关,android手机大行其道自不必说,其他的嵌入式设备,如果智能家电,象智能电视,机顶盒,智能厨具,家庭影院,等等,似乎那个不用android,就不智能了,这个行业真实瞬息万变呢!
1楼 hbykdx2010昨天 23:40
[个人作品,含源码] Android 程序之在线词典, 觉得有帮助的,请过来顶一下 http://blog.csdn.net/ostrichmyself/archive//4633838.aspx
下面是部分贴图,有兴趣可以到我的博客上瞅瞅,谢谢!
交些Android开发的同道中人(加我为好友,谢谢),本人业余型的
多个activity的应用,按home后,再进程序,如果保持之前的状态?急 现在小女遇到一个问题。
在开发一个应用,
有多个activity,
现在运行程序启运进入主activityA,然后进入第二个activityB
这时我不退出程序,而按了home键。
这时如果我在进入该应用,则直接出现了主activityA,而不会直接进入B
在应用里我全设了
Android入门系列一(Android学习方法)
Android学习方法 一:了解什么是Android 二: 建立开发环境 三: 阅读SDK文档 四: 背景知识
2、面向对象
3、设计模式
4、J2ME、Brew、Sym
xml里面product的值在哪里设置的? &!--AproductwithnoSDcard==notremovable.--&
&boolname=&
WebView的异常
异常如图。代码没有问题。空指针也不是代码的异常。有人知道什么情况吗?
------解决方案-------------------- 代码没问题就不会报空指针了。贴出来看看
------解决方案-------------------- handler没有注册完就使用了。
android疑问:文件下载问题
第一步就卡住,可能是因为tomcat不太熟悉。我尽可能把自己的程序背景所给出,希望各位能帮我查看问题所在。
1)tomcat开的服务器,里面有一个xml文件
2)把xml文件里的文本赋值给一个String,名为&xml&;
3)点击一个按钮的时候。System.out.ptintln(&t
请问安装Android sdk时遇到这种情况怎么办
------解决方案-------------------- 不需要都安装用哪个安哪个就好了。
版本越高越好,向下兼容的,但是也有个不好,就是有的低版本的用户用不了你开发的软件。
但是用低版本开发的软件是所有机子都可以用的。
现在一般安个api8然后再安个api17就好了
一个用的人最多,一个最新。
【Android很个性的列表】怎么实现的?有图。。。 如图。。~
这个是160手机助手的一个页面。。
求助,他的这个列表是怎么实现的。。。。
一个是每个row有不同的TextView,还是就一个?如果就一个,那颜色是怎么整的。。。。
另外他的那个“详细信息”的标题栏是怎么实现的。我在htc手机系统设置里见过,求解。。
------解决方案-------------
4.0的Launcher中CellLayout的缩放是在哪个处理的? 如题,代码跟踪了半天,一直没找到,即从allapps界面长按icon拖拽到桌面上时,此时workspace上的cellLayout是有缩放的效果,请问大侠们,这个缩放处理的代码是在哪里呢?我找了workspace和cellLayout这2个类中的代码,不过没找到缩放处理的代码?求指教
------解决思路-----------
if语句判断一直通过不了,我都想砸电脑了,求解大神指导,,, 代码如下,获取服务器返回的信息,LOGCAT显示为,1,2,3,result=1,5,6,但是到这个if判断的时候,它就一直判断是错误,明明获得的reuslt是1,但是就是判断为假,怎么弄都不知道,卡了一晚上,我都想哭了,重启过电脑,吐过血,发过疯可还是不行,看来只有砸电脑了,,,
try{ URLurl=ne
目前学习Android有哪些书比较好(为了自己学习,与为了以后别人学习) 目前学习Android有哪些书比较好(为了自己学习,与为了以后别人学习);
大家都根据自己的学习经验说说看那本书对你影响最大?
哪本书适合入门?
哪本书适合提升?
哪本书是android游戏开发的经典?
哪本书是android应用开发的经典?
哪本书介绍的全面?
希望大家根据自己的学习
请教:获取ListView的第N项的View 大家好,我需要把一些信息放在屏幕上。需求是使用一个ListView,对于ListView的每一项,表现为,左边是一张图片,右边是一些重要文本。图片是从网络上下载下来的。
由于下载图片比较费时,而且图片也不是很重要,因此在实现的时候,需要先把ListView显示在屏幕上,在后台下载图片,没下载完一个图片,就实时的更新一下,这个时候需要获取到ListVi
怎么将手机数据上传到服务器? 我手头有个项目是用安卓手机实时生成少量数据,希望每隔一定时间经GPRS上传到服务器,远程打开一个网页,在这个网页中实时刷新最新的数据。请问大家应该怎么实现呢?
这里面有几个点我不太清楚:
1.手机上传数据使用什么技术
2.服务器使用什么技术处理
3.网页刷新使用什么技术实现
请各位大侠多多指教
------解决方案--
急求:TabHost下的Tab的activity跳转
TabHost里面的一个内容页是activity,数据来了之后“折线图”要进行更新,所以要能在当前同一个Tab下进行跳转到新的activity....但是我一跳转就会把整个tabHost都抹掉,而不是tabhost里面的内容进行变换...
------解决方案-------------------- 如果仅仅改变折线图,感觉重新画个折线图
一般在ListView显示的ItemView的最右边都有个箭头,是系统自带的还是自己加图片的? 一般在ListView显示的ItemView的最右边都有个箭头,是系统自带的还是自己加图片的?
------解决方案-------------------- 自己加图片……
------解决方案-------------------- Listview的每个item都是自定义出来的,所以图片可以随意
郁闷Notification中的build()怎么用 如题:
最近在使用Notification的时候发现之前的setLatestEventInfo()过时了看google的API上说让用Builder,尝试了下又发现这个Builder()下的getNotification()也过时了让用build(),好吧继续尝试,但是发现这个build()在最后总是执行失败,之前的各种set看到都成功了,想
textview里面的内容如何居中 一个textview,我给他设置一个背景图片,然后在settext,怎么能做到让settext的内容在这个背景图片中居中?可以做到吗?(不加其他的布局什么的)
------解决方案-------------------- android:gravity=&center&
------解决方案-----------------
android Mediaplayer的简单程序 程序的功能很简单、就是通过
intent.setAction(Intent.ACTION_GET_CONTENT);
intent.setType(&Audio/*&);
查询可以播放的音频文件、在onActivityResult中用Uriuri=intent.getData();获得路径、然后播放的功能、JS程序员总是嘲笑PHP没有闭包,今天抽空写一篇文章来专门介绍一下PHP的闭包。从5.3版本开始PHP就增加了匿名函数支持,经过数个版本迭代到现在的PHP5.6、PHP7,PHP语言的闭包已经非常完善了。再结合Swoole提供的事件驱动支持,PHP的闭包功能非常强大而且很优雅。
匿名函数是闭包的核心,匿名函数在PHP里实际上是一个Closure类的对象(请注意是对象)。与普通的面向对象编程方式不同,匿名函数的代码是直接写在调用处的,不需要额外写一个类,编写方法的代码。这样的好处就是更直接。下面的示例是设置一个定时器,每2秒输出hello world。
function timer () {
echo "hello world";
Swoole\Timer::tick(2000, 'timer');
Swoole\Timer::tick(2000, function () {
echo "hello world";
非闭包的传统写法,先要声明一个函数,再转入函数名称字符串。两段代码是分离的,不够直观。而闭包的写法把定时器的声明和定时器要执行的代码写在了一起,逻辑非常清晰直观。使用闭包语法可以很方便编写回调函数。在事件驱动编程、排序、array_walk等需要用户传入一段执行代码的场景中,闭包的写法非常优雅。
闭包更强大的地方在于它可以直接在调用处引入外部变量。PHP中实现的方法就是use关键词。
如果刚才的定时器需要传入一个变量,传统的写法只能通过全局变量来实现。与JS不同,PHP的变量引入是显式的,如果要引用外部变量必须使用use来声明。而JS是隐式的,匿名函数内部可以随意操作外部变量,无需声明。这样好处是少写了一点代码,缺点是存在风险和混乱。
$str = "hello world";
function timer () {
Swoole\Timer::tick(2000, 'timer');
$str = "hello world";
Swoole\Timer::tick(2000, function () use ($str) {
闭包写法使用use直接引入了当前的$str变量,而不需要使用global全局变量。另外如果是在swoole的事件驱动编程模式,使用global就无法实现异步并发了,因为global全局变量只有1个,如果同时有多个客户端请求,每个请求要查询数据库,输出不同的内容,传统的编程方法就不太容易实现,需要使用全局变量数组,以客户端的ID为KEY保存各自的数据。
$requestArray = array();
$dbResultArray = array();
function my_request($request, $response) {
global $dbResultArray, $requestA
$queryId = $db->query($sql, 'my_request');
$requestArray[$request->fd] = array($request, $response);
$dbResultArray[$queryId] = $request->
function get_result($queryId, $queryResult) {
global $dbResultArray, $requestA
list($request, $response) = $requestArray[$dbResultArray[$queryId]];
$response->end($queryResult);
$server-&on('request', 'my_request');
$server->on('request', function ($request, $response) {
$queryId = $db->query($sql, function ($queryId, $queryResult) use ($request, $response) {
$response->end($queryResult);
传统的写法非常复杂,需要反复多次从全局数组保存/提取数据。而闭包的写法非常简洁优雅,只用了几行代码就实现了同样的功能。闭包写法非常适合用来编写异步非阻塞回调模式的服务器程序。目前热门的编程语言中只有PHP和JS具备这种能力。
闭包更多特性
在类的方法中使用匿名函数,5.4以上的版本无需使用use引入$this,直接可以在匿名函数中使用$this来调用当前对象的方法。在swoole编程中,可以利用此特性减少$serv对象的use引入传递。
class Server extends Swoole\Server {
function onReceive($serv, $fd, $reactorId, $data) {
$db->query($sql, function ($queryId, $queryResult) use ($fd) {
$this->send($fd, $queryResult);
另外如果希望在闭包函数中修改外部变量,可以在use时为变量增加&引用符号即可。注意对象类型不需要加&,因为在PHP中对象默认就是传引用而非传值。
Swoole扩展自带的Task进程功能非常强大,可以用来实现各种复杂的业务逻辑。本文主要介绍使用task/finish功能实现程序内的Map-Reduce并发任务处理。一个聊天服务经常会有群聊需求,我的群组和群组内成员,另外群组内成员需要按照积分排序,类似与这样的功能就可以使用Swoole简单实现。
传统多线程方案
创建2个全局变量Map,group_map以group_id为Key,存储成员set。user_map以uid为Key存储当前用户加入的所有group。
多线程环境下实际上不能直接操作这2个Map,必须要加锁。当添加用户到一个组或者用户退出一个组时需要操作这2个map,必须要加锁。如果操作很频繁,实际上锁的碰撞是很严重的,这部分操作就会变成串行的。同时只有一个线程可以对map进行操作。锁的争抢也会带来大量线程切换浪费很多CPU资源。
lock.lock();
group_map[group_id].append([uid, score]);
user_map[uid].append(group_id);
group_map.sortByScore();
lock.unlock();
基于Swoole的Task功能
基于Swoole的Task功能,可以将任务切片,然后hash投递到不同的Task进程,完成任务。排序功能可以直接使用PHP提供的SplHeap实现,时间复杂度为O(logn),如果要实现查询功能,如根据UID查询用户加入的所有群组,根据GroupId查询有哪些成员。可以先计算Hash找到对应Task进程,然后通过task/taskwait发送指令,直接读取进程的变量查找到信息。
$serv->set(array("task_worker_num" => 24));
$serv->task(array("cmd" => "user", "uid" => $uid, "gid" => $gid, "score" => $score), $gid % $task_worker_num);
$serv->task(array("cmd" => "group", "uid" => $uid, "gid" => $gid), $uid % $task_worker_num);
class MyMaxHeap extends SplHeap
public function compare($value1, $value2)
return ($value1['score'] - $value2['score']);
function onTask($serv, $taskId, $srcWorkerId, $data) {
static $userMap = array();
static $groupMap = array();
if ($data['cmd'] == 'group')
if (!isset($groupMap[$data['gid']]))
$groupMap[$data['gid']] = new MyMaxHeap();
$heap = $groupMap[$data['gid']];
$heap->insert(array("uid" => $data['uid'], "score" => $data['score']));
elseif ($data['cmd'] == 'user')
$userMap[$data['uid']][] = $data['gid'];
由于Task进程只有数组操作,所以是非阻塞的,只需要开启与CPU核数相同的进程数量即可。进程间无任何加锁争抢,性能非常好。Swoole的Task进程通信使用UnixSocket,是内核提供的全内存通信方式无任何IO,一写一读单进程可达100万/秒。虽然没有直接读变量的速度快,但性能也足够了。
————–伟大的分割线—————
PHP饭米粒(phpfamily) 由一群靠谱的人建立,愿为PHPer带来一些值得细细品味的精神食粮!
本文由 rango 独家授权 php饭米粒发布,转载请注明本来源信息和以下的二维码(长按可识别二维码关注):
并发IO问题一直是服务器端编程中的技术难题,从最早的同步阻塞直接Fork进程,到Worker进程池/线程池,到现在的异步IO、协程。PHP程序员因为有强大的LAMP框架,对这类底层方面的知识知之甚少,本文目的就是详细介绍PHP进行并发IO编程的各种尝试,最后再介绍Swoole的使用,深入浅出全面解析并发IO问题。
多进程/多线程同步阻塞
最早的服务器端程序都是通过多进程、多线程来解决并发IO的问题。进程模型出现的最早,从Unix系统诞生就开始有了进程的概念。最早的服务器端程序一般都是Accept一个客户端连接就创建一个进程,然后子进程进入循环同步阻塞地与客户端连接进行交互,收发处理数据。
多线程模式出现要晚一些,线程与进程相比更轻量,而且线程之间是共享内存堆栈的,所以不同的线程之间交互非常容易实现。比如聊天室这样的程序,客户端连接之间可以交互,比聊天室中的玩家可以任意的其他人发消息。用多线程模式实现非常简单,线程中可以直接向某一个客户端连接发送数据。而多进程模式就要用到管道、消息队列、共享内存,统称进程间通信(IPC)复杂的技术才能实现。
代码实例:
多进程/线程模型的流程是
创建一个 socket,绑定服务器端口(bind),监听端口(listen),在PHP中用stream_socket_server一个函数就能完成上面3个步骤,当然也可以使用更底层的sockets扩展分别实现。
进入while循环,阻塞在accept操作上,等待客户端连接进入。此时程序会进入随眠状态,直到有新的客户端发起connect到服务器,操作系统会唤醒此进程。accept函数返回客户端连接的socket
主进程在多进程模型下通过fork(php: pcntl_fork)创建子进程,多线程模型下使用pthread_create(php: new Thread)创建子线程。下文如无特殊声明将使用进程同时表示进程/线程。
子进程创建成功后进入while循环,阻塞在recv(php: fread)调用上,等待客户端向服务器发送数据。收到数据后服务器程序进行处理然后使用send(php: fwrite)向客户端发送响应。长连接的服务会持续与客户端交互,而短连接服务一般收到响应就会close。
当客户端连接关闭时,子进程退出并销毁所有资源。主进程会回收掉此子进程。
这种模式最大的问题是,进程/线程创建和销毁的开销很大。所以上面的模式没办法应用于非常繁忙的服务器程序。对应的改进版解决了此问题,这就是经典的Leader-Follower模型。
代码实例:
它的特点是程序启动后就会创建N个进程。每个子进程进入Accept,等待新的连接进入。当客户端连接到服务器时,其中一个子进程会被唤醒,开始处理客户端请求,并且不再接受新的TCP连接。当此连接关闭时,子进程会释放,重新进入Accept,参与处理新的连接。
这个模型的优势是完全可以复用进程,没有额外消耗,性能非常好。很多常见的服务器程序都是基于此模型的,比如Apache、PHP-FPM。
多进程模型也有一些缺点。
这种模型严重依赖进程的数量解决并发问题,一个客户端连接就需要占用一个进程,工作进程的数量有多少,并发处理能力就有多少。操作系统可以创建的进程数量是有限的。
启动大量进程会带来额外的进程调度消耗。数百个进程时可能进程上下文切换调度消耗占CPU不到1%可以忽略不接,如果启动数千甚至数万个进程,消耗就会直线上升。调度消耗可能占到CPU的百分之几十甚至100%。
另外有一些场景多进程模型无法解决,比如即时聊天程序(IM),一台服务器要同时维持上万甚至几十万上百万的连接(经典的C10K问题),多进程模型就力不从心了。
还有一种场景也是多进程模型的软肋。通常Web服务器启动100个进程,如果一个请求消耗100ms,100个进程可以提供1000qps,这样的处理能力还是不错的。但是如果请求内要调用外网Http接口,像QQ、微博登录,耗时会很长,一个请求需要10s。那一个进程1秒只能处理0.1个请求,100个进程只能达到10qps,这样的处理能力就太差了。
有没有一种技术可以在一个进程内处理所有并发IO呢?答案是有,这就是IO复用技术。
IO复用/事件循环/异步非阻塞
其实IO复用的历史和多进程一样长,Linux很早就提供了select系统调用,可以在一个进程内维持1024个连接。后来又加入了poll系统调用,poll做了一些改进,解决了1024限制的问题,可以维持任意数量的连接。但select/poll还有一个问题就是,它需要循环检测连接是否有事件。这样问题就来了,如果服务器有100万个连接,在某一时间只有一个连接向服务器发送了数据,select/poll需要做循环100万次,其中只有1次是命中的,剩下的99万9999次都是无效的,白白浪费了CPU资源。
直到Linux 2.6内核提供了新的epoll系统调用,可以维持无限数量的连接,而且无需轮询,这才真正解决了C10K问题。现在各种高并发异步IO的服务器程序都是基于epoll实现的,比如Nginx、Node.js、Erlang、Golang。像Node.js这样单进程单线程的程序,都可以维持超过1百万TCP连接,全部归功于epoll技术。
IO复用异步非阻塞程序使用经典的Reactor模型,Reactor顾名思义就是反应堆的意思,它本身不处理任何数据收发。只是可以监视一个socket句柄的事件变化。
Reactor有4个核心的操作:
add添加socket监听到reactor,可以是listen socket也可以使客户端socket,也可以是管道、eventfd、信号等
set修改事件监听,可以设置监听的类型,如可读、可写。可读很好理解,对于listen socket就是有新客户端连接到来了需要accept。对于客户端连接就是收到数据,需要recv。可写事件比较难理解一些。一个SOCKET是有缓存区的,如果要向客户端连接发送2M的数据,一次性是发不出去的,操作系统默认TCP缓存区只有256K。一次性只能发256K,缓存区满了之后send就会返回EAGAIN错误。这时候就要监听可写事件,在纯异步的编程中,必须去监听可写才能保证send操作是完全非阻塞的。
del从reactor中移除,不再监听事件
callback就是事件发生后对应的处理逻辑,一般在add/set时制定。C语言用函数指针实现,JS可以用匿名函数,PHP可以用匿名函数、对象方法数组、字符串函数名。
Reactor只是一个事件发生器,实际对socket句柄的操作,如connect/accept、send/recv、close是在callback中完成的。具体编码可参考下面的伪代码:
Reactor模型还可以与多进程、多线程结合起来用,既实现异步非阻塞IO,又利用到多核。目前流行的异步服务器程序都是这样的方式:如
Nginx:多进程Reactor
Nginx+Lua:多进程Reactor+协程
Golang:单线程Reactor+多线程协程
Swoole:多线程Reactor+多进程Worker
协程是什么
协程从底层技术角度看实际上还是异步IO Reactor模型,应用层自行实现了任务调度,借助Reactor切换各个当前执行的用户态线程,但用户代码中完全感知不到Reactor的存在。
PHP并发IO编程实践
PHP相关扩展
Stream:PHP内核提供的socket封装
Sockets:对底层Socket API的封装
Libevent:对libevent库的封装
Event:基于Libevent更高级的封装,提供了面向对象接口、定时器、信号处理的支持
Pcntl/Posix:多进程、信号、进程管理的支持
Pthread:多线程、线程管理、锁的支持
PHP还有共享内存、信号量、消息队列的相关扩展
PECL:PHP的扩展库,包括系统底层、数据分析、算法、驱动、科学计算、图形等都有。如果PHP标准库中没有找到,可以在PECL寻找想要的功能。
PHP语言的优劣势
PHP的优点:
第一个是简单,PHP比其他任何的语言都要简单,入门的话PHP真的是可以一周就入门。C++有一本书叫做《21天深入学习C++》,其实21天根本不可能学会,甚至可以说C++没有3-5年不可能深入掌握。但是PHP绝对可以7天入门。所以PHP程序员的数量非常多,招聘比其他语言更容易。
PHP的功能非常强大,因为PHP官方的标准库和扩展库里提供了做服务器编程能用到的99%的东西。PHP的PECL扩展库里你想要的任何的功能。
另外PHP有超过20年的历史,生态圈是非常大的,在Github可以找到很多代码。
PHP的缺点:
性能比较差,因为毕竟是动态脚本,不适合做密集运算,如果同样用PHP写再用c++写,PHP版本要比它差一百倍。
函数命名规范差,这一点大家都是了解的,PHP更讲究实用性,没有一些规范。一些函数的命名是很混乱的,所以每次你必须去翻PHP的手册。
提供的数据结构和函数的接口粒度比较粗。PHP只有一个Array数据结构,底层基于HashTable。PHP的Array集合了Map,Set,Vector,Queue,Stack,Heap等数据结构的功能。另外PHP有一个SPL提供了其他数据结构的类封装。
PHP更适合偏实际应用层面的程序,业务开发、快速实现的利器
PHP不适合开发底层软件
使用C/C++、JAVA、Golang等静态编译语言作为PHP的补充,动静结合
借助IDE工具实现自动补全、语法提示
PHP的Swoole扩展
基于上面的扩展使用纯PHP就可以完全实现异步网络服务器和客户端程序。但是想实现一个类似于多IO线程,还是有很多繁琐的编程工作要做,包括如何来管理连接,如何来保证数据的收发原则性,网络协议的处理。另外PHP代码在协议处理部分性能是比较差的,所以我启动了一个新的开源项目Swoole,使用C语言和PHP结合来完成了这项工作。灵活多变的业务模块使用PHP开发效率高,基础的底层和协议处理部分用C语言实现,保证了高性能。它以扩展的方式加载到了PHP中,提供了一个完整的网络通信的框架,然后PHP的代码去写一些业务。它的模型是基于多线程Reactor+多进程Worker,既支持全异步,也支持半异步半同步。
Swoole的一些特点:
Accept线程,解决Accept性能瓶颈和惊群问题
多IO线程,可以更好地利用多核
提供了全异步和半同步半异步2种模式
处理高并发IO的部分用异步模式
复杂的业务逻辑部分用同步模式
底层支持了遍历所有连接、互发数据、自动合并拆分数据包、数据发送原子性。
Swoole的进程/线程模型:
Swoole程序的执行流程:
使用PHP+Swoole扩展实现异步通信编程
实例代码在/swoole/swoole-src 主页查看。
TCP服务器与客户端
异步TCP服务器:
在这里new swoole_server对象,然后参数传入监听的HOST和PORT,然后设置了3个回调函数,分别是onConnect有新的连接进入、onReceive收到了某一个客户端的数据、onClose某个客户端关闭了连接。最后调用start启动服务器程序。swoole底层会根据当前机器有多少CPU核数,启动对应数量的Reactor线程和Worker进程。
异步客户端:
客户端的使用方法和服务器类似只是回调事件有4个,onConnect成功连接到服务器,这时可以去发送数据到服务器。onError连接服务器失败。onReceive服务器向客户端连接发送了数据。onClose连接关闭。
设置完事件回调后,发起connect到服务器,参数是服务器的IP,PORT和超时时间。
同步客户端:
同步客户端不需要设置任何事件回调,它没有Reactor监听,是阻塞串行的。等待IO完成才会进入下一步。
异步任务:
异步任务功能用于在一个纯异步的Server程序中去执行一个耗时的或者阻塞的函数。底层实现使用进程池,任务完成后会触发onFinish,程序中可以得到任务处理的结果。比如一个IM需要广播,如果直接在异步代码中广播可能会影响其他事件的处理。另外文件读写也可以使用异步任务实现,因为文件句柄没办法像socket一样使用Reactor监听。因为文件句柄总是可读的,直接读取文件可能会使服务器程序阻塞,使用异步任务是非常好的选择。
异步毫秒定时器
这2个接口实现了类似JS的setInterval、setTimeout函数功能,可以设置在n毫秒间隔实现一个函数或 n毫秒后执行一个函数。
异步MySQL客户端
swoole还提供一个内置连接池的MySQL异步客户端,可以设定最大使用MySQL连接数。并发SQL请求可以复用这些连接,而不是重复创建,这样可以保护MySQL避免连接资源被耗尽。
异步Redis客户端
异步的Web程序
程序的逻辑是从Redis中读取一个数据,然后显示HTML页面。使用ab压测性能如下:
同样的逻辑在php-fpm下的性能测试结果如下:
WebSocket程序
swoole内置了websocket服务器,可以基于此实现Web页面主动推送的功能,比如WebIM。有一个开源项目可以作为参考。/matyhtf/php-webim
PHP+Swoole协程
异步编程一般使用回调方式,如果遇到非常复杂的逻辑,可能会层层嵌套回调函数。协程就可以解决此问题,可以顺序编写代码,但运行时是异步非阻塞的。腾讯的工程师基于Swoole扩展和PHP5.5的Yield/Generator语法实现类似于Golang的协程,项目名称为TSF(Tencent Server Framework),开源项目地址:/tencent-php/tsf。目前在腾讯公司的企业QQ、QQ公众号项目以及车轮忽略的查违章项目有大规模应用 。
TSF使用也非常简单,下面调用了3个IO操作,完全是串行的写法。但实际上是异步非阻塞执行的。TSF底层调度器接管了程序的执行,在对应的IO完成后才会向下继续执行。
在树莓派上使用PHP+Swoole
PHP和Swoole都可以在ARM平台上编译运行,所以在树莓派系统上也可以使用PHP+Swoole来开发网络通信的程序。
12月1日发布了swoole-1.7.21版本,修复了很多BUG。这是swoole-1.7最后一个版本了,以后不再更新。我们启动了雄心勃勃的swoole-1.8开发计划。在介绍1.8的计划前,先讲一段小插曲。
性能测试游戏
昨天在1.7.21发布后,正好赶上PHP7也发布,忍不住想要做下性能测试。安装了一下HHVM,准备对比PHP5.6、PHP7、HHVM3.9的性能。对WordPress程序的测试中,PHP7确实已经与HHVM很接近了。不过这不是重点,接下来我又测试了一下新写的PHP版本WebSocket客户端。这一测问题就来了。我发现PHP在这个场景下性能非常差。当时的数据是:
5.6.9 cost 419ms
5.6.99-hhvm cost 905ms
7.0.0RC7 cost 777ms
压测10万次WebSocket协议打包,竟然消耗了23秒,PHP7比PHP5.6提升了1倍性能,但和HHVM还是有较大的差距。这个结果倒是在预料之中,毕竟Zend PHP没有JIT,这样纯密集计算的场景下性能不会太好。我顺手加了一个C版本的websocket_pack。再次压测,结果震惊了。
5.6.9 cost 419ms
5.6.99-hhvm cost 905ms
7.0.0RC7 cost 777ms
C-websocket_pack cost 57.ms
C语言版本只用了57ms就完成了10万次的打包。比PHP版本性能高出300多倍。即使是有JIT的HHVM也无法和C语言版本的websocket_pack相提并论。好了接下来就聊聊swoole-1.8的开发计划。
Swoole-1.8的开发计划
异步模式下reload/max_request的支持
swoole-1.7下如果Server是纯异步非阻塞的模式,使用reload/max_request可能会导致某些事件丢失。所以WIKI中明确写了异步模式不应该设置max_request,reload会出现一些错误。1.8会解决此问题,对异步模式下Worker进程退出逻辑将发生一些修改。底层会确保Worker进程中的事件全部处理完后才会退出。
网络协议部分的调整
swoole-1.8将增加Http异步客户端(同步阻塞模式直接用CURL就可以了)、WebSocket同步/异步客户端。1.8还将增加http2.0、mqtt协议的支持。
另外一个重要调整就是,1.8将支持Server多协议。目前1.7版本一个swoole_server只能使用一种网络协议,比如swoole_http_server就只能处理HTTP协议,无法再支持固定包头+包体、EOF协议。有一些场景下一个Server需要做多件事情。新的版本会支持混合多协议。
正是因为上面的性能测试,让我下定决定在swoole内逐步开始支持绝大部分通用的网络协议。C语言实现的客户端在性能上远远超过其他语言。Swoole要让PHP在网络通信方面超过其他语言,比如Java、Node.js、Golang、Erlang等,只有C语言实现一条路可选。
扩展内RPC的支持
RPC作为服务器端程序中很重要的一项功能,Swoole没有理由不去支持它。1.8版本将会内置RPC的基础模块,包括Server端和Client端,Client端会同时支持异步和同步,并且支持php-fpm下调用。
配套工具的支持
经常有PHPer向我反馈想要有一套命令行工具可以简单地实现Server程序的start、stop、reload、status,以及通过inotify监控实现自动reload,新的swoole-1.8开发计划中将包含这些工具,我们会提供一些通用的、非侵入式的工具。
Swoole-1.8什么时候发布
我们已经启动了开发,新特性会逐个在子版本上线,预计2016年7月份会上齐所有特性。
昨天和一个前同事聊天,各种吐槽PHP,吐槽Swoole,他认为PHP到处是坑,PHP局限很大。PHP+Swoole不适合做高并发服务器,C+Swoole才是最好的方案。C++有各种数据结构,C++可以开线程,C++可以共享对象。看来有必要好好得说明一下了。
PHP比C/C++或Java少了什么?多线程,多线程,多线程……
是的。PHP比C/C++、Java少了多线程。PHP只有多进程的方案,所以PHP里的全局变量和对象不是共享的、数据结构也不能跨进程操作、Socket文件描述符不能共享等等。所以PHP有局限?
多线程看似比多进程要强大很多,实际上我可以负责任的告诉你,多线程带来的坑更多。
数据同步问题会让你崩溃的。要么就牺牲性能到处加锁,要么就用地狱难度的无锁并发编程,据我所知目前国内能掌握此项技能的人凤毛麟角。
不要以为加锁就万事大吉了,你会在死锁问题上栽个大跟头。当你的程序逻辑复杂后,锁越来越难控制了,一旦死锁你的程序基本上就完了。
某个线程挂了那所有线程都会退出
反而在看多进程,其实就简单的多了。
配合进程间通信,基本上你可以实现任意的数据共享。比如利用一个进程专门存数据结构和对象,其他进程的数据操作全部投递到此进程来
多进程不需要锁
多进程可以使用共享内存的数据结构实现一些多线程的功能。如Swoole提供的Table、Atomic可以实现数据共享,但成本很低。未来还会加入共享内存队列
所谓PHP限制了Swoole,这完全是无稽之谈。合理利用Swoole提供的Table、Atomic、SendMessage/PipeMessage、Task完全可以实现异步非阻塞的代码逻辑。
C++写出来的程序性能更好?
这完全是盲目的迷信,密集计算的程序C++确实是有优势的。而并发服务器核心是IO,并非大规模密集运算。C++从语言层面来看并没有什么优势。另外C++中的大部分数据结构在PHP中都有对应的实现,实在不行自己写个专门的扩展也能解决之。
高并发的服务器单机能维持10W连接、每秒可处理3-5W笔消息收发。这种性能水准已经可以应用在BAT的核心系统上了。
开发效率快的意义是什么?
这位同事还说PHP开发Server虽然比C++快了,但是追求性能的极致还是要用C++。我要告诉你效率高了究竟意义何在。开发一套好程序不是一件容易的事情,需要程序员投入大量时间和精力。开发效率提升的意义并不是简单的我可以更少时间完工,而是剩下的时间你可以增加单元测试、修复BUG、提升用户体验、完善细节、提供配套工具、优化性能、增加关键日志、增加监控报警、增加容灾方案。
TCP协议在底层机制上解决了UDP协议的顺序和丢包重传问题。但相比UDP又带来了新的问题,TCP协议是流式的,数据包没有边界。应用程序使用TCP通信就会面临这些难题。一些程序在本机测试是正确的,上线后就出现各种奇怪的BUG。如下面的伪代码,客户端向服务器端发送一个json字符串,服务器端接收此字符串。在慢速网络中Server无法正确接收完整的JSON字符串。
$client-&send(json_encode('a' =& $data_10k, 'b' =& $data_5k));
$pkg = $server-&recv(); //Server收到的数据只有一小部分
$client-&send("hello1");
$client-&send("hello2");
$client-&send("hello3");
$pkg = $server-&recv(); //Server会一次性收到3个数据包
因为TCP通信是流式的,在接收1个大数据包时,可能会被拆分成多个数据包发送。多次Send底层也可能会合并成一次进行发送。这里就需要2个操作来解决:
分包:Server收到了多个数据包,需要拆分数据包
合包:Server收到的数据只是包的一部分,需要缓存数据,合并成完整的包
具体编码实现这里就不讲了,这是一个比较复杂的编程过程,稍有不慎就会出现严重的BUG
Swoole如何解决此问题
swoole提供了通用协议的支持,如Http和WebSocket。自定义协议可以使用Length/EOF 2种协议解析方式来完美解决此问题。从1.7.18版本开始,Swoole的Server/Client都支持了Length/EOF的协议处理方式,应用层代码只需要配置一下参数,就无需关注底层分包合包了。每次onReceive收到的数据包总是完整的。
Server/Client的配置是相同的
Http/WebSocket
swoole内置对http/websocket 2种协议的支持,如果要实现一个http服务或者websocket服务,直接用swoole_http_server和swoole_websocket_server即可。
$server-&set(array('open_eof_split' =& true, 'package_eof' =& "\r\n"));
EOF协议处理的原理是每个数据包结尾加一串特殊字符表示包已结束。如memcache、ftp、stmp都使用\r\n作为结束符。发送数据时只需要在包末尾增加\r\n即可。使用EOF协议处理,一定要确保数据包中间不会出现EOF,否则会造成分包错误。
$server-&set(array(
'open_length_check' =& true,
'package_max_length' =& 81920,
'package_length_type' =& 'n', //see php pack()
'package_length_offset' =& 0,
'package_body_offset' =& 2,
固定包头的协议非常通用,在BAT的服务器程序中经常能看到。这种协议的特点是一个数据包总是由包头+包体2部分组成。包头由一个字段指定了包体或整个包的长度,长度一般是使用2字节/4字节整数来表示。服务器收到包头后,可以根据长度值来精确控制需要再接收多少数据就时完整的数据包。Swoole的配置可以很好的支持这种协议,可以灵活地设置4项参数应对所有情况。
Swoole的Server和异步Client都是在onReceive回调函数中处理数据包,当设置了协议处理后,只有收到一个完整数据包时才会触发onReceive事件。同步客户端在设置了协议处理后,调用 $client-&recv() 不再需要传入长度,recv函数在收到完整数据包或发生错误后返回。
最近PHP官方终于发布了传说中的PHP7,虽然只是alpha版。PHP7号称是新一代的PHP,官方开发组对Zend引擎底层做了大量修改来优化PHP的性能。可以说PHP7这个版本的主题就是性能优化。
在过去PHP一直以开发效率快著称,而语言本身的性能较差(当然比Python,Ruby还是要快一些的)。普通的Web网站都是IO密集型的程序,瓶颈在MySQL上,所以体现不出PHP的性能劣势。但在密集计算方面比C/C++、Java等静态编译语言差几十倍甚至上百倍。另外使用设计非常复杂的开发框架,如Symfony、Laravel等,程序性能也会明显下降。
现在随着PHP越来越流行,像Facebook、新浪微博这样超大型规模的网站都在使用PHP。PHP语言性能问题就越来越严重了。Facebook有几十万台服务器,如果现有的PHP程序可以提升一部分性能,将会节约大量的服务器资源。所以就有了HHVM、Hack。Hack为PHP增加了类型,HHVM是一个重新设计的PHP引擎,实际项目中使用HHVM可以提近70%的性能。实际项目70%性能提升这是一个什么概念?腾讯QQ农场最初使用PHP开发,后因为性能问题使用C语言重构,完成后性能提升了100%。
PHP官方也注意到了这个问题,所以就有了PHP7的开发计划。最新公布的PHP7-alpha在WordPress项目中测试的表现已经超越了HHVM。未来PHP将会同时具备极高的开发效率和极高的性能,再结合Swoole做异步编程,PHP势必会更加流行。
本文简单介绍一下PHP7做了哪些优化,可以提升如此多性能。
zval使用栈内存
在Zend引擎和扩展中,经常要创建一个PHP的变量,底层就是一个zval指针。之前的版本都是通过MAKE_STD_ZVAL动态的从堆上分配一个zval内存。而PHP7可以直接使用栈内存。PHP代码中创建的变量也进行了优化,PHP7直接在栈内存上预分配zval。这样节约了大量内存分配和内存管理的操作。
zval * MAKE_STD_ZVAL(val);
二 zend_string存储hash值,array查询不再需要重复计算hash
PHP7为字符串单独创建了新类型叫做zend_string,除了char *指针和长度之外,增加了一个hash字段,用于保存字符串的hash值。PHP中array是核心数据结构,PHP程序中往往都有大量的$array[$key]操作,虽然hashtable查找的时间复杂度是O(1),但$key要转为hash值是要经过计算的。不仅仅是array操作,实际上PHP底层对于类属性、类方法、函数,访问时都要先通过hashtable查找到对应的指针,再执行对应的操作。PHP7之前Zend引擎会有大量的CPU时间用于计算hash值。
实际上PHP程序运行起来之后,大部分情况下$key的值都是不变的。PHP7干脆将这个hash值保存起来,下次直接使用,这样就节省了大量的hash计算操作,PHP的hashtable与C数组的性能一致。
从实际项目进行callgrind性能分析,会发现alloc和hash 2项操作就占用了相当大比例的CPU时间。PHP7优化之后这2项操作占用的CPU时间降低了非常多。(注:zend_hash仍然占12%,因为整体CPU降低了,所以总的耗时降低了不少)
三 hashtable桶内直接存数据
PHP5的hashtable每个元素都是一个 Bucket *,而PHP7直接存Bucket,减少了内存申请次数,提升了Cache命中率和内存访问速度。
四 zend_parse_parameters改为宏实现
PHP的C扩展函数与PHP中的变量进行参数输入时,要使用zend_parse_parameters()函数,这个函数根据一个字符串参数找到对应PHP的zval指针,然后进行赋值。 这个函数实际上有一定的性能消耗。PHP7直接使用宏替换了zend_parse_parameters函数,C扩展中不再需要使用zend_parse_parameters进行逐个参数的查找,宏展开后自动会实现参数赋值。仅此一项就提升了5%的性能。
五 新增加4种OPCODE
很多PHP程序中会大量使用call_user_function, is_int/string/array,
strlen , defined 函数。PHP5 都是以扩展函数的方式提供,PHP7中这4类函数改成ZendVM的OPCODE指令,执行更快。
六 其他更多优化
除了上面5个主要优化点之外,PHP7还有其他更多的细节性能优化。如基础类型int、float、bool等改为直接进行值拷贝,排序算法改进,PCRE with JIT,execute_data和opline使用全局寄存器等等。PHP7对性能的优化会继续进行下去。
PHP7-alpha相比PHP5.6性能提升了近3倍。下面是WordPress在PHP7上的表现:
PHP7的新特性
除了性能优化外,PHP7新增加了2项重要的新特性。
1. 变量类型
PHP7版本函数的参数和返回值增加了类型限定。为什么PHP要加入类型,实际上此项特性是为了PHP7.1版本的JIT特性做准备,增加类型后PHP JIT可以准确判断变量类型,生成最佳的机器指令。
function test(int $a, string $b, array $c) : int {
2. 错误异常
PHP程序出错后过去Zend引擎会发生致命错误并终止程序运行,PHP7可以使用try/catch捕获错误。底层使用Exeception代替了Fatal Error。这个特性表示PHP语言正在向一个更加规范的方向发展。应用层与底层在错误抛出的方式全部统一为异常。
non_exists_func();
} catch (EngineException $e) {
echo "Exception: {$e-&getMessage()}\n";
$test = new class("Hello World") {
public function __construct($greeting) {
$this-&greeting = $
最初PHP7性能优化的方向并不是以上所讲的,而是JIT。JIT是just in time的缩写,表示运行时将指令转为二进制机器码。Java语言的JVM引擎底层就是使用JIT将Java字节码编译为二进制机器码执行。PHP7开发过程中有一个中间版本是基于JIT,后来开发组发现使用JIT后,对于实际项目并没有有太大的性能提升,所以PHP7最终放弃了JIT方案,PHP7.0-final版本不会携带JIT特性。
但如果是密集计算类程序就不同了,使用JIT将PHP OpCode编译为机器码,运算的性能会大幅提升。PHP官方开发组在2014年底重启了JIT的开发工作。
PHP的异步网络通信扩展Swoole
PHP在大部分程序员印象中都是用来做Web网站的。PHP没有像Python的Twisted、Tornado,Java的Netty、Mina,JavaScript的Node.js等框架,无法实现异步网络通信程序。PHP的Swoole扩展就是为了弥补此项缺陷而诞生的开源项目。Swoole是一个标准的PHP扩展,为PHP提供了一系列异步IO、事件驱动、并行数据结构功能。
Swoole与Node.js非常相似,不同之处是Swoole在并行提供了底层支持。Node.js是一个单进程单线程的程序,在多核服务器上无法发挥全部CPU核的计算能力。需要程序员自行使用child_process/cluster扩展或者启动多实例,使程序能够利用到多核优势。而Swoole在底层就支持了多线程/多进程,程序启动后就会创建好多个IO线程和多个Worker进程。程序员仅需配置线程/进程数量即可。
使用Swoole开发的TCP服务器程序:
$serv = new swoole_server("127.0.0.1", 9501);
$serv-&on('connect', function ($serv, $fd){
echo "Client:Connect.\n";
$serv-&on('receive', function ($serv, $fd, $from_id, $data) {
$serv-&send($fd, $data);
$serv-&on('close', function ($serv, $fd) {
echo "Client: Close.\n";
$serv-&start();
Swoole同样也内置了http_server和WebSocket服务器的支持。swoole_http_server与传统的php-fpm不同,它是在PHP内进行事件循环的,基于swoole_http_server完全可以开发出类似Java应用服务器一样,可以控制完整对象生命周期的程序。swoole_http_server天然支持异步IO,可以很方便的实现支持大量TCP连接的Comet服务。swoole_websocket_server可以用来实现支持Web实时推送的程序。
使用Swoole的Web服务器程序:
$http = new swoole_http_server("0.0.0.0", 9501);
$http-&on('request', function ($request, $response) {
$response-&header("Content-Type", "text/ charset=utf-8");
$response-&end("
Hello Swoole. #".rand()."
$http-&start();
可以预见PHP语言未来会在性能方面有明显的提升,越来越接近C/C++、Java等静态编译语言。再加上Swoole扩展,PHP的使用范围可以扩展到移动通信、云计算、网络游戏、物联网、车联网、智能家居等领域。
PHP虽然未必是最好的编程语言,但PHP在向着这个方向在发展。
swoole-1.7.14增加了tick定时器,解决了addtimer存在的各种问题。
swoole_timer_tick(1000, function ($timer_id) {
echo "tick-1000ms\n";
swoole_timer_clear($timer_id);
swoole_timer_tick(2000, function () {
echo "tick-2000ms\n";
swoole_timer_tick和swoole_timer_after原理是相同的,区别是after只执行一次就会销毁。tick定时器会持续执行,直到调用了swoole_timer_clear清除定时器。
与PHP代码中的pcntl_alarm不同,swoole的定时器是基于EventLoop实现的,不依赖系统信号和PHP的tick机制,所以效率非常高。即使添加大量定时器,CPU消耗也是很低的。PHP在过去没有像JavaScript这样方便的定时器功能,现在有swoole就可以很好地解决。
在网络游戏程序中经常要用定时器,PHP+swoole将会成为最佳的技术方案。
最近群里很多朋友询问我是如何学习编程的,今天抽个时间大致讲讲。
和所有人一样,上学学的第一门编程语言是C语言。我记得我当时学的很认真,非常认真。和别人最大的不同是我买了一本老外写的C语言编程书,没被谭浩强误导。另外还买了一本《现代操作系统》一书看了很久。学习期间写了不少读书笔记。真正要说学到什么了,这个还真不好说,到最后我对C语言也是似懂非懂的状态。
用学物理、数学的方法学编程语言是行不通的,编程需要的是实践。
学Java的目的很明确,为了找一份好工作。面向对象、J2EE、Java设计模式、SSH的书买了好几本。最后还是没能够入门。原因我后来分析主要是2点,1、Java太复杂了,根本就不适合初学者。2、犯了和学习C语言同样的错误,读书太多,实践太少(主要是不会搭建Java的开发环境)。
偶然的机会了解到了PHP语言,按照网上的教程一搞,写了一个MySQL的网页程序,竟然运行成功了。这实在太神奇了。你们无法理解当时我的心情,可以说到了 高兴地3天不用吃饭的程度。呵呵,当时我就下定决心以后就是它了。PHP让我进入了一个新的境界,我可以大量地实践,写出各种程序。而且也顺利找到了工作,干的风生水起,一个接一个地做了很多项目。后来又进入腾讯,在腾讯有机会去开发维护一个超过2亿用户,日活超过8千万,机器规模超过1000多台的超大系统。我的人生的各种成果都归功于PHP这门伟大的编程语言。
重新回到C语言
当然后面也逐渐认识到PHP的不足,不过很快我就发现可以写C扩展来弥补。重新学起C语言,我发现竟然很顺利。原因应该是我在写PHP程序的过程中,工程实践能力已经磨练到非同一般。其实不只是C语言,只要我愿意,C++,Java,Python,Golang任何语言都可以快速掌握。然后就开始搞Swoole这个项目,边学边做。经过这2年的时间,现在可以说我已经掌握C语言了。
为什么会回到C语言,原因很简单:所有底层软件都是C语言写的,包括操作系统、底层库、驱动、其他编程语言本身。C语言一切现代软件技术的根源。
我的个人感悟
我的编程之路轨迹就是 C语言(起始) -& Java(过客)-& PHP(主)-& C语言(终点)。
编程语言专注于一个就行了,Java、PHP、Python、Ruby 任意一个都行。只要能把一门编程语言学精通,其他语言学起来也很容易。
工作时选择的编程语言,应该是一主多辅。做Web得了解Javascript,做APP需要掌握Android、Object-C,写桌面程序需要学习C++。带着问题去学就好了。
不要选择难或者复杂的编程语言作为入门,易于上手的编程语言更容易成功。公司有一个搞Java 9年的人,技术等级很低。很好的例子。另外一个只工作了5年的PHPer现在已经是T3-2的技术Leader了。
编程最核心的是动手实践
多看一看别的编程语言和技术,开阔一下视野
编程做到最后还是会回到C语言的,当然很多人中途就已经离开编程岗位了
分类: , , ,
最近到处在争论这些话题,发现很多人对一些基础的常识并不了解,在此发表一文做一下解释。此文未必能解答所有问题,各位能有一个大致的了解就好。
C10K的由来
大家都知道互联网的基础就是网络通信,早期的互联网可以说是一个小群体的集合。互联网还不够普及,用户也不多。一台服务器同时在线100个用户估计在当时已经算是大型应用了。所以并不存在什么C10K的难题。互联网的爆发期应该是在www网站,浏览器,雅虎出现后。最早的互联网称之为Web1.0,互联网大部分的使用场景是下载一个Html页面,用户在浏览器中查看网页上的信息。这个时期也不存在C10K问题。
Web2.0时代到来后就不同了,1方面是普及率大大提高了,用户群体几何倍增长。2是互联网不再是单纯的浏览万维网网页,逐渐开始进行交互,而且应用程序的逻辑也变的更复杂,从简单的表单提交,到即时通信和在线实时互动。C10K的问题才体现出来了。每一个用户都必须与服务器保持TCP连接才能进行实时的数据交互。Facebook这样的网站同一时间的并发TCP连接可能会过亿。
腾讯QQ也是有C10K问题的,只不过他们是用了UDP这种原始的包交换协议来实现的,绕开了这个难题。当然过程肯定是痛苦的。如果当时有epoll技术,他们肯定会用TCP。后来的手机QQ,微信都采用TCP协议。
这时候问题就来了,最初的服务器都是基于进程/线程模型的,新到来一个TCP连接,就需要分配1个进程(或者线程)。而进程又是操作系统最昂贵的资源,一台机器无法创建很多进程。如果是C10K就要创建1万个进程,那么操作系统是无法承受的。如果是采用分布式系统,维持1亿用户在线需要10万台服务器,成本巨大,也只有Facebook,Google,雅虎才有财力购买如此多的服务器。这就是C10K问题的本质。
实际上当时也有异步模式,如:select/poll模型,这些技术都有一定的缺点,如selelct最大不能超过1024,poll没有限制,但每次收到数据需要遍历每一个连接查看哪个连接有数据请求。
Epoll异步非阻塞
既然有了C10K问题,程序员们就开始行动去解决它。于是FreeBSD推出了kqueue,Linux推出了epoll,Windows推出了IOCP。这些操作系统提供的功能就是为了解决C10K问题。因为Linux是互联网企业中使用率最高的操作系统,Epoll就成为C10K killer、高并发、高性能、异步非阻塞这些技术的代名词了。
epoll技术的编程模型就是异步非阻塞回调,也可以叫做Reactor,事件驱动,事件轮循(EventLoop)。Epoll就是为了解决C10K问题而生。使用Epoll技术,使得小公司也可以玩高并发。不需要购买很多服务器,有几台服务器就可以服务大量用户。Nginx,libevent,node.js这些就是Epoll时代的产物。
C100K,C1M,C10M,C100M …
C10K问题解决后,程序员又提出了更高的挑战,也就是最近在火热争论的C100K,C1M等。Epoll既然能解决C10K,解决什么C100K,C1M也是可以的。只不过这个已经没有意义了。一个公司有1亿用户难道他买不起1万台服务器嘛。WhatsApp有2亿用户,卖了150亿美元。1万台服务器最多花费5000万美元。
看到阿里技术保障部的人也在谈C10K话题,我要补充一下,搞路由器、交换机、网关、防火墙之类基础网络设备的人,就不要参与C10K话题了。我们说的是应用层程序。
协程,coroutine
当程序员还沉浸在解决C10K问题带来的成就感时,一个新的问题被抛出了。异步嵌套回调太TM难写了。尤其是Node.js层层回调,缩进了几十层,要把程序员逼疯了。于是一个新的技术被提出来了,那就是协程(coroutine)。这个技术本质上也是异步非阻塞技术,它是将事件回调进行了包装,让程序员看不到里面的事件循环。程序员就像写阻塞代码一样简单。比如调用 client-&recv() 等待接收数据时,就像阻塞代码一样写。实际上是底层库在执行recv时悄悄保存了一个状态,比如代码行数,局部变量的值。然后就跳回到EventLoop中了。什么时候真的数据到来时,它再把刚才保存的代码行数,局部变量值取出来,又开始继续执行。
这个就像时间禁止的游戏一样,国王对巫师说“我必须马上得到宝物,不然就砍了你的脑袋”,巫师念了一句时间停止的咒语,直到过了1年后勇士们才把宝物送来。这时候巫师解开咒语,把宝物交给国王。这里国王就可以理解成协程,他根本没感觉到时间停止,在他停止到醒来期间发生了什么他不知道,也不关心。
这就是协程的本质。协程是异步非阻塞的另外一种展现形式。Golang,Erlang,Lua协程都是这个模型。
再回到同步阻塞这个话题,不知道大家看完协程是否感觉得到,实际上协程和同步阻塞是一样的。答案是的。所以协程也叫做用户态进/用户态线程。区别就在于进程/线程是操作系统充当了EventLoop调度,而协程是自己用Epoll进行调度。
协程的优点是它比系统线程开销小,缺点是如果其中一个协程中有密集计算,其他的协程就不运行了。操作系统进程的缺点是开销大,优点是无论代码怎么写,所有进程都可以并发运行。
Erlang解决了协程密集计算的问题,它基于自行开发VM,并不执行机器码。即使存在密集计算的场景,VM发现某个协程执行时间过长,也可以进行中止切换。Golang由于是直接执行机器码的,所以无法解决此问题。所以Golang要求用户必须在密集计算的代码中,自行Yield。
实际上同步阻塞程序的性能并不差,它的效率很高,不会浪费资源。当进程发生阻塞后,操作系统会将它挂起,不会分配CPU。直到数据到达才会分配CPU。多进程只是开多了之后副作用太大,因为进程多了互相切换有开销。所以如果一个服务器程序只有1000左右的并发连接,同步阻塞模式是最好的。
异步回调和协程哪个性能好
协程虽然是用户态调度,实际上还是需要调度的,既然调度就会存在上下文切换。所以协程虽然比操作系统进程性能要好,但总还是有额外消耗的。而异步回调是没有切换开销的,它等同于顺序执行代码。所以异步回调程序的性能是要优于协程模型的。
这里是指Nginx这种多进程异步非阻塞程序。Node.js/Redis此类程序如果不开多个进程,由于无法利用多核计算优势,所以性能并不好。在Node.js中可以使用childprocess/cluster等扩展开启多进程以解决此问题。}

我要回帖

更多关于 java游戏服务器框架 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信