[实践OK]Swoole、PHP与MySQL:连接池,swoole扩展实现真正的PHP数据库连接池。

jackxiang 2016-1-3 13:08 | |
背景:天峰兄弟及Swoole的出现解决了长连接问题,但对mysql的线程池还需要进一步解决成一个体系,这块web直接连, 那么连接池也就是多余的 ,RPC形成一个过程也就让性能消耗最小,非常直接且跨平台,再谈一点这块的一个背景:也包括在PHP出现前的WEB1.0阶段,如,在新浪企业邮箱就有基于Sun Solaris 系统上面用c++写Mysql的长连接实现邮箱用户验证登录,那时候的长连接是基于Solaris 下的RPC实现(那时硬件也是sun提供的),对mysql那一端形成一个远程过程的调用,通过XDR数据结构进行解析mysql传来的数据项(RPC也为sun最新提出并后来在linux上默认支持),也就是说像用户登录验证这一块用Mysql的长连接来实现,提高其效率运行相当稳定,后面这个系统迁移到了FreeBSD后,出现了mysql长连接的服务经常出现假死,也就是说进程还在,但是已经连接不上mysql了,重新启动这个RPC服务又好了,原因未知,当时我对c++不了解(现在也不太了解,只听说要看是否形成coredump啥的),当年我还写过一个判断死了就杀死,重启动,判断的程序也老半天回不来,于是我又改成了一个多线程,如果超时没有回来,就干掉那个进程,重启Rpc服务,再后来,这套C++的cgi被替换成了php,再后来基于FreeBSD的系统迁移到了Linux,也就是现在一直在linux上,linux也就强大了起来,回想起来,当年一个登录服务如此极致,现在都变成了直接查询mysql了,这个长连接技术有还有用吗?我只能说对有上千台上万台的服务器可能有用,能节省一定的机器成本罢。但是,追求技术永无止境,需要有这样的一些东西来繁荣我们这个PHP的市场,长连接这个话题不再是Java做成了连接池,像c++也能做成连接池,像腾讯广平就有c++团队还有写cgi实现长连接Mysql服务,据说前二年吧更多关注了H5,像实时技术,比如Tail技术在web上的实现,有转向nodejs的趋势(node试想通过在google这颗大树下提供出来的V8引擎让前端程序员为排头兵统一后端服务及接口),而此时的PHP拿不出这样的技术,是很危险的,有了swoole起到填补作用,我更多的是觉得官方应该重视这个技术,而不是形成一个扩展,像H5的来到,像websocket的进入,这些东西对于Node来讲,从前端向后端的统一,而PHp呢?没有谁来解决,那么从用户角度来讲,开发者用户的流失或迁移,对PHP本身也是一个损失,但我还是说PHP是最好的语言没有之一,期望其能伴随潮流,与时俱进,更好满足当前web端新的需求。

发牢骚:port其实是通过源码编译的,所以不好。FreeBSD这不是都提供了嘛,还要怎么的,有点像人们的皮带,一天不系,你觉得不舒服,要勒紧吗?现代这帮人典型的吃现成的,导致了FreeBSD的没落。
源码包自然有必要提供, 但是你不能要求每个用户用一个软件都编译半天吧,源码的好处是只要你微调得当,性能是最大化的
,然并卵,现在机器性能都挺好,还有8M的嵌入式没法支持,什么不支持,我是发现还有比我懒的人了,听说有交叉编译也不会是在8M上编译啊。

前企业邮箱杀rpc的shell,都快忘记了,做个备份: http://jackxiang.com/post/1273/   2008-9-23 18:31 八年前的事情了。
从Solaris 到FreeBSD再到linux(Centos),其最后居然是linux 居上,而像sun的java被收购,最后FreeBSD的开源太底层(基于系统OS开源),BSD 的代码不是被控制在任何一个人手里,而 Linux的内核基本上被 Linus Torvalds ( Linux创始人)所控制,BSD 并没有单一的人来说什么可以或什么不可以进入代码。但BSD太自由了难度反而大了,人少了是根本原因,再就是商业化的需求没有满足到,被linux干下坡路了,但是,Debian Linux操作系统创始人去世 年仅42岁 ....,我想这个事件会给linux带来不可估量的损失,为什么,debian linux向FreeBSD学习port技术后,发展出的ubutu系统..不说了,反上这个哥们算是能善于学习,他死了...linux社区未来不太好说,但会有波澜是肯定的了。
————————————————————————————————————————————————————————————————
PHP的数据库连接池一直以来都是一个难题,很多从PHP语言转向Java的项目,大多数原因都是因为Java有更好的连接池实现。PHP的MySQL扩展提供了长连接的API,但在PHP机器数量较多,规模较大的情况下,mysql_pconnect非但不能节约MySQL资源,反而会加剧数据库的负荷。

假设有100台PHP的应用服务器,每个机器需要启动100个apache或fpm工作进程,那每个进程都会产生一个长连接到MySQL。这一共会产生1万个My SQL连接。大家都知道MySQL是每个连接会占用1个线程。那MYSQL就需要创建1万个线程,这样大量的系统资源被浪费在线程间上下文切换上。而你的业务代码中并不是所有地方都在做数据库操作,所以这个就是浪费的。

连接池就不同了,100个worker进程,公用10个数据库连接即可,当操作完数据库后,立即释放资源给其他worker进程。这样就算有100台PHP的服务器,那也只会创建1000个MySQL的连接,完全可以接受的。

首先,环境约定如下:
说一下测试环境吧:OS CentOS 7.1 x86;PHP 5.4.44;Mysql 5.7.9-log;swoole-1.7.22。

一)开始编译,网上好多都是编译过了,但是出现某些函数找不到运行时会警告,特别标名一下原因:
以前确实没有好的办法来解决此问题的,现在有了swoole扩展,利用swoole提供的task功能可以很方便做出一个连接池来。
编译时要注意一下:

还是出现:[2015-06-29 18:58:24 *9092.0] WARN zm_deactivate_swoole: Fatal error: Call to undefined function swoole_get_mysqli_sock()
因为参数:编译swoole时需要加enable-async-mysql参数来开启 swoole_get_mysqli_sock

php -r "print_r(get_defined_functions());"|grep swoole_get_mysqli_sock 并没发现有这个函数~可能新版本移掉了吧。
实践发现,这块swoole的官方对这块的编译参数并不太提及,不是没有这个函数,这个swoole_get_mysqli_sock函数通过源码里发现是有的。
是因为configure里出现了问题,出现在这儿,对比一下编译且运行Ok的./configure选项就知道了,正确的如下:

一些博文里的:--enable-async-mysql 后面有路径,这块在swoole-src-swoole-1.7.22-stable里是没有这个路径反而编译正确了。
————————————————————服务端的代码贴在这儿—————————————————————————————
代码如下:


rango有一个更详细的连接池服务端的代码放这儿了:
http://rango.swoole.com/archives/288

二)客户端代码如下:


三)运行一下看有没有返回:
[root@iZ25dcp92ckZ mysql.swoole.com]# php  mysqlSwooleCli.php
string(292) "array (
  0 =>
  array (
    'linkid' => '3',
    'linkname' => '猪头党乐园',
    'linkurl' => 'http://www.gipsky.com/',
    'linklogo' => '',
    'linkdesc' => '',
    'linkgptoid' => '19',
    'linkorder' => '3',
    'isdisplay' => '1',
    'empty1' => '',
    'empty2' => '',
  ),
)
"

最后,这个到底是不是真长连接在mysql这儿了呢?我们验证一下,连接mysql看下:


还有问题?优化如下,我提出我为何要坚持引入RPC的原因:
摘录来自:http://bokjan.com/prog/php-db-conn-pool-with-swoole.html  现在没了。
现在可以运行了,本次实验是成功的。但是如果使用dbcp_query()这个函数,每次调用都要发起一次TCP连接,执行的语句多了,肯定出问题。这个时候我们就可以把它封装成一个类了,单纯实现这个会比较的简单,但是打出来要点时间,这里就不写了。

我的看法:对于这位兄弟提到的每次调用都要发起一次TCP连接,而这个问题在RPC里直接供给前端WEB会得到很好的解决,为什么呢?
目前这种搞法是:一个web请求来到服务器后,这个服务器再生成一个连接swoole的连接池的端口,这儿是:9508端口它再去长连接Mysql的3306端口。
那么,如果每次来一个用户这个9508就会再进去一个连接,再到Mysql的3306接口,这块这个9508取到数据完后,销毁这个socket的fd句柄,来得越多,
这个是不是就会出现很多句柄在这儿生生死列,也就是上面这个兄弟讲的每次调用都要发起一次TCP连接,执行的语句多了,肯定出问题。不是封装的事,
而这种架构在我看来本来就有问题,为此,我提出我的一个引入RPC的看法,以解决每次都生生死死的效率问题,思路如下:
这块RPC引入带来了额外的XDR兼容跨平台的数据接口的打包、解包、返回同样需要打包,再到客户端揭包的一个客外的socket数据流量,不是RPC最大8K性能问题所在。
架构如下:在每台服务器上的RPC服务器上启动一次性多个RPC,每个RPC连接一个Mysql的长链接,而rpc的client直接放到Apache/nginx的cgi目录下,这样从
Web端传过来的请求,直接通过WEB传到RPC服务器器直达Mysql,而这个RPC服务服务这块并不需要重新销毁重新生成请求,有更多连接过来只是再多起几个RPC的server(同时Mysql的长连接也多了几个),也就是说通过RPC的Client与RPC的Server长连接类似KeepAlive,直接打通了Mysql数据库,这样提高了效率,因为这个连接池不管怎么样,都需要给web端来访问,当前解决的就是web端用户一下就来了很多人的一个问题,还形成了可扩展的一个Client和Server模型。
总之的总之,Rpc调用远程就像调用一个函数一样,避免了重新销毁重新生成请求的一个消耗,也避免了下面的serialize和unserialize的一个性能问题,也就真正实现了最大化性能和架构可扩展的解决方案,也就是为何我建议加一个RPC功能,把底层做到极致,通过简单配置就能打通Mysql的各个表结构。

最后:今天做的是数据库连接池的实现。从上面的代码我们可以看见,程序与连接池之间的数据交换是使用php序列进行的。这里会有两次的serialize、unserialize,绝对也是一个开销。Rango的文章里面有说到“MySQL是每个连接会占用1个线程……大量的系统资源被浪费在线程间上下文切换上……不是所有地方都在做数据库操作,所以这个就是浪费的。”再看看他那篇文章的假设:“假设有100台PHP的应用服务器,每个机器需要启动100个apache或fpm工作进程。”这肯定不是一个小项目,确实就适合用连接池了。写的东西是用来练手或者解闷儿的?常规方法已经可以了。不要忘了一点:程序与连接池的交互我们应该还是用Swoole实现的,Swoole可是一个TCP/UDP扩展。而Swoole只能运行在Linux平台上面,但是Linux平台上的MySQL是可以用UNIX Socket通讯的。

来自:http://rango.swoole.com/archives/265
http://bokjan.com/prog/php-db-conn-pool-with-swoole.html

作者:jackxiang@向东博客 专注WEB应用 构架之美 --- 构架之美,在于尽态极妍 | 应用之美,在于药到病除
地址:https://jackxiang.com/post/8418/
版权所有。转载时必须以链接形式注明作者和原始出处及本声明!


最后编辑: jackxiang 编辑于2016-1-26 16:27
评论列表
发表评论

昵称

网址

电邮

打开HTML 打开UBB 打开表情 隐藏 记住我 [登入] [注册]