这几天群里来了安正超大神,一下子群情'波'涛汹涌呀,什么?你不知道超神是谁,这是他的个人网站可以去瞧一瞧.
.......对了,差点忘了,群里也来了一个'似乎'没有存在感的仁兄 - Drupal猎人(QQ号:打上马赛克),为什么说他呢,因为听说他技术也很厉害,不过在某些方面比他的技术还要那啥,你懂得(哔哔,学生卡)。
名言篇
留下 1/3 的时间学习,留下 1/3 的时间思考,剩下 1/3 才是写代码 --- Little
干正事吧,好好学习,也可以跟大神一样溜 --- Jellybool
人生如此短暂,我花费了打炮的时间来学门技术,我容易吗我? --- Drupal猎人
Js事件的冒泡用法 --- Little
js推荐这种把事件绑定在body上,用冒泡来做,不要直接在每个元素上都绑事件。
写js,也是写php面向对象类似。前面定义各种类、方法、属性,最后把事件绑定在body上,通过冒泡找id或者找元素。这样你哪怕换了模板,只要流程不是有大变化,js也不用大改的。
比如:开始音乐和暂停音乐。只不过是click触发的,如果加一个 ESC按键触发,只需要改个触发事件即可。
很少在 js 里会注册 window. 下的全局变量,一切都是私有变量。内部的用 _,注册到外面,一般只用 APP. 这样的大写类。$ 开头的表示 jquery selector,闭包 return 出去了。
PHP opcache --- Little
群友问:php 开启了 opcache, 如果在命令行执行 opcache_reset(), 会重置 包括 php-pfm 的缓存么
答:不会,每个php-cli进程都有自己的opcache空间,所有php-fpm进程是一个opcache空间,
生产环境opcache的timestamp检查关掉,发布代码的时候再更新下opcache,可以避免很多stat系统调用
※ 深入浅出FastCGI/php-fpm --- Little
这一个话题包括Little讲解php-fpm和PHP与Mysql连接的整合,方方觉得两个丢在一起可以更能理解
先看个图,标准的PHP单进程CLI和CGI生命周期。php进程启动,需要zend core、加载扩展(MINIT)、接收请求(RINIT) 这样的。一个进程只服务一次命令行或HTTP请求,就退出。
而FastCGI/php-fpm 就是改造后的多进程的 CGI,类似于资源池,预先启动 100个 php-fpm 进程,提前MINT,nginx的请求来了,直接进入 RINIT -> RSHUTDOWN 循环。请求结束,进程不退出,一个进程至少服务上万次请求才退出。
为什么一定要退出?怕RINIT->RSHUTDOWN循环,有哪个代码写的不好,变量一直没释放,内存泄露GC又回收不了。php-fpm里的pm.max_requests配置就是设置RINT循环多少次,退出进程。
再来看几个 TSF、swoole、workerman、php-pm,都是 php 启动cli进程,用php管理子进程,php解析HTTP协议。
生命周期连 RINIT -> MINIT 循环都省了,没写在 类属性里的变量,裸写的变量都是 进程级全局变量,比 php-fpm 下的 $_GET、$_POST、$_SERVER、$_SESSION、$_COOKIE 这些全局变量范围还大,是进程级的。意味着你 写了个 a.php,里面定义了 $a = 1; 赋值之后,下次请求过来,只要正好分配到了这个进程,依然还能取到普通定义的 $a 变量。
这意味着什么?像 Laravel 里的 $app 这些变量,只要写在最外面,因为没有触发 RSHUTDOWN,又没有主动 unset,GC引用计数器一直大于 0,变量不会消失。
那怎么解决每次请求 $_GET 和 $_POST 不一样的问题?这些 swoole、workerman 进程管理器自己实现了小型化的 INIT -> SHUTDOWN 过程,维护一些引用计数呗,自己的 a.php 完成后,这种框架帮你 unset($_GET)。
问题来了,稳定不稳定?swoole、workman框架本身稳定,但因为完全改变了php生命周期,业务开发人员不熟悉,一不小心写了 global、static 这样的变量,全局用了,内存越占越大,崩溃。又或者 写了个 exit,把整个进程 exit 而不是 request ext 了。
举个例子来理解,用 swoole、php-pm 这种进程模式,假设只启动一个进程,可能会出现以下情况。
1、写个页面 http://www.xxx.com/a.php,内容是 $a = 1,而到一个完全不相关的页面 http://www.xxx.com/b.php,内容就是var_dump($a); 居然能输出 int(1); 很神奇
2、写个页面 http://www.xxx.com/c.php,里面执行完一段逻辑后,exit('error'),本以为页面输出个 error,是正常的拒绝访问。再刷新,502了,什么情况。
那哪种情况比较适合使用swoole
Little答:
特别繁忙的某个接口、业务逻辑简单、依赖较少的情况,可以用。有良好的编码习惯,不用全局变量,不写 exit 只用 return 不会有问题。但是无法保证用到的哪个 vendor 不出现这些东西,所以不能大规模使用。
Abraham因为涉及较多框架,深切表示很多 vendor 里面 的写法真是千奇百怪,有的根本都没见过,
比如解析 var_dump 去判断对象
当然我只是看了这些进程早期版本的实现,现在php不是有某种注入技术,可以改写原生函数么。说不定启动swoole的时候,已经用某种方法把 exit 改写了呢。
这里是Little对于的一些理解,读者还是需要与现在版本进行比较
对于swoole、workman等框架本身稳定,但因为完全改变了php生命周期是否意味着更适合做一些微框架,写纯Restful API?
Little答:
这种完全改写生命周期的事情,就有点像 HHVM 对 PHP做成预定义变量类型的改变,性能提升很大,但是用起来要谨慎,并不是全兼容的。
Little在这里并没有直接回答此问题,因为在之前就介绍了,Little对于项目是超越框架的,不局限于框架之类的东西.所以方方感觉也是蛮正常的
websocket最好不要php直接来做,目前php的websocket包,都是启动一个独立的进程监听端口解析协议,跟 swoole、workerman 这些东西同一种性质,不能确保100%好用及可用。
推荐 借助 openresty(nginx) + lua + redis 来做。
openresty(nginx) 通过 redis 插件 监听websocket,同时挂起一个到 redis 的连接,用 redis 的 sub 方法,订阅某个 key。php 这边只需要 调用 Redis 的 pub 方法,写入 redis 的 key 即可。
一个由TIME_WAIT 引起的话题 --- Little
TIME_WAIT多了
Abraham回答
fpm 每个进程结束都销毁连接,无法利用持久连接,持久连接指的是请求内的吧,你的情况可以减小 mysql 的等待超时时间,或者直接更改操作系统维护的 tcp 连接数
little最后解答
我刚那个 TIME_WAIT 问题改成php-fpm 连接 mysql 用 persistent 是可以解决的。刚刚只是改错了文件,没想到有个老项目的代码还在这台机器上跑。
虽然php并没有mysql连接池,但是 PDO 的persistent参数是基于单个php-fpm进程,仔细看了下php-fpm的生命周期,我配置了 204800 个request请求才会重启这个php-fpm进程,所以连接复用的情况还是很大,单机400个php-fpm进程,单个进程要跑满20万次请求等重启,怎么都要一两天才行。
Abraham的理解
以前一直以为持久连接是基于 fpm 子进程中处理 request 的线程。
为什么上面要放Abraham最开始的回复以及后来的理解,一来是Abraham对于很多人而言其实已经技术很好了,但是仍然对某些深层的不能正确理解,拿出来让其他人看看是否自己也有这种理解失误;二来嘛,嘿嘿嘿,拖出去,弹鸡鸡弹到死
Little
关掉持久连接,会在request shutdown 阶段close mysql连接。而打开持久连接,并不会每次请求都关闭,是同一个php-fpm进程中复用连接,直到这个php-fpm进程结束了。
然而php-fpm的mysql并没有连接池,比如开了400个php-fpm进程,那就会和mysql保持400个连接,无论现在访问量高还是低。如果前端机器太多,mysql服务器会保持太多的连接数,mysql服务器并不能承受太多个连接,哪怕这个连接什么事都没干,所以并不是一个很好的解决办法,只能再加一层mysql proxy。而java的实现就有连接池,好很多,果当前并发请求量只有100,那么只会有100个java到mysql的连接。这100个连接在所有java线程中复用。
Abraham再提问
如果能保持 php-fpm 的进程数那么多个连接。show status like '%connections%',理论上应该不变的对吧?
Little 回复
1:15 的时候,我在一台机器上开了持久连接。1:40左右的时候,我多开了一台php机器。
所以我的结论是 php-fpm 到 mysql,应当使用 persistent。如果机器数、php-fpm进程数很多,后端mysql挂不了这么多空闲连接。那在 php-fpm 与 mysql 之间加一层 mysql proxy。这个 mysql proxy 就是相当于多进程多机器的连接池。
多机器多进程 php-fpm (可能有5000个连接) -> mysql proxy -> (可能就剩20个并发执行mysql的活跃连接了) mysql server。
默认就开启持久连接才好,出现问题,再看看是不是考虑关掉。毕竟 php 连mysql,一个新连接会多三次 TCP握手过程,虽然是内网,速度感觉不出来。我这是给一个老项目做优化,让他暂时不挂。我别的项目都是直接上持久连接。
开启持久连接, 可能会导致那些问题
Little答
理论上是没有什么问题,就是看你的mysql服务器受不受得住,受不住就要加 proxy。
另外,有空闲连接,如果你的网站并发量比较小,空闲连接时间长了,可能会被mysql踢掉,会产生一个 mysql has gone away 这个错误,你要程序中处理,我不知道 laravel 有没有自动处理,理论上这种全栈框架应该都会有个配置自动处理这个问题。
我全文搜了下 gone away,发现 laravel 里有处理gone away,业务层就不用关心了。
Illuminate\Databasetry\Connection -> run() 和 tryAgainIfCausedByLostConnection()
我可不可以这样理解,当你的项目已经需要利用 php-fpm 持久连接提供的性能福利时,你同时也需要审视你的数据访问层架构,重新设计它
Little答
应该这么说,持久连接应该是在每一个环节都是尽量使用的。比如用户浏览器到nginx,用HTTP/1.1,开keepalive。nginx到php-fpm,upstream里开keepalive,fastcgi conn 还要配个什么。php到mysql,也开持久连接。这样每个环节都省掉多次不必要的TCP三次握手。而MySQL性能问题和这个连接关系并不是太大,只是mysql服务器一直以来都有个毛病,连接数超过一定的数量就受不了,哪怕这个连接是空闲的什么事都没干,不像nginx随便挂上十几万的连接数都轻轻松松。
整理者方方小结 --- 还是好好学习