共计 13873 个字符,预计需要花费 35 分钟才能阅读完成。
导读 | Codis 是一个分布式 Redis 解决方案, 对于上层的应用来说, 连接到 Codis Proxy 和连接原生的 Redis Server 没有明显的区别 (不支持的命令列表), 上层应用可以像使用单机的 Redis 一样使用, Codis 底层会处理请求的转发, 不停机的数据迁移等工作, 所有后边的一切事情, 对于前面的客户端来说是透明的, 可以简单的认为后边连接的是一个内存无限大的 Redis 服务. |
Codis 是 Wandoujia Infrastructure Team 开发的一个分布式 Redis 服务, 用户可以看成是一个无限内存的 Redis 服务, 有动态扩 / 缩容的能力. 对偏存储型的业务更实用, 如果你需要 SUBPUB 之类的指令, Codis 是不支持的. 时刻记住 Codis 是一个分布式存储的项目. 对于海量的 key, value 不太大(<= 1M), 随着业务扩展缓存也要随之扩展的业务场景有特效.
Codis github 地址:https://github.com/CodisLabs/codis
Codis FAQ:https://github.com/CodisLabs/codis/blob/master/doc/FAQ_zh.md
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 分支,基于 2.8.13 开发,加入了 slot 的支持和原子的数据迁移指令,Codis 上层的 codis-proxy 和 codis-config 只能和这个版本的 Redis 交互才能正常运行。
ZooKeeper: 用来存放数据路由表和 codis-proxy 节点的元信息,codis-config 发起的命令都会通过 ZooKeeper 同步到各个存活的 codis-proxy
注:Codis 支持按照 Namespace 区分不同的产品,拥有不同的 product name 的产品,各项配置都不会冲突。
主机名 | IP 地址 | 角色 |
---|---|---|
linux-node1 | 10.1.1.148 | Zookeeper、redis_master,redis_slave |
linux-node2 | 10.1.1.149 | Zookeeper、redis_master,redis_slave |
linux-node3 | 10.1.1.150 | zookeeper,codis-proxy、redis_master,redis_slave |
注:生产环境建议 redis 放到单独的服务器上面
注:部署单个 codis-proxy 节点的环境,建议相关的安装包本地下载完上传到服务器上面。
# 在 codis-proxy(linux-node3)服务器上面操作
yum install -y git gcc make
g++ gcc-c++ automake
openssl-devel zlib-devel
Go 下载地址:http://www.golangtc.com/download
cd /usr/local/src/
tar zxvf go1.7.linux-amd64.tar.gz -C /opt
注意:Zookeeper 已搭建完成的下面安装 JDK 和 zookeeper 的步骤可以跳过。
JDK 下载地址:http://www.oracle.com/technetwork/java/javase/downloads/index.html
rpm -ivh jdk-8u65-linux-x64.rpm
Zookeeper 下载地址:http://zookeeper.apache.org/releases.html#download
wget http://mirrors.cnnic.cn/apache/zookeeper/zookeeper-3.4.8/zookeeper-3.4.8.tar.gz
tar zxvf zookeeper-3.4.8.tar.gz -C /opt/
修改环境变量:vim /etc/profile 在最后添加以下内容
#codis path
export GOROOT=/opt/go
export GOPATH=/opt/codis
JAVA_HOME=/usr/java/jdk1.8.0_101
CLASS_PATH=$JAVA_HOME/lib:$JAVA_HOME/jre/lib
export PATH=$PATH:$GOROOT/bin:$JAVA_HOME/bin:
# 测试环境变量设置
# 执行 source /etc/profile 是环境变量生效,测试 go 是否正常安装:
cat hello.go
package main
import "fmt"
func main(){fmt.Printf("hello,world\n")
}
[root@linux-node-2 ~]# go run hello.go
hello,world!
[root@linux-node-3 ~]# yum -y install git
[root@linux-node-3 opt]# go get -u -d github.com/CodisLabs/codis
可以看到 no buildable Go source files in /opt/codis/src/github.com/wandoulabs/codis,我们在上面环境就是设置 GOAPTH=/opt/codis,所以只要执行上面的获取命令,就会下载在 /opt/codis 下:
我们进到提示的路径进行安装,安装过程比较久,耐心等待:
[root@linux-node-3 ~]# cd /opt/codis/src/github.com/CodisLabs/codis
目录执行 make
命令编译代码,并执行make
gotest 来跑测试
make
make
gotest
建议只通过 go get 命令来下载 codis,除非你非常熟悉 go 语言的目录引用形式从而不会导致代码放错地方。该命令会下载 master 分支的最新版,我们会确保 master 分支的稳定。
执行全部指令后,会在 bin 文件夹内生成 codis-config、codis-proxy、codis-server 三个可执行文件。另外, bin/assets 文件夹是 codis-config 的 dashboard http 服务需要的前端资源, 需要和 codis-config 放置在同一文件夹下)
git clone https://github.com/CodisLabs/codis.git
git 仓库下载完毕后,我们接下来进行如下的操作。如下:
mkdir -p /opt/codis /src/github.com/CodisLabs/
cp -r codis /opt/codis/src/github.com/CodisLabs/
cd /opt/codis/src/github.com/CodisLabs/codis/
# 以上操作完毕后,就和通过 go 下载安装方式一样, 执行 make
命令进行编译,然后执行make
gotest 命令进行测试
wget https://github.com/CodisLabs/codis/archive/3.0.3.tar.gz -P /usr/local/src tar zxvf 3.0.3.tar.gz && cd codis-3.0.3 &&make
&&make
gotest
#codis 安装完成之后 codis-config 主要的使用方法
# ./bin/codis-config -h (master)
usage: codis-config [-c] [-L] [--log-level=]
[...]
options:
-c 配置文件地址
-L 日志输出文件地址
--log-level= 输出日志级别 (debug < info (default) < warn < error < fatal)
commands:
server redis 服务器组管理
slot slot 管理
dashboard 启动 dashboard 服务
action 事件管理 (目前只有删除历史事件的日志)
proxy proxy 管理
# ./bin/codis-proxy -h
usage: codis-proxy [-c] [-L] [--log-level=] [--cpu=] [--addr=] [--http-addr=]
options:
-c 配置文件地址
-L 日志输出文件地址
--log-level= 输出日志级别 (debug < info (default) < warn < error < fatal)
--cpu= proxy 占用的 cpu 核数, 默认 1, 最好设置为机器的物理 cpu 数的一半到 2 / 3 左右
--addr= proxy 的 redis server 监听的地址, 格式 :, 如: localhost:9000, :9001
--http-addr= proxy 的调试信息启动的 http server, 可以访问 http://debug_http_server_addr/debug/vars
codis 安装完毕后,我们现在来配置 codis 集群,在正式配置集群之前,先创建相关的目录,然后复制相关文件到新的目录下。
使用如下命令:
cd /opt/codis/src/github.com/CodisLabs/codis/
mkdir -p /opt/codis/{log,redis_conf}
cp -rf bin/ /opt/codis/
cp config.ini /opt/codis/
cp extern/redis-test/conf/6379.conf /opt/codis/redis_conf/
cp extern/redis-test/conf/6380.conf /opt/codis/redis_conf/
[root@linux-node3 codis]# egrep -v "^#|^$" config.ini
coordinator=zookeeper
zk=192.168.1.148:2181,192.168.1.149:2181,192.168.1.150:2181
product=test
dashboard_addr=192.168.1.150: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_3
该配置文件中,我们需要注意三个参数:zk、dashboard_addr、proxy_id。
其中 zk 是表示 zookeeper 集群的服务器 IP 地址,dashboard_addr 表示 codis web 管理的 IP 地址及端口,proxy_id 表示 codis 的 id,注意每台 codis 服务器该值要唯一。
每台 codis 服务器上,启动两个 redis 实例(也可以启动多个 redis 实例),配置两个 redis,如下:
daemonize no
pidfile /var/run/redis.pid
port 6379
tcp-backlog 511
timeout 0
tcp-keepalive 0
loglevel notice
logfile /var/log/redis/6379.log
databases 16
stop-writes-on-bgsave-error yes
rdbcompression yes
rdbchecksum yes
dbfilename dump6379.rdb
dir /opt/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 实例
\cp /opt/codis/redis_conf/6379.conf 6380.conf
sed -i 's/6379/6380/g' /opt/codis/redis_conf/6380.conf
mkdir -p /var/log/redis/
# 上面操作需要在每台 codis 服务上面都执行。
echo 512 > /proc/sys/net/core/somaxconn
sysctl vm.overcommit_memory=1
注:优化系统的参数
执行 bin/codis-config slot init,该命令会在 zookeeper 上创建 slot 相关信息
/opt/codis/bin/codis-config -c /opt/codis/config.ini slot init
#linux-node3、linux-node1、linux-node2
/opt/codis/bin/codis-server /opt/codis/redis_conf/6379.conf &
/opt/codis/bin/codis-server /opt/codis/redis_conf/6380.conf &
ps -ef | grep codis-server
# 在三台 codis-proxy 上面分别启动 redis 实例
# 尽量在 redis 安装目录或者让 redi 的 log 目录下启动 dashboard,这样方便查看启动产生的日志,nohup.out
nohup /opt/codis/bin/codis-config -c /opt/codis/config.ini dashboard &
# 从图中可以看出 dashboard 已经启动完成,然后去 web 页面访问,codis dashboard 访问端口是 18087
# 查看 codis dashboard,浏览器访问 http://192.168.1.150:18087/,如下:
#codis dashboard 启动完成之后默认页面如上图所示,我们可以通过 web 页面新建 redis_group,把刚刚启动好的 redis 实例加到不同的组当中、也可以通过命令行执行创建 redis_group 的操作。
# 每一个 Server Group 作为一个 Redis 服务器组存在, 只允许有一个 master, 可以有多个 slave, group id 仅支持大于等于 1 的整数
# 在 codis dashboard 上使用命令新建 server group
[root@linux-node3 ~]# cd /opt/codis/
[root@linux-node3 codis]# ./bin/codis-config server -h
usage:
codis-config server list
codis-config server add
codis-config server remove
codis-config server promote
codis-config server add-group
codis-config server remove-group
# 如: 添加三个 server group, 每个 group 有两个 redis 实例,group 的 id 分别为 1 和 2,redis 实例为一主一从。
# 注:这里我把 linux-node3,linux-node2,linux-node1 上面的两个 redis 实例分别加到不同的 server group 当中,详细如下:
group_1: linux-node3 redis_6379 linux-node2 redis_6380
group_2: linux-node2 redis_6379 linux-node1 redis_6380
group_3: linux-node1 redis_5379 linux-node3 redis_6380
其中 redis 的 6379 端口作为 redis master,6380 作为 slave
# 添加一个 group,group 的 id 为 1,并添加一个 redis master(192.168.1.150:6379)到该 group
./bin/codis-config server add 1 192.168.1.150:6379 master
# 添加一个 redis slave(192.168.1.149:6380)到该 group
./bin/codis-config server add 1 192.168.1.149:6380 slave
# 添加一个 group,group 的 id 为 2,并添加一个 redis master(192.168.1.149:6379)到该 group
./bin/codis-config server add 2 192.168.1.149:6379 master
# 添加一个 redis slave(192.168.1.148:6380)到该 group
./bin/codis-config server add 2 192.168.1.148:6380 slave
# 添加一个 group,group 的 id 为 3,并添加一个 redis master(192.168.1.148:6379)到该 group
./bin/codis-config server add 3 192.168.1.148:6379 master
# 添加一个 redis slave(192.168.1.150:6380)到该 group
./bin/codis-config server add 3 192.168.1.150:6380 slave
注意:每组添加的第一个 redis 实例不能被删除,因为 codis 默认把该 redis 实例设置为 master。
# 从图中我们可以看出 server group 已经创建成功,到 codis dashboard 上面查看:
https://github.com/CodisLabs/codis/blob/master/doc/FAQ_zh.md
Codis 采用 Pre-sharding 的技术来实现数据的分片, 默认分成 1024 个 slots (0-1023), 对于每个 key 来说, 通过以下公式确定所属的 Slot Id : SlotId = crc32(key) % 1024 每一个 slot 都会有一个且必须有一个特定的 server group id 来表示这个 slot 的数据由哪个 server group 来提供.
[root@linux-node3 codis]# ./bin/codis-config slot -h
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=]
# 设置编号为 [0, 340] 的 slot 由 server group 1 提供服务, 编号 [341, 682] 的 slot 由 server group 2 提供服务,编号[683, 1023] 的 slot 由 server group 3 提供服务
./bin/codis-config slot range-set 0 340 1 online
./bin/codis-config slot range-set 341 682 2 online
./bin/codis-config slot range-set 683 1023 3 online
/opt/codis/bin/codis-config -c /opt/codis/config.ini slot info 1
/opt/codis/bin/codis-config -c /opt/codis/config.ini slot info 2
/opt/codis/bin/codis-config -c /opt/codis/config.ini slot info 3
#slot 操作完成可以到 codis dashboard 查看状态。如下:
./bin/codis-proxy -c config.ini -L ./log/proxy.log --cpu=1 --addr=0.0.0.0:19000 --http-addr=0.0.0.0:11000
nohup /opt/codis/bin/codis-proxy -c /opt/codis/config.ini –log-level=error -L /opt/codis/log/proxy.log –cpu=1 –addr=0.0.0.0:19000 –http-addr=0.0.0.0:11000 &
# 下面对以上命令中的参数进行解释:
-c 配置文件地址。-L 日志输出文件地址。–log-level= 输出日志级别(debug –cpu= proxy 占用的 cpu 核数, 默认 1, 最好设置为机器的物理 cpu 数的一半到 2 / 3 左右。–addr= proxy 的 redis server 监听的地址, 格式:, 如: localhost:9000, :9001。–http-addr= proxy 的调试信息启动的 http server, 可以访问 http://debug_http_server_addr/debug/vars。
#codis-proxy 启动后,我们可以在 dashboard 上进行查看,如下:
# 到此 codis 集群搭建完毕,为了性能需求,建议 redis 实例和 codis-proxy 放到不同的服务器上面。
codis 集群搭建完毕后,测试连接 codis 集群。要连接 codis 集群,我们只需要连接 codis-proxy 即可。codis-proxy 服务器地址:192.168.1.150,然后加 19000 端口。使用 redis-cli 命令连接,如下:
# 在某台已安装 redis 实例的服务器上面测试:
通过上图,我们可以很明显的看到连接 codis 集群是 ok 的。
我们现在对 codis 集群做一些压力测试,同时在 dashboard 上观察键值对的情况。如下:
./redis-benchmark -h 192.168.1.150 -p 19000 -c 1000 -d 1000 -t set -n 100000 -r 100000
上述命令的意思是,使用 redis-benchmark 压力测试命令连接 codis 集群,同时并发 10000 个(-c),测试 set 操作(-t),每个测试数据集是 100 字节(-d), 请求数是 100000(-n),使用使用随机数插入数值(-r)
安全和透明的数据迁移是 Codis 提供的一个重要的功能, 也是 Codis 区别于 Twemproxy 等静态的分布式 Redis 解决方案的地方
数据迁移的最小单位是 key, 我们在 codis redis 中添加了一些指令, 实现基于 key 的迁移, 如 SLOTSMGRT 等 (命令列表), 每次会将特定 slot 一个随机的 key 发送给另外一个 codis redis 实例, 这个命令会确认对方已经接收, 同时删除本地的这个 k-v 键值, 返回这个 slot 的剩余 key 的数量, 整个操作是原子的.
在 codis-config 管理工具中, 每次迁移任务的最小单位是 slot
如: 将 slot id 为 [0-340] 的 slot 的数据, 迁移到 server group 2 上, –delay 参数表示每迁移一个 key 后 sleep 的毫秒数, 默认是 0, 用于限速.
#codis dashboard 上面查看每个组中的数据状态,
# 可以看出刚刚我们做过压测之后,每个 redis 组处理的 key 基本一致,然后我们把 group_1 中 slot 上的数据迁移到 group_2 上,–delay 参数表示每迁移一个 key 后 sleep 的毫秒数, 默认是 0, 用于限速.
$ bin/codis-config slot migrate 0 511 2 --delay=10
/opt/codis/bin/codis-config -c /opt/codis/config.ini slot migrate 0 340 2 --delay=10
# 查看 codis dashboard 上,group 状态:
# 再次刷新,3s 后
# 对比 group 中两次的 key 变化,我们可以看出 group_1 中的 slot 数据正在向 group_2 迁移。
迁移的过程对于上层业务来说是安全且透明的, 数据不会丢失, 上层不会中止服务.
注意, 迁移的过程中打断是可以的, 但是如果中断了一个正在迁移某个 slot 的任务, 下次需要先迁移掉正处于迁移状态的 slot, 否则无法继续 (即迁移程序会检查同一时刻只能有一个 slot 处于迁移状态).
Codis 支持动态的根据实例内存, 自动对 slot 进行迁移, 以均衡数据分布.
$ bin/codis-config slot rebalance
要求:
所有的 codis-server 都必须设置了 maxmemory 参数
所有的 slots 都应该处于 online 状态, 即没有迁移任务正在执行
所有 server group 都必须有 Master
Server group 实现主从切换,当同一个组中的某一台 redis 实例停止的时候, 组中的其它 redis 实例自动切换为 master 角色,提供服务。
需要安装 codis-ha 插件,codis-ha 可以安装在任意节点即可,只需要在一个节点上面安装。(本实验安装在 linux-node2 上)
go get github.com/ngaut/codis-ha
[root@linux-node2 ~]# cd /opt/codis/src/github.com/ngaut/
[root@linux-node2 ngaut]# cp -r codis-ha /opt/
[root@linux-node2 ngaut]# cd /opt/codis-ha/
[root@linux-node2 codis-ha]# go build
nohup $codis_ha_home/codis-ha -codis-config=192.168.1.150:18087 -log-level=info -productName=test &> /opt/codis-ha/codis-ha.log
注:-codis-config 后跟 dashboard 所在服务器 ip
重新启动 codis-proxy 服务
注:(以 150 机器配置为例,其余 codis-proxy 只需要修改下名称即可)
先停掉 codis-proxy
[root@linux-node3 ~]# /opt/codis/bin/codis-config -c /opt/codis/config.ini proxy offline codis_proxy_3
# 不同的机器只需要更改 codis_proxy 即可(本实验之重启了一台 codis-proxy,且 codis-ha 服务已生效)
重新启动 codis-proxy
nohup /opt/codis/bin/codis-proxy -c /opt/codis/config.ini --log-level=error -L /opt/codis/log/proxy.log --cpu=1 --addr=0.0.0.0:19000 --http-addr=0.0.0.0:11000 &
设置 codis-proxy 状态为 online
[root@linux-node3 ~]# /opt/codis/bin/codis-config -c /opt/codis/config.ini proxy online codis_proxy_3
注:如果重启一台 codis-proxy 之后 codis-ha 服务没生效,需要重启三台 codis-proxy
停止任意节点的 zookeeper,检查 codis-proxy,dashboard 是否正常 >
以 zookeeper 状态为 leader 那台服务器测试(linux-node2)
查看 zookeeper 状态确认是否为 leader
[root@linux-node2 ~]# /opt/zookeeper/bin/zkServer.sh status
停止 linux-node2 上面的 zookeeper 节点,让其他 zookeeper 节点自动选择 leader 节点并提供服务
[root@linux-node2 ~]# /opt/zookeeper/bin/zkServer.sh stop
# 随机检测其他节点的 zookeeper 状态,检测到 linux-node2 的 zookeeper 节点已经被选为 leader,开始提供服务
# 重启刷新 codis dashboard 可以看到 dashboard 是正常的。
检测 redis 客户端是否能正常访问 codis-proxy
以 linux-node3 为例:
[root@linux-node3 ~]# cd /opt/codis/src/github.com/CodisLabs/codis/extern/redis-2.8.21/src/
# 新建 scripts 目录,存放 redis 常用命令
[root@linux-node3 src]# mkdir -p /opt/codis/scripts/
[root@linux-node3 src]# cp redis-cli redis-server redis-benchmark redis-check-dump /opt/codis/scripts/
从上面可以看出我之前 set 的两个 key 都能正常 get 到
停止任意 group 中的 redis master 实例,检查 redis slave 是否自动切换为 master 模拟 linux-node1(192.168.1.148)上面的 redis-master 6379 端口挂掉
登录到 codis-ha 所在的节点(linux-node2)查看 codis-ha 日志,打印如下
打开 dashboard 界面,可以看到如下:
从图中可以看到 group_3 的 salve 节点已经自动切换到 master 状态
客户端写入数据,检查切换后的 redis master 实例是否有新 key 增加
#linux-nod3 新增 key
重新刷新 dashboard 界面,查看 key 变化
# 恢复 linux-node1 上的 redis 实例的 6379 端口
[root@linux-node1 ~]# /opt/codis/bin/codis-server /opt/codis/redis_conf/6379.conf &
[root@linux-node1 ~]# ps -ef | grep -v grep | grep codis-server
root 16620 13759 0 Aug27 pts/0 00:04:52 /opt/codis/bin/codis-server *:6380
root 27574 13759 2 19:40 pts/0 00:00:00 /opt/codis/bin/codis-server *:6379
刷新 dashboard 界面查看 group 中 redis 实例状态变化
可以看到 linux-node1 上面的端口为 6379 的 redis 实例已经启动并自动成为 group_3 中的 slave
备注:假如当 maste 挂掉是,redis-ha 检测自动将 slave 切换为 master,但是 master 恢复后,如果状态仍为 offline,可以把这个 redis 节点从 group 中移出,再重新添加。
/opt /bin/zkCli.sh
delete /zk/codis/db_test/dashboard
# 重启 codis dashboard
nohup /usr/local/codis/bin/codis-config -c /usr/local/codis/config.ini dashbo
ard &