BeetleX也扩展了RedisClient驱动,写这些高并发应用的驱动性能测试分析是必不可少的。在最近一次测试中发现测试采样度不足,引起的一些问题;通过这一次的问题也警醒一下自己在以后设计上要考虑更多细节的特性需求。
发现问题
在写组件的时候往往会拿同样的组件产品做一些简单的测试性能对象,最近也拿BeetleX.Redis组件和其他组件做了一下性能测试,经过两次测试环境的采样发现的结果差异还是很大的,如下是两个环境的测试结果:
4核环境
20核环境
从两个测试环境的结果来看StackExchange.Redis由原来并发数量最差,到后面领先了50%。
问题所在
这问题分析很久,包括用Redis-cli来测发现该实例的Redis极限只能达到10RPS,但测试结果来看StackExchange.Redis已远超过Redis自身的交互吞吐量了。经过查看代码和相关文档才发现StackExchange.Redis的async默认即支持Redis的pipeline特性,并完美支持多线程环境操作。
以代码上来看StackExchange.Redis为了让组件默认支持这特性让Redis吞吐量最大化,所以在设计上也变得非常复杂和更多异步切换处理;这样引起了代码处理量比较多性能有些损耗。所以在StackExchange.Redis在配置低的情况达不到Redis满负载时浪费比较多的CPU资源性能相对就变得差了。
当StackExchange.Redis运行在资源比较多的服务器上时,高压下轻易压爆了Redis的交互IO量时那pipeline特性就可以在IO量不变的情况下更一步提高吞吐量。这是没有默认实现pipeline的组件所能比的。
总结
在这里不得不佩服StackExchange.Redis设计者的思路,在Client资源充足的情况把Redis的吞吐最大化发挥到极致。当然这种设计也是针对性的,他是利用Redis的特性和大量的CPU来提高Redis的吞量能力。对于没有默认支持pipeline只能跑到Redis交互的上限,受Redis的限制对应Client所使用的CPU自然也就无法上去来提高吞吐。
StackExchange.Redis在20线程下可以跑到30%的CPU,其他则只能跑到15%. StackExchange.Redis的这种设计缺点就是Client资源比较低的情况下性能就相对来说差一些。
所以性能好坏还真的很难通过一些简单的采样就能得出个合理的结果,关注性能更多应该关注代码是否能更好地发挥相关资源的作用。很多时候测试方式都受制定者的主观因素影响。如果没有这次测试我还真自认为BeetleX.Redis设计很不错。
StackExchange.Redis超时问题
用过StackExchange.Redis的朋友相信一定碰到Timeout的问题...,其实这个和StackExchange.Redis设计有些关系,最直接的办法是加大线程池和超时时间配置。对于CPU硬件资源少的还是加大超时时间比较好。主要原因是pipeline方式下意味着同时多个指令处理,但当前会话awaiter receive线程就一个最后会触发多个响应,如果响应的消息后面有逻辑处理那自然会影响后面消息处理。
void IResultBox.ActivateContinuations()
{if ((Task.CreationOptions & TaskCreationOptions.RunContinuationsAsynchronously) == 0)ThreadPool.UnsafeQueueUserWorkItem(s_ActivateContinuations, this);elseActivateContinuationsImpl();
}
从最终代码来看,ActivateContinuationsImpl则由当前awaiter receive线程触发,串行得太多后面的消息处理不及时就会引起超时。
ConnectionMultiplexer会每秒探测还在队列中等待响应的命令,如果有超时直接就触发对应命令的timeout(由于全局性的pipeline处理非常复杂也有可能 是处理过程存在一些问题,可以尝试用同步指令,从测试结果来看同步指令没用pipeline).