目前公司的業務是php語言開發,使用的是laravel開源架構。laravel架構使用predis連接配接redis,但是目前predis不支援主從式redis叢集,這點很坑。
看了看目前網上的解決方案,國内豌豆莢開源的codis很不錯。這篇文章,我們主要介紹codis叢集的安裝。
codis是一個分布式redis叢集解決方案,對于上層的應用來說, 連接配接到codis-proxy和連接配接原生的redis-server沒有明顯的差別。
上層應用可以像使用單機的redis一樣使用,codis底層會處理請求的轉發,不停機的資料遷移等工作。所有後邊的一切事情,對于前面的用戶端來說是透明的,可以簡單的認為後邊連接配接的是一個記憶體無限大的redis服務。
codis由四部分組成:
codis proxy(codis-proxy)
codis dashboard(codis-config)
codis redis(codis-server)
zookeeper/etcd
codis-proxy是用戶端連接配接的redis代理服務,codis-proxy本身實作了redis協定,表現得和一個原生的redis沒什麼差別(就像twemproxy),對于一個業務來說,可以部署多個codis-proxy,codis-proxy本身是無狀态的。
codis-config是codis的管理工具,支援包括:添加/删除redis節點,添加/删除proxy節點,發起資料遷移等操作。
codis-config本身還自帶了一個http-server,會啟動一個dashboard,使用者可以直接在浏覽器上觀察codis叢集的運作狀态。
codis-server是codis項目維護的一個redis分支,基于redis2.8.21開發,加入了slot的支援和原子的資料遷移指令。codis上層的codis-proxy和codis-config隻能和這個版本的redis互動才能正常運作。
codis依賴zookeeper來存放資料路由表和codis-proxy節點的元資訊,codis-config發起的指令都會通過zookeeper同步到各個存活的codis-proxy。
codis支援按照namespace區分不同的産品,擁有不同的product name的産品,各項配置都不會沖突。
codis架構圖如下:

<b>二、環境準備</b>
除了zookeeper叢集之外,我們還需要安裝go語言環境,因為codis是基于go語言開發的。
安裝基礎依賴,使用如下指令:
yum install -y git gcc make g++ gcc-c++ automake openssl-devel zlib-*
基礎依賴安裝往後,我們現在開始配置go語言環境。
codis是基于go語言開發的,是以我們要在所有伺服器上都配置go語言環境。
下載下傳go語言包,如下:
下載下傳完畢後,解壓到/usr/local,如下:
tar -c /usr/local -xf go1.4.2.linux-amd64.tar.gz
把go加入到系統的環境變量,如下:
vim /etc/profile
export path=$path:/usr/local/go/bin
export gopath=/usr/local/
讓剛剛加入的環境變量生效,并檢視go是否配置成功,如下:
source /etc/profile
env
go version
通過上圖,我們可以很明顯的看出go語言環境配置成功。
codis的安裝,我們可以通過三種不同的方式進行:通過go下載下傳安裝、通過git方式和通過源碼方式,下面分别介紹下。
按照官方github介紹,codis首選方式是通過go下載下傳安裝的。指令如下:
go get -u -d github.com/codislabs/codis
注意:由于衆所周知的原因,這一步比較慢,需要耐心等待。
上一步執行完畢後,我們切換到/usr/local/src/github.com/codislabs/codis目錄下,進行編譯,如下:
cd /usr/local/src/github.com/codislabs/codis
make
注意:這一步也比較慢,需要耐心等待。
make編譯執行完畢後後,會在bin目錄下生成codis-config、codis-proxy、codis-server三個可執行檔案以及assets目錄。
其中assets是codis-config的dashboard http 服務需要的前端資源,需要和codis-config放置在同一目錄下。如下:
ll bin/
編譯完畢後,我們現在來測試編譯的結果,使用如下指令:
make gotest
通過上圖,我們可以看到codis已經安裝成功。
上一章節,我們介紹了codis通過go下載下傳安裝的方法,這一章節,我們通過git方式進行安裝。
首先下載下傳codis最新的git倉庫,使用如下指令:
git倉庫下載下傳完畢後,我們接下來進行如下的操作。如下:
mkdir -p /usr/local/src/github.com/codislabs/
cp -r codis /usr/local/src/github.com/codislabs/
cd /usr/local/src/github.com/codislabs/codis/
以上操作完畢後,就和通過go下載下傳安裝方式一樣了。執行make指令進行編譯,然後執行make gotest指令進行測試。
下面我們來介紹下,通過源碼方式的安裝。下載下傳codis源碼檔案,如下:
wget https://github.com/codislabs/codis/archive/3.0.3.tar.gz
解壓源碼包,如下:
tar -xf 3.0.3.tar.gz
cd codis-3.0.3/
然後進行編譯,使用make指令,如下:
這一步很慢,需要下載下傳各種依賴,然後是make gotest。
基本和go下載下傳安裝方式一樣,不過需要說明下,我通過源碼方式一直沒有安裝成功。報如下錯誤:
是以以上三種安裝的方法,建議使用第一、二種,盡管有點慢。
codis安裝完畢後,我們現在來配置codis叢集。在正式配置叢集之前,先建立相關的目錄,然後複制相關檔案到新的目錄下。使用如下指令:
mkdir -p /usr/local/codis/{log,redis_conf}
cp -rf bin/ /usr/local/codis/
cp config.ini /usr/local/codis/
cp extern/redis-test/conf/6379.conf /usr/local/codis/redis_conf/20189.conf
cp extern/redis-test/conf/6380.conf /usr/local/codis/redis_conf/20190.conf
上述操作完畢後,我們來修改codis的配置檔案config.ini,在此我們隻需要修改相關的選項即可。如下:
vim /usr/local/codis/config.ini
coordinator=zookeeper
<b>zk=192.168.1.9:2181,192.168.1.124:2181,192.168.1.231:2181</b>
product=test
<b>dashboard_addr=192.168.1.9:18087</b>
password=
backend_ping_period=5
session_max_timeout=1800
session_max_bufsize=131072
session_max_pipeline=1024
zk_session_timeout=30000
<b>proxy_id=proxy_9</b>
該配置檔案中,我們需要注意三個參數:zk、dashboard_addr、proxy_id。
其中zk是表示zookeeper叢集的伺服器ip位址,dashboard_addr表示codis web管理的ip位址及端口,proxy_id表示codis的id,注意每台codis伺服器該值要<b>唯一</b>。
另外兩台伺服器的codis配置檔案,内容如下:
到此codis配置檔案修改完畢。
codis配置檔案修改完畢後,我們現在來修改redis配置檔案。
每台codis伺服器上,我們要啟動兩個redis執行個體(也可以啟動多個redis執行個體),是以我們要配置兩個redis。如下:
vim /usr/local/codis/redis_conf/20189.conf
daemonize no
pidfile /var/run/redis20189.pid
port 20189
tcp-backlog 511
timeout 0
tcp-keepalive 0
loglevel notice
logfile /var/log/redis/20189.log
databases 16
stop-writes-on-bgsave-error yes
rdbcompression yes
rdbchecksum yes
dbfilename dump20189.rdb
dir /usr/local/codis/
slave-serve-stale-data yes
slave-read-only yes
repl-disable-tcp-nodelay no
slave-priority 100
appendonly no
appendfilename “appendonly.aof”
appendfsync everysec
no-appendfsync-on-rewrite no
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
lua-time-limit 5000
slowlog-log-slower-than 10000
slowlog-max-len 128
latency-monitor-threshold 0
notify-keyspace-events “”
hash-max-ziplist-entries 512
hash-max-ziplist-value 64
list-max-ziplist-entries 512
list-max-ziplist-value 64
set-max-intset-entries 512
zset-max-ziplist-entries 128
zset-max-ziplist-value 64
hll-sparse-max-bytes 3000
activerehashing yes
client-output-buffer-limit normal 0 0 0
client-output-buffer-limit slave 256mb 64mb 60
client-output-buffer-limit pubsub 32mb 8mb 60
hz 10
aof-rewrite-incremental-fsync yes
配置第二個redis執行個體,如下:
yes|cp /usr/local/codis/redis_conf/20189.conf /usr/local/codis/redis_conf/20190.conf
sed -i ‘s/20189/20190/g’ /usr/local/codis/redis_conf/20190.conf
mkdir -p /var/log/redis/
每台codis伺服器上redis配置完畢後,我們來啟動redis執行個體,如下:
/usr/local/codis/bin/codis-server /usr/local/codis/redis_conf/20189.conf &
/usr/local/codis/bin/codis-server /usr/local/codis/redis_conf/20190.conf &
ps -ef |grep codis-server
注意:我們每台codis伺服器上的redis執行個體都需要啟動。
redis執行個體全部啟動後,我們現在來啟動codis的dashboard。
<b>注意,我們一定要在192.168.1.9這台codis伺服器上啟動該指令,這是因為我們在4.1章節codis配置檔案中配置的dashboard位址就是192.168.1.9。</b>
使用如下指令啟動dashboard:
nohup /usr/local/codis/bin/codis-config -c config.ini dashboard &
netstat -tunlp
codis dashboard通路端口是18087,如下:
<a href="http://codis.ilanni.com:18087/admin/">http://codis.ilanni.com:18087/admin/</a>
通過上圖,我們可以很明顯的看出codis dashboard已經正常啟動。
codis dashboard已經正常啟動後,我們現在來創添加redis server分組。如下:
這樣我們第一個redis server分組就建立完畢了,可以使用同樣的方法建立第二、三個分組。如下:
這樣三組redis server分組就建立完畢了。
當然我們也可以通過指令進行建立,如下:
/usr/local/codis/bin/codis-config server add-group 1
/usr/local/codis/bin/codis-config server add-group 2
/usr/local/codis/bin/codis-config server add-group 3
/usr/local/codis/bin/codis-config server list
<b>注意:上述指令在三台codis伺服器上任意一台上執行。</b>
到此codis的redis server分組添加完畢。
redis server分組添加完畢後,我們現在來為每一個分組添加redis執行個體。先為group_1添加redis執行個體,如下:
這個裡面可以填寫任何一個codis伺服器上的redis執行個體(哪怕不是codis伺服器的redis執行個體),在此我們填寫的是192.168.1.9這台伺服器上的redis執行個體。
可以看到group_1組的第一個redis執行個體,被自動配置為master類型。
現在我們來添加第二個redis執行個體,如下:
通過上圖,我們可以很明顯的看到第二個添加的redis執行個體被預設配置為slave類型。
<b>注意:redis官方的支援的叢集也是master-slave主從式的叢集。</b>
group_2組和group_3組的redis執行個體添加和上面的操作一樣,如下:
這樣三組redis執行個體就全部添加完畢。
當然我們也可以通過指令進行添加,如下:
/usr/local/codis/bin/codis-config server add <b>1</b> 192.168.1.9:20189 <b>master</b>
/usr/local/codis/bin/codis-config server add 1 192.168.1.9:20190 slave
/usr/local/codis/bin/codis-config server add <b>2</b> 192.168.1.124:20189 <b>master</b>
/usr/local/codis/bin/codis-config server add 2 192.168.1.124:20190 slave
/usr/local/codis/bin/codis-config server add <b>3</b> 192.168.1.231:20189 <b>master</b>
/usr/local/codis/bin/codis-config server add 3 192.168.1.231:20190 slave
<b>注意:上述指令中的數字,表示的是哪一個分組,master/slave表示的是所屬類型。</b>
我們也可以通過指令檢視,各個redis server組的資訊,如下:
注意:每組添加的第一個redis執行個體不能被删除,因為codis預設把該redis執行個體設定為master。
到此redis server分組的redis執行個體添加完畢。
codis采用pre-sharding的技術來實作資料的分片,預設分成1024個slot(0-1023)。對于每個key來說,可以通過以下公式确定所屬的slot id:slotid=crc32(key)%1024。
每一個slot都會有一個且必須有一個特定的server group id來表示這個slot的資料由哪個server group來提供。
在配置設定slot之前,我們需要初始化slot。
在codis伺服器<b>任意一台</b>上執行bin/codis-config slot init指令,該指令會在zookeeper上建立slot相關資訊。如下:
cd /usr/local/codis
/usr/local/codis/bin/codis-config -c config.ini slot init
slot初始化完畢後,我們現在來配置設定slot範圍。如下:
上圖中的new group id是自定義的。
通過上圖,我們可以看到第一組slot配置設定成功。
現在來檢視slot配置設定資訊,如下:
通過上圖,我們可以很明顯的看出組1配置設定的slot是0-334,335以後還沒有配置設定。現在來配置設定剩下的slot,如下:
這樣slot已經全部配置設定完畢。
當然我們也可以通過指令進行配置設定,如下:
/usr/local/codis/bin/codis-config slot range-set 0 334 1 online
/usr/local/codis/bin/codis-config slot range-set 335 669 2 online
/usr/local/codis/bin/codis-config slot range-set 670 1023 3 online
檢視slot資訊,如下:
/usr/local/codis/bin/codis-config slot info 1
/usr/local/codis/bin/codis-config slot info 2
/usr/local/codis/bin/codis-config slot info 3
以上全部配置完畢後,我們來啟動codis-proxy,使用如下指令:
nohup /usr/local/codis/bin/codis-proxy -c /usr/local/codis/config.ini –log-level=error -l/usr/local/codis/log/proxy.log –cpu=8 –addr=0.0.0.0:19000 –http-addr=0.0.0.0:11000 &
下面對以上指令中的參數進行解釋:
-c 配置檔案位址。
-l 日志輸出檔案位址。
–log-level=<loglevel> 輸出日志級别(debug<info (default)<warn<error<fatal)。
–cpu=<cpu_num> proxy占用的cpu核數,預設1,最好設定為機器的實體cpu數的一半到2/3左右。
–addr=<proxy_listen_addr> proxy的redis server監聽的位址, 格式<ip or hostname>:<port>, 如: localhost:9000, :9001。
–http-addr=<debug_http_server_addr> proxy的調試資訊啟動的http server,可以通路 http://debug_http_server_addr/debug/vars。
codis-proxy啟動後,我們可以在dashboard上進行檢視,如下:
到此codis叢集就搭建完畢。
codis叢集搭建完畢後,現在我們來連接配接codis叢集。要連接配接codis叢集,我們隻需要連接配接codis-proxy即可。即連接配接4.7章節中的codis-proxy伺服器位址,然後加19000端口。使用redis-cli指令連接配接,如下:
redis-cli -h 192.168.1.9 -p 19000
info
通過上圖,我們可以很明顯的看到連接配接codis叢集是ok的。
我們現在對codis叢集做一些壓力測試,同時在dashboard上觀察鍵值對的情況。如下:
redis-benchmark -h 192.168.1.9 -p 19000 -c 10000 -d 100 -t set -n 100000 -r 100000
上述指令的意思是,使用redis-benchmark壓力測試指令連接配接codis叢集,同時并發10000個(-c),測試set操作(-t),每個測試資料集是100位元組(-d),請求數是100000(-n),使用使用随機數插入數值(-r)。
通過上圖,可以很明顯的看到codis叢集的性能還是很不錯的呢。
而我們laravel架構中redis的配置直接填寫codis-proxy的連接配接位址即可。如下:
在codis搭建和使用過程中,我們還是會碰到一些其他問題的,下面就稍微提下。
如果要kill的dashboard的話,強烈建議通過kill -15 pid來關閉。
如果是直接使用kill -9進行kill的話,可能會報zk節點不存在錯誤的話。
這樣的話,我們需要通過連接配接zookeeper叢集,删除相關節點,然後再進行操作。删除節點操作,操作如下:
cd /usr/local/zookeeper/
./bin/zkcli.sh -server 127.0.0.1:2181
ls /zk/codis/db_test
rmr /zk/codis/db_test
在這裡我們大緻介紹下,codis在與阿裡雲的slb(負載均衡)進行內建的線上生産環境的案例。
目前我們連接配接codis叢集是通過單個codis-proxy來進行的,如果這個節點挂了,就會出現了單點故障的危險。
是以我們這邊在codis叢集每台伺服器上,都啟動一個codis-proxy。然後前端使用slb進行統一接入,slb後端就有三台codis-proxy伺服器。
codis叢集的搭建和前面是一樣的,隻是在此我們的codis所在ecs(伺服器)隻有内網ip(省錢),而且slb我們是要的也是内網(省錢)。如下:
這樣的話,用戶端連接配接slb的位址就是連接配接codis整個叢集了,同時也避免了單點故障的問題。
<b>注意:阿裡雲slb後端的ecs,不能telnet通其前端的slb。</b>