Codis集群的搭建與使用
一、簡介
Codis是一個分布式的Redis解決方案,對於上層的應用來說,連接Codis Proxy和連接原生的Redis Server沒有明顯的區別(不支持的命令列表),上層應用可以像使用單機的Redis一樣使用,Codis底層會處理請求的轉發,不停機的數據遷移等工作,所有后邊的一切事情,對於前面客戶端來說是透明的,可以簡單的認為后邊連接是一個內存無限大的Redis服務。
Codis架構圖:
以上我們可以看到codis-proxy是單個節點的,因為我們可以通過結合keepalived來實現高可用:
codis-proxy 提供連接集群redis服務的入口
codis-redis-group 實現redis讀寫的水平擴展,高性能
codis-redis 實現redis實例服務,通過codis-ha實現服務的高可用
二、組件說明
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分支,基於2.8.13開發,加入了slot的支持和原子的數據遷移指令,Codis上層的codis-proxy和codis-config只能和這個版本的Redis交互才能正常運行。
ZooKeeper :用來存放數據路由表和codis-proxy節點的元信息,codis-config發起的命令都會通過ZooKeeper同步到各個存活的codis-proxy
說明:
Codis支持按照Namespace區分不同的產品,擁有不同的product name 的產品,各項配置都不會沖突。
實驗環境:
角色 ip地址 主機名 codis-redis-master 192.168.10.128 redis-master codis-redis-slave 192.168.10.129 redis-slave
zk,codis-proxy 192.168.10.130 codis-proxy
三、安裝配置(我這里只部署單個codis-proxy節點的環境),在codis-proxy服務器上操作:
1、安裝go:
下載安裝包:(貌似海外服務器才能下載)
wget https://storage.googleapis.com/golang/go1.4.1.linux-amd64.tar.gz
下載后,解壓就可以用了:
[root@codis-proxy ~]# tar -zxf go1.4.1.linux-amd64.tar.gz -C /usr/local/
2、安裝JDK,自己在官網下載一個安裝即可,版本要求不嚴格,后面的zookeeper會用到JDK:
[root@codis-proxy ~]# sh jdk-6u34-fcs-bin-b04-linux-amd64-19_jul_2012.bin
[root@codis-proxy ~]# mv jdk1.6.0_34 /usr/local/
[root@codis-proxy ~]# mv /usr/local/jdk1.6.0_34 /usr/local/jdk
3、安裝zookeeper
[root@codis-proxy ~]# wget http://mirrors.cnnic.cn/apache/zookeeper/zookeeper-3.4.6/zookeeper-3.4.6.tar.gz [root@codis-proxy ~]# tar -xzf zookeeper-3.4.6.tar.gz -C /usr/local/
然后修改環境變量為 vim /etc/profile 在最后添加以下內容:
export GOROOT=/usr/local/go export GOPATH=/usr/local/codis JAVA_HOME=/usr/local/jdk CLASS_PATH=$JAVA_HOME/lib:$JAVA_HOME/jre/lib export ZOOKEEPER_HOME=/usr/local/zookeeper-3.4.6 export PATH=$PATH:$GOROOT/bin:$JAVA_HOME/bin:$ZOOKEEPER_HOME/bin
然后執行 source /etc/profile
編輯一個文件來測試一下go是否正常使用了:
[root@codis-proxy ~]# cat hello.go package main import "fmt" func main(){ fmt.Printf("hello,world\n") } [root@codis-proxy ~]# go run hello.go hello,world
可以看到,正常打印hello,world了,在命令直接輸入java,如果出現一大堆使用說明,就意味java安裝成功了。
4、安裝codis,安裝codis時依賴go,所以在安裝codis先裝好go,上面已經裝好:
獲取codis:
[root@codis-proxy ~]# yum install -y git [root@codis-proxy ~]# go get github.com/wandoulabs/codis package github.com/wandoulabs/codis imports github.com/wandoulabs/codis imports github.com/wandoulabs/codis: no buildable Go source files in /usr/local/codis/src/github.com/wandoulabs/codis [root@codis-proxy ~]#
可以看到no buildable Go source files in /usr/local/codis/src/github.com/wandoulabs/codis,我們在上面環境就是設置GOAPTH=/usr/local/codis,所以只要執行上面的獲取命令,就會下載在/usr/local/codis下:
我們進到提示的路徑進行安裝,安裝過程比較久,耐心等待:
[root@codis-proxy ~]# cd /usr/local/codis/src/github.com/wandoulabs/codis [root@codis-proxy codis]# ls bootstrap.sh cmd doc docker Dockerfile extern Godeps Makefile MIT-LICENSE.txt pkg README.md sample test vitess_license [root@codis-proxy codis]# sh bootstrap.sh downloading dependcies, it may take a few minutes...
執行腳本完成看,會看到make gotest的結果:
Hint: To run 'make test' is a good idea ;) make[2]: Leaving directory `/usr/local/codis/src/github.com/wandoulabs/codis/extern/redis-2.8.13/src' make[1]: Leaving directory `/usr/local/codis/src/github.com/wandoulabs/codis/extern/redis-2.8.13' go test ./pkg/... ./cmd/... -race ? github.com/wandoulabs/codis/pkg/env [no test files] ok github.com/wandoulabs/codis/pkg/models 5.773s ok github.com/wandoulabs/codis/pkg/proxy/cachepool 0.009s ? github.com/wandoulabs/codis/pkg/proxy/group [no test files] ok github.com/wandoulabs/codis/pkg/proxy/parser 0.016s ? github.com/wandoulabs/codis/pkg/proxy/redisconn [no test files] ? github.com/wandoulabs/codis/pkg/proxy/redispool [no test files] ok github.com/wandoulabs/codis/pkg/proxy/router 16.092s ? github.com/wandoulabs/codis/pkg/proxy/router/topology [no test files] ok github.com/wandoulabs/codis/pkg/utils 0.008s ok github.com/wandoulabs/codis/cmd/cconfig 0.016s ? github.com/wandoulabs/codis/cmd/proxy [no test files] [root@codis-proxy codis]#
會在 codis/bin 文件夾生成 codis-config, codis-proxy 兩個可執行文件, (另外, bin/assets 文件夾是 codis-config 的 dashboard http 服務需要的前端資源, 需要和 codis-config 放置在同一文件夾下)
將編譯好后,把bin目錄和一些腳本復制過去/usr/local/codis目錄下:
[root@codis-proxy codis]# mkdir -p /usr/local/codis/{log,redis_conf} [root@codis-proxy codis]# cp -rf bin /usr/local/codis/ [root@codis-proxy codis]# cp sample/config.ini /usr/local/codis/bin/ [root@codis-proxy codis]# cp sample/redis_conf/6381.conf /usr/local/codis/redis_conf/ [root@codis-proxy codis]# cp -rf src/github.com/wandoulabs/codis/sample/* ./
到止,codis-proxy的環境幾乎搭建好了,下面進行配置:
5、進行配置
在codis-proxy上操作
配置zookeeper,修改配置文件:
[root@codis-proxy ~]# cp /usr/local/zookeeper-3.4.6/conf/zoo_sample.cfg /usr/local/zookeeper-3.4.6/conf/zoo.cfg [root@codis-proxy ~]# cd /usr/local/zookeeper-3.4.6/conf/ [root@codis-proxy conf]# vim zoo.cfg tickTime=2000 initLimit=10 syncLimit=5 dataDir=/data/zookeeper/data autopurge.snapRetainCount=500 autopurge.purgeInterval=24 clientPort=2181 #server.1=codis-1:2888:3888 #server.2=codis-2:2888:3888 #server.2=codis-3:2888:3888
設置myid:
設置myid在我們配置的dataDir指定的目錄下面,創建一個myid文件,里面內容為一個數字,用來標識當前主機,conf/zoo.cfg文件配置的srver.X中的X為什么數字,則myid文件就輸入這個數字,我只有一台zk,所以配置文件里可以不配置server.X,但還是要配置myid的,echo一個數字1進去即可。如果有多台zk,則分別在zk服務器上echo對應的數字進對應的myid文件
[root@codis-proxy conf]# mkdir -p /data/zookeeper/data [root@codis-proxy conf]# echo "1" > /data/zookeeper/data/myid
啟動zookeeper,因為環境變量里已經添加了路徑,直接執行命令即可,執行zkServer.sh start后,要等一會再執行zkServer status:
[root@codis-proxy ~]# zkServer.sh start JMX enabled by default Using config: /usr/local/zookeeper-3.4.6/bin/../conf/zoo.cfg Starting zookeeper ... STARTED [root@codis-proxy ~]# zkServer.sh status JMX enabled by default Using config: /usr/local/zookeeper-3.4.6/bin/../conf/zoo.cfg Mode: standalone [root@codis-proxy ~]#
在那個目錄下啟動,就在那個目錄下產生一個啟動的日志zookeeper.out,查看這個日志可以查看是否正常啟動了。由於單個zookeeper,所以這里指示Mode: standalone,如果有多個zk節點,就只有一個Mode: leader的狀態,別的都是Mode: follower狀態。
由於我現在是做一個機器做zk,所以這里幾乎不用修改:
[root@codis-proxy codis]# cd /usr/local/codis
[root@codis-proxy codis]# cat config.ini zk=localhost:2181 //zookeeper的地址, 如果是zookeeper集群,可以這么寫: zk=hostname1:2181,hostname2:2181,hostname3:2181,hostname4:2181,hostname5:2181,如果是etcd,則寫成http://hostname1:port,http://hostname2:port,http://hostname3:port product=test //產品名稱, 這個codis集群的名字, 可以認為是命名空間, 不同命名空間的codis沒有交集 proxy_id=proxy_1 //proxy會讀取, 用於標記proxy的名字, 針對多個proxy的情況, 可以使用不同的config.ini, 只需要更改 proxy_id 即可 net_timeout=5 //檢測狀態時間間隔 dashboard_addr=localhost:18087 //dashboard 服務的地址,CLI 的所有命令都依賴於 dashboard 的 RESTful API,所以必須啟動 coordinator=zookeeper //如果用etcd,則將zookeeper替換為etcd [root@codis-1 bin]#
6、查看一下啟動流程:
[root@codis-proxy codis]# cat usage.md 0. start zookeeper //啟動zookeeper服務 1. change config items in config.ini //修改codis配置文件 2. ./start_dashboard.sh //啟動 dashboard 3. ./start_redis.sh //啟動redis實例 4. ./add_group.sh //添加redis組,一個redis組只能有一個master 5. ./initslot.sh //初始化槽 6. ./start_proxy.sh //啟動proxy 7. ./set_proxy_online.sh //上線proxy項目 8. open browser to http://localhost:18087/admin //訪問web [root@codis-proxy codis]#
這只是一個參考,有些順序不是必須的,但啟動dashboard前,必須啟動zookeeper服務,這是必須的,后面有很多操作,都可以在web頁面完成,例如添加/刪除組,添加/刪除redis實例等。
7、我們到啟動dashboard這一步了,我們修改一下腳本,運行即可啟動,修改為如下:(由本來的../修改為./)
[root@codis-proxy codis]# cat start_dashboard.sh #!/bin/sh nohup ./bin/codis-config -c config.ini -L ./log/dashboard.log dashboard --addr=:18087 --http-log=./log/requests.log &>/dev/null & [root@codis-proxy codis]#
啟動后查看下是否正常啟動了:
[root@codis-proxy codis]# sh start_dashboard.sh [root@codis-proxy codis]# ps -ef |grep dashboard root 1463 1 2 20:38 pts/0 00:00:00 ./bin/codis-config -c config.ini -L ./log/dashboard.log dashboard --addr=:18087 --http-log=./log/requests.log root 1470 1277 0 20:38 pts/0 00:00:00 grep dashboard [root@codis-proxy codis]#
8、初始化槽(修改腳本,把../修改為./以及把后面的一些暫時沒用的東西注釋掉)
[root@codis-proxy codis]# cat initslot.sh #!/bin/sh echo "slots initializing..." ./bin/codis-config -c config.ini slot init -f echo "done" #echo "set slot ranges to server groups..." #./bin/codis-config -c config.ini slot range-set 0 511 1 online #./bin/codis-config -c config.ini slot range-set 512 1023 2 online #echo "done" [root@codis-proxy codis]#
執行初始化槽腳本:
[root@codis-proxy codis]# sh initslot.sh slots initializing... { "msg": "OK", "ret": 0 } done [root@codis-proxy codis]#
9、分別在服務器redis-master和redis-slave上搭建好redis實例:
(1)在redis-master操作:
[root@redis-master ~]# tar zxf redis-2.8.19.tar.gz -C /usr/local/ [root@redis-master ~]# mkdir -p /data/cluster/7000 [root@redis-master ~]# cd /usr/local/redis-2.8.19/ [root@redis-master redis-2.8.19]# make && make install
[root@redis-master redis-2.8.19]# cp redis.conf /data/cluster/7000/
修改配置文件的端口為7000和把daemonize on修改為daemonize yes(注意:如果要上線到生產環境,配置文件根據需要修改)
[root@redis-master redis-2.8.19]# cd /data/cluster/7000/ [root@redis-master 7000]# sed -i '/port/s/6379/7000/' redis.conf [root@redis-master 7000]# sed -i '/daemonize/s/no/yes/' redis.conf
啟動實例:
[root@redis-master 7000]# redis-server redis.conf [root@redis-master 7000]# ps -ef |grep 7000 root 2902 1 0 21:18 ? 00:00:01 redis-server *:7000 root 2939 1183 0 21:37 pts/1 00:00:00 grep 7000 [root@redis-master 7000]#
redis-master已經啟動成功了
(2)配置redis-slave,操作同上:(本以為要在redis-slave配置文件里添加 slaveof 192.168.10.128 7000 來配置主從關系,經過測試發現,是不用進行這樣配置的)
在redis-slave服務器上操作:
[root@redis-slave ~]# tar zxf redis-2.8.19.tar.gz -C /usr/local/ [root@redis-slave ~]# cd /usr/local/redis-2.8.19/
[root@redis-slave redis-2.8.19]# make && make install
[root@redis-slave redis-2.8.19]# mkdir -p /data/cluster/7001
[root@redis-slave redis-2.8.19]# cp redis.conf /data/cluster/7001/
修改配置文件的端口為7000和把daemonize on修改為daemonize yes
[root@redis-slave redis-2.8.19]# cd /data/cluster/7001/ [root@redis-slave 7001]# sed -i '/port/s/6379/7001/' redis.conf [root@redis-slave 7001]# sed -i '/daemonize/s/no/yes/' redis.conf
啟動redis實例:
[root@redis-slave 7001]# redis-server redis.conf [root@redis-slave 7001]# ps -ef |grep 7001 root 2988 1 0 08:28 ? 00:00:00 redis-server *:7001 root 3016 1304 0 08:40 pts/1 00:00:00 grep 7001 [root@redis-slave 7001]#
redis-slave已經正常啟動。實例已經配置完,可以去web頁面操作了。
10、添加組ID,為組添加主從實例,一個組里只能有一個redis-master:
訪問dashboard的IP加端口:http://192.168.10.130:18087(最好用Firefox瀏覽器,別的瀏覽器坑了我N久,說多都是淚啊!!)
點New Server Group添加組:
點Add New Redis Instance添加主機:(第一個添加的都是master,也只能有一個master)
添加redis-slave主機:
添加完成后可以看到:
給組group_1分配槽的范圍為1-1023:
成功后返回:
11、測試一下redis-master和redis-slave是否正常同步數據了:
在redis-master上寫數據:
[root@redis-master 7000]# redis-cli -c -p 7000 127.0.0.1:7000> set name test OK 127.0.0.1:7000> set age 24 OK 127.0.0.1:7000>
在redis-slave查看是否把數據同步過來了:
[root@redis-slave 7001]# redis-cli -c -p 7001 127.0.0.1:7001> get name "test" 127.0.0.1:7001> get age "29" 127.0.0.1:7001>
可以看到正常同步的。
11、啟動codis-proxy,在codis-proxy服務器上操作:
修改腳本start_proxy.sh腳本,把../修改為./(該腳本意思是如果有啟動的proxy,先停了,再啟動;proxy_1是config.ini里定義的,如果有多個Proxy,只需要修改proxy_id即可):
[root@codis-proxy codis]# cat start_proxy.sh #!/bin/sh echo "shut down proxy_1..." ./bin/codis-config -c config.ini proxy offline proxy_1 echo "done" echo "start new proxy..." nohup ./bin/codis-proxy --log-level info -c config.ini -L ./log/proxy.log --cpu=8 --addr=0.0.0.0:19000 --http-addr=0.0.0.0:11000 & echo "done" echo "sleep 3s" sleep 3 tail -n 30 ./log/proxy.log [root@codis-proxy codis]#
啟動並檢查進程:

看到日志有點小報錯,但可以忽略,后面出現[warning] proxy_1 wait to be online,意思是等待上線:
查看進程:
[root@codis-proxy codis]# ps -ef |grep proxy avahi 1087 1 0 Apr22 ? 00:00:00 avahi-daemon: running [codis-proxy.local] root 4016 1 0 14:34 pts/0 00:00:00 ./bin/codis-proxy --log-level info -c config.ini -L ./log/proxy.log --cpu=8 --addr=0.0.0.0:19000 --http-addr=0.0.0.0:11000 root 4028 1277 0 14:39 pts/0 00:00:00 grep proxy [root@codis-proxy codis]#
上線proxy_1,修改腳本並運行腳本(把../修改為./):
[root@codis-proxy codis]# cat set_proxy_online.sh #!/bin/sh echo "set proxy_1 online" ./bin/codis-config -c config.ini proxy online proxy_1 echo "done" [root@codis-proxy codis]# sh set_proxy_online.sh
{
"msg": "OK",
"ret": 0
}
[root@codis-proxy codis]#
去web面面刷新即可看到proxy_1:
下面做個測試,測試大體內容是:
1)假設redis-master有問題,也可以理解為掛了
2)在頁面把redis-slave手動提升為master
3)把有問題的master下線
4)假如剛剛有問題的master已經正常恢復服務了,轉為redis-slave
在上面已經測試了,在redis-master寫數據,redis-slave上是可以看到數據同步過來的,現在在redis-slave上寫數據,看主能不能看到:
[root@redis-slave 7001]# redis-cli -c -p 7001 127.0.0.1:7001> set MySQL innodb OK 127.0.0.1:7001> get MySQL "innodb" 127.0.0.1:7001>
在redis-master查看:
[root@redis-master 7000]# redis-cli -c -p 7000 127.0.0.1:7000> keys * 1) "name" 2) "age" 127.0.0.1:7000>
可以看到,redis-slave寫數據,是不會到master上的。
現在模擬redis-master down機了:
[root@redis-master 7000]# ps -ef |grep 7000 root 3063 1 0 Apr15 ? 00:00:18 redis-server *:7000 root 3460 1183 0 02:21 pts/1 00:00:00 grep 7000 [root@redis-master 7000]# kill -9 3063 [root@redis-master 7000]#
在web頁面刷新可以看到master已經掛了:
我們把redis-slave提升master,點Promote to Master按鈕即可:
可以看到本來的master已經變成offline下線狀態了,這時我們把它刪除掉,點后面的X刪除主機。假如剛剛下線的redis服務器恢復正常了,再點上面的Add New Redis Instance按鈕添加主機:
可以看到剛剛下線redis服務器,現在變成了redis-slave的角色了。我們測試下數據是否正常同步:
注意:現在7001端口的角色是master的了,在它上面寫數據:
[root@redis-slave 7001]# redis-cli -c -p 7001 127.0.0.1:7001> set aa bb OK 127.0.0.1:7001> set cc dd OK 127.0.0.1:7001>
新的redis-slave上查看數據也是就7000端口對應的redis:
[root@redis-master 7000]# redis-cli -c -p 7000 127.0.0.1:7000> get aa "bb" 127.0.0.1:7000> get cc "dd" 127.0.0.1:7000>
可以看到是正常同步的。嘻嘻
12、下面我們進行redis壓力測試說明和樣例:
[root@redis-master ~]# redis-benchmark --help Usage: redis-benchmark [-h <host>] [-p <port>] [-c <clients>] [-n <requests]> [-k <boolean>] -h <hostname> Server hostname (default 127.0.0.1) --主機ip地址 -p <port> Server port (default 6379) --端口 -s <socket> Server socket (overrides host and port) --socket(如果測試在服務器上測可以用socket方式) -a <password> Password for Redis Auth --redis的認證密碼 -c <clients> Number of parallel connections (default 50) --客戶端連接數 -n <requests> Total number of requests (default 100000) --總請求數 -d <size> Data size of SET/GET value in bytes (default 2) --set、get的value大小 -dbnum <db> SELECT the specified db number (default 0) --選擇哪個數據庫測試(一般0-15) -k <boolean> 1=keep alive 0=reconnect (default 1) --是否采用keep alive模式 -r <keyspacelen> Use random keys for SET/GET/INCR, random values for SADD --隨機產生鍵值時的隨機數范圍 Using this option the benchmark will expand the string __rand_int__ inside an argument with a 12 digits number in the specified range from 0 to keyspacelen-1. The substitution changes every time a command is executed. Default tests use this to hit random keys in the specified range. -P <numreq> Pipeline <numreq> requests. Default 1 (no pipeline). --pipeline的個數(如果使用pipeline會把多個命令封裝在一起提高效率) -q Quiet. Just show query/sec values --僅僅查看每秒的查詢數 --csv Output in CSV format --用csv方式輸出 -l Loop. Run the tests forever --循環次數 -t <tests> Only run the comma separated list of tests. The test --指定命令 names are the same as the ones produced as output. -I Idle mode. Just open N idle connections and wait. --僅打開n個空閑鏈接 Examples: Run the benchmark with the default configuration against 127.0.0.1:6379: $ redis-benchmark Use 20 parallel clients, for a total of 100k requests, against 192.168.1.1: $ redis-benchmark -h 192.168.1.1 -p 6379 -n 100000 -c 20 --測試set、get、mset、sadd等場景下的性能 Fill 127.0.0.1:6379 with about 1 million keys only using the SET test: $ redis-benchmark -t set -n 1000000 -r 100000000 --測試set隨機數的性能 Benchmark 127.0.0.1:6379 for a few commands producing CSV output: $ redis-benchmark -t ping,set,get -n 100000 --csv --使用csv的輸出方式測試 Benchmark a specific command line: $ redis-benchmark -r 10000 -n 10000 eval 'return redis.call("ping")' 0 --測試基本命令的速度 Fill a list with 10000 random elements: $ redis-benchmark -r 10000 -n 10000 lpush mylist __rand_int__ --測試list入隊的速度 On user specified command lines __rand_int__ is replaced with a random integer with a range of values selected by the -r option.
13、命令維護說明:

總結:
一、搭建環境前,先好好看一下codis的資料還有zookeeper的資料,思路理清了,搭建起來就沒那么吃力了,剛剛開始時,我糾結了N久zookeeper到底有什么用,哈哈,好好看看相關的資料就明白它的作用了。
二、codis還有別的測試,我上面就不一一寫出了,如果上線到生產環境中,一定要做好各種測試,希望大家也能夠把自己研究出來的東西分享出來,一起學習,一起進步^.^。
三、 reids-cluster計划在redis3.0中推出了,因為剛推出不久,相信會存在不少的坑,所以生產環境中的redis集群可以考慮豌豆莢開源的codis,還有Twitter開源的twemproxy,這兩個已經有很多公司在線上使用了,推薦大家使用,后期會補上twemproxy的博客。
參考資料:
codis官網:https://github.com/wandoulabs/codis
https://github.com/wandoulabs/codis/blob/master/doc/tutorial_zh.md
https://github.com/wandoulabs/codis/blob/master/doc/FAQ_zh.md
http://xmarker.blog.163.com/blog/static/226484057201422491547716/ (redis壓力測試)
作者:陸炫志 出處:xuanzhi的博客 http://www.cnblogs.com/xuanzhi201111 |