并发500,2000次请求,从10s到300ms的接口优化,我做了哪些事

  小程序的接口刚写完时,一个接口的调用时间大概是700ms左右,100并发的100请求的压测结果是9s,用的都是项目的底层方法(有缓存),框架是ci.而上线城市服务的三星基准是500并发500ms以下,远远不达标,于是乎开始了漫长的优化.在此记录一下从10+s优化到300ms的过程.

  • xhprof分析了代码,发现ci的cache的redis驱动做了这样一件事,耗费了大量的性能
    • $serialized = $this->_redis->sMembers('_ci_redis_serialized');
              if ( ! empty($serialized))
              {
                  $this->_serialized = array_flip($serialized);
              }

      ci的redis驱动初始化程序里有这样一段代码,这段代码的含义是将"_ci_redis_serialized"这个集合中的所有成员都取出来.然后反转键值对.而_ci_redis_serialized这个集合对于ci的涵义就是为了记录序列化后的数据.因为ci的cache可以把任何数据都放入cache中,包括字符串,数组,对象等,而redis只能存储一种数据格式.所以就需要将数组或者对象序列化存入redis中(当然如果你不打算存储对象完全可以用json格式来存储数据).问题就出在这里,一个项目的key往往是数量级的增长的,像我们就有16w的key是经过序列化存放在这里的(除了数据缓存,还有大量的用户session数据也是需要序列化的),在大量的cache操作下,这个过程就耗费了大量的性能.解决方案是:既然用了集合,直接用SISMEMBER来判断这个集合中是否有这个成员就行了,redis的内置方法性能是非常好的(好到可以忽略)这次优化后从10+s下降到了3s左右.

  • 在获取数据的底层方法上,在小程序再封装一层
  • 只提供给前端必要的字段
    • 这两个步骤让时间下降到了1s+,很简单的逻辑,减少不必要的数据传递,减少冗余数据.
  • 去除session中的_get_lock,这个是防止两个进程同时写一个key,因为基本发生不了所以先去除
  • 不经过service层,直接访问数据库,为了能让service层(数据层)能单独部署,所以内部访问模型是通过curl来请求的.现在改造成直接通过ci的model来访问(也就是直连数据库了)
    • 这两个步骤让时间下降到了600~700ms,基本上没有太大的优化.
  • 最后的优化是OPcache,让服务器来缓存php文件.
    • 最后这个步骤让时间优化到了300ms,单独访问一次接口只要30ms左右.弊端是当你发布文件后,可能最多会有1分钟的生效期(类似于缓存了1分钟的本地文件)

 

  最后的结果就是小程序的压测结果通过啦~

posted @ 2017-10-31 11:13  JOSEFA  阅读(4945)  评论(0编辑  收藏  举报
shopify traffic stats View My Stats