.net ServiceStack.Redis 性能调优

摘要:
最近在调试生产环境时,ServiceStack版本4.0.60=null)OnDispose的RedisClient中发现了一个非常严重的性能问题;base.Dispose();}RedisClient.cs:publicdelegatevoidRedisClientDisposeEventHandler;以下是修改前后的结果比较:1.100个线程,每个线程完成2000次Redis调用,每次调用GetClient。从以上三种场景的测试中,我们可以看到,当RedisClient访问压力持续增加时,原始版本的响应时间呈现指数级增长。当压力达到一定程度时,RedisClient访问几乎被阻止,这需要很长时间才能缓解。重组后的RedisClient的性能得到了极大的提高,尤其是在高并发交付的性能方面,这直接杀死了原始版本!

     最近在debug生产环境的问题时,发现了ServiceStack 4.0.60版本RedisClient存在一个非常严重的性能问题。在高并发下,PooledRedisClientManager.GetClientRedis.DisposeClient会导致High CPU,并且持续非常长的时间才能自动修复下面是Demo程序压测还原问题后,工具的分析结果。

.net ServiceStack.Redis 性能调优第1张

通过分析源代码发现:原来获取RedisClient的逻辑中通过锁方式实现,并且当连接被占满后再获取连接时,需要循环遍历数组中所有的连接对象判断是否有可用连接,会非常消耗CPU。Dispose方法也存在循环遍历的问题。尝试了很多种修改方案后,都不尽人意,果断把这两段逻辑重写,下面是相关代码,已经经过压测。

PooledRedisClientManager.cs:

private ConcurrentQueue<RedisClient> deactiveClientQueue = new ConcurrentQueue<RedisClient>(); private static object lckObj = new object(); private static object waitObj = new object(); private int redisClientSize = 0; private int maxRedisClient = 500; //PooledRedisClientManager的构造函数中初始化此值:maxRedisClient = this.Config.MaxWritePoolSize;
//GetReadOnlyClient方法也可按此方式修改 public IRedisClient GetClient() { RedisClient client = null; var poolTimedOut = false; DateTime startTime = DateTime.Now; while (true) { bool getResult = deactiveClientQueue.TryDequeue(out client); if (getResult == false) { if (redisClientSize >= maxRedisClient) { Thread.Sleep(3); if (PoolTimeout.HasValue) { // wait for a connection, cry out if made to wait too long if ((DateTime.Now - startTime).TotalMilliseconds >= PoolTimeout.Value) { poolTimedOut = true; break; } } } else { client = CreateRedisClient(); if (client != null) return client; } } else { if (client != null) { InitClient(client); return client; } else { client = CreateRedisClient(); if (client != null) return client; } } } if (poolTimedOut == true) { throw new TimeoutException(PoolTimeoutError); } return client; } private RedisClient CreateRedisClient() { if (redisClientSize >= maxRedisClient) return null; lock (lckObj) { if (redisClientSize >= maxRedisClient) return null; Random dom = new Random((int)DateTime.Now.Ticks); var newClient = InitNewClient(RedisResolver.CreateMasterClient(dom.Next(100))); newClient.OnDispose += (isRecycle) => { if (isRecycle == true) { try { deactiveClientQueue.Enqueue(newClient); } catch { lock (lckObj) { redisClientSize--; } } } else { lock (lckObj) { redisClientSize--; } } }; redisClientSize++; return newClient; } }
RedisClient.cs:
public event RedisClientDisposeEventHandler OnDispose; public override void Dispose() { if (OnDispose != null) OnDispose(this.HadExceptions == false); base.Dispose(); }
RedisClient.cs:    
public delegate void RedisClientDisposeEventHandler(bool isRecycle);

下面是修改前后的结果对比:

1.100个线程,每个线程完成2000次Redis调用,每次调用GetClient。 改造前12s,改造后8.5s,提升近50%。老版本CPU消耗稍高,并具有持续性。

.net ServiceStack.Redis 性能调优第2张

.net ServiceStack.Redis 性能调优第3张

2.200个线程,每个线程完成2000次Redis调用,每次调用GetClient。 改造前378s,改造后19s,提升提升近20倍。老版本CPU消耗非常高(解决100%),并具有持续性。新版本CPU占用了仅有原来的一半。

.net ServiceStack.Redis 性能调优第4张

.net ServiceStack.Redis 性能调优第5张

3.300个线程,每个线程完成2000次Redis调用,每次调用GetClient。 改造前1580s(26分钟),改造后29s,提升提升近55倍老版本CPU消耗非常高(解决100%),并具有持续性。新版本CPU占用了仅有原来的一半。

.net ServiceStack.Redis 性能调优第6张

.net ServiceStack.Redis 性能调优第7张

    通过上述三个场景的测试可以看出,当RedisClient访问压力持续增加时,原版本的响应时间呈现指数性增长,当达到一定压力时,RedisClient访问几乎阻塞,需要非常长时间才能缓解。重构后的RedisClient在性能上有大幅度提升,特别是在高并发下的性能表现,直接秒杀原版本!

免责声明:文章转载自《.net ServiceStack.Redis 性能调优》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇MySQL定义异常和异常处理方法noip2013 车站分级下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

MySQL常见问题

1、mysql如何做分页 mysql数据库做分页用limit关键字,它后面跟两个参数startIndex和pageSize 2、mysql引擎有哪些,各自的特点是什么? innodb和myisam两个引擎,两者区别是innodb支持事物,myisam不支持innodb支持外键,myisam不支持innodb不支持全文索引,myisam支持全文索引innod...

如何解决Linux 系统下 ifconfig 命令无网络接口 ens33

今天我在做Redis的哨兵集群模式的时候,以前都是好的,也不知道从什么时候开始就无法连接Redis服务器了,就是运行如下命令,没有效果:redis-server redis.conf,然后在通过命令查看redis的状态,始终没有启动Redis,命令如下:ps -ef|grep redis 或者 ps -ef|grep 6379 ,查询不到Redis服务器的...

面试官:Redis 单线程已经很快,为何 6.0要引入多线程?有啥优势?

作者:Java斗帝之路 链接:https://www.jianshu.com/p/ba2f082ff668 Redis作为一个基于内存的缓存系统,一直以高性能著称,因没有上下文切换以及无锁操作,即使在单线程处理情况下,读速度仍可达到11万次/s,写速度达到8.1万次/s。但是,单线程的设计也给Redis带来一些问题: 只能使用CPU一个核; 如果删除的键...

Redis 如何存储上亿级别的用户状态?

作者:铂赛东链接:https://www.jianshu.com/p/ee79ae681b74 1 前段时间,在网上看到一道面试题: 如何用redis存储统计1亿用户一年的登陆情况,并快速检索任意时间窗口内的活跃用户数量。 觉得很有意思,就仔细想了下 。并做了一系列实验,自己模拟了下 。还是有点收获的,现整理下来。和大家一起分享。 Redis是一个内存数...

go redis锁

redis经常用作分布式锁,这里记录一个简单的锁代码如下: package main import ( "crypto/rand" "encoding/base64" "fmt" "github.com/go-redis/redis" "io" "sync" "time" ) func main(...

PHP安装Redis扩展

记录一次中windows上给php安装redis扩展流程; 一、PHP中Redis扩展安装配置 上一张图看一下当前PHP版本信息,因为版本非常多,稍微不慎将会安装失败。 我本地是xampp集成环境, 由图可见 1、我php版本是7.1.13,所以下载php_redis.dll扩展要下载7.1版本的 2、Architecture是x86,所以redis应该选...