redis高可用性スキームredis sentinelの紹介と実践
73629 ワード
シナリオの紹介
まずredis高可用性スキームsentinelについていくつか紹介します.redisの基本的な使用と各種の概念原理の内容は比較的に多く,本稿では展開しない.1冊の本を推薦して、本文の中の内容も主にこの本の中から学ぶことができます.
**『Redis開発と運維』付磊張益軍編著**
redis sentinelはredisが公式に提供する高可用性スキームである.主な機能はredisの主従レプリケーションに基づいて,ノード障害検出,主ノード選挙,障害切替などの機能を提供する.サービスが単一のredisノードのダウンタイムによって障害を起こさないことを保証します.
まず、プライマリ・スレーブ・レプリケーションについて説明します.プライマリ・スレーブ・レプリケーションは、複数のredisサービスをプライマリ・ノードとスレーブ・ノードに分割し、プライマリ・ノードはデータの書き込みと、新しいデータを所属するスレーブ・ノードに同期します.
プライマリスレーブレプリケーションの基本原理は,初回レプリケーション時にプライマリノードがrdbデータファイルを保存し,スレーブノードに送信し,ノードから読み出したデータをデータ同期することである.rdbファイルの後のファイルマスターノードは、増加するたびに、バッファからスレーブノードに送信されてインクリメンタル同期されるバッファを保持する.
redisのプライマリスレーブレプリケーショントポロジーには、プライマリスレーブ、プライマリマルチスレーブ、ツリーマスタースレーブがあります.ここでは,よく用いられる一主多従構造について論じる.構造図は次のとおりです.
redisは,ノードの主従関係を維持するために,スレーブノードで以下の構成を用いる限り,主ノードを指定することができる.
主従関係を切断すると、ノードから次のコマンドが実行されます.
プライマリ・スレーブ・レプリケーション・モードでは、プライマリ・ノードのデータはスレーブ・ノードに同期され続けます.プライマリノードに障害が発生し、サービスが提供されない場合は、ノードからプライマリノードに切り替えてデータの読み書きを継続するサービスを探すことができます.また,各ノードの主従関係を調整し,スレーブノードを主ノードとし,残りのスレーブノードが指す主ノードもそれに応じて調整する.
ノードからプライマリノードに切り替える操作と、プライマリ・セカンダリ・トポロジー関係を調整する操作は複雑で、手作業で調整するのに時間がかかり、タイムリーではありません.従ってredisはsentinelコンポーネントを公式に提供し,このプロセスを自動化した.
導入プロセス
次にdockerコンテナを使用して3つのredisおよびsentinelノードを配置し、redisのプライマリ・スレーブ・レプリケーションおよびフェイルオーバの実装を説明します.
redisは、プライマリ・セカンダリのプライマリ・セカンダリ・レプリケーション構造を使用します.sentinelノードは3つのノードを配置します.sentinelの数が3なのは理由があります.これは、プライマリノードのダウンタイムを認定し、新しいプライマリノードを選択する際に、半分以上(半分を除く)のsentinelノードが参加する必要があるためです.したがって、3つのノードは、単一の導入を回避する最小の数です.
すべてのノードの配置にはdockerが使用されます.各ノードの詳細は次のとおりです.
ロール#ロール#
ポート
説明
master
6380
redisプライマリノード
slave-1
6381
redisスレーブノード1
slave-2
6382
redisスレーブノード2
sentinel-1
26380
sentinelノード1
sentinel-2
26381
sentinelノード2
sentinel-3
26382
sentinelノード3
トポロジーは次のとおりです.
![](/home/markfengfeng/ダウンロード/未命名ファイル(1).png)
プライマリ・セカンダリ・レプリケーションの導入
まずredisノードを配置し、主従関係を構成します.
主従構成に基づいてredisの構成ファイルを3部修正します.slave-1,slace-2はmasterノードのスレーブノードである.
マスターノードconf
slave-1ノード、slave-1.conf
slave-2ノード、slave-2.conf
dockerデプロイメントコマンドは、次のとおりです.プロファイルはdockerコンテナにマッピングされます.
**注意事項**すべてのノードパスワード構成は同じで、sentinelがプライマリノードパスワードを構成するため、異なる場合は正常に切り替えることができません. プライマリノードもmasterauthを構成する必要があります.現在のプライマリノードは、障害とリカバリが発生した後、ノードから切り替える可能性があるためです. redisポートとdockerマッピングのポートは一致している必要があります.プライマリノードは,スレーブノードを探す際にノードのredisポートから探すが,ポートが一致せず,スレーブノードが見つからない. プロファイルの1つである データの一貫性を保証するために、ノードから書き込み機能が提供されず、プロファイルに
デプロイが完了すると、
プライマリノード
2つのスレーブノード
プライマリ・スレーブ・ノードのレプリケーション情報から、プライマリ・スレーブ・ノードの構成が成功したことがわかります.
主従レプリケーションテスト
主従同期の機能をテストします.
プライマリノードでのデータ追加コマンドの実行
ノードからのデータの表示
プライマリノードのデータがスレーブノードに同期できることがわかる.
sentinel配備
次にsentinelノードを配置します.同様にdockerコンテナを使用して配置します.
3つのノードを配置しますが、ポートが異なる以外は同じ構成です.Redisノードアドレスを構成するには、プライマリノードアドレスを使用します.redis自体はGossipプロトコルを用い,masterノードを介してmasterノード上のスレーブノード情報とsentinelノード情報を各sentinelに提供する.
次のように構成されています.
sentinel-1ノード
sentinel-2ノード
sentinel-3ノード
dockerデプロイメントコマンドは、docker hubから私のdockerミラーをダウンロードします.すべての構成項目をコンテナの環境変数として構成します.したがって、上記の構成項目はdocker配置コマンドの環境変数に反映されます.
ここでは,ノードの1つの配置コマンドを出して,各環境変数の意味を説明する.
各コンテナがそれぞれ起動すると、redis-cliクライアントを使用してsentinelノードの1つに接続します.
この場合sentinelによるredisノードのモニタリングは成功した.
フェイルオーバーテスト
次に、プライマリノードがダウンタイムした後、sentinelが自動的にフェイルオーバーできるかどうかをテストします.
まずsentinelのログを開き、
6381を参照するマスタースレーブ情報は、以下の通りであり、6381はマスターノードであり、6382はスレーブノードである.
6380を6381のスレーブノードに再切り替えます.さらに6381の主従情報を見ると,6380がその従ノードとなることがわかる.
sentinelの構成は終わりました.
クライアント接続
次に、クライアント・プログラムがsentinelを介してredisデータベースに接続する方法について説明します.sentinelが自動的に切り替えた場合、クライアントプログラムの実行に影響しません.
きほんげんり
簡単に言えば、sentinelが各ノードを管理していることが知られており、現在のプライマリノードが誰であるかが知られています.ではsentinelのノードをクライアントに構成するプログラムで、クライアントはsentinelを通じて利用可能なプライマリノード接続を取得します.詳細な実装については、redisが提供するクライアントパッケージが実装されています.以下に、いくつかの例を示します.
JAvaプログラム接続
JAvaプログラムがsentinel接続でredisを使用する簡単な例.
redisのjarパッケージを先に導入
JAvaプログラムは以下の通りです
プログラムテスト
プログラムを実行して、コンソールの結果
いくつかの接続プールの初期化情報が印刷され、基本的な流れの順序がわかります.利用可能なsentinelノードによりredisマスターノード を取得する.プライマリノードのアドレス172.17.0.1:6380が見つかり、sentinelは のリスニングを開始した.プライマリノードの接続プール を作成する.最後にredisの関連apiを実行し、setとgetの結果 を印刷します.
その後、
しばらくするとsentinelがプライマリノードを切り替え、今回sentinelで取得したプライマリノードは172.17.0.1:6381になりました.
redisのノードフェイルオーバーは,クライアントプログラムがsentinelによって正しい利用可能なプライマリノードを取得できることが分かる.
スプリングの構成
通常spring構成redisの接続プールを使用するが、ここでもspring構成の例を示す.
redis関連とspring関連jarパッケージインポート
Springの構成、spring-redis.xml.redisのspring構成と比較します.違いは RedisSentinelConfigurationの構成を追加 RedisFactoryの構成を調整し、sentinelの構成をredisFactoryに注入します.クライアントがsentinelを介してredis接続を取得するため、redisのアドレス構成を削除します.
簡単なjavaプログラムは以下の通りです.
プログラムテスト
プログラムを実行し、メインノードシミュレーションのダウンタイムを途中で停止した場合、コンソールの結果は次のとおりです.
接続が開始されたのは6380ノードで、正常に動作していることがわかります.その後、プライマリノードがダウンタイムした後、プログラムに接続異常が発生しました.sentinelがプライマリノードを自動的に切り替えた後、コンソールはプライマリノードが6382ポートに切り替えたノードを印刷し、プログラムが正常に実行される.
結論
以上はsentinelの基礎的なトポロジーの導入プロセスです.実践的な操作からsentinelがredisノードの障害を自動的に検出し,切り替えることができることが分かった.また,クライアントに対してこれらの変化を遮断し,単純なクライアントプログラムの処理を行う.これはredisの高可用性をある程度保証する.
しかし、クライアントがプライマリノードのみを読み書きできるなど、サーバのパフォーマンスが単一のノードに制限されるという問題もあります.これらの問題を解決するためにredisはredis clusterのクラスタスキームを提供し,後続の研究で関連紹介を整理して送った.
まずredis高可用性スキームsentinelについていくつか紹介します.redisの基本的な使用と各種の概念原理の内容は比較的に多く,本稿では展開しない.1冊の本を推薦して、本文の中の内容も主にこの本の中から学ぶことができます.
**『Redis開発と運維』付磊張益軍編著**
redis sentinelはredisが公式に提供する高可用性スキームである.主な機能はredisの主従レプリケーションに基づいて,ノード障害検出,主ノード選挙,障害切替などの機能を提供する.サービスが単一のredisノードのダウンタイムによって障害を起こさないことを保証します.
まず、プライマリ・スレーブ・レプリケーションについて説明します.プライマリ・スレーブ・レプリケーションは、複数のredisサービスをプライマリ・ノードとスレーブ・ノードに分割し、プライマリ・ノードはデータの書き込みと、新しいデータを所属するスレーブ・ノードに同期します.
プライマリスレーブレプリケーションの基本原理は,初回レプリケーション時にプライマリノードがrdbデータファイルを保存し,スレーブノードに送信し,ノードから読み出したデータをデータ同期することである.rdbファイルの後のファイルマスターノードは、増加するたびに、バッファからスレーブノードに送信されてインクリメンタル同期されるバッファを保持する.
redisのプライマリスレーブレプリケーショントポロジーには、プライマリスレーブ、プライマリマルチスレーブ、ツリーマスタースレーブがあります.ここでは,よく用いられる一主多従構造について論じる.構造図は次のとおりです.
redisは,ノードの主従関係を維持するために,スレーブノードで以下の構成を用いる限り,主ノードを指定することができる.
slaveof {masterIP} {masterPort}
masterauth {masterPassword}
主従関係を切断すると、ノードから次のコマンドが実行されます.
slaveof no one
プライマリ・スレーブ・レプリケーション・モードでは、プライマリ・ノードのデータはスレーブ・ノードに同期され続けます.プライマリノードに障害が発生し、サービスが提供されない場合は、ノードからプライマリノードに切り替えてデータの読み書きを継続するサービスを探すことができます.また,各ノードの主従関係を調整し,スレーブノードを主ノードとし,残りのスレーブノードが指す主ノードもそれに応じて調整する.
ノードからプライマリノードに切り替える操作と、プライマリ・セカンダリ・トポロジー関係を調整する操作は複雑で、手作業で調整するのに時間がかかり、タイムリーではありません.従ってredisはsentinelコンポーネントを公式に提供し,このプロセスを自動化した.
導入プロセス
次にdockerコンテナを使用して3つのredisおよびsentinelノードを配置し、redisのプライマリ・スレーブ・レプリケーションおよびフェイルオーバの実装を説明します.
redisは、プライマリ・セカンダリのプライマリ・セカンダリ・レプリケーション構造を使用します.sentinelノードは3つのノードを配置します.sentinelの数が3なのは理由があります.これは、プライマリノードのダウンタイムを認定し、新しいプライマリノードを選択する際に、半分以上(半分を除く)のsentinelノードが参加する必要があるためです.したがって、3つのノードは、単一の導入を回避する最小の数です.
すべてのノードの配置にはdockerが使用されます.各ノードの詳細は次のとおりです.
ロール#ロール#
ポート
説明
master
6380
redisプライマリノード
slave-1
6381
redisスレーブノード1
slave-2
6382
redisスレーブノード2
sentinel-1
26380
sentinelノード1
sentinel-2
26381
sentinelノード2
sentinel-3
26382
sentinelノード3
トポロジーは次のとおりです.
![](/home/markfengfeng/ダウンロード/未命名ファイル(1).png)
プライマリ・セカンダリ・レプリケーションの導入
まずredisノードを配置し、主従関係を構成します.
主従構成に基づいてredisの構成ファイルを3部修正します.slave-1,slace-2はmasterノードのスレーブノードである.
slaveof {masterip} {masterport}
構成でマスターをプライマリノードとして指定します.重要な構成項目は次のとおりです.マスターノードconf
# IP
bind 0.0.0.0
#
port 6380
#
requirepass password
masterauth password
slave-1ノード、slave-1.conf
bind 0.0.0.0
port 6381
requirepass password
#master IP
slaveof 172.17.0.1 6380
#master
masterauth password
slave-2ノード、slave-2.conf
bind 0.0.0.0
requirepass password
port 6382
slaveof 172.17.0.1 6380
masterauth password
dockerデプロイメントコマンドは、次のとおりです.プロファイルはdockerコンテナにマッピングされます.
docker run --name master -v /data/redis/master.conf:/usr/local/etc/redis/redis.conf -p 6380:6380 -d 192.168.168.98:5000/redis redis-server /usr/local/etc/redis/redis.conf
docker run --name slave-1 -v /data/redis/slave-1.conf:/usr/local/etc/redis/redis.conf -p 6381:6381 -d 192.168.168.98:5000/redis redis-server /usr/local/etc/redis/redis.conf
docker run --name slave-2 -v /data/redis/slave-2.conf:/usr/local/etc/redis/redis.conf -p 6382:6382 -d 192.168.168.98:5000/redis redis-server /usr/local/etc/redis/redis.conf
**注意事項**
rename-command CONFIG "Sstarnetpbx"
は、後でsentinelが障害ノードを切り替えることに影響します.slave-read-only yes
を追加するには、一般的にデフォルトの構成項目があり、変更する必要はありません.デプロイが完了すると、
info replication
コマンドを使用してredisのプライマリ・セカンダリ・サービス情報を表示できます.プライマリノード
markfengfeng@markfengfeng:~$ redis-cli -p 6380 -a password6380
127.0.0.1:6380> info replication
# Replication
role:master #
connected_slaves:2 #
slave0:ip=172.17.0.1,port=6381,state=online,offset=28,lag=1 # 1 , , ,
slave1:ip=172.17.0.1,port=6382,state=online,offset=28,lag=1 # 2 , , ,
master_replid:73e9036c293329e91c87f719794d0095438c6154
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:28
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:28
2つのスレーブノード
markfengfeng@markfengfeng:~$ redis-cli -p 6381 -a password6381
127.0.0.1:6381> info replication
# Replication
role:slave #
master_host:172.17.0.1 #
master_port:6380 #
master_link_status:up #
master_last_io_seconds_ago:8
master_sync_in_progress:0
slave_repl_offset:1652
slave_priority:100
slave_read_only:1
connected_slaves:0
master_replid:73e9036c293329e91c87f719794d0095438c6154
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:1652
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:1652
127.0.0.1:6381> exit
markfengfeng@markfengfeng:~$ redis-cli -p 6382 -a password6382
127.0.0.1:6382> info replication
# Replication
role:slave
master_host:172.17.0.1
master_port:6380
master_link_status:up
master_last_io_seconds_ago:5
master_sync_in_progress:0
slave_repl_offset:1708
slave_priority:100
slave_read_only:1
connected_slaves:0
master_replid:73e9036c293329e91c87f719794d0095438c6154
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:1708
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:15
repl_backlog_histlen:1694
プライマリ・スレーブ・ノードのレプリケーション情報から、プライマリ・スレーブ・ノードの構成が成功したことがわかります.
主従レプリケーションテスト
主従同期の機能をテストします.
プライマリノードでのデータ追加コマンドの実行
markfengfeng@markfengfeng:~$ redis-cli -p 6380 -a password6380
127.0.0.1:6380> set name xiehuafeng
OK
127.0.0.1:6380> get name
"xiehuafeng"
127.0.0.1:6380>
ノードからのデータの表示
markfengfeng@markfengfeng:~$ redis-cli -p 6381 -a password6381
127.0.0.1:6381> get name
"xiehuafeng"
127.0.0.1:6381> exit
markfengfeng@markfengfeng:~$ redis-cli -p 6382 -a password6382
127.0.0.1:6382> get name
"xiehuafeng"
プライマリノードのデータがスレーブノードに同期できることがわかる.
sentinel配備
次にsentinelノードを配置します.同様にdockerコンテナを使用して配置します.
3つのノードを配置しますが、ポートが異なる以外は同じ構成です.Redisノードアドレスを構成するには、プライマリノードアドレスを使用します.redis自体はGossipプロトコルを用い,masterノードを介してmasterノード上のスレーブノード情報とsentinelノード情報を各sentinelに提供する.
次のように構成されています.
sentinel-1ノード
port 26380 #sentinel
sentinel monitor master 172.17.0.1 6380 2 # redis ,2 sentinel
sentinel auth-pass master password #
sentinel down-after-milliseconds master 30000 # 30 ,
sentinel parallel-syncs master 1 # ,
sentinel-2ノード
port 26381
sentinel monitor master 172.17.0.1 6380 2
sentinel auth-pass master password
sentinel down-after-milliseconds master 30000
sentinel parallel-syncs master 1
sentinel-3ノード
port 26382
sentinel monitor master 172.17.0.1 6380 2
sentinel auth-pass master password
sentinel down-after-milliseconds master 30000
sentinel parallel-syncs master 1
dockerデプロイメントコマンドは、docker hubから私のdockerミラーをダウンロードします.すべての構成項目をコンテナの環境変数として構成します.したがって、上記の構成項目はdocker配置コマンドの環境変数に反映されます.
docker run --name sentinel-1 -p 26380:26380 -e SENTINEL_PORT=26380 -e MASTER_NAME=master -e QUORUM=2 -e DOWN_AFTER=30000 -e PARALLEL_SYNCS=1 -e MASTER=172.17.0.1:6380 -e AUTH_PASS=password -d s7anley/redis-sentinel-docker
docker run --name sentinel-2 -p 26381:26381 -e SENTINEL_PORT=26381 -e MASTER_NAME=master -e QUORUM=2 -e DOWN_AFTER=30000 -e PARALLEL_SYNCS=1 -e MASTER=172.17.0.1:6380 -e AUTH_PASS=password -d s7anley/redis-sentinel-docker
docker run --name sentinel-3 -p 26382:26382 -e SENTINEL_PORT=26382 -e MASTER_NAME=master -e QUORUM=2 -e DOWN_AFTER=30000 -e PARALLEL_SYNCS=1 -e MASTER=172.17.0.1:6380 -e AUTH_PASS=password -d s7anley/redis-sentinel-docker
ここでは,ノードの1つの配置コマンドを出して,各環境変数の意味を説明する.
docker run --name sentinel-1 -p 26380:26380
-e SENTINEL_PORT=26380 # sentinel 26380
-e MASTER_NAME=master #
-e QUORUM=2 # 2
-e DOWN_AFTER=30000 #30000 redis ,
-e PARALLEL_SYNCS=1 # , , 1
-e MASTER=172.17.0.1:6380 # ip
-e AUTH_PASS=password #
-d s7anley/redis-sentinel-docker
各コンテナがそれぞれ起動すると、redis-cliクライアントを使用してsentinelノードの1つに接続します.
info
コマンドを使用して接続の詳細を表示markfengfeng@markfengfeng:~$ redis-cli -p 26380
127.0.0.1:26380> info
# Server
redis_version:2.8.16
redis_git_sha1:00000000
redis_git_dirty:0
redis_build_id:9b6f16e360929887
redis_mode:sentinel
os:Linux 4.15.0-45-generic x86_64
arch_bits:64
multiplexing_api:epoll
gcc_version:4.8.2
process_id:1
run_id:0c53be652240cc31135318c7f94bae86329b97bd
tcp_port:26380
uptime_in_seconds:258
uptime_in_days:0
hz:10
lru_clock:8899661
config_file:/etc/redis/sentinel.conf
# Sentinel
sentinel_masters:1
sentinel_tilt:0
sentinel_running_scripts:0
sentinel_scripts_queue_length:0
master0:name=master,status=ok,address=172.17.0.1:6380,slaves=2,sentinels=3
# sentinel ok,master ip, 。 sentinel
この場合sentinelによるredisノードのモニタリングは成功した.
フェイルオーバーテスト
次に、プライマリノードがダウンタイムした後、sentinelが自動的にフェイルオーバーできるかどうかをテストします.
まずsentinelのログを開き、
docker stop master
でプライマリノードを手動で閉じます.1:X 14 Mar 14:53:41.237 # +sdown master master 172.17.0.1 6380
1:X 14 Mar 14:53:41.325 # +new-epoch 1
1:X 14 Mar 14:53:41.333 # +vote-for-leader 06b9bedb039ec5cd119e2ea24ae02f3513e7a4f8 1
1:X 14 Mar 14:53:41.333 # +odown master master 172.17.0.1 6380 #quorum 2/2
1:X 14 Mar 14:53:41.333 # Next failover delay: I will not start a failover before Thu Mar 14 14:59:42 2019
1:X 14 Mar 14:53:42.538 # +config-update-from sentinel 06b9bedb039ec5cd119e2ea24ae02f3513e7a4f8 172.17.0.5 26381 @ master 172.17.0.1 6380
1:X 14 Mar 14:53:42.538 # +switch-master master 172.17.0.1 6380 172.17.0.1 6381
# 6381
1:X 14 Mar 14:53:42.538 * +slave slave 172.17.0.1:6382 172.17.0.1 6382 @ master 172.17.0.1 6381 6382 6381
6381を参照するマスタースレーブ情報は、以下の通りであり、6381はマスターノードであり、6382はスレーブノードである.
markfengfeng@markfengfeng:~$ redis-cli -p 6381 -a password
127.0.0.1:6381> info replication
# Replication
role:master
connected_slaves:1
slave0:ip=172.17.0.1,port=6382,state=online,offset=74435,lag=1
master_replid:7be531aa99fa3f3b73effb05823a273163c7b078
master_replid2:67b5a57405c7a737ba461f2ee9d949909014f7ac
master_repl_offset:74701
second_repl_offset:10923
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:74701
docker start master
コマンドを使用してmasterノードを起動1:X 14 Mar 15:00:05.866 * +convert-to-slave slave 172.17.0.1:6380 172.17.0.1 6380 @ master 172.17.0.1 6381
6380を6381のスレーブノードに再切り替えます.さらに6381の主従情報を見ると,6380がその従ノードとなることがわかる.
127.0.0.1:6381> info replication
# Replication
role:master
connected_slaves:2
slave0:ip=172.17.0.1,port=6382,state=online,offset=113285,lag=1
slave1:ip=172.17.0.1,port=6380,state=online,offset=113418,lag=1
master_replid:7be531aa99fa3f3b73effb05823a273163c7b078
master_replid2:67b5a57405c7a737ba461f2ee9d949909014f7ac
master_repl_offset:113418
second_repl_offset:10923
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:113418
sentinelの構成は終わりました.
クライアント接続
次に、クライアント・プログラムがsentinelを介してredisデータベースに接続する方法について説明します.sentinelが自動的に切り替えた場合、クライアントプログラムの実行に影響しません.
きほんげんり
簡単に言えば、sentinelが各ノードを管理していることが知られており、現在のプライマリノードが誰であるかが知られています.ではsentinelのノードをクライアントに構成するプログラムで、クライアントはsentinelを通じて利用可能なプライマリノード接続を取得します.詳細な実装については、redisが提供するクライアントパッケージが実装されています.以下に、いくつかの例を示します.
JAvaプログラム接続
JAvaプログラムがsentinel接続でredisを使用する簡単な例.
redisのjarパッケージを先に導入
<dependencies>
<dependency>
<groupId>redis.clientsgroupId>
<artifactId>jedisartifactId>
<version>2.8.2version>
dependency>
dependencies>
JAvaプログラムは以下の通りです
public class JedisSentinelDemo {
private static JedisSentinelPool pool = null;
public void initPool() {
// sentinel set
Set<String> sentinels = new HashSet<String>();
sentinels.add("172.17.0.1:26380");
sentinels.add("172.17.0.1:26381");
sentinels.add("172.17.0.1:26382");
// , api
GenericObjectPoolConfig config = new GenericObjectPoolConfig();
// sentinel , ( ,sentinel , , ,redis )
pool = new JedisSentinelPool("master", sentinels, config, 10000, "password");
}
public static void main(String[] args) {
JedisSentinelDemo demo = new JedisSentinelDemo();
demo.initPool();
Jedis jedis = null;
try {
int incre = 1;
while (true) {
Thread.currentThread().sleep(10000);
jedis = pool.getResource();
jedis.set("number", String.valueOf(incre++));
String value = jedis.get("number");
System.out.println(value);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (jedis != null)
jedis.close();
}
}
}
プログラムテスト
プログラムを実行して、コンソールの結果
18, 2019 11:10:29 redis.clients.jedis.JedisSentinelPool initSentinels
: Trying to find master from available Sentinels...
18, 2019 11:10:29 redis.clients.jedis.JedisSentinelPool initSentinels
: Redis master running at 172.17.0.1:6380, starting Sentinel listeners...
18, 2019 11:10:29 redis.clients.jedis.JedisSentinelPool initPool
: Created JedisPool to master at 172.17.0.1:6380
1
2
redis.clients.jedis.exceptions.JedisConnectionException: Unexpected end of stream.
at redis.clients.util.RedisInputStream.ensureFill(RedisInputStream.java:199)
at redis.clients.util.RedisInputStream.readByte(RedisInputStream.java:40)
at redis.clients.jedis.Protocol.process(Protocol.java:147)
at redis.clients.jedis.Protocol.read(Protocol.java:211)
at redis.clients.jedis.Connection.readProtocolWithCheckingBroken(Connection.java:297)
at redis.clients.jedis.Connection.getStatusCodeReply(Connection.java:196)
at redis.clients.jedis.Jedis.set(Jedis.java:69)
at test.JedisSentinelDemo.main(JedisSentinelDemo.java:35)
redis.clients.jedis.exceptions.JedisConnectionException: Could not get a resource from the pool
at redis.clients.util.Pool.getResource(Pool.java:53)
at redis.clients.jedis.JedisSentinelPool.getResource(JedisSentinelPool.java:209)
at test.JedisSentinelDemo.main(JedisSentinelDemo.java:34)
Caused by: redis.clients.jedis.exceptions.JedisConnectionException: java.net.ConnectException: (Connection refused)
at redis.clients.jedis.Connection.connect(Connection.java:164)
at redis.clients.jedis.BinaryClient.connect(BinaryClient.java:80)
at redis.clients.jedis.BinaryJedis.connect(BinaryJedis.java:1677)
at redis.clients.jedis.JedisFactory.makeObject(JedisFactory.java:87)
at org.apache.commons.pool2.impl.GenericObjectPool.create(GenericObjectPool.java:868)
at org.apache.commons.pool2.impl.GenericObjectPool.borrowObject(GenericObjectPool.java:435)
at org.apache.commons.pool2.impl.GenericObjectPool.borrowObject(GenericObjectPool.java:363)
at redis.clients.util.Pool.getResource(Pool.java:49)
... 2 more
Caused by: java.net.ConnectException: (Connection refused)
at java.net.PlainSocketImpl.socketConnect(Native Method)
at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:350)
at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:206)
at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:188)
at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392)
at java.net.Socket.connect(Socket.java:589)
at redis.clients.jedis.Connection.connect(Connection.java:158)
... 9 more
redis.clients.jedis.exceptions.JedisException: Could not return the resource to the pool
at redis.clients.util.Pool.returnBrokenResourceObject(Pool.java:103)
at redis.clients.jedis.JedisSentinelPool.returnBrokenResource(JedisSentinelPool.java:234)
at redis.clients.jedis.JedisSentinelPool.returnBrokenResource(JedisSentinelPool.java:17)
at redis.clients.jedis.Jedis.close(Jedis.java:3355)
at test.JedisSentinelDemo.main(JedisSentinelDemo.java:43)
Caused by: java.lang.IllegalStateException: Invalidated object not currently part of this pool
at org.apache.commons.pool2.impl.GenericObjectPool.invalidateObject(GenericObjectPool.java:640)
at redis.clients.util.Pool.returnBrokenResourceObject(Pool.java:101)
... 4 more
redis.clients.jedis.exceptions.JedisConnectionException: Could not get a resource from the pool
at redis.clients.util.Pool.getResource(Pool.java:53)
at redis.clients.jedis.JedisSentinelPool.getResource(JedisSentinelPool.java:209)
at test.JedisSentinelDemo.main(JedisSentinelDemo.java:34)
Caused by: redis.clients.jedis.exceptions.JedisConnectionException:
......
18, 2019 11:11:30 redis.clients.jedis.JedisSentinelPool initPool
: Created JedisPool to master at 172.17.0.1:6381
4
5
いくつかの接続プールの初期化情報が印刷され、基本的な流れの順序がわかります.
その後、
docker stop master
でプライマリノードを停止すると、プログラムに接続異常が発生します.しばらくするとsentinelがプライマリノードを切り替え、今回sentinelで取得したプライマリノードは172.17.0.1:6381になりました.
redisのノードフェイルオーバーは,クライアントプログラムがsentinelによって正しい利用可能なプライマリノードを取得できることが分かる.
スプリングの構成
通常spring構成redisの接続プールを使用するが、ここでもspring構成の例を示す.
redis関連とspring関連jarパッケージインポート
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0modelVersion>
<groupId>com.xhfgroupId>
<artifactId>JedisSentinelSpringartifactId>
<version>0.0.1-SNAPSHOTversion>
<name>JedisSentinelSpringname>
<dependencies>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-beansartifactId>
<version>4.0.8.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-coreartifactId>
<version>4.0.8.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-contextartifactId>
<version>4.0.8.RELEASEversion>
dependency>
<dependency>
<groupId>redis.clientsgroupId>
<artifactId>jedisartifactId>
<version>2.8.2version>
dependency>
<dependency>
<groupId>org.springframework.datagroupId>
<artifactId>spring-data-redisartifactId>
<version>1.7.5.RELEASEversion>
dependency>
<dependency>
<groupId>com.fasterxml.jackson.coregroupId>
<artifactId>jackson-databindartifactId>
<version>2.8.4version>
dependency>
dependencies>
project>
Springの構成、spring-redis.xml.redisのspring構成と比較します.違いは
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd"
xmlns:cache="http://www.springframework.org/schema/cache
http://www.springframework.org/schema/cache
http://www.springframework.org/schema/cache/spring-cache.xsd">
<bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig" >
<property name="maxIdle" value="10000" />
<property name="maxWaitMillis" value="10000" />
<property name="testOnBorrow" value="true" />
bean >
<bean id="redisSentinelConfiguration" class="org.springframework.data.redis.connection.RedisSentinelConfiguration">
<property name="master">
<bean class="org.springframework.data.redis.connection.RedisNode">
<property name="name" value="master">
property>
bean>
property>
<property name="sentinels">
<set>
<bean class="org.springframework.data.redis.connection.RedisNode">
<constructor-arg name="host" value="172.17.0.1" />
<constructor-arg name="port" value="26380" />
bean>
<bean class="org.springframework.data.redis.connection.RedisNode">
<constructor-arg name="host" value="172.17.0.1" />
<constructor-arg name="port" value="26381" />
bean>
<bean class="org.springframework.data.redis.connection.RedisNode ">
<constructor-arg name="host" value="172.17.0.1" />
<constructor-arg name="port" value="26382" />
bean>
set>
property>
bean>
<bean id="connectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory" >
<property name="password" value="password" />
<property name="timeout" value="30000" >property>
<constructor-arg name="sentinelConfig" ref="redisSentinelConfiguration"> constructor-arg>
<constructor-arg name="poolConfig" ref="poolConfig">constructor-arg>
bean >
<bean id="keySerializer" class="org.springframework.data.redis.serializer.GenericToStringSerializer">
<constructor-arg index="0" type="java.lang.Class" value="java.lang.Object" />
bean>
<bean id="serializer" class="org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer">
bean>
<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
<property name="connectionFactory" ref="connectionFactory" />
<property name="defaultSerializer" ref="serializer" />
<property name="keySerializer" ref="keySerializer" />
<property name="hashKeySerializer" ref="keySerializer" />
bean>
beans>
簡単なjavaプログラムは以下の通りです.
public class Main {
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring-redis.xml");
RedisTemplate<String, String> template = (RedisTemplate<String, String>) context.getBean("redisTemplate");
int incre = 0;
while (true) {
try {
Thread.currentThread().sleep(5000);
template.opsForValue().set("number", String.valueOf(incre++));
String city = template.opsForValue().get("number");
System.out.println(city);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
プログラムテスト
プログラムを実行し、メインノードシミュレーションのダウンタイムを途中で停止した場合、コンソールの結果は次のとおりです.
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
18, 2019 12:36:50 redis.clients.jedis.JedisSentinelPool initSentinels
: Trying to find master from available Sentinels...
18, 2019 12:36:50 redis.clients.jedis.JedisSentinelPool initSentinels
: Redis master running at 172.17.0.1:6380, starting Sentinel listeners...
18, 2019 12:36:50 redis.clients.jedis.JedisSentinelPool initPool
: Created JedisPool to master at 172.17.0.1:6380
0
1
2
3
4
5
6
7
8
9
org.springframework.data.redis.RedisConnectionFailureException: Cannot get Jedis connection; nested exception is redis.clients.jedis.exceptions.JedisConnectionException: Could not get a resource from the pool
at org.springframework.data.redis.connection.jedis.JedisConnectionFactory.fetchJedisConnector(JedisConnectionFactory.java:198)
at org.springframework.data.redis.connection.jedis.JedisConnectionFactory.getConnection(JedisConnectionFactory.java:345)
at org.springframework.data.redis.core.RedisConnectionUtils.doGetConnection(RedisConnectionUtils.java:129)
at org.springframework.data.redis.core.RedisConnectionUtils.getConnection(RedisConnectionUtils.java:92)
at org.springframework.data.redis.core.RedisConnectionUtils.getConnection(RedisConnectionUtils.java:79)
at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:191)
at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:166)
at org.springframework.data.redis.core.AbstractOperations.execute(AbstractOperations.java:88)
at org.springframework.data.redis.core.DefaultValueOperations.set(DefaultValueOperations.java:169)
at Main.main(Main.java:12)
Caused by: redis.clients.jedis.exceptions.JedisConnectionException: Could not get a resource from the pool
at redis.clients.util.Pool.getResource(Pool.java:53)
at redis.clients.jedis.JedisSentinelPool.getResource(JedisSentinelPool.java:209)
at redis.clients.jedis.JedisSentinelPool.getResource(JedisSentinelPool.java:17)
at org.springframework.data.redis.connection.jedis.JedisConnectionFactory.fetchJedisConnector(JedisConnectionFactory.java:191)
... 9 more
Caused by: redis.clients.jedis.exceptions.JedisConnectionException: java.net.ConnectException: (Connection refused)
at redis.clients.jedis.Connection.connect(Connection.java:164)
at redis.clients.jedis.BinaryClient.connect(BinaryClient.java:80)
at redis.clients.jedis.BinaryJedis.connect(BinaryJedis.java:1677)
at redis.clients.jedis.JedisFactory.makeObject(JedisFactory.java:87)
at org.apache.commons.pool2.impl.GenericObjectPool.create(GenericObjectPool.java:868)
at org.apache.commons.pool2.impl.GenericObjectPool.borrowObject(GenericObjectPool.java:435)
at org.apache.commons.pool2.impl.GenericObjectPool.borrowObject(GenericObjectPool.java:363)
at redis.clients.util.Pool.getResource(Pool.java:49)
... 12 more
Caused by: java.net.ConnectException: (Connection refused)
at java.net.PlainSocketImpl.socketConnect(Native Method)
at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:350)
at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:206)
at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:188)
at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392)
at java.net.Socket.connect(Socket.java:589)
at redis.clients.jedis.Connection.connect(Connection.java:158)
... 19 more
...............
18, 2019 12:38:12 redis.clients.jedis.JedisSentinelPool initPool
: Created JedisPool to master at 172.17.0.1:6381
18, 2019 12:38:12 redis.clients.jedis.JedisSentinelPool initPool
: Created JedisPool to master at 172.17.0.1:6381
16
17
接続が開始されたのは6380ノードで、正常に動作していることがわかります.その後、プライマリノードがダウンタイムした後、プログラムに接続異常が発生しました.sentinelがプライマリノードを自動的に切り替えた後、コンソールはプライマリノードが6382ポートに切り替えたノードを印刷し、プログラムが正常に実行される.
結論
以上はsentinelの基礎的なトポロジーの導入プロセスです.実践的な操作からsentinelがredisノードの障害を自動的に検出し,切り替えることができることが分かった.また,クライアントに対してこれらの変化を遮断し,単純なクライアントプログラムの処理を行う.これはredisの高可用性をある程度保証する.
しかし、クライアントがプライマリノードのみを読み書きできるなど、サーバのパフォーマンスが単一のノードに制限されるという問題もあります.これらの問題を解決するためにredisはredis clusterのクラスタスキームを提供し,後続の研究で関連紹介を整理して送った.