使用Ruby通过冗长的结构来使用Redis
下列是一份备忘录。
-
- Redisを冗長構成で立ち上げる
-
- Redis Sentinelでフェイルオーバーさせる
- この環境をRubyで使う
Redis是一种开源的内存数据结构存储系统。
一种基于内存的键值存储系统。非常快速。通过将数据复制到一个或多个从节点来提高读取性能。只有主节点可以进行写入操作,但从节点也可以进行读取操作。
Redis Sentinel是什么
Redis的故障转移机制是由Sentinel之间相互监视,当主节点死亡时选择下一个主节点。Sentinel可以从外部修改Redis的配置。
通过Sentinel的投票,决定主控是否存活以及在主控死亡时提升哪个从控。通常情况下,会运行3个或更多的Sentinel。
在进行故障转移时,需要注意Redis Sentinel会修改Redis的配置文件(redis.conf)。
在这个例子中,Redis使用端口6379,Redis Sentinel使用端口26379。
此文的结构 (Cǐ de
-
- xxx.xxx.xxx.aaa – 初期設定でマスターになるホスト。Redis Sentinelを動かしている
-
- xxx.xxx.xxx.bbb – 初期設定でスレーブになるホスト。Redis Sentinelを動かしている
- xxx.xxx.xxx.ccc – 初期設定でスレーブになるホスト。Redis Sentinelを動かしている
安装
我在CentOS 6.x上进行了测试。
安装Redis需要jemalloc。只需打开EPEL存储库并运行yum命令(应该可以)。
$ yum install -y ftp://195.220.108.108/linux/remi/enterprise/6/test/i386/redis-3.0.0-2.el6.remi.i686.rpm --enablerepo=epel
Redis的设置
暂时将/etc/redis.conf进行修改。
-
- bind 127.0.0.1 があったらコメントアウトする。(他のホストからアクセスさせる場合)
-
- slaveof xxx.xxx.xxx.aaa 6379 を指定する。(レプリケーションのスレーブになるホストだけ)
- 必要に応じて、パスワードやiptablesなどを使ってセキュリティを確保する。
自动启动的设置
$ chkconfig redis on
Redis启动
$ service redis start
Redis Sentinel的配置
通常情况下,需要在三个或以上的主机上运行Redis Sentinel。即使没有运行Redis的节点也可以。需要修改/etc/redis-sentinel.conf文件。
- sentinel monitor mymaster xxx.xxx.xxx.aaa 6379 2 を書く (このアドレスとポートは、Redisマスターを示す)
设置自动启动
$ chkconfig redis-sentinel on
Redis Sentinel的启动
$ service redis-sentinel start
确认操作
当安装和确认操作在所有机器上都完成后,尝试确认其功能。
在大师的确认下
$ redis-cli info replication
# Replication
role:master
connected_slaves:2
slave0:ip=xxx.xxx.xxx.bbb,port=6379,state=online,offset=326322,lag=1
slave1:ip=xxx.xxx.xxx.ccc,port=6379,state=online,offset=326322,lag=2
master_repl_offset:326322
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:2
repl_backlog_histlen:326321
确认在奴隶身份下
$ redis-cli info replication
# Replication
role:slave
master_host:xxx.xxx.xxx.aaa
master_port:6379
master_link_status:up
master_last_io_seconds_ago:0
master_sync_in_progress:0
slave_repl_offset:301712
slave_priority:100
slave_read_only:1
connected_slaves:0
master_repl_offset:0
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0
写下来试试看
由于从属方无法进行写入操作,因此需要连接到主控方。从属方仍然可以进行读取操作。
aaa是主机,bbb是自己(从机),ccc是另一台主机(从机)。
$ redis-cli -h xxx.xxx.xxx.aaa set foo "FOO" # マスターにつないで書く
OK
$ redis-cli -h xxx.xxx.xxx.aaa get foo # マスターにつないで読む
"FOO"
$ redis-cli get foo # ローカルにつないで読む
"FOO"
$ redis-cli -h xxx.xxx.xxx.ccc get foo # 別のスレーブにつないで読む
"FOO"
尝试执行故障转移
当停止Redis主节点时,应该有一个从节点将会晋升为主节点。
$ service redis stop
Stopping redis-server: [ OK ]
$ redis-cli info replication
Could not connect to Redis at 127.0.0.1:6379: Connection refused # 止まっている
当检查从属bbb的状态时,我们发现bbb变成了主节点。
$ redis-cli info replication
# Replication
role:master
connected_slaves:1
slave0:ip=xxx.xxx.xxx.ccc,port=6379,state=online,offset=16735,lag=1
master_repl_offset:17017
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:2
repl_backlog_histlen:17016
查看在【Slave】ccc上的状态时,发现【bbb】已成为主节点。
$ redis-cli info replication
# Replication
role:slave
master_host:xxx.xxx.xxx.bbb
master_port:6379
master_link_status:up
master_last_io_seconds_ago:0
master_sync_in_progress:0
slave_repl_offset:40395
slave_priority:100
slave_read_only:1
connected_slaves:0
master_repl_offset:0
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0
原本是主节点的aaa重新启动并检查状态,但bbb仍然保持为主节点。重新启动原本的主节点也无法成为主节点。
$ service redis start
Starting redis-server: [ OK ]
$ redis-cli info replication
# Replication
role:slave
master_host:xxx.xxx.xxx.bbb
master_port:6379
master_link_status:up
master_last_io_seconds_ago:1
master_sync_in_progress:0
slave_repl_offset:57832
slave_priority:100
slave_read_only:1
connected_slaves:0
master_repl_offset:0
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0
尝试使用Ruby
为了使用Ruby中的Redis,我们可以使用redis Gem。这个Gem支持Sentinel。只需将与Sentinel相关的参数提供给Redis.new的参数即可。
require 'redis'
sentinels = [
{ host: 'xxx.xxx.xxx.aaa', port: 26379 },
{ host: 'xxx.xxx.xxx.bbb', port: 26379 },
{ host: 'xxx.xxx.xxx.ccc', port: 26379 },
]
redis = Redis.new(url:'redis://mymaster', sentinels: sentinels, role:'master')
p redis.get('foo') #=> "FOO" # 読んでみる
redis.set('bar', 'BAR') # 書いてみる
p redis.get('bar') #=> "BAR"