CYQ.Data 对于分布式缓存Redis、MemCache高可用的改进及性能测试
背景:
随着.net core 在 linux 下的热动,相信动不动就要分布式或集群的应用的需求,会慢慢火起来。
所以这段时间一直在研究和思考分布式集群的问题,同时也在思考把几个框架的思维相对提升到这个level。
最近大力重构了框架两个点:一个是分布式缓存,一个是数据库主从备。
今天,先分享分布式缓存的改进的两个点:
1、高可用:能动态增加或减少redis、memcache的实例,而不影响程序。
2、高性能:保障在高并发下的稳定性及性能。
1、redis、memcache 分布式下的高可用。
要实现分布下应用下的高可用,就两种方法:
1、建立集群,内部调度,对外只提供一个接口。
优点:对所有不同的开发语言和客户端都通用。
缺点:建立相对复杂,需要有专业的运维知识,而且对于提供出口的服务器,也要再做一次主备,整体硬件成本高。
2、由客户端进行调度。
优点:比较简单,不需要专业知识,上千个redis实例,随时就动起来。
缺点:如果项目有混合多种后端语言开发的,还得有多种客户端实现。
而 cyq.data 就是.net core 下集成了操作redis的一种客户端实现。
下面来看简单的使用过程:
1、指定配置外链:
原有的配置
<add key="redisservers" value="127.0.0.1:6379,127.0.0.1:6380 - pwd123456"/> <add key="redisserversbak" value="127.0.0.1:6381 - pwd123456"/>
将配置写在原的config中,1是不适合大量的实例,要写很长;2是当修改时,会引发(window下)程序重启(关键是netcore下还不重启)。
改进后配置(文件后缀可以指定*.ini,*.txt):
<add key="redisservers" value="redis.ini"/> <add key="redisserversbak" value="redisbak.txt"/>
对应的redis.init 文件(一行一个实例):
127.0.0.1:6379 127.0.0.1:6380 - pwd123456 127.0.0.1:6381 - pwd123456 127.0.0.1:6382 - pwd123456 127.0.0.1:6383 - pwd123456
...
可以无限加
将配置外置后,程序会自动监控文件的变化,每次修改都会即时生效,内部自动调整算法,真正实现高可用。
接下来,你就可以无限的找服务器,启动n多个实例,想加就加,想减就减。
一个高可用的分布式缓存就是这么简单了,当然了,以后要复杂,那就慢慢学习了。
2、关于一致性hash及主备的说明:
在框架内部的算法中,如果节点失败,会检测有没有设置备用节点:
如果没有,会直接失败。
如果有,会从备用实例中获取来维持服务,如果备用也实例也失败,而本次请求失败(下一次请求会更换备用节点)。
这样可以避免在高并发的情况下产生滚雪球的情景,由一个点失败最终导致把所有的服务器都压倒。
ps:在cyq.data 中,redis 和 memcache 的底层实现是一样的,所以使用方式都是一样的。
2、redis、memcache 分布式下的性能测试。
对于原有的代码改进重构,大约变化了50%左右的代码。
本次在性能上的优化:除了底层socket小调整,就是命令的合并操作,以及队列池的优化,以及多线程下的稳定保障。
其实,web are 只是个客户端,要说高性能,其实都是比较出来,只要比别人的快一些,好像就真的高性能了。
所以,测试都需要有个比较的对象。
目前测试的是本机,win7,redis 老版本 2.4.6。
1、先自己和自己比:新改动的版本和旧版本比较:
旧版本的测试数据:
新版本的测试数据:
跑在linux下时(1核2g内存的centos7,redis 版本 3.2.12):
经过整体的代码改动,性能还是整体提升不少的,还支持了高可用的扩展。
只是离官方传说的10w/s,还差了几倍,我猜如果把5个命令打包一起发送,应该就差不多了。
2、看看其它客户端的:
一开始我是没做比较测试,只是刚好在网上看到另一个客户端,写着就是高性能redis客户端。
看到上面的测试数据,我有点惊讶,这有6w的数据是怎么飘出来的:
set_json using 1 threads run 100000 [use time 5.76s 17,370.26/sec] set_json using 4 threads run 100000 [use time 1.87s 53,407.17/sec] set_json using 8 threads run 100000 [use time 1.65s 60,517.8/sec]
但上面没写明是在window还是linux也没写版本,
于是,我就下载了它的客户端(它自带test运行程序),
然后连上我的redis,运行了一下:
实际上它的数据是这样的:
这是我打开的姿势不对么,还是对环境有特定的要求?
3、和redis自带的redis-benchmark.exe工具进行比较。
想找找 redis 在window平台测试相关的文章,发现都是redis原生的测试工具的介绍,于是,也用它测试了一下:
发现原生的果然强悍,最高的时候可以飘到6w,看来用c就是不一样。
搞的我都怀疑,你们都是在用并发测试,而我只是用多线程。
4、用了传说中性能好到要收费的:stackexchange.redis
感觉也差不多啊,收费我还是支持的。
关键是这货相同的netcore代码,放linux centos7 跑不动:
竟然连不上了,直接返回错误了:
{"success":false,"msg":"it was not possible to connect to the redis server(s). internalfailure (none, last-recv: 803) on 127.0.0.1:6379/interactive, idle, last: get, origin: readfrompipe, outstanding: 2, last-read: 0s ago, last-write: 0s ago, unanswered-write: 0s ago, keep-alive: 60s, state: connectedestablished, mgr: 9 of 10 available, in: 0, last-heartbeat: never, global: 0s ago, v: 2.0.571.20511"}
总结:
cyq.data 经过重构升级后,整体提升了不少。
这里要了解一下:cyq.data 集成的分布式缓存操作,和其它单独的客户端是不一样的。
因为其它客户端是把所有的redis命令都实现了,你可以其它客户端操作完整的redis。
而 cyq.data 只是:
get、set、del、contains、clear。
统一了所有类型并保持最简单的缓存操作接口。
最后,献上测试代码:
appconfig.cache.redisservers = "127.0.0.1:6379";//,127.0.0.1:6380 - c123456,127.0.0.1:6381 - c123456,127.0.0.1:6382 - c123456,127.0.0.1:6383 - c123456"; //appconfig.cache.redisservers = "redis.txt"; //appconfig.cache.redisserversbak = "redis.bak.txt"; int readcount = 100000, userdbcount = 1; threadpool.setmaxthreads(1000, 1000); new threadrun(1, readcount, userdbcount).start(); new threadrun(4, readcount, userdbcount).start(); new threadrun(8, readcount, userdbcount).start(); new threadrun(10, readcount, userdbcount).start(); new threadrun(20, readcount, userdbcount).start(); new threadrun(50, readcount, userdbcount).start(); new threadrun(100, readcount, userdbcount).start(); new threadrun(200, readcount, userdbcount).start(); new threadrun(500, readcount, userdbcount).start(); new threadrun(1000, readcount, userdbcount).start(); new threadrun(2000, readcount, userdbcount).start(); new threadrun(5000, readcount, userdbcount).start(); new threadrun(2000, readcount, userdbcount).start(); new threadrun(2000, readcount, userdbcount).start(); new threadrun(500, readcount, userdbcount).start(); new threadrun(200, readcount, userdbcount).start(); new threadrun(100, readcount, userdbcount).start(); new threadrun(50, readcount, userdbcount).start(); new threadrun(20, readcount, userdbcount).start(); new threadrun(10, readcount, userdbcount).start(); new threadrun(8, readcount, userdbcount).start(); new threadrun(4, readcount, userdbcount).start(); new threadrun(1, readcount, userdbcount).start(); cachemanage.redisinstance.clear();//操作对象 console.writeline("end"); console.writeline(cachemanage.redisinstance.workinfo); console.writeline(cachemanage.redisinstance.cacheinfo.tojson(false, false)); ---------------------------------------------------------- public class threadrun { int threadcount, setorreadcount, usedbcount; cachemanage cache; public threadrun(int threadcount, int setorreadcount, int usedbcount) { cache = cachemanage.redisinstance;//操作对象 cache.clear(); this.threadcount = threadcount; this.setorreadcount = setorreadcount; this.usedbcount = usedbcount; } system.diagnostics.stopwatch glowatch = new system.diagnostics.stopwatch(); int runendcount = 0; bool isend = false; public void start() { appconfig.cache.redisusedbcount = usedbcount; glowatch.start(); for (int i = 0; i < threadcount; i++) { threadpool.queueuserworkitem(new waitcallback(threadstart), setorreadcount / threadcount); } while (!isend) { thread.sleep(10); } } public void threadstart(object readcount) { string rndkey = guid.newguid().tostring().substring(0, 5); int max = (int)readcount; for (int i = 0; i < max; i++) { string key = rndkey + "key" + i; if(cache.set(key, guid.newguid().tostring())) { } else { console.writeline("set 失败 key :" + key); } } interlocked.increment(ref runendcount); if (runendcount >= threadcount && !isend) { isend = true; //glowatch.stop(); //ng 2000ms //x 1000ms long t = glowatch.elapsedmilliseconds; console.writeline(string.format("threadcount : {0} , run : {1} time {2} ms ,{3} requests per second. ", threadcount, setorreadcount, t.tostring(), setorreadcount * 1000 / t)); } } }
上一篇: 爆笑冷人糗事小笑话
下一篇: 那年费尽心思的搞到一妹子的微信