zoukankan      html  css  js  c++  java
  • 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));
    
                }
            }
        }
  • 相关阅读:
    unity, 显示像素图,以及iOS下像素图变模糊解决办法
    unity, iOS集成微信
    unity, PlayerPrefs.GetInt(key,defaultValue)
    unity, 对于Debug.Log输出的log,可以双击定位到代码
    unity, UGUI Image shader
    unity, use particleSystem with UGUI
    unity, UGUI Text fadeIn
    unity, write/read txt file
    unity, get Canvas Scaler referenceResolution
    unity, change parent and keep localPosition or worlPosition
  • 原文地址:https://www.cnblogs.com/cyq1162/p/10634267.html
Copyright © 2011-2022 走看看