缓存取数服务目前仅保留单一只能对外输出缓存数据,因此对他的性能要求期望是很高的,虽然使用的redis作为存储介质,但由于存在一些单key缓存数据比较大等问题,一开始缓存取数取数服务性能并不高
这里记录些还记得的优化过的地方
- 一开始就换掉原来阻塞请求的spingMVC,改用webFlux的reactor模型 这样并没有提升实质性的性能,但为高吞吐量奠定基石
- 代码质量优化,取数据的流程代码,严格按照Java代码开发规范,像能不创建对象就不创建对象,省略不必要的操作,不能存在魔法值等等
- 尽量使用单例,使用字符串常量,字符类型虽然好用但其操作其实是所有类型最吃性能的,其思想是在保证业务正确前提下把能准备的事情都给做掉
下面的就需要研究业务项目才能发现
- 使用reactor模型非必要一定不要阻塞操作,尽量使用其提供的策略像Supplier等懒式方法
- 存储结构改动,首先redis 内存是宝贵的想压缩存储数据
- 早前沿用前人的一版,大概流程
- 从数据源取回数据 -》序列化为JSON形式-》String转二进制-》二进制GZIP压缩-》二进制用自写算法(不知道哪抄的)编码成String -》String存储redis(这个过程还要String 转二进制)
- 这个过程业务可用没问题但本身就很繁琐,我推测其目的
- 只知道redis的Value只能存字符串如果直接用压缩的二进制toString,取回来的时候不知道怎么再转回来
- 知道Value可存二进制,但直接存二进制担心安全问题,像某些数据库存二进制是非安全类型,会改变其内容,就必须存String的Value
- 我第一次优化这个流程没考虑清楚存二进制的问题,只做了二进制用自学写算法这块优化,首先发现这个自写算法性能不高并且编码的结果也比较大,然后改用成JDK8以后新增神器Base64来搞,并且在切入时机取巧少了两次toByte过程,这样存储大小已经有了大幅减少,但其实只要编码就依然会增大内存
- 后边深入研究后最终优化流程
- 从数据源取回数据 -》序列化为JSON形式-》String转二进制-》二进制GZIP压缩-》二进制直接存Redis
- 这个过程少了Base64及String、二进制之间的各种转化,直接存压缩后的二进制,其本质就是抓住了Redis可以直接存二进制而且是安全类,整个过程只有一个Gzip压缩的过程
- redisClient改动
- 首先springBoot 二点几之后redis客户端默认都用Lettuce,这就说明了这款客户端是优秀的,而我用的ReactiveRedis也必须使用Lettuce才能支持
- 这个Client前前后后其实改了很多次,有用SpringBoot AutoConfiguer,也有用自己配置但其实最后都无所谓的,主要是要了解里边的过程,这里记录大概过程
- 我先是自己写连接配置主动配置了 Netty的IOThread、ComputationThread这些默认是CPU核数
- 后来翻源码发现Lettuce的 Netty默认情况下就是单线连接其实是单实例,(这其实nettyIO多路复用的思想,所谓的零拷贝)但流程中的NioEpollEvent可以调整可以配置成多线程,至此如果是不经常那么依赖redis,IO不存在瓶颈情况下,目前的配置是最合适的,网上找各种资料,也是认为这样配置的,能充分发挥redis Netty连接优势
- 但上线请求单机300qps存在性能瓶颈,CPU只能用到60%3H吞吐量上不去了,经过一番调查,Netty的单连接卡瓶颈了,随后发现了有相关地方可以改连接实例,调整连接数,使用连接池
- 具体改动为在创建LettuceFactory时需要将ShareNativeConetion默认值true改为False
- 最后我用的是AutoConfigure
- spring.redis.lettuce.pool.max-active=350
spring.redis.lettuce.pool.min-idle=100
spring.redis.lettuce.pool.max-idle=320
spring.redis.lettuce.pool.max-wait=2000
spring.redis.lettuce.pool.time-between-eviction-runs=-1
- 注意最后一个参数必须是负数,意为保持长连接不中断,不然对于我这种频繁使用redis业务,会被搞死
- 因为使用了AutoConfigure,Netty相关的配置就不能直接在代码中配置了,但可以在JVM参数中配置,其实只要配置一个就够了 -Dio.netty.eventLoopThreads=15
整体提升效果
不算上前期效果,只看最近一版
- redis内存减低百分之二十, 从单台980M到770M 共4台,减少800M内存使用
- CPU使用从单台支撑300QPS 用2.8H 降低到 1H
- 响应时间从单台峰值400qps时段 平均耗时从 18ms 降低到 3ms 从Sentinel 实时计算拿到数据