k8s部署微服務項目實戰--react前端+springboot/python后端-第六節 k8s環境下中間件搭建-redis集群以及使用


上一節我們介紹了mariaDB集群的搭建,這一節我們介紹一下redis集群的搭建以及在springboot中使用redis集群。

一、redis集群的搭建

redis集群的搭建我們同樣沒有使用operator的形式,而是同樣手動搭建,基本上是按照下面兩篇博文中的步驟來的,在此再次感謝相關的作者:

https://blog.csdn.net/zhutongcloud/article/details/90768390

https://www.jianshu.com/p/a34790c730cf

總結起來的步驟就是下面幾步:

第一步:前期准備階段,跟上一節mariaDB集群一樣,使用nfs存儲來作為Redis的后端存儲,NFS的路徑設置為/appl/install(完全可以自行更改);創建StorageClass指定為nfs以便動態創建PVC和PV。

kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
    name: nfs
provisioner: mynfs
reclaimPolicy: Delete
volumeBindingMode: Immediate

第二步:創建redis專用的namespace以及用到的ConfigMap:

namespace:
kind: Namespace
apiVersion: v1
metadata:
  name: demo-redis
ConfigMap:
kind: ConfigMap
apiVersion: v1
metadata:
  name: redis-cluster-configmap
  namespace: demo-redis
data:
  redis.conf: |
    appendonly yes
    protected-mode no
    cluster-enabled yes
    cluster-config-file /var/lib/redis/nodes.conf
    cluster-node-timeout 5000
    dir /var/lib/redis
    port 6379

注意:redis有兩種持久化存儲的格式,一種是AOF一種是RDB,我這里使用的是AOF,因此在redis.conf中添加了“appendonly yes”。

第三步:使用StatefulSet來創建redis的pod(注意:這里沒有遵從上面兩個鏈接博文中的先創建pv,pvc然后關聯pod,而是直接動態創建pvc和pv):

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: redis-app
  namespace: demo-redis
spec:
  serviceName: redis-headless-service
  replicas: 6
  selector:
    matchLabels:
      app: redis
      appCluster: redis-cluster
  template:
    metadata:
      labels:
        app: redis
        appCluster: redis-cluster
    spec:
      terminationGracePeriodSeconds: 20
      affinity:
        podAntiAffinity:
          preferredDuringSchedulingIgnoredDuringExecution:
          - weight: 100
            podAffinityTerm:
              labelSelector:
                matchExpressions:
                - key: app
                  operator: In
                  values:
                  - redis
              topologyKey: kubernetes.io/hostname
      containers:
      - name: redis
        image: "redis"
        command:
          - "redis-server"
        args:
          - "/etc/redis/redis.conf"
          - "--protected-mode"
          - "no"
        resources:                        
          requests:
            cpu: 1000m
            memory: 1Gi
          limits:
            cpu: 1000m                     
            memory: 1Gi 
        ports:
            - name: redis
              containerPort: 6379
              protocol: "TCP"
            - name: cluster
              containerPort: 16379
              protocol: "TCP"
        volumeMounts:
          - name: redis-conf
            mountPath: /etc/redis        
          - name: redis-data
            mountPath: /var/lib/redis
      volumes:
      - name: redis-conf
        configMap:
          name: redis-cluster-configmap
          items:
            - key: redis.conf
              path: redis.conf
  volumeClaimTemplates:
  - metadata:
      name: redis-data
      annotations:
        volume.beta.kubernetes.io/storage-class: "nfs"
    spec:
      accessModes:
      - ReadWriteOnce
      resources:
        requests:  
          storage: 2Gi

注意,我們創建了6個副本,符合我們redis集群3+3的配置。

第四步:創建headless service,同時創建提供與其他微服務或者外網訪問的service:

Headless service:

apiVersion: v1
kind: Service
metadata:
  name: redis-headless-service-------與上面StatefulSet中的serviceName一致。
  namespace: demo-redis
  labels:
    app: redis
spec:
  ports:
  - name: redis-port
    port: 6379
  clusterIP: None
  selector:
    app: redis
    appCluster: redis-cluster

與其他微服務互通的sevice:

apiVersion: v1
kind: Service
metadata:
  name: redis-access-service
  namespace: demo-redis
  labels:
    app: redis
spec:
  type: NodePort
  ports:
  - name: redis-port
    protocol: TCP
    port: 6379
    targetPort: 6379
    nodePort: 32379
  selector:
    app: redis
    appCluster: redis-cluster

注意:這里如果redis集群不會對外提供服務的話,沒有必要使用NodePort這種方式,只分配內網訪問的port即可。

完成上面的4步並且執行這些yaml文件之后,我們僅僅是創建並啟動了6個redis的pod, 但是它們並沒有形成一個集群,下面的第五步才是通過配置將這6個pod構建成一個集群。

第五步:進入當前任何一個redis的pod中開始搭建cluster集群:

  1. 進入pod中,執行命令類似於“kubectl exec -it redis-app-2 -n demo-redis /bin/bash”

  2. 因為構建的時候需要使用dig命令,由於我們的yaml文件中使用的redis鏡像中沒有這個命令,需要自行安裝,如果你跟我一樣使用了公司服務器,訪問外網需要代理,那請首先在redis容器(ubuntu系統)中配置代理,修改/etc/apt/apt.conf添加:    

    Acquire::http::Proxy "xxx";

    Acquire::https::Proxy "xxx";

  3. 然后執行apt-get update以及apt-get install dnsutils安裝dig命令。

  4. 在redis5之后的版本中,手動搭建redis集群已經不需要安裝redis-trib.rb,已經集成到redis-cli中(如果您安裝的redis鏡像版本是redis5之前的,那就需要自行安裝redis-trib.rb,使用redis-trib命令),因此確保下面的命令正確運行:

   redis-cli --cluster create `dig +short redis-app-0.redis-headless-service.demo-redis.svc.cluster.local`:6379 `dig +short redis-app-1.redis-headless-service.demo-redis.svc.cluster.local`:6379 `dig +short redis-app-2.redis-headless-service.demo-redis.svc.cluster.local`:6379 `dig +short redis-app-3.redis-headless-service.demo-redis.svc.cluster.local`:6379 `dig +short redis-app-4.redis-headless-service.demo-redis.svc.cluster.local`:6379 `dig +short redis-app-5.redis-headless-service.demo-redis.svc.cluster.local`:6379 --cluster-replicas 1

  注意:類似與`dig +short redis-app-0.redis-headless-service.demo-redis.svc.cluster.local`這種命令指的是動態獲得每一個pod的ip地址,因為pod的ip每次重啟都是不同的,因此一定不能hardcode,而使用StatefulSet以及headless service能為我們創建的每個pod分配一個固定的dns域名,格式為$podName.$headlessServiceName.$namespace.svc.cluster.local,這樣是不是就很好理解了呢?所以整個上面這條命令就是將每個pod的動態ip和端口構建成一個集群,后面的參數“--cluster-replicas 1”代表是一主一從的方式,所以最終就是三主三從。

  5. 此時redis集群已經創建成功,為了驗證,登陸任何一個pod,執行下面的操作就能看到當前pod的角色以及整個cluster的信息:

    

/usr/local/bin/redis-cli -c
127.0.0.1:6379> cluster nodes
97a7d4758a3cd17a5a7a88cb0858e049d7116836 10.244.1.236:6379@16379 slave 2cad20ff68341b6fd77b617265922cc9f16ef26d 0 1636509315819 2 connected
3e18ab8e60d3f9189e34268ad68db6f8b9a763d7 10.244.2.219:6379@16379 master - 0 1636509317326 3 connected 10923-16383
fcdd840cc2e621e4ea720b5bf8f50b3b0b3f2e9a 10.244.1.240:6379@16379 myself,slave 3e18ab8e60d3f9189e34268ad68db6f8b9a763d7 0 1636509316000 3 connected
094ad7e577e813abc1596f47e57867f517f2920b 10.244.2.220:6379@16379 slave ee85f7ef9256585308195b494770d76721823702 0 1636509317828 1 connected
ee85f7ef9256585308195b494770d76721823702 10.244.1.235:6379@16379 master - 0 1636509316823 1 connected 0-5460
2cad20ff68341b6fd77b617265922cc9f16ef26d 10.244.1.241:6379@16379 master - 0 1636509316522 2 connected 5461-10922

127.0.0.1:6379> cluster info
cluster_state:ok
cluster_slots_assigned:16384
cluster_slots_ok:16384
cluster_slots_pfail:0
cluster_slots_fail:0
cluster_known_nodes:6
cluster_size:3
cluster_current_epoch:6
cluster_my_epoch:3
cluster_stats_messages_ping_sent:991
cluster_stats_messages_pong_sent:1063
cluster_stats_messages_meet_sent:1
cluster_stats_messages_sent:2055
cluster_stats_messages_ping_received:1063
cluster_stats_messages_pong_received:992
cluster_stats_messages_received:2055

127.0.0.1:6379> role
1) "slave"
2) "10.244.2.219"
3) (integer) 6379
4) "connected"
5) (integer) 840

最后補充一點我們創建的這套redis集群的信息,包括3個master和3個slave,3個master的slot(槽位,redis用0~16383個槽位來存儲key)分別為0~5460,5461~10922,10923~16383, 添加key的時候就會通過算法計算后加入對應槽位的master中並且同步到該master對應的slave中。

二、在springboot中訪問redis集群

Springboot中訪問redis集群也是相當簡單,首先在application.properties中配置redis集群的node信息、訪問的服務名以及訪問端口:

spring.redis.cluster.nodes=redis-app-0.redis-headless-service.demo-redis.svc.cluster.local:6379,redis-app-1.redis-headless-service.demo-redis.svc.cluster.local:6379,redis-app-2.redis-headless-service.demo-redis.svc.cluster.local:6379,redis-app-3.redis-headless-service.demo-redis.svc.cluster.local:6379,redis-app-4.redis-headless-service.demo-redis.svc.cluster.local:6379,redis-app-5.redis-headless-service.demo-redis.svc.cluster.local:6379
spring.redis.host=redis-access-service.demo-redis
spring.redis.port=6379

其次在pom.xml中加入redis的依賴:

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>                

再次,配置redis的全局配置文件RedisConfig.java,其中重寫了RedisTemplate的實例方法,方法中定義了redis的key和value的序列化器。

package com.example.demo.configuration;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;


@Configuration
public class RedisConfig {
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(factory);
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }
}

再再次,按照處理此類中間件的慣例,我們應該將redis的常用操作封裝成一個工具類,此類工具類網上比比皆是,大家自行搜索,我列出我使用的工具類:

RedisClientUtil.java

package com.example.demo.utils;

import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

@Component
public class RedisClientUtil {
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;--------注意這里使用的就是上面重寫的實例方法
    
    /**
     * 指定緩存失效時間
     */
    public boolean expire(String key, long time) {
        try {
            if(time > 0) {
                redisTemplate.expire(key, time, TimeUnit.SECONDS);
            }
            return true;
        } catch(Exception e) {
            e.printStackTrace();
            return false;
        }
    }
    /**
     * 根據key獲取過期時間
     */
    public long getExpire(String key) {
        return redisTemplate.getExpire(key, TimeUnit.SECONDS);
    }
    /**
     * 判斷key是否存在
     *
     * @param key 鍵
     * @return true 存在 false不存在
     */
    public boolean hasKey(String key) {
        try {
            return redisTemplate.hasKey(key);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }
    /**
     * 刪除緩存
     */
    @SuppressWarnings("unchecked")
    public void del(String... key) {
        if(key != null && key.length > 0) {
            if(key.length == 1) {
                redisTemplate.delete(key[0]);
            }
            else {
                redisTemplate.delete((Collection<String>) CollectionUtils.arrayToList(key));
            }
        }
    }
    /**
     * 普通緩存獲取
     *
     * @param key 鍵
     * @return 值
     */
    public Object get(String key) {
        return key == null ? null : redisTemplate.opsForValue().get(key);
    }

    /**
     * 普通緩存放入
     *
     * @param key   鍵
     * @param value 值
     * @return true成功 false失敗
     */
    public boolean set(String key, Object value) {
        try {
            redisTemplate.opsForValue().set(key, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }

    }

    /**
     * 普通緩存放入並設置時間
     *
     * @param key   鍵
     * @param value 值
     * @param time  時間(秒) time要大於0 如果time小於等於0 將設置無限期
     * @return true成功 false 失敗
     */
    public boolean set(String key, Object value, long time) {
        try {
            if (time > 0) {
                redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
            } else {
                set(key, value);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 遞增
     *
     * @param key   鍵
     * @param delta 要增加幾(大於0)
     * @return
     */
    public long incr(String key, long delta) {
        if (delta < 0) {
            throw new RuntimeException("遞增因子必須大於0");
        }
        return redisTemplate.opsForValue().increment(key, delta);
    }

    /**
     * 遞減
     *
     * @param key   鍵
     * @param delta 要減少幾(小於0)
     * @return
     */
    public long decr(String key, long delta) {
        if (delta < 0) {
            throw new RuntimeException("遞減因子必須大於0");
        }
        return redisTemplate.opsForValue().increment(key, -delta);
    }

    // ================================Map=================================

    /**
     * HashGet
     *
     * @param key  鍵 不能為null
     * @param item 項 不能為null
     * @return 值
     */
    public Object hget(String key, String item) {
        return redisTemplate.opsForHash().get(key, item);
    }

    /**
     * 獲取hashKey對應的所有鍵值
     *
     * @param key 鍵
     * @return 對應的多個鍵值
     */
    public Map<Object, Object> hmget(String key) {
        return redisTemplate.opsForHash().entries(key);
    }

    /**
     * HashSet
     *
     * @param key 鍵
     * @param map 對應多個鍵值
     * @return true 成功 false 失敗
     */
    public boolean hmset(String key, Map<String, Object> map) {
        try {
            redisTemplate.opsForHash().putAll(key, map);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * HashSet 並設置時間
     *
     * @param key  鍵
     * @param map  對應多個鍵值
     * @param time 時間(秒)
     * @return true成功 false失敗
     */
    public boolean hmset(String key, Map<String, Object> map, long time) {
        try {
            redisTemplate.opsForHash().putAll(key, map);
            if (time > 0) {
                expire(key, time);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 向一張hash表中放入數據,如果不存在將創建
     *
     * @param key   鍵
     * @param item  項
     * @param value 值
     * @return true 成功 false失敗
     */
    public boolean hset(String key, String item, Object value) {
        try {
            redisTemplate.opsForHash().put(key, item, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 向一張hash表中放入數據,如果不存在將創建
     *
     * @param key   鍵
     * @param item  項
     * @param value 值
     * @param time  時間(秒) 注意:如果已存在的hash表有時間,這里將會替換原有的時間
     * @return true 成功 false失敗
     */
    public boolean hset(String key, String item, Object value, long time) {
        try {
            redisTemplate.opsForHash().put(key, item, value);
            if (time > 0) {
                expire(key, time);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 刪除hash表中的值
     *
     * @param key  鍵 不能為null
     * @param item 項 可以使多個 不能為null
     */
    public void hdel(String key, Object... item) {
        redisTemplate.opsForHash().delete(key, item);
    }

    /**
     * 判斷hash表中是否有該項的值
     *
     * @param key  鍵 不能為null
     * @param item 項 不能為null
     * @return true 存在 false不存在
     */
    public boolean hHasKey(String key, String item) {
        return redisTemplate.opsForHash().hasKey(key, item);
    }

    /**
     * hash遞增 如果不存在,就會創建一個 並把新增后的值返回
     *
     * @param key  鍵
     * @param item 項
     * @param by   要增加幾(大於0)
     * @return
     */
    public double hincr(String key, String item, double by) {
        return redisTemplate.opsForHash().increment(key, item, by);
    }

    /**
     * hash遞減
     *
     * @param key  鍵
     * @param item 項
     * @param by   要減少記(小於0)
     * @return
     */
    public double hdecr(String key, String item, double by) {
        return redisTemplate.opsForHash().increment(key, item, -by);
    }

    // ============================set=============================

    /**
     * 根據key獲取Set中的所有值
     *
     * @param key 鍵
     * @return
     */
    public Set<Object> sGet(String key) {
        try {
            return redisTemplate.opsForSet().members(key);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 根據value從一個set中查詢,是否存在
     *
     * @param key   鍵
     * @param value 值
     * @return true 存在 false不存在
     */
    public boolean sHasKey(String key, Object value) {
        try {
            return redisTemplate.opsForSet().isMember(key, value);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 將數據放入set緩存
     *
     * @param key    鍵
     * @param values 值 可以是多個
     * @return 成功個數
     */
    public long sSet(String key, Object... values) {
        try {
            return redisTemplate.opsForSet().add(key, values);
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }

    /**
     * 將set數據放入緩存
     *
     * @param key    鍵
     * @param time   時間(秒)
     * @param values 值 可以是多個
     * @return 成功個數
     */
    public long sSetAndTime(String key, long time, Object... values) {
        try {
            Long count = redisTemplate.opsForSet().add(key, values);
            if (time > 0)
                expire(key, time);
            return count;
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }

    /**
     * 獲取set緩存的長度
     *
     * @param key 鍵
     * @return
     */
    public long sGetSetSize(String key) {
        try {
            return redisTemplate.opsForSet().size(key);
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }

    /**
     * 移除值為value的
     *
     * @param key    鍵
     * @param values 值 可以是多個
     * @return 移除的個數
     */
    public long setRemove(String key, Object... values) {
        try {
            Long count = redisTemplate.opsForSet().remove(key, values);
            return count;
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }
    // ===============================list=================================

    /**
     * 獲取list緩存的內容
     *
     * @param key   鍵
     * @param start 開始
     * @param end   結束 0 到 -1代表所有值
     * @return
     */
    public List<Object> lGet(String key, long start, long end) {
        try {
            return redisTemplate.opsForList().range(key, start, end);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 獲取list緩存的長度
     *
     * @param key 鍵
     * @return
     */
    public long lGetListSize(String key) {
        try {
            return redisTemplate.opsForList().size(key);
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }

    /**
     * 通過索引 獲取list中的值
     *
     * @param key   鍵
     * @param index 索引 index>0時, 0 表頭,1 第二個元素,依次類推;index<0時,-1,表尾,-2倒數第二個元素,依次類推 * @return */
 public Object lGetIndex(String key, long index) { try { return redisTemplate.opsForList().index(key, index); } catch (Exception e) { e.printStackTrace(); return null; } }

 /** * 將list放入緩存 * * @param key 鍵 * @param value 值 * @return */
 public boolean lSet(String key, Object value) { try { redisTemplate.opsForList().rightPush(key, value); return true; } catch (Exception e) { e.printStackTrace(); return false; } }

 /** * 將list放入緩存 * * @param key 鍵 * @param value 值 * @param time 時間(秒) * @return */
 public boolean lSet(String key, Object value, long time) { try { redisTemplate.opsForList().rightPush(key, value); if (time > 0)
                expire(key, time);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 將list放入緩存
     *
     * @param key   鍵
     * @param value 值
     * @return
     */
    public boolean lSet(String key, List<Object> value) {
        try {
            redisTemplate.opsForList().rightPushAll(key, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 將list放入緩存
     *
     * @param key   鍵
     * @param value 值
     * @param time  時間(秒)
     * @return
     */
    public boolean lSet(String key, List<Object> value, long time) {
        try {
            redisTemplate.opsForList().rightPushAll(key, value);
            if (time > 0)
                expire(key, time);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 根據索引修改list中的某條數據
     *
     * @param key   鍵
     * @param index 索引
     * @param value 值
     * @return
     */
    public boolean lUpdateIndex(String key, long index, Object value) {
        try {
            redisTemplate.opsForList().set(key, index, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 移除N個值為value
     *
     * @param key   鍵
     * @param count 移除多少個
     * @param value 值
     * @return 移除的個數
     */
    public long lRemove(String key, long count, Object value) {
        try {
            Long remove = redisTemplate.opsForList().remove(key, count, value);
            return remove;
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }
}

最后,就是實際的訪問調用:

    @RequestMapping(value = "/run", method = RequestMethod.POST)
    public Result Run(@RequestBody String jsonString) {
        try {
            String redisKey = UUID.randomUUID().toString().replaceAll("-", "");
            JSONObject jsonObject = JSONObject.parseObject(jsonString);
            String caseName = jsonObject.getString("CaseName");
            List<String> logList = new ArrayList<>();
            logList.add("Ready to execute the case: "+caseName);
//insert into redis Map<String, Object>mapForRedis = new HashMap<>(); mapForRedis.put("jobID", redisKafkaKey); mapForRedis.put("caseName", caseName); mapForRedis.put("logList", logList); redisClientUtil.hmset(redisKey, mapForRedis); ......
//add redisKey into jsonObject and pass to other micro-service jsonObject.put("redisKey", redisKey ); String passStrToExecute = JSONObject.toJSONString(jsonObject); String executeUrl = "http://execute-service:9000/Execute/runSingleCase"; String resForExecute = OkHttpClientUtil.doPostJson(executeUrl, null, passStrToExecute); if(resForExecute == "") { redisClientUtil.del(redisKafkaKey); return ResultFactory.buildFailResult("Execute-service throw error!"); } return ResultFactory.buildSuccessResult(null); }

而其他微服務如果需要從redis集群中取數據也只需要簡單的調用即可:

OK,到此為止,您是否覺得redis集群的搭建和使用很簡單呢?其實主要是因為我們的應用場景簡單,讓我們過濾掉了一下redis集群更強大的功能比如說哨兵模式~~


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM