Codis简介
Codis是豌豆荚使用Go和C语言开发、以代理的方式实现的一个Redis分布式集群解决方案,且完全兼容Twemproxy。Twemproxy对于上一层的应用来说, 连接Codis Proxy(Redis代理服务)和连接原生的Redis服务器没有明显的区别,上一层应用能够像使用单机的Redis一样对待。Codis底层会处理请求的转发、不停机的数据迁移等工作, 所有底层的一切处理, 对于客户端来说是透明的。总之,可以简单的认为后台连接的是一个内存无限大的Redis服务。Codis遵循MIT开源协议发布,更多关于Codis的信息请登录其在GitHub的主页查看。
Codis是一个分布式Redis解决方案, 对于上层的应用来说, 连接到Codis Proxy和连接原生的Redis Server没有明显的区别(不支持的命令列表), 上层应用可以像使用单机的Redis一样使用, Codis底层会处理请求的转发, 不停机的数据迁移等工作, 所有后边的一切事情, 对于前面的客户端来说是透明的, 可以简单的认为后边连接的是一个内存无限大的Redis服务。
Codis 由四部分组成:
Codis Proxy (codis-proxy)
Codis Manager (codis-config)
Codis Redis (codis-server)
ZooKeepercodis-proxy是客户端连接的Redis代理服务, codis-proxy本身实现了Redis协议, 表现得和一个原生的Redis没什么区别(就像Twemproxy), 对于一个业务来说, 可以部署多个codis-proxy, codis-proxy本身是无状态的. 可以执行多个Codis Dashboard(codis-config)是Codis的管理工具, 支持包括, 添加/删除 Redis 节点, 添加/删除Proxy节点, 发起数据迁移等操作. codis-config本身还自带了一个 http server, 会启动一个dashboard, 用户可以直接在浏览器上观察Codis集群的运行状态。
Codis Redis(codis-server)是Codis项目维护的一个Redis分支, 基于2.8.21开发, 加入了slot的支持和原子的数据迁移指令. Codis上层的codis-proxy和codis-config只能和这个版本的Redis交互才能正常运行。
Codis依赖ZooKeeper来存放数据路由表和codis-proxy节点的元信息, codis-config 发起的命令都会通过ZooKeeper同步到各个存活的codis-proxy。
Codis支持按照Namespace区分不同的产品, 拥有不同的product name 的产品, 各项配置都不会冲突。
Codis架构
安装并测试Codis
本文使用Codis分支的2.0版本,也是很多公司正在用的版本,Codis3.0版本已经有分支了,但是有线上使用的用户不确定。Codis3.0可以不依赖zookeeper,dashboard和proxy直接通过HTTP方式通讯。出于稳定性考虑,我们还是用目前的版本,避免在新版本上踩坑。
Codis新增一个group的概念,每个group包含一个Redis Master和至少一个Redis Slave。Codis可以支持数据热迁移.Codis采用预先分片机制,分成1024个slots,也就是最多可以支持1024个Codis server,这些信息保存在zookeeper中。
安装GO语言和编译安装codis
Codis由Go语言写的,所以需要安装Go语言包
安装go
wget https://storage.googleapis.com/golang/go1.6.linux-amd64.tar.gz wget http://golangtc.com/static/go/1.6/go1.6.linux-amd64.tar.gz #国内镜像地址 tar -C /usr/local -xzf go1.6.linux-amd64.tar.gz 配置环境变量 vi /etc/profile 在最后添加: export GOROOT=/usr/local/go export PATH=$PATH:$GOROOT/bin 保存,执行: source /etc/profile 判断go是否安装成功,运行go version [root@centos6 local]# go version go version go1.6 linux/amd64 也可以yum安装: #yum -y install gcc gcc-c++ make git wget go #export GOROOT=/usr/local/go #export PATH=$GOROOT/bin:$JAVA_HOME/bin:$PATH
安装codis
#安装编译工具 yum groupinstall "Development Tools" #下载并编译codis mkdir $HOME/goproj export GOPATH=$HOME/goproj export PATH=$PATH:$GOROOT/bin:$GOPATH/bin go get -u -d github.com/CodisLabs/codis cd $GOPATH/src/github.com/CodisLabs/codis make make gotest
这里需要注意的是,最好按照Codis的文档使用go get载codis,我尝试过自己下载需要的依赖包然后编译codis,但是总是报错说是GOPATH设置不正确。这里对于初次接触GO项目编译的人来说有点诡异。
编译完成后会在bin目录下生成3个二进制文件
codis-config Codis的管理工具,支持添加/删除Redis节点,添加/删除Proxy节点,执行Auto Rebalance等操作
codis-server Codis 项目维护的一个Redis分支, 基于2.8.21开发, 加入了slot的支持和原子的数据迁移指令. Codis上层的codis-proxy和codis-config只能和这个版本的Redis交互才能正常运行.
codis-proxy 是客户端连接的 Redis 代理服务, codis-proxy本身实现了Redis协议, 表现得和一个原生的Redis没什么区别(就像 Twemproxy), 对于一个业务来说, 可以部署多个codis-proxy, codis-proxy本身是无状态的.
Codis 支持按照 Namespace 区分不同的产品, 拥有不同的product name的产品, 各项配置都不会冲突
另外值得注意的是
bin/assets文件夹是codis-config的dashboard http服务需要的前端资源, 需要和codis-config放置在同一文件夹下。
把Codis编译完成后直接复制bin目录下的codis-proxy,codis-config,codis-server三个二进制文件和assets资源目录到其他机器上也是可以直接运行的。目标主机不一定安装有go语言。
常用命令维护说明
server: 主要用来添加,删除,提权,查找 server group(实际操作zookeeper) ./codis-config server --help codis-config server list codis-config server addcodis-config server remove codis-config server promote codis-config server add-group codis-config server remove-group slot: 主要用来初始化,迁移,设置range-set,查询slot ./codis-config slot --help usage: codis-config slot init [-f] codis-config slot info codis-config slot set codis-config slot range-set codis-config slot migrate [--delay= ] codis-config slot rebalance [--delay= ] dashboard: 主要用来启动dashboard ./codis-config dashboard --help usage: codis-config dashboard [--addr=] [--http-log= ] options: --addr listen ip:port, e.g. localhost:12345, :8086, [default: :8086] --http-log http request log [default: request.log ] action: 主要用来操作codis保存的事件记录,并解除zk锁(迁移异常会出现锁) ./codis-config action --help usage: codis-config action (gc [-n | -s ] | remove-lock) options: gc: gc -n N gc -s Sec keep last N actions; keep last Sec seconds actions; remove-lock force remove zookeeper lock; proxy: 主要用来实现proxy上线,下线,查询 ./codis-config proxy usage: codis-config proxy list codis-config proxy offline codis-config proxy online codis-proxy 主要用来启动proxy进程 ./codis-proxy --help usage: proxy [-c ] [-L ] [--log-level= ] [--cpu= ] [--addr= ] [--http-addr= ] options: -cset config file -Lset output log file, default is stdout --log-level= set log level: info, warn, error, debug [default: info] --cpu= num of cpu cores that proxy can use --addr= proxy listen address, example: 0.0.0.0:9000 --http-addr= debug vars http server codis-server 主要用来启动 codis(redis 实例) ./codis-server --help Usage: ./redis-server [/path/to/redis.conf] [options] ./redis-server - (read config from stdin) ./redis-server -v or --version ./redis-server -h or --help ./redis-server --test-memory Examples: ./redis-server (run the server with default conf) ./redis-server /etc/redis/6379.conf ./redis-server --port 7777 ./redis-server --port 7777 --slaveof 127.0.0.1 8888 ./redis-server /etc/myredis.conf --loglevel verbose Sentinel mode: ./redis-server /etc/sentinel.conf –sentinel codis-ha 主要来实现 server_group 中的主从 ha ./codis-ha --help Usage of ./codis-ha: -codis-config="localhost:18087": api server address -productName="test": product name, can be found in codis-proxy's config
Codis不支持的命令
KEYS, MOVE, OBJECT, RENAME, RENAMENX, SORT, SCAN, BITOP,MSETNX, BLPOP, BRPOP, BRPOPLPUSH, PSUBSCRIBE,PUBLISH, PUNSUBSCRIBE, SUBSCRIBE, UNSUBSCRIBE, DISCARD, EXEC, MULTI, UNWATCH, WATCH, SCRIPT EXISTS, SCRIPT FLUSH, SCRIPT KILL, SCRIPT LOAD, AUTH, ECHO, SELECT, BGREWRITEAOF, BGSAVE, CLIENT KILL, CLIENT LIST, CONFIG GET, CONFIG SET, CONFIG RESETSTAT, DBSIZE, DEBUG OBJECT, DEBUG SEGFAULT, FLUSHALL, FLUSHDB, INFO, LASTSAVE, MONITOR, SAVE, SHUTDOWN, SLAVEOF, SLOWLOG, SYNC, TIME、MIGRATE、RANDOMKEY、PSUBSCRIBE、PUBLISH、PUNSUBSCRIBE、BGREWRITEAOF、RESTORE、SLOTSCHECK、SLOTSDEL、SLOTSINFO、SLOTSMGRTONE、SLOTSMGRTSLOT、SLOTSMGRTTAGONE、SLOTSMGRTTAGSLOT
Codis半支持的命令
需要将以下key放入同一slot才能支持,方式采用{},如key为”bar{zap}”,则只会对zap进行hash
RPOPLPUSH、SDIFF、SINTER、SINTERSTORE、SMOVE、SUNION、SUNIONSTORE、ZINTERSTORE、ZUNIONSTORE、PFMERGE、EVAL、EVALSHA
安装JDK
zookeeper依赖java环境,所以要先安装JDK。
wget --no-cookies --no-check-certificate --header "Cookie: oraclelicense=accept-securebackup-cookie" rpm" -O jdk-7u65-linux-x64.rpm rpm -ivh jdk-7u65-linux-x64.rpm
安装和配置zookeeper
wget http://www.us.apache.org/dist/zookeeper/zookeeper-3.4.6/zookeeper-3.4.6.tar.gz tar zxvf zookeeper-3.4.6.tar.gz cd zookeeper-3.4.6/conf/ cp zoo_sample.cfg zoo.cfg
部署zookeeper集群
复制三份zookeeper实例
cp -rf zookeeper-3.4.6 zookeeper-1 cp -rf zookeeper-3.4.6 zookeeper-2 cp -rf zookeeper-3.4.6 zookeeper-3
创建三个数据和日志目录
mkdir -p /var/local/zookeeper/data{1..3} mkdir -p /var/local/zookeeper/logs{1..3}
编辑配置文件
修改数据目录和日志目录.并且添加server(由于是一台机器,端口则不能相同),如下
vim /opt/zookeeper-1/conf/zoo.cfg tickTime=2000 initLimit=10 syncLimit=5 dataDir=/var/local/zookeeper/data1 dataLogDir=/var/local/zookeeper/logs1 clientPort=2181 server.1=server01:2887:3887 server.2=server01:2888:3888 server.3=server01:2889:3889 vim /opt/zookeeper-2/conf/zoo.cfg tickTime=2000 initLimit=10 syncLimit=5 dataDir=/var/local/zookeeper/data2 clientPort=2182 server.1=192.168.119.100:2887:3887 server.2=192.168.119.100:2888:3888 server.3=192.168.119.100:2889:3889 vim /opt/zookeeper-3/conf/zoo.cfg tickTime=2000 initLimit=10 syncLimit=5 dataDir=/var/local/zookeeper/data3 clientPort=2183 server.1=192.168.119.100:2887:3887 server.2=192.168.119.100:2888:3888 server.3=192.168.119.100:2889:3889
创建myid文件
要在每台机器的dataDir下,新建一个myid文件,里面存放一个数字,用来标识当前主机。
echo "1">/var/local/zookeeper/data1/myid echo "2">/var/local/zookeeper/data2/myid echo "3">/var/local/zookeeper/data3/myid
启动三个zookeeper
/opt/zookeeper-1/bin/zkServer.sh start /opt/zookeeper-2/bin/zkServer.sh start /opt/zookeeper-3/bin/zkServer.sh start
查看是否已经自动选举
/opt/zookeeper-1/bin/zkServer.sh status JMX enabled by default Using config: /opt/zookeeper-1/bin/../conf/zoo.cfg Mode: follower /opt/zookeeper-2/bin/zkServer.sh status JMX enabled by default Using config: /opt/zookeeper-2/bin/../conf/zoo.cfg Mode: leader /opt/zookeeper-3/bin/zkServer.sh status JMX enabled by default Using config: /opt/zookeeper-3/bin/../conf/zoo.cfg Mode: follower
备注
三种类型的节点
Leader : 处理写请求,最终更新状态;
Follower : 处理客户端请求,参与投票;
Observer : 不参加投票,只处理客户端请求,主要是为提升zookeeper的性能;当leader重启或宕机后,通过paxos算法,重新选出Leader,并以Leader为准,进行数据同步;
关于为Server ID,是标识host机器在集群中的机器序号,在每个ZK机器上,需要在dataDir目录下创建一个myid文件,myid中就是这个Server ID的数字。
连接测试
/opt/zookeeper-1/bin/zkCli.sh -server 192.168.119.100:2181 Connecting to 192.168.119.100:2181 2016-03-25 13:35:12,972 [myid:] - INFO [main:Environment@100] - Client environment:zookeeper.version=3.4.6-1569965, built on 02/20/2014 09:09 GMT 2016-03-25 13:35:12,974 [myid:] - INFO [main:Environment@100] - Client environment:host.name=localhost 2016-03-25 13:35:12,974 [myid:] - INFO [main:Environment@100] - Client environment:java.version=1.8.0_65 2016-03-25 13:35:12,987 [myid:] - INFO [main:Environment@100] - Client environment:java.vendor=Oracle Corporation 2016-03-25 13:35:12,987 [myid:] - INFO [main:Environment@100] - Client environment:java.home=/usr/java/jdk1.8.0_65/jre 2016-03-25 13:35:12,987 [myid:] - INFO [main:Environment@100] - Client environment:java.class.path=/opt/zookeeper-1/bin/../build/classes:/opt/zookeeper-1/bin/../build/lib/*.jar:/opt/zookeeper-1/bin/../lib/slf4j-log4j12-1.6.1.jar:/opt/zookeeper-1/bin/../lib/slf4j-api-1.6.1.jar:/opt/zookeeper-1/bin/../lib/netty-3.7.0.Final.jar:/opt/zookeeper-1/bin/../lib/log4j-1.2.16.jar:/opt/zookeeper-1/bin/../lib/jline-0.9.94.jar:/opt/zookeeper-1/bin/../zookeeper-3.4.6.jar:/opt/zookeeper-1/bin/../src/java/lib/*.jar:/opt/zookeeper-1/bin/../conf:.:/usr/java/jdk1.8.0_65//lib/dt.jar:/usr/java/jdk1.8.0_65//lib/tools.jar 2016-03-25 13:35:12,987 [myid:] - INFO [main:Environment@100] - Client environment:java.library.path=/usr/java/packages/lib/amd64:/usr/lib64:/lib64:/lib:/usr/lib 2016-03-25 13:35:12,987 [myid:] - INFO [main:Environment@100] - Client environment:java.io.tmpdir=/tmp 2016-03-25 13:35:12,987 [myid:] - INFO [main:Environment@100] - Client environment:java.compiler=2016-03-25 13:35:12,987 [myid:] - INFO [main:Environment@100] - Client environment:os.name=Linux 2016-03-25 13:35:12,987 [myid:] - INFO [main:Environment@100] - Client environment:os.arch=amd64 2016-03-25 13:35:12,988 [myid:] - INFO [main:Environment@100] - Client environment:os.version=2.6.32-573.12.1.el6.x86_64 2016-03-25 13:35:12,988 [myid:] - INFO [main:Environment@100] - Client environment:user.name=root 2016-03-25 13:35:12,988 [myid:] - INFO [main:Environment@100] - Client environment:user.home=/root 2016-03-25 13:35:12,988 [myid:] - INFO [main:Environment@100] - Client environment:user.dir=/root 2016-03-25 13:35:12,989 [myid:] - INFO [main:ZooKeeper@438] - Initiating client connection, connectString=192.168.119.100:2181 sessionTimeout=30000 watcher=org.apache.zookeeper.ZooKeeperMain$MyWatcher@67424e82 Welcome to ZooKeeper! JLine support is enabled 2016-03-25 13:35:13,125 [myid:] - INFO [main-SendThread(192.168.119.100:2181):ClientCnxn$SendThread@975] - Opening socket connection to server 192.168.119.100/192.168.119.100:2181. Will not attempt to authenticate using SASL (unknown error) 2016-03-25 13:35:13,236 [myid:] - INFO [main-SendThread(192.168.119.100:2181):ClientCnxn$SendThread@852] - Socket connection established to 192.168.119.100/192.168.119.100:2181, initiating session [zk: 192.168.119.100:2181(CONNECTING) 0] 2016-03-25 13:35:13,345 [myid:] - INFO [main-SendThread(192.168.119.100:2181):ClientCnxn$SendThread@1235] - Session establishment complete on server 192.168.119.100/192.168.119.100:2181, sessionid = 0x153ab70dc7c0001, negotiated timeout = 30000 WATCHER:: WatchedEvent state:SyncConnected type:None path:null [zk: 192.168.119.100:2181(CONNECTED) 0] ls / [zk, zookeeper] [zk: 192.168.119.100:2181(CONNECTED) 1] ls /zookeeper [quota] [zk: 192.168.119.100:2181(CONNECTED) 2]
启动Codis服务
启动dashboard
codis-config和codis-proxy使用config.ini这个配置文件,编辑配置文件
grep -v '#' config.ini|grep -v '^$' #根据实际情况,修改以下项 coordinator=zookeeper zk=192.168.119.100:2181,192.168.119.100:2182,192.168.119.100:2183 product=test dashboard_addr=192.168.119.100:18087 password= backend_ping_period=5 session_max_timeout=1800 session_max_bufsize=131072 session_max_pipeline=1024 zk_session_timeout=30000 proxy_id=proxy_1 #如果有多个proxy,proxy_id 需要唯一。
启动dashboard
bin/codis-config dashboard
关闭dashboard
我们在启动了dashboard后,他打印了一堆内容,如下
[code]2015/09/26 11:18:41 dashboard.go:160: [INFO] dashboard listening on addr: :18087 2015/09/26 11:18:41 dashboard.go:143: [INFO] dashboard node created: /zk/codis/db_test/dashboard, {"addr": "localhost:18087", "pid": 1701} 2015/09/26 11:18:41 dashboard.go:144: [WARN] ********** Attention ********** 2015/09/26 11:18:41 dashboard.go:145: [WARN] You should use `kill {pid}` rather than `kill -9 {pid}` to stop me, 2015/09/26 11:18:41 dashboard.go:146: [WARN] or the node resisted on zk will not be cleaned when I'm quiting and you must remove it manually 2015/09/26 11:18:41 dashboard.go:147: [WARN] *******************************
是的,如内容说说的,如果要关闭,记得要kill -{pid}
要不然突然电脑没电之类的bug,导致异常退出的时候,就得手动关闭,要不然在下次启动的时候,就会遇到下面的内容:
bin/codis-config dashboard 2015/09/26 11:14:10 dashboard.go:160: [INFO] dashboard listening on addr: :18087 2015/09/26 11:14:10 dashboard.go:234: [PANIC] create zk node failed [error]: dashboard already exists: {"addr": "192.168.0.123:18087", "pid": 30155} [stack]: 3 /usr/local/codis/src/github.com/wandoulabs/codis/cmd/cconfig/dashboard.go:234 main.runDashboard 2 /usr/local/codis/src/github.com/wandoulabs/codis/cmd/cconfig/dashboard.go:54 main.cmdDashboard 1 /usr/local/codis/src/github.com/wandoulabs/codis/cmd/cconfig/main.go:84 main.runCommand 0 /usr/local/codis/src/github.com/wandoulabs/codis/cmd/cconfig/main.go:151 main.main ... ...
备注:必须先启动zookeeper
通过NGINX代理认证访问dashboard
配置nginx
yum install -y nginx httpd vim /etc/nginx/conf.d/codis.conf 修改如下参数: server { listen 80; server_name 192.168.119.100; location / { auth_basic "codis auth"; auth_basic_user_file /etc/nginx/passwd.db; proxy_set_header Host $host; proxy_set_header X-Forwarded-For $remote_addr; proxy_pass http://192.168.119.100:18087; } }
启动nginx
htpasswd -c /etc/nginx/passwd codis service nginx start chkconfig nginx on chkconfig httpd off
备注:因为codis的dashboard不支持认证访问,可以通过nginx代理做认证访问(htpasswd的apache的一个命令工具,用于生成http 基本认证的密码文件),可以用防火墙关闭18087端口。通过URL浏览器访问codis的dashboard。
初始化slots
初始化slots(codis-config上操作)
bin/codis-config slot init
该命令会在zookeepr上创建slot相关信息
重新初始化slot
bin/codis-config slot init -f { "msg": "OK", "ret": 0 }