微服务倡导将复杂的单体应用拆分为若干个功能简单、松耦合的服务,这样可以降低开发难度、增强扩展性、便于敏捷开发。当前被越来越多的开发者推崇,很多互联网行业巨头、开源社区等都开始了微服务的讨论和实践。虽然微服务现在如火如荼并且出现了诸如: Dubbo、SpringCloud、thrift 、grpc 等以微服务著名的微服务开发框架以及Docker这类的容器技术,但对其实践其实仍处于探索阶段。
几天前在测试环境上升级了一波数据对接开放平台接口,并且灰常开心的通知友商可以进行数据对接的测试了。然而就在这次数据对接平台接口开放测试的第二天,就出事了,测试服php-fpm容器内项目全部时好时坏,接口全部出现间接性请求响应缓慢的情况。查看slow日志,发现大量内网接口请求相关的慢日志记录:
最开始奶嘴以为是某个内网服务接口故障导致的请求阻塞,遂在php-fpm容器内使用bash下直接curl测试疑似故障的接口。但是直接使用curl请求的内网接口响应速度都在200ms之内,多番测试之后依旧毫无进展,然而测试服的故障还在继续。并且这时候还被友商的开发人员吐槽一波,你们家测试接口咋这么辣鸡,传个商品数据都要三五秒的。。
没办法只好祭出大杀器tcpdump了(说实话虽然tcpdump是linux下的网络调试神器,但是奶嘴平时是真不喜欢用。。因为有时候抓包数据太多滚动数据太快密密麻麻一堆看着眼花o(╥﹏╥)o),由于母机已经安装好了tcpdump,但是FPM容器在部署的时候并没有安装tcpdump所以直接nsenter切换到FPM容器网络然后进行抓包。
不过这次依然没有什么进展,tcpdump中显示的内网请求基本都在80~300ms之内就响应了。这次奶嘴开始怀疑是否是FPM进程中内网域名DNS的解析问题。直接在母机上使用dig @internal.dns.xxx.com测试内网dns server,然而内网的dns server正常的不能再正常了。
没办法只能查看FPM的进程调用栈来排查问题了,直接 ps -aux | grep php-fpm找到FPM的master进程,然后strace -frt -e trace=network -p pid查FPM进程的所有网络调用(PS:因为从FPM慢日志得出的信息基本是可以定位到是CURL网络调用这块出现了问题,所以不需要跟踪FPM进程的其他调用情况。)如下图:
发现请求会在accept、socket、sendto以及recvfrom这几块调用上阻塞、并且随机无规律。这个时候奶嘴才想起来FPM下 CURL 是阻塞的,并且CURL阻塞的同时也会阻塞FPM的worker进程,所以现在这个问题应该是FPM子进程数量配置的太少并发请求进来导致堵死所有FPM的worker没跑了(PS:商品上传接口同时并发的请求了商品快照、商品分类、商品品牌等接口,所以存在并发请求内网的情况。加上友商测试的时候上传频率也不低)。遂打开FPM的配置扫了一眼,果不其然,子进程最大值只配了20个。调整配置到100个重启FPM容器。经过一个大上午的忙碌,终于一切又恢复平静了,友商的开发也不吐槽开放平台接口上传数据慢了。。最幸运的是出现问题的不是生产环境,不然生产环境故障半天的话可能就要被老板祭天了(ಡωಡ)
总结
由于微服务架构下拆分了N个内网微系统,内网服务互相调用的非常频繁,为了完成某个特定的业务功能很可能会同时调用多个微应用接口,所以在内网调用、网络阻塞方面要特别注意,很容易翻车
PHP-FPM在微服务的应用中应当尽量、尽可能的少用curl因为curl的IO模型是阻塞的,容易占满FPM的WORKER。
在框架选型上可以选择一些swoole扩展框架因为sw在底层对php的很多阻塞操作进行了hook自动切换为协程调用,防止了堵死整个WORKER。
如果一定需要使用PHP-FPM框架来做微服务尽量使用fsockopen来做外部请求,并且将stream_set_blocking设为非阻塞模式。
如果多个服务同一台Docker母机时可以考虑映射出一个共享的sock文件,通过unix domain socket(即IPC)进行通信
本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可,非商业性质可转载须署名链接,详见本站版权声明。