資安

Redis 集群之Redis+Codis方案

【轉載請註明出處】:https://www.jianshu.com/p/41f97c494fc4

Redis 集群解決方案有哪些

Redis 的集群解決方案有社區的,也有官方的,社區的解決方案有 Codis 和Twemproxy,Codis是由我國的豌豆莢團隊開源的,Twemproxy是Twitter團隊的開源的;官方的集群解決方案就是 Redis Cluster,這是由 Redis 官方團隊來實現的。下面的列表可以很明顯地表達出三者的不同點。

Codis Twemproxy Redis Cluster
resharding without restarting cluster Yes No Yes
pipeline Yes Yes No
hash tags for multi-key operations Yes Yes Yes
multi-key operations while resharding Yes - No(details)
Redis clients supporting Any clients Any clients Clients have to support cluster protocol
codis和twemproxy最大的區別有兩個:
  • codis支持動態水平擴展,對client完全透明不影響服務的情況下可以完成增減redis實例的操作;
  • codis是用go語言寫的並支持多線程,twemproxy用C並只用單線程。 後者又意味著:codis在多核機器上的性能會好於twemproxy;codis的最壞響應時間可能會因為GC的STW而變大,不過go1.5發佈後會顯著降低STW的時間;如果只用一個CPU的話go語言的性能不如C,因此在一些短連接而非長連接的場景中,整個系統的瓶頸可能變成accept新tcp連接的速度,這時codis的性能可能會差於twemproxy。
codis和redis cluster的區別:
  • redis cluster基於smart client和無中心的設計,client必須按key的哈希將請求直接發送到對應的節點。這意味著:使用官方cluster必須要等對應語言的redis driver對cluster支持的開發和不斷成熟;client不能直接像單機一樣使用pipeline來提高效率,想同時執行多個請求來提速必須在client端自行實現異步邏輯。 而codis因其有中心節點、基於proxy的設計,對client來說可以像對單機redis一樣去操作proxy(除了一些命令不支持),還可以繼續使用pipeline並且如果後臺redis有多個的話速度會顯著快於單redis的pipeline。
  • codis使用zookeeper來作為輔助,這意味著單純對於redis集群來說需要額外的機器搭zk。

Codis介紹

Codis 是一個分佈式 Redis 解決方案, 對於上層的應用來說, 連接到 Codis Proxy 和連接原生的 Redis Server 沒有顯著區別 (不支持的命令列表), 上層應用可以像使用單機的 Redis 一樣使用, Codis 底層會處理請求的轉發, 不停機的數據遷移等工作, 所有後邊的一切事情, 對於前面的客戶端來說是透明的, 可以簡單的認為後邊連接的是一個內存無限大的 Redis 服務。
image.png

Codis 3.x 由以下組件組成:
  1. Codis Server:基於 redis-3.2.8 分支開發。增加了額外的數據結構,以支持 slot 有關的操作以及數據遷移指令。具體的修改可以參考文檔 redis 的修改
  2. Codis Proxy:客戶端連接的 Redis 代理服務, 實現了 Redis 協議。 除部分命令不支持以外(不支持的命令列表),表現的和原生的 Redis 沒有區別(就像 Twemproxy)。

    • 對於同一個業務集群而言,可以同時部署多個 codis-proxy 實例;
    • 不同 codis-proxy 之間由 codis-dashboard 保證狀態同步。
  3. Codis Dashboard:集群管理工具,支持 codis-proxy、codis-server 的添加、刪除,以及據遷移等操作。在集群狀態發生改變時,codis-dashboard 維護集群下所有 codis-proxy 的狀態的一致性。

    • 對於同一個業務集群而言,同一個時刻 codis-dashboard 只能有 0個或者1個;
    • 所有對集群的修改都必須通過 codis-dashboard 完成。
  4. Codis Admin:集群管理的命令行工具。

    • 可用於控制 codis-proxy、codis-dashboard 狀態以及訪問外部存儲。
  5. Codis FE:集群管理界面。

    • 多個集群實例共享可以共享同一個前端展示頁面;
    • 通過配置文件管理後端 codis-dashboard 列表,配置文件可自動更新。
  6. Storage:為集群狀態提供外部存儲。

    • 提供 Namespace 概念,不同集群的會按照不同 product name 進行組織;
    • 目前僅提供了 Zookeeper、Etcd、Fs 三種實現,但是提供了抽象的 interface 可自行擴展。
Codis 分片原理

在Codis中,Codis會把所有的key分成1024個槽,這1024個槽對應著的就是Redis的集群,這個在Codis中是會在內存中維護著這1024個槽與Redis實例的映射關係。這個槽是可以配置,可以設置成 2048 或者是4096個。看你的Redis的節點數量有多少,偏多的話,可以設置槽多一些。
Codis中key的分配算法,先是把key進行CRC32 後,得到一個32位的數字,然後再hash%1024後得到一個餘數,這個值就是這個key對應著的槽,這槽後面對應著的就是redis的實例。

CRC32:CRC本身是“冗餘校驗碼”的意思,CRC32則表示會產生一個32bit(8位十六進制數)的校驗值。由於CRC32產生校驗值時源數據塊的每一個bit(位)都參與了計算,所以數據塊中即使只有一位發生了變化,也會得到不同的CRC32值。

Codis之間的槽位同步

Codis把槽位信息同步的工作交給了ZooKeeper來管理,當Codis的Codis Dashbord 改變槽位的信息的時候,其他的Codis節點會監聽到ZooKeeper的槽位變化,會及時同步過來。如圖:
image.png

Codis中的擴容

因為Codis是一個代理中間件,所以這個當需要擴容Redis實例的時候,可以直接增加redis節點。在槽位分配的時候,可以手動指定Codis Dashbord來為新增的節點來分配特定的槽位。
在Codis中實現了自定義的掃描指令SLOTSSCAN,可以掃描指定的slot下的所有的key,將這些key遷移到新的Redis的節點中(話外語:這個是Codis定製化的其中一個好處)。
首先,在遷移的時候,會在原來的Redis節點和新的Redis裡都保存著遷移的槽位信息,在遷移的過程中,如果有key打進將要遷移或者正在遷移的舊槽位的時候,這個時候Codis的處理機制是,先是將這個key強制遷移到新的Redis節點中,然後再告訴Codis,下次如果有新的key的打在這個槽位中的話,那麼轉發到新的節點。

自動均衡策略

面對著上面講的遷移策略,如果有成千上萬個節點新增進來,都需要我們手動去遷移嗎?那豈不是得累死啊。當然,Codis也是考慮到了這一點,所以提供了自動均衡策略。自動均衡策略是這樣的,Codis 會在機器空閒的時候,觀察Redis中的實例對應著的slot數,如果不平衡的話就會自動進行遷移。

Codis安裝

依賴環境準備

下面這兩個依賴環境的搭建這裡不再介紹。

  • 安裝Zookeeper
  • 安裝Go
1. 下載Codis安裝包

官方的發佈頁找到最新的發佈版本下載最新版3.2.2,如需要編譯安裝請參考官方的編譯安裝說明
下載安裝包之後解壓並重命名目錄,進入解壓目錄創建config文件夾用來放置配置文件地址,創建logs文件夾將來放日誌文件。

2.安裝codis-dashboard(集群管理工具)

config文件夾中創建配置文件dashboard.toml(這些配置文件模板在源碼config目錄中可以找到),文件內容:

##################################################
#                                                #
#                  Codis-Dashboard               #
#                                                #
##################################################

# Set Coordinator, only accept "zookeeper" & "etcd" & "filesystem".
# for zookeeper/etcd, coorinator_auth accept "user:password" 
# Quick Start
#coordinator_name = "filesystem"
#coordinator_addr = "/tmp/codis"
coordinator_name = "zookeeper"
coordinator_addr = "zk1:2181,zk2:2182,zk3:2183"
coordinator_auth = ""

# Set Codis Product Name/Auth.
product_name = "codis-demo"
product_auth = ""

# Set bind address for admin(rpc), tcp only.
admin_addr = "0.0.0.0:18080"

# Set arguments for data migration (only accept 'sync' & 'semi-async').
migration_method = "semi-async"
migration_parallel_slots = 100
migration_async_maxbulks = 200
migration_async_maxbytes = "32mb"
migration_async_numkeys = 500
migration_timeout = "30s"

# Set configs for redis sentinel.
sentinel_client_timeout = "10s"
sentinel_quorum = 2
sentinel_parallel_syncs = 1
sentinel_down_after = "30s"
sentinel_failover_timeout = "5m"
sentinel_notification_script = ""
sentinel_client_reconfig_script = ""

為了方便管理創建啟動腳本start-dashboard.sh,腳本內容:

#!/bin/sh
#set -x
nohup  ./codis-dashboard --ncpu=4 --config=./config/dashboard.toml --log=./logs/dashboard.log --log-level=WARN &>/dev/null &

執行啟動腳本啟動dashboard,然後瀏覽器訪問18080端口已經可以返回當前集群信息。

啟動參數說明:

$ ./codis-dashboard -h
Usage:
    codis-dashboard [--ncpu=N] [--config=CONF] [--log=FILE] [--log-level=LEVEL] [--host-admin=ADDR]
    codis-dashboard  --default-config
    codis-dashboard  --version

Options:
    --ncpu=N                    最大使用 CPU 個數
    -c CONF, --config=CONF      指定啟動配置文件
    -l FILE, --log=FILE         設置 log 輸出文件
    --log-level=LEVEL           設置 log 輸出等級:INFO,WARN,DEBUG,ERROR;默認INFO,推薦WARN

默認配置文件:

$ ./codis-dashboard --default-config | tee dashboard.toml
##################################################
#                                                #
#                  Codis-Dashboard               #
#                                                #
##################################################

# Set Coordinator, only accept "zookeeper" & "etcd"
coordinator_name = "zookeeper"
coordinator_addr = "127.0.0.1:2181"

# Set Codis Product {Name/Auth}.
product_name = "codis-demo"
product_auth = ""

# Set bind address for admin(rpc), tcp only.
admin_addr = "0.0.0.0:18080"

| 參數 | 說明 |
| - | - |
| coordinator_name | 外部存儲類型,接受 zookeeper/etcd |
| coordinator_addr | 外部存儲地址 |
| product_name | 集群名稱,滿足正則 w[w.-]* |
| product_auth | 集群密碼,默認為空 |
| admin_addr | RESTful API 端口 |

3.安裝codis-proxy(客戶端連接的 Redis 代理服務)

config文件夾中創建配置文件proxy.toml,文件內容:

##################################################
#                                                #
#                  Codis-Proxy                   #
#                                                #
##################################################

# Set Codis Product Name/Auth.
product_name = "codis-demo"
product_auth = ""

# Set auth for client session
#   1. product_auth is used for auth validation among codis-dashboard,
#      codis-proxy and codis-server.
#   2. session_auth is different from product_auth, it requires clients
#      to issue AUTH <PASSWORD> before processing any other commands.
session_auth = ""

# Set bind address for admin(rpc), tcp only.
admin_addr = "0.0.0.0:11080"

# Set bind address for proxy, proto_type can be "tcp", "tcp4", "tcp6", "unix" or "unixpacket".
proto_type = "tcp4"
proxy_addr = "0.0.0.0:19000"

# Set jodis address & session timeout
#   1. jodis_name is short for jodis_coordinator_name, only accept "zookeeper" & "etcd".
#   2. jodis_addr is short for jodis_coordinator_addr
#   3. jodis_auth is short for jodis_coordinator_auth, for zookeeper/etcd, "user:password" is accepted.
#   4. proxy will be registered as node:
#        if jodis_compatible = true (not suggested):
#          /zk/codis/db_{PRODUCT_NAME}/proxy-{HASHID} (compatible with Codis2.0)
#        or else
#          /jodis/{PRODUCT_NAME}/proxy-{HASHID}
jodis_name = "zookeeper"
jodis_addr = "zk1:2181,zk2:2182,zk3:2183"
jodis_auth = ""
jodis_timeout = "20s"
jodis_compatible = false

# Set datacenter of proxy.
proxy_datacenter = ""

# Set max number of alive sessions.
proxy_max_clients = 1000

# Set max offheap memory size. (0 to disable)
proxy_max_offheap_size = "1024mb"

# Set heap placeholder to reduce GC frequency.
proxy_heap_placeholder = "256mb"

# Proxy will ping backend redis (and clear 'MASTERDOWN' state) in a predefined interval. (0 to disable)
backend_ping_period = "5s"

# Set backend recv buffer size & timeout.
backend_recv_bufsize = "128kb"
backend_recv_timeout = "30s"

# Set backend send buffer & timeout.
backend_send_bufsize = "128kb"
backend_send_timeout = "30s"

# Set backend pipeline buffer size.
backend_max_pipeline = 20480

# Set backend never read replica groups, default is false
backend_primary_only = false

# Set backend parallel connections per server
backend_primary_parallel = 1
backend_replica_parallel = 1

# Set backend tcp keepalive period. (0 to disable)
backend_keepalive_period = "75s"

# Set number of databases of backend.
backend_number_databases = 16

# If there is no request from client for a long time, the connection will be closed. (0 to disable)
# Set session recv buffer size & timeout.
session_recv_bufsize = "128kb"
session_recv_timeout = "30m"

# Set session send buffer size & timeout.
session_send_bufsize = "64kb"
session_send_timeout = "30s"

# Make sure this is higher than the max number of requests for each pipeline request, or your client may be blocked.
# Set session pipeline buffer size.
session_max_pipeline = 10000

# Set session tcp keepalive period. (0 to disable)
session_keepalive_period = "75s"

# Set session to be sensitive to failures. Default is false, instead of closing socket, proxy will send an error response to client.
session_break_on_failure = false

# Set metrics server (such as http://localhost:28000), proxy will report json formatted metrics to specified server in a predefined period.
metrics_report_server = ""
metrics_report_period = "1s"

# Set influxdb server (such as http://localhost:8086), proxy will report metrics to influxdb.
metrics_report_influxdb_server = ""
metrics_report_influxdb_period = "1s"
metrics_report_influxdb_username = ""
metrics_report_influxdb_password = ""
metrics_report_influxdb_database = ""

# Set statsd server (such as localhost:8125), proxy will report metrics to statsd.
metrics_report_statsd_server = ""
metrics_report_statsd_period = "1s"
metrics_report_statsd_prefix = ""

編寫啟動腳本start-proxy.sh,文件內容:

#!/bin/sh
#set -x
nohup ./codis-proxy --ncpu=4 --config=./config/proxy.toml  --log=./logs/proxy.log --log-level=WARN &>/dev/null &

執行啟動腳本,然後訪問11080端口可以查看代理信息。

codis-proxy 啟動後,處於 waiting 狀態,監聽 proxy_addr 地址,但是不會 accept 連接,添加到集群並完成集群狀態的同步,才能改變狀態為 online。添加的方法有以下兩種:

  • 通過 codis-fe 添加:通過 Add Proxy 按鈕,將 admin_addr 加入到集群中;
  • 通過 codis-admin 命令行工具添加,方法如下:
    $ ./codis-admin --dashboard=127.0.0.1:18080 --create-proxy -x 127.0.0.1:11080

其中 127.0.0.1:18080 以及 127.0.0.1:11080 分別為 dashboard 和 proxy 的 admin_addr地址;

添加過程中,dashboard 會完成如下一系列動作:

  • 獲取 proxy 信息,對集群 name 以及 auth 進行驗證,並將其信息寫入到外部存儲中;
  • 同步 slots 狀態;
  • 標記 proxy 狀態為 online,此後 proxy 開始 accept 連接並開始提供服務;

啟動參數說明:

$ ./codis-proxy -h
Usage:
    codis-proxy [--ncpu=N] [--config=CONF] [--log=FILE] [--log-level=LEVEL] [--host-admin=ADDR] [--host-proxy=ADDR] [--ulimit=NLIMIT]
    codis-proxy  --default-config
    codis-proxy  --version

Options:
    --ncpu=N                    最大使用 CPU 個數
    -c CONF, --config=CONF      指定啟動配置文件
    -l FILE, --log=FILE         設置 log 輸出文件
    --log-level=LEVEL           設置 log 輸出等級:INFO,WARN,DEBUG,ERROR;默認INFO,推薦WARN
    --ulimit=NLIMIT             檢查 ulimit -n 的結果,確保運行時最大文件描述不少於 NLIMIT

默認配置文件:

$ ./codis-proxy --default-config | tee proxy.toml
##################################################
#                                                #
#                  Codis-Proxy                   #
#                                                #
##################################################

# Set Codis Product {Name/Auth}.
product_name = "codis-demo"
product_auth = ""

# Set bind address for admin(rpc), tcp only.
admin_addr = "0.0.0.0:11080"

# Set bind address for proxy, proto_type can be "tcp", "tcp4", "tcp6", "unix" or "unixpacket".
proto_type = "tcp4"
proxy_addr = "0.0.0.0:19000"

# Set jodis address & session timeout.
jodis_addr = ""
jodis_timeout = 10
jodis_compatible = false

# Proxy will ping-pong backend redis periodly to keep-alive
backend_ping_period = 5

# If there is no request from client for a long time, the connection will be droped. Set 0 to disable.
session_max_timeout = 1800

# Buffer size for each client connection.
session_max_bufsize = 131072

# Number of buffered requests for each client connection.
# Make sure this is higher than the max number of requests for each pipeline request, or your client may be blocked.
session_max_pipeline = 1024

# Set period between keep alives. Set 0 to disable.
session_keepalive_period = 60

| 參數 | 說明 |
| - | - |
| product_name | 集群名稱,參考 dashboard 參數說明|
| product_auth | 集群密碼,默認為空 |
| admin_addr | RESTful API 端口 |
| proto_type | Redis 端口類型,接受 tcp/tcp4/tcp6/unix/unixpacket |
| proxy_addr | Redis 端口地址或者路徑 |
| jodis_addr | Jodis 註冊 zookeeper 地址 |
| jodis_timeout | Jodis 註冊 session timeout 時間,單位 second |
| jodis_compatible | Jodis 註冊 zookeeper 的路徑 |
| backend_ping_period | 與 codis-server 探活週期,單位 second,0 表示禁止 |
| session_max_timeout | 與 client 連接最大讀超時,單位 second,0 表示禁止 |
| session_max_bufsize | 與 client 連接讀寫緩衝區大小,單位 byte |
| session_max_pipeline | 與 client 連接最大的 pipeline 大小 |
| session_keepalive_period | 與 client 的 tcp keepalive 週期,僅 tcp 有效,0 表示禁止 |

注:Codis3 會將 jodis 節點註冊在 /jodis/{PRODUCT_NAME} 下,這點與 Codis2 不太兼容,所以為了兼容性,可以考慮將 jodis_compatible 設置成 true。

4.安裝codis-server(優化版的Redis)

codis-server的配置文件和redis一樣,其本身就是在redis上改進而來。
創建配置文件config/codis-server/7001/redis.conf,文件內容:

bind 127.0.0.1
port 7001

daemonize no

pidfile /var/run/codis_7001.pid

logfile "./logs/codis-server/7001/redis.log"

save 900 1
save 300 10
save 60 10000

dir ./config/codis-server/7001
  
appendonly yes
appendfsync always

創建啟動腳本start-server-7001.sh ,腳本內容:

#!/bin/sh
#set -x
nohup ./codis-server ./config/codis-server/7001/redis.conf &>/dev/null &

同樣的方法可以配置多個codis-server。

啟動完成後,可以通過 codis-fe 提供的界面或者 codis-admin 命令行工具添加到集群中。

5.啟動codis-fe(集群管理界面)

這是個可選組件,也可以通過codis-admin命令行工具來管理集群。
編寫啟動腳本start-fe.sh,腳本內容:

#!/bin/sh
#set -x
nohup ./codis-fe --ncpu=1 --log=./logs/fe.log --log-level=WARN --zookeeper=zk1:2181,zk2:2182,zk3:2183 --listen=0.0.0.0:8090 &>/dev/null &

執行啟動腳本,然後訪問8090端口即可看到集群管理界面。

啟動參數說明:

$ ./codis-fe -h
Usage:
    codis-fe [--ncpu=N] [--log=FILE] [--log-level=LEVEL] [--assets-dir=PATH](--dashboard-list=FILE|--zookeeper=ADDR|--etcd=ADDR|--filesystem=ROOT) --listen=ADDR
    codis-fe  --version

Options:
    --ncpu=N                        最大使用 CPU 個數
    -d LIST, --dashboard-list=LIST  配置文件,能夠自動刷新
    -l FILE, --log=FILE             設置 log 輸出文件
    --log-level=LEVEL               設置 log 輸出等級:INFO,WARN,DEBUG,ERROR;默認INFO,推薦WARN
    --listen=ADDR                   HTTP 服務端口

配置文件 codis.json 可以手動編輯,也可以通過 codis-admin 從外部存儲中拉取,例如:

$ ./codis-admin --dashboard-list --zookeeper=127.0.0.1:2181 | tee codis.json
[
    {
        "name": "codis-demo",
        "dashboard": "127.0.0.1:18080"
    },
    {
        "name": "codis-demo2",
        "dashboard": "127.0.0.1:28080"
    }
]
6.使用codis-admin(集群管理的命令行工具)

執行./codis-admin -h可以查看可執行的命令及參數。

  • dashboard服務和proxy服務的停用

    ./codis-admin  --proxy=127.0.0.1:11080 --shutdown
    ./codis-admin --dashboard=127.0.0.1:18080 --shutdown
  • codis-dashboard 異常退出的修復
    當 codis-dashboard 啟動時,會在外部存儲上存放一條數據,用於存儲 dashboard 信息,同時作為 LOCK 存在。當 codis-dashboard 安全退出時,會主動刪除該數據。當 codis-dashboard 異常退出時,由於之前 LOCK 未安全刪除,重啟往往會失敗。因此 codis-admin 提供了強制刪除工具:

    1. 確認 codis-dashboard 進程已經退出(很重要);
    2. 運行 codis-admin 刪除 LOCK:
    ./codis-admin --remove-lock --product=codis-demo --zookeeper=zk1:2181,zk2:2182,zk3:2183
  • codis-proxy 異常退出的修復
    通常 codis-proxy 都是通過 codis-dashboard 進行移除,移除過程中 codis-dashboard 為了安全會向 codis-proxy 發送 offline 指令,成功後才會將 proxy 信息從外部存儲中移除。如果 codis-proxy 異常退出,該操作會失敗。此時可以使用 codis-admin 工具進行移除:

    1. 確認 codis-proxy 進程已經退出(很重要);
    2. 運行 codis-admin 刪除 proxy:
    $ ./codis-admin --dashboard=127.0.0.1:18080 --remove-proxy --addr=127.0.0.1:11080 --force

    選項 --force 表示,無論 offline 操作是否成功,都從外部存儲中將該節點刪除。所以操作前,一定要確認該 codis-proxy 進程已經退出。

7.配置codis-ha

創建啟動腳本start-ha.sh,腳本內容:

#!/bin/sh
#set -x
nohup ./codis-ha --log=./logs/ha.log --log-level=WARN --dashboard=127.0.0.1:18080 &>/dev/null &

因為 codis-proxy 是無狀態的,可以比較容易的搭多個實例,達到高可用性和橫向擴展。

對 Java 用戶來說,可以使用基於 Jedis 的實現 Jodis ,來實現 proxy 層的 HA:

  • 它會通過監控 zookeeper 上的註冊信息來實時獲得當前可用的 proxy 列表,既可以保證高可用性;
  • 也可以通過輪流請求所有的proxy實現負載均衡。

如果需要異步請求,可以使用基於Netty開發的 Nedis

對下層的 redis 實例來說,當一個 group 的 master 掛掉的時候,應該讓管理員清楚,並手動的操作,因為這涉及到了數據一致性等問題(redis的主從同步是最終一致性的)。因此 codis 不會自動的將某個 slave 升級成 master
關於外部 codis-ha 工具,這是一個通過 codis-dashboard 開放的 RESTful API 實現自動切換主從的工具。該工具會在檢測到 master 掛掉的時候主動應用主從切換策略,提升單個 slave 成為新的 master。

需要注意,codis 將其中一個 slave 升級為 master 時,該組內其他 slave 實例是不會自動改變狀態的,這些 slave 仍將試圖從舊的 master 上同步數據,因而會導致組內新的 master 和其他 slave 之間的數據不一致。因此當出現主從切換時,需要管理員手動創建新的 sync action 來完成新 master 與 slave 之間的數據同步(codis-ha 不提供自動操作的工具,因為這樣太不安全了)。

Codis使用

訪問http://IP:8090打開集群管理,左側的菜單顯示了codis集群列表。

添加組和sever

image.png

可以看到添加的三個codis-sever中7002是master,7001和7003是slave,這裡的主從關係不用在配置文件中來配置主從關係,codis會自己配置主從,可以使用redis客戶端做個測試:

$ ./redis-cli -c -p 7002
127.0.0.1:7002> set k1 v1
OK
127.0.0.1:7002> set k2 v2
OK
127.0.0.1:7002> exit

$ ./redis-cli -c -p 7001
127.0.0.1:7001> get k1
"v1"
127.0.0.1:7001> get k2
"v2"
127.0.0.1:7001> set k3 v3
# (error) READONLY You can't write against a read only slave.
127.0.0.1:7001> exit

$ ./redis-cli -c -p 7003
127.0.0.1:7003> get k1
"v1"
127.0.0.1:7003> get k2
"v2"
127.0.0.1:7003> set k3 v3
# (error) READONLY You can't write against a read only slave.
127.0.0.1:7003> exit

可以看到只能在master寫入,寫入的數據也同步到了slave上。此時去看7003和7001的配置文件發現codis自動在配置文件上加了slaveof 127.0.0.1 7002,假設此時master掛掉了
image.png
可以看到codis-ha自動將7003當成master,然後將7001和7002都停止了(進程不在),好危險,因為redis是最終一致性,此時的數據一致性也無法保證,而且將其他節點直接停止,此時的7003壓力將非常大。此時再將7001和7002重啟,然後再次加入,發現codis會將這兩個實例再次停止,原因很簡單,因為原來7002是master,7001是7002的slave,配置並沒有變,不允許有兩個master,而且當前7003是master,因此需要手動更改配置文件才能加入這個組。

假設現在沒有codis-ha,master 7003掛掉了會怎麼樣。

image.png
可以看到,並沒有將其他節點當成master的操作,此時重啟7003就可以恢復當前組的集群狀態,也不需要手動更改配置文件和手動加入組。

添加proxy

image.png

設置Slots

image.png

使用redis客戶端連接proxy測試:

$ ./redis-cli -c -p 19000
127.0.0.1:19000> get k1
"v1"
127.0.0.1:19000> set k001 v001
OK
127.0.0.1:19000> get k001
"v001"
127.0.0.1:19000> exit

此時假設master 7003掛了(沒用codis-ha)會怎麼樣?

$ ./redis-cli -c -p 19000
127.0.0.1:19000> get k1
(error) ERR handle response, backend conn reset
127.0.0.1:19000> set k002 v002
(error) ERR handle response, backend conn reset
127.0.0.1:19000> exit

可以看到變成了既不可讀也不可寫了,這時啟動codis-ha繼續測試

image.png

codis-ha選擇了7001作為master,使用redis客戶端連接proxy測試:

$ ./redis-cli -c -p 19000
127.0.0.1:19000> get k1
"v1"
127.0.0.1:19000> get k001
"v001"
127.0.0.1:19000> set k0002 v0002
OK
127.0.0.1:19000> get k0002
"v0002"
127.0.0.1:19000> exit

可以看到當前是可以讀也可以寫的。

【轉載請註明出處】:https://www.jianshu.com/p/41f97c494fc4

Leave a Reply

Your email address will not be published. Required fields are marked *