公司有一个Web Service,访问量不大, 但也不算小, 每天几百万的量级。正常情况下, 平均每个请求响应的时间在200毫秒左右。
每天几百万的访问量, 那么程序每秒请求处理数量在几十个左右, 高峰期也就上百, 而服务器上php处理请求的进程数是大于这个数的,因此, 服务器的处理能力勉强能满足当前量级的请求, 除了少数时候高峰期会出现不稳定的状况, 大多数时候也算是相安无事, 但是从服务器失败请求的数量来看应该离服务器处理能力极限的临界点不远了。
这个Web Service有一个特点, 它并不是面向终端的 , 而是为另一套Web Service提供底层数据用, 那套Web Service会进行数据缓存,不会把所有数据请求转发到我们这里,它替我们挡掉了大部份压力。然而, 天有不测风云,某一天高峰期那套Web Service的缓存机制坏掉了, 所有数据请求全都转发到我们的Web Service上, 结果, 我们Web Service访问量成倍增长,服务器超出了承受能力的范围而无法正常响应,然后用户各种投诉,领导各种不满,压力自然而然就到了我的身上。
我分析了一下问题的原因,Web Service 每个请求的响应时间为200毫秒上下, 服务器的并发处理能力并不是很高, 也就是说在每个200毫秒内,服务器处理请求数量是有极限的, 当每200毫秒的请求量大于这个极限的时候, 后面进来的请求就不能被及时处理, 只能排队等待,这就跟堵车一下,车的数量远大于马路的吞吐量时,自然是越堵越多。堵车没有时间限制, 反正迟早能开走, 只是花点时间等待而已。 而服务器处理请求就不一样了,如果指定的时间范围内无法及时处理请求,那么这些请求就会坏掉, 也就是我们通常看到的502或者504。我们的服务器也是因为这个原因而出现了大量的坏掉的请求,导致业务受到影响。
整个Web Service大约有百分之八十流量是流向其中五个接口(页面)的,因此我只要集中优化这五个接口,将它们的响应时间降下来,那么服务器并发请求的处理能力将会得到提升。
这个Web Service是由php实现,近一年内也不断的在优化其性能,但总是无法彻底解决问题, 虽然服务器的并发处理能力得到了一定程度的提升, 但是, 每个请求的响应时长总是降不下去。 被逼无奈, 我决定以换技术重新实现的方式尝试着解决问题,毕竟高性能服务器的编写并非php所擅长的, 而golang似乎更加适合做这件事。
我仔细的看过这个Web Service的每一行php代码, 发现存在以下影响性能的问题
-
没有数据库连接池, 也没有单例, 每一次读写数据库都会简单粗暴的执行openconnection和close connection
-
使用memcached。我觉得memcached也影响性能,因为会有网络开销,如果不是多个程序共享内存需要, 根本没有必要使用, 但在php中却无法避免,因为php无法直接操作内存
-
没有多线程,没有办法并行处理问题, 如只能通过串行的方式从多个数据库中读取数据
-
编写代码时没有考虑到时间复杂度问题, 各种无意义的foreach太多
除了代码中存在的问题, php技术本身也有性能痛点存在, 如
-
解释执行代码, 但也没有像java一样的即时编译机制
-
请求必须通过apache和phpfpm服务器中转, 然后再交由php自身
而golang正好克服了这些问题
-
非常方便的使用数据库连接池
-
直接操纵内存, 不使用第三方缓存软件
-
goroutine, 多线程中的战斗机
-
纯编译型语言, 跟C类似编译出来的就是最低层的机器码
-
程序本身就有Web服务器的功能, 不依赖第三方Web服务器
因此, 从理论上来讲, golang在性能方面完胜php。
因为只需要重写Web Service中的5个接口, 工作量并不算太大, 总共大概只花了2天的工作量就完全成了重写的工作,并且将旧的php版本中存在的问题全都避免掉。
将程序部署至生产环境后,我对两个版本的程序在性能上做了大致的对比
php
golang
php
golang
从图中可以看出,同样的功能, 同样的数据, 但是在请求的时间上却确差了许多倍。
在并发量处理方面, 我写了一段Java程序,开100个线程去请求测试环境下的接口, 代码大概长这个样子
php实现的版本, 在这段程序运行20秒左右的时间后,服务器就出现无法响应的状况,大致情况应该与之前线上服务事故原因相同,车太多, 路太小, 堵住了。
而golang的版本, 不管程序开多久, 都一直稳定的运行着, 程序的进程对于服务器资源也没有太大的消耗,因此可以断定,在真实的生产环境下golang写的版本的表现肯定将优于php版本。
现在, 使用golang新实现的版本还在测试当中, 需要确保接口返回的数据与php版本接口的返回的数据没有一毫偏差才可以正式切换服务。
系统还没有真式使用, 重写所带来的效果也还没有体现, 但是我还是义无反顾将这件事提前发在公众号上, 这充分的说明了, 我对于这次重写有足够的自信,对golang的表现也有充足的信心,让程序性能提升10是可以实现的。