c# redis stackexchange.redis 工具类

    /// <summary>
    /// 描 述:redis操作方法
    /// </summary>
    public class RedisCache
    {
        #region  -- 连接信息 --

        /// <summary>
        /// syncTimeout:允许同步操作的时间(ms)
        /// connectTimeout:连接操作的超时时间(ms)
        /// responseTimeout:socket 健康检查的响应时间(ms,毫秒)
        /// </summary>
        public static string connString = "127.0.0.1:6379,password=,abortConnect=False,syncTimeout = 30000,connectTimeout=30000,responseTimeout=30000,allowAdmin = true";

        /// <summary>
        /// redis锁对象
        /// </summary>
        private static readonly object Locker = new object();
        private static ConnectionMultiplexer _instance;

        /// <summary>
        /// 单例获取链接对象
        /// </summary>
        public static ConnectionMultiplexer _conn
        {
            get
            {
                if (_instance == null)
                {
                    lock (Locker)
                    {
                        if (_instance == null || !_instance.IsConnected)
                        {
                            _instance = GetManager();
                        }
                    }
                }
                return _instance;
            }
        }

        /// <summary>
        /// 获取redis对象
        /// </summary>
        /// <param name="connectionString">链接字符串</param>
        /// <returns></returns>
        private static ConnectionMultiplexer GetManager(string connectionString = null)
        {
            connectionString = connectionString ?? connString;
            var connect = ConnectionMultiplexer.Connect(connectionString);

            //注册如下事件
            connect.ConnectionFailed += RedisCacheEvent.MuxerConnectionFailed;
            connect.ConnectionRestored += RedisCacheEvent.MuxerConnectionRestored;
            connect.ErrorMessage += RedisCacheEvent.MuxerErrorMessage;
            connect.ConfigurationChanged += RedisCacheEvent.MuxerConfigurationChanged;
            connect.HashSlotMoved += RedisCacheEvent.MuxerHashSlotMoved;
            connect.InternalError += RedisCacheEvent.MuxerInternalError;

            return connect;
        }

        /// <summary>
        /// 获取redis客户端根据库ID号
        /// </summary>
        /// <param name="dbId">redis库Id</param>
        /// <returns></returns>
        private static IDatabase GetClientManager(int dbId)
        {
            ////预防死锁
            _conn.PreserveAsyncOrder = false;
            IDatabase dbObj = _conn.GetDatabase(dbId);
            return dbObj;
        }
        #endregion

        #region 执行redis命令公共方法

        /// <summary>
        /// 执行redis命令公共方法
        /// </summary>
        /// <typeparam name="T">返回类型(泛型)</typeparam>
        /// <param name="func">委托(参数为委托)</param>
        /// <returns></returns>
        private static T Do<T>(Func<IDatabase, T> func, long dbId = 0)
        {
            try
            {
                var database = GetClientManager((int)dbId);
                return func(database);
            }
            catch (Exception ex)
            {               
                WriteSqlErrorLog(ex);
                return default(T);
            }
        }

        #endregion

        #region 判断Key是否存在 

        /// <summary>
        /// 判断Key是否存在
        /// </summary>
        /// <param name="key">键</param>
        /// <param name="dbId">redis库名称</param>
        /// <returns></returns>
        public static bool ExitKey(string key, long dbId = 0)
        {
            var res = Do(db =>
            {
                return db.KeyExists(key);
            }, dbId);
            return res;
        }


        #endregion

        #region 模糊查找KeyList 

        /// <summary>
        /// 模糊查找KeyList
        /// </summary>
        /// <param name="keyname"></param>
        /// <param name="dbId"></param>
        /// <returns></returns>

        public static List<string> SearchKeyList(string keyname, long dbId = 0)
        {
            List<string> res = new List<string>();
            try
            {
                var db = _conn.GetEndPoints();
                foreach (var ep in db)
                {
                    var server = _conn.GetServer(ep);
                    var keys = server.Keys((int)dbId, "*" + keyname + "*");
                    foreach (var item in keys)
                    {
                        res.Add((string)item);
                    }
                }
                return res;
            }
            catch (Exception ex)
            {
                WriteSqlErrorLog(ex);
                throw;
            }
        }

        #endregion

        #region 获取某个库所有的Key 

        /// <summary>
        /// 获取某个库所有的Key
        /// </summary>
        /// <param name="dbId"></param>
        /// <returns></returns>
        public static List<string> GetAllKeys(long dbId = 0)
        {
            List<string> res = new List<string>();
            try
            {
                var db = _conn.GetEndPoints();
                foreach (var ep in db)
                {
                    var server = _conn.GetServer(ep);
                    var keys = server.Keys((int)dbId);
                    foreach (var item in keys)
                    {
                        res.Add((string)item);
                    }
                }
                return res;
            }
            catch (Exception ex)
            {
                WriteSqlErrorLog(ex);
                throw;
            }
        }

        #endregion

        #region  -- Key-Value -- 

        /// <summary>
        /// 设置单体
        /// </summary>
        /// <typeparam name="T">值类型</typeparam>
        /// <param name="key">键值</param>
        /// <param name="t">值</param>
        /// <param name="dbId">库Id</param>
        /// <returns></returns>
        public static bool Set<T>(string key, T t, long dbId = 0)
        {
            var res = Do(db =>
            {
                return db.StringSet(key, ConvertJson(t));
            }, dbId);
            return res;
        }

        /// <summary>
        /// 设置单体
        /// </summary>
        /// <typeparam name="T">值类型</typeparam>
        /// <param name="key">键值</param>
        /// <param name="t">值</param>
        /// <param name="timeSpan">保存时间</param>
        /// <param name="dbId">库Id</param>
        /// <returns></returns>
        public static bool Set<T>(string key, T t, TimeSpan timeSpan, long dbId = 0)
        {
            var res = Do(db =>
            {
                return db.StringSet(key, ConvertJson(t), timeSpan);
            }, dbId);
            return res;
        }

        /// <summary>
        /// 设置单体
        /// </summary>
        /// <typeparam name="T">值类型</typeparam>
        /// <param name="key">键值</param>
        /// <param name="t">值</param>
        /// <param name="dateTime">过期时间</param>
        /// <returns></returns>
        public static bool Set<T>(string key, T t, DateTime dateTime, long dbId = 0)
        {
            var res = Do(db =>
            {
                DateTime nowDate = DateTime.Now;
                TimeSpan timeSpan = dateTime.Subtract(nowDate).Duration();
                return db.StringSet(key, ConvertJson(t), timeSpan);
            }, dbId);
            return res;
        }

        /// <summary>
        /// 获取单体
        /// </summary>
        /// <typeparam name="T">值类型</typeparam>
        /// <param name="key">键值</param>
        /// <returns></returns>
        public static T Get<T>(string key, long dbId = 0) where T : class
        {
            var resStr = Do(db =>
            {
                return db.StringGet(key);
            }, dbId);

            var res = ConvertObj<T>(resStr);
            return res;
        }

        /// <summary>
        /// 移除单体
        /// </summary>
        /// <param name="key">键值</param>
        public static bool Remove(string key, long dbId = 0)
        {
            try
            {
                var res = Do(db =>
                {
                    return db.KeyDelete(key);
                }, dbId);

                return res;
            }
            catch (Exception ex)
            {
                WriteSqlErrorLog(ex);
                throw;
            }
        }

        /// <summary>
        /// 清空所有缓存
        /// </summary>
        public static bool RemoveAll(long dbId = 0)
        {
            try
            {
                var db = _conn.GetEndPoints();
                foreach (var ep in db)
                {
                    var server = _conn.GetServer(ep);
                    server.FlushDatabase((int)dbId);
                }
                return true;
            }
            catch (Exception ex)
            {
                WriteSqlErrorLog(ex);
                throw;
            }

        }

        #endregion

        #region  -- List --

        /// <summary>
        /// 添加列表
        /// </summary>
        /// <typeparam name="T">类型</typeparam>
        /// <param name="key">键值</param>
        /// <param name="t">值</param>
        /// <param name="sp">过期时间</param>
        /// <param name="dbId">库</param>
        public static void List_Add<T>(string key, T t, TimeSpan sp, long dbId = 0)
        {
            var res = Do(db =>
            {
                if (db.ListRightPush(key, ConvertJson(t)) >= 0)
                {
                    return db.KeyExpire(key, sp);
                }
                else
                {
                    return false;
                };
            }, dbId);
        }

        /// <summary>
        /// 移除列表某个值
        /// </summary>
        /// <typeparam name="T">类型</typeparam>
        /// <param name="key">键值</param>
        /// <param name="t">值</param>
        /// <param name="dbId">库</param>
        /// <returns></returns>
        public static bool List_Remove<T>(string key, T t, long dbId = 0)
        {
            var res = Do(db =>
            {
                return db.ListRemove(key, ConvertJson(t)) >= 0;
            }, dbId);

            return res;
        }

        /// <summary>
        /// 移除列表所有值
        /// </summary>
        /// <typeparam name="T">类型</typeparam>
        /// <param name="key">键值</param>
        /// <param name="dbId">库Id</param>
        public static void List_RemoveAll(string key, long dbId = 0)
        {
            var res = Do(db =>
            {
                return db.KeyDelete(key);
            }, dbId);

        }

        /// <summary>
        /// 获取列表数据条数
        /// </summary>
        /// <param name="key"></param>
        /// <param name="dbId"></param>
        /// <returns></returns>
        public static long List_Count(string key, long dbId = 0)
        {
            var res = Do(db =>
            {
                return db.ListLength(key);
            }, dbId);

            return res;
        }

        /// <summary>
        /// 获取指定条数列表数据
        /// </summary>
        /// <typeparam name="T">类型</typeparam>
        /// <param name="key">键值</param>
        /// <param name="start">开始编号</param>
        /// <param name="count">条数</param>
        /// <param name="dbId">库</param>
        /// <returns></returns>
        public static List<T> List_GetRange<T>(string key, int start, int count, long dbId = 0)
        {
            var res = Do(db =>
            {
                int end = start + count - 1;
                var resArry = db.ListRange(key, start, end);
                List<T> result = ConvetList<T>(resArry);
                return result;
            }, dbId);

            return res;
        }

        /// <summary>
        /// 获取列表所有数据
        /// </summary>
        /// <typeparam name="T">类型</typeparam>
        /// <param name="key">键值</param>
        /// <param name="dbId">库数据</param>
        /// <returns></returns>
        public static List<T> List_GetList<T>(string key, long dbId = 0)
        {
            var res = Do(db =>
            {
                List<T> result = new List<T>();
                var resArry = db.ListRange(key);
                result.AddRange(ConvetList<T>(resArry));
                return result;
            }, dbId);

            return res;
        }
        /// <summary>
        /// 获取列表分页数据
        /// </summary>
        /// <typeparam name="T">类型</typeparam>
        /// <param name="key">键值</param>
        /// <param name="pageIndex">页码</param>
        /// <param name="pageSize">每页条数</param>
        /// <param name="dbId">库</param>
        /// <returns></returns>
        public static List<T> List_GetList<T>(string key, int pageIndex, int pageSize, long dbId = 0)
        {
            var res = Do(db =>
            {
                List<T> result = new List<T>();
                int start = pageSize * (pageIndex - 1);
                int end = pageIndex * pageSize - 1;
                var resArry = db.ListRange(key, start, end);
                result.AddRange(ConvetList<T>(resArry));
                return result;
            }, dbId);

            return res;
        }

        #endregion

        #region  -- Set -- 

        /// <summary>
        /// 添加集合
        /// </summary>
        /// <typeparam name="T">类型</typeparam>
        /// <param name="key">键值</param>
        /// <param name="t">数值</param>
        /// <param name="sp">有效时间</param>
        /// <param name="dbId">库</param>
        public static bool Set_Add<T>(string key, T t, TimeSpan sp, long dbId = 0)
        {
            var res = Do(db =>
            {
                string value = ConvertJson(t);
                if (db.SetAdd(key, value) == true)
                {
                    return db.KeyExpire(key, sp);
                }
                else
                {
                    return false;
                }
            }, dbId);

            return res;
        }

        /// <summary>
        /// 集合是否包含指定数据
        /// </summary>
        /// <typeparam name="T">类型</typeparam>
        /// <param name="key">键值</param>
        /// <param name="t">数值</param>
        /// <param name="dbId">库</param>
        /// <returns></returns>
        public static bool Set_Contains<T>(string key, T t, long dbId = 0)
        {
            var res = Do(db =>
            {
                return db.SetContains(key, ConvertJson(t));
            }, dbId);

            return res;
        }

        /// <summary>
        /// 移除集合某个值
        /// </summary>
        /// <typeparam name="T">类型</typeparam>
        /// <param name="key">键值</param>
        /// <param name="t">数值</param>
        /// <param name="dbId">库</param>
        /// <returns></returns>
        public static bool Set_Remove<T>(string key, T t, long dbId = 0)
        {
            var res = Do(db =>
            {
                return db.SetRemove(key, ConvertJson(t));
            }, dbId);

            return res;
        }

        #endregion

        #region  -- SortedSet -- 

        /// <summary>
        ///  添加数据到 SortedSet
        /// </summary>
        /// <typeparam name="T">类型</typeparam>
        /// <param name="key">集合id</param>
        /// <param name="t">数值</param>
        /// <param name="sp">有效时间</param>
        /// <param name="score">排序码</param>
        /// <param name="dbId">库</param>
        public static bool SortedSet_Add<T>(string key, T t, TimeSpan sp, double score, long dbId = 0)
        {
            var res = Do(db =>
            {
                string value = ConvertJson(t);
                if (db.SortedSetAdd(key, value, score) == true)
                {
                    return db.KeyExpire(key, sp);
                }
                else
                {
                    return false;
                }
            }, dbId);
            return res;
        }

        /// <summary>
        /// 移除数据从SortedSet
        /// </summary>
        /// <typeparam name="T">类型</typeparam>
        /// <param name="key">集合id</param>
        /// <param name="t">数值</param>
        /// <param name="dbId">库</param>
        /// <returns></returns>
        public static bool SortedSet_Remove<T>(string key, T t, long dbId = 0)
        {
            var res = Do(db =>
            {
                return db.SortedSetRemove(key, ConvertJson<T>(t));
            }, dbId);
            return res;
        }

        /// <summary>
        /// 修剪SortedSet
        /// </summary>
        /// <param name="key">键值</param>
        /// <param name="size">保留的条数</param>
        /// <param name="dbId">库</param>
        /// <param name="stop">删除的最大排名</param>
        /// <returns></returns>
        public static long SortedSet_Trim(string key, int size, long dbId = 0, long stop = 9999999)
        {
            var res = Do(db =>
            {
                return db.SortedSetRemoveRangeByRank(key, size, stop);
            }, dbId);
            return res;
        }

        /// <summary>
        /// 获取SortedSet的长度
        /// </summary>
        /// <param name="key">键值</param>
        /// <param name="dbId">库</param>
        /// <returns></returns>
        public static long SortedSet_Count(string key, long dbId = 0)
        {
            var res = Do(db =>
            {
                return db.SortedSetLength(key);
            }, dbId);
            return res;
        }

        /// <summary>
        /// 获取SortedSet的分页数据
        /// </summary>
        /// <typeparam name="T">类型</typeparam>
        /// <param name="key">键值</param>
        /// <param name="pageIndex">页码</param>
        /// <param name="pageSize">每页条数</param>
        /// <param name="dbId">库</param>
        /// <returns></returns>
        public static List<T> SortedSet_GetList<T>(string key, int pageIndex, int pageSize, long dbId = 0)
        {
            var res = Do(db =>
            {
                var list = db.SortedSetRangeByValue(key, RedisValue.Null, RedisValue.Null, Exclude.None, (pageIndex - 1) * pageSize, pageSize);
                var result = ConvetList<T>(list);
                return result;
            }, dbId);

            return res;

        }

        /// <summary>
        /// 获取SortedSet的全部数据
        /// </summary>
        /// <typeparam name="T">类</typeparam>
        /// <param name="key">键值</param>
        /// <param name="dbId">库</param>
        /// <returns></returns>
        public static List<T> SortedSet_GetListALL<T>(string key, long dbId = 0)
        {
            var res = Do(db =>
            {
                var list = db.SortedSetRangeByRank(key);
                List<T> result = ConvetList<T>(list);
                return result;
            }, dbId);

            return res;
        }

        #endregion

        #region  -- Hash -- 

        /// <summary>
        /// 判断某个数据是否已经被缓存
        /// </summary>
        /// <typeparam name="T">类型</typeparam>
        /// <param name="key">hashID</param>
        /// <param name="dataKey">键值</param>
        /// <param name="dbId">库</param>
        /// <returns></returns>
        public static bool Hash_Exist(string key, string dataKey, long dbId = 0)
        {
            var res = Do(db => db.HashExists(key, dataKey), dbId);
            return res;
        }

        /// <summary>
        /// 存储数据到hash表需要设置失效时间
        /// </summary>
        /// <typeparam name="T">类型</typeparam>
        /// <param name="key">hashID</param>
        /// <param name="dataKey">键值</param>
        /// <param name="t">数值</param>
        /// <param name="dbId">库</param>
        /// <returns></returns>
        public static bool Hash_Set<T>(string key, string dataKey, T t, TimeSpan sp, long dbId = 0)
        {
            var res = Do(db =>
            {
                string value = ConvertJson(t);
                if (db.HashSet(key, dataKey, value) == true)
                {
                    return db.KeyExpire(key, sp);
                }
                else
                {
                    return false;
                }
            }, dbId);
            return res;
        }

        /// <summary>
        /// 存储数据到hash表不需要设置失效时间
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <param name="dataKey"></param>
        /// <param name="t"></param>
        /// <param name="dbId"></param>
        /// <returns></returns>
        public static bool Hash_Set<T>(string key, string dataKey, T t, long dbId = 0)
        {
            var res = Do(db =>
            {
                string value = ConvertJson(t);
                return db.HashSet(key, dataKey, value);
            }, dbId);
            return res;
        }

        /// <summary>
        /// 存储文本数据到hash表需要设置失效时间
        /// </summary>
        /// <param name="key"></param>
        /// <param name="dataKey"></param>
        /// <param name="value"></param>
        /// <param name="dbId"></param>
        /// <returns></returns>
        public static bool Hash_SetText(string key, string dataKey, string value, TimeSpan sp, long dbId = 0)
        {
            var res = Do(db =>
            {
                if (db.HashSet(key, dataKey, value) == true)
                {
                    return db.KeyExpire(key, sp);
                }
                else
                {
                    return false;
                }
            }, dbId);
            return res;
        }

        /// <summary>
        /// 存储文本数据到hash表不需要设置失效时间
        /// </summary>
        /// <param name="key"></param>
        /// <param name="dataKey"></param>
        /// <param name="value"></param>
        /// <param name="dbId"></param>
        /// <returns></returns>
        public static bool Hash_SetText(string key, string dataKey, string value, long dbId = 0)
        {
            var res = Do(db =>
            {
                return db.HashSet(key, dataKey, value);
            }, dbId);
            return res;
        }

        /// <summary>
        /// 获取hashid数据集中所有key的集合
        /// </summary>
        public static List<string> GetHashKeys(string hashid, long dbId = 0)
        {
            var res = Do(db =>
            {
                List<string> result = new List<string>();
                RedisValue[] arr = db.HashKeys(hashid);
                foreach (var item in arr)
                {
                    if (!item.IsNullOrEmpty)
                    {
                        result.Add(item.ToString());
                    }
                }
                return result;
            }, dbId);
            return res;
        }

        /// <summary>
        /// 获取hashid数据集中的所有value集合
        /// </summary>
        public static List<string> GetHashValues(string hashid, long dbId = 0)
        {
            var res = Do(db =>
            {
                List<string> result = new List<string>();
                RedisValue[] arr = db.HashValues(hashid);
                foreach (var item in arr)
                {
                    if (!item.IsNullOrEmpty)
                    {
                        result.Add(item.ToString());
                    }
                }
                return result;
            }, dbId);
            return res;
        }

        /// <summary>
        /// 移除hash中的某值
        /// </summary>
        /// <param name="key">hashID</param>
        /// <param name="dataKey">键值</param>
        /// <param name="dbId">库</param>
        /// <returns></returns>
        public static bool Hash_Remove(string key, string dataKey, long dbId = 0)
        {
            var res = Do(db =>
            {
                return db.HashDelete(key, dataKey);
            }, dbId);
            return res;
        }

        /// <summary>
        /// 移除整个hash
        /// </summary>
        /// <param name="key">hashID</param>
        /// <param name="dbId">库</param>
        /// <returns></returns>
        public static bool Hash_Remove(string key, long dbId = 0)
        {
            var res = Do(db =>
            {
                RedisValue[] arr = db.HashKeys(key);
                return db.HashDelete(key, arr) >= 0;
            }, dbId);
            return res;
        }

        /// <summary>
        /// 从hash表获取数据
        /// </summary>
        /// <typeparam name="T">类型</typeparam>
        /// <param name="key">hashID</param>
        /// <param name="dataKey">键值</param>
        /// <param name="dbId">库</param>
        /// <returns></returns>
        public static T Hash_Get<T>(string key, string dataKey, long dbId = 0)
        {
            var res = Do(db =>
            {
                string value = db.HashGet(key, dataKey);
                return ConvertObj<T>(value);

            }, dbId);
            return res;
        }

        /// <summary>
        /// 从hash表获取文本数据
        /// </summary>
        /// <typeparam name="T">类型</typeparam>
        /// <param name="key">hashID</param>
        /// <param name="dataKey">键值</param>
        /// <param name="dbId">库</param>
        /// <returns></returns>
        public static string Hash_GetText(string key, string dataKey, long dbId = 0)
        {
            var res = Do(db =>
            {
                return db.HashGet(key, dataKey);
            }, dbId);
            return res;
        }


        /// <summary>
        /// 获取整个hash的数据
        /// </summary>
        /// <typeparam name="T">类型</typeparam>
        /// <param name="key">hashID</param>
        /// <param name="dbId">库</param>
        /// <returns></returns>
        public static List<T> Hash_GetAll<T>(string key, long dbId = 0)
        {
            var res = Do(db =>
            {
                List<T> list = new List<T>();
                RedisValue[] arr = db.HashKeys(key);
                foreach (var item in arr)
                {
                    if (!item.IsNullOrEmpty)
                    {
                        string value = db.HashGet(key, item);
                        list.Add(ConvertObj<T>(value));
                    }
                }
                return list;

            }, dbId);
            return res;

        }

        #endregion

        #region  -- Stream 消息队列 --

        /// <summary>
        /// 发送Redis消息
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        public static bool SendMessage(string key, string value, long dbId = 0)
        {
            Do(db => db.ListLeftPush(key, value), dbId);
            return true;
        }

        /// <summary>
        /// 接收Redis消息
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        public static T ReceiveMessages<T>(string key, long dbId = 0)
        {
            return Do(db =>
            {
                var value = db.ListRightPop(key);
                return ConvertObj<T>(value);
            }, dbId);
        }


        #endregion

        #region 帮助方法

        /// <summary>
        /// 反序列化
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="value"></param>
        /// <returns></returns>
        private static T ConvertObj<T>(RedisValue value)
        {
            try
            {
                if (typeof(T).Name.Equals(typeof(string).Name))
                {
                    return JsonConvert.DeserializeObject<T>($"'{value}'");
                }

                if (value.HasValue)
                {
                    return JsonConvert.DeserializeObject<T>(value);
                }
                else
                {
                    return default(T);
                }
            }
            catch (Exception ex)
            {
                WriteSqlErrorLog(ex);
                throw;
            }
        }

        /// <summary>
        /// 反序列化(集合)
        /// </summary>
        /// <typeparam name="T">返回值类型</typeparam>
        /// <param name="values">反序列化参数</param>
        /// <returns></returns>
        private static List<T> ConvetList<T>(RedisValue[] values)
        {
            List<T> result = new List<T>();
            if (values != null)
            {
                foreach (var item in values)
                {
                    var model = ConvertObj<T>(item);
                    result.Add(model);
                }
            }

            return result;
        }


        /// <summary>
        /// 序列化
        /// </summary>
        /// <typeparam name="T">类</typeparam>
        /// <param name="value">值</param>
        /// <returns></returns>
        private static string ConvertJson<T>(T value)
        {
            try
            {
                string result = value is string ? value.ToString() : JsonConvert.SerializeObject(value);
                return result;
            }
            catch (Exception ex)
            {
                WriteSqlErrorLog(ex);
                throw;
            }

        }


        #endregion

        #region  操作报错写入日志

        public static void WriteSqlErrorLog(Exception ex)
        {
           
        }
        #endregion

    }

 

上一篇:mpvue——引入echarts打包vendor过大


下一篇:Docker源码分析--环境搭建