Java中高级核心知识全面解析(3),美团Java面试算法题
特別是當出現?斷線重復制?的情況是時,為了讓從服務器補足斷線時確實的那一小部分數據,卻要執行一次如此耗資源的?SYNC?命令,顯然是不合理的。
③、PSYNC 命令的引入
所以在?Redis 2.8?中引入了?PSYNC?命令來代替?SYNC?,它具有兩種模式:
部分復制的原理主要是靠主從節點分別維護一個?復制偏移量,有了這個偏移量之后斷線重連之后一比較,之后就可以僅僅把從服務器斷線之后確實的這部分數據給補回來了。
3.Redis Sentinel 哨兵
上圖展示了一個典型的哨兵架構圖,它由兩部分組成,哨兵節點和數據節點:
- 哨兵節點: 哨兵系統由一個或多個哨兵節點組成,哨兵節點是特殊的 Redis 節點,不存儲數據;
- 數據節點: 主節點和從節點都是數據節點;
在復制的基礎上,哨兵實現了 自動化的故障恢復 功能,下方是官方對于哨兵功能的描述:
- 監控(Monitoring): 哨兵會不斷地檢查主節點和從節點是否運作正常。
- 自動故障轉移(Automatic failover): 當?主節點?不能正常工作時,哨兵會開始?自動故障轉移操作,它會將失效主節點的其中一個?從節點升級為新的主節點,并讓其他從節點改為復制新的主節點。
- 配置提供者(Configuration provider): 客戶端在初始化時,通過連接哨兵來獲得當前 Redis服務的主節點地址。
- 通知(Notification): 哨兵可以將故障轉移的結果發送給客戶端。
其中,監控和自動故障轉移功能,使得哨兵可以及時發現主節點故障并完成轉移。而配置提供者和通知功能,則需要在與客戶端的交互中才能體現。
1)快速體驗
①、第一步:創建主從節點配置文件并啟動
正確安裝好 Redis 之后,我們去到 Redis 的安裝目錄 (mac 默認在?/usr/local/?),找到?redis.conf文件復制三份分別命名為?redis-master.conf / redis-slave1.conf / redis-slave2.conf?,分別作為1?個主節點和2?個從節點的配置文件 (下圖演示了我本機的?redis.conf?文件的位置)
打開可以看到這個?.conf?后綴的文件里面有很多說明的內容,全部刪除然后分別改成下面的樣子:
#redis-master.conf port 6379 daemonize yes logfile "6379.log" dbfilename "dump-6379.rdb" #redis-slave1.conf port 6380 daemonize yes logfile "6380.log" dbfilename "dump-6380.rdb" slaveof 127.0.0.1 6379 #redis-slave2.conf port 6381 daemonize yes logfile "6381.log" dbfilename "dump-6381.rdb" slaveof 127.0.0.1 6379然后我們可以執行?redis-server <config file path>?來根據配置文件啟動不同的 Redis 實例,依次啟動主從節點:
redis-server /usr/local/redis-5.0.3/redis-master.conf redis-server /usr/local/redis-5.0.3/redis-slave1.conf redis-server /usr/local/redis-5.0.3/redis-slave2.conf節點啟動后,我們執行?redis-cli?默認連接到我們端口為?6379?的主節點執行?info Replication?檢查一下主從狀態是否正常:(可以看到下方正確地顯示了兩個從節點)
②、第二步:創建哨兵節點配置文件并啟動
按照上面同樣的方法,我們給哨兵節點也創建三個配置文件。(哨兵節點本質上是特殊的 Redis 節點,所以配置幾乎沒什么差別,只是在端口上做區分就好)
# redis-sentinel-1.conf port 26379 daemonize yes logfile "26379.log" sentinel monitor mymaster 127.0.0.1 6379 2 # redis-sentinel-2.conf port 26380 daemonize yes logfile "26380.log" sentinel monitor mymaster 127.0.0.1 6379 2 # redis-sentinel-3.conf port 26381 daemonize yes logfile "26381.log" sentinel monitor mymaster 127.0.0.1 6379 2其中,?sentinel monitor mymaster 127.0.0.1 6379 2?配置的含義是:該哨兵節點監控?127.0.0.1:6379?這個主節點,該主節點的名稱是?mymaster?,最后的?2?的含義與主節點的故障判定有關:至少需要?2?個哨兵節點同意,才能判定主節點故障并進行故障轉移。
執行下方命令將哨兵節點啟動起來:
redis-server /usr/local/redis-5.0.3/redis-sentinel-1.conf --sentinel redis-server /usr/local/redis-5.0.3/redis-sentinel-2.conf --sentinel redis-server /usr/local/redis-5.0.3/redis-sentinel-3.conf --sentinel使用?redis-cil?工具連接哨兵節點,并執行?info Sentinel?命令來查看是否已經在監視主節點了:
# 連接端口為 26379 的 Redis 節點 ? ~ redis-cli -p 26379 127.0.0.1:26379> info Sentinel # Sentinel sentinel_masters:1 sentinel_tilt:0 sentinel_running_scripts:0 sentinel_scripts_queue_length:0 sentinel_simulate_failure_flags:0 master0:name=mymaster,status=ok,address=127.0.0.1:6379,slaves=2,sentinels=3此時你打開剛才寫好的哨兵配置文件,你還會發現出現了一些變化:
③、第三步:演示故障轉移
首先,我們使用?kill -9?命令來殺掉主節點,同時?在哨兵節點中執行?info Sentinel?命令來觀察故障節點的過程:
? ~ ps aux | grep 6379 longtao 74529 0.3 0.0 4346936 2132 ?? Ss 10:30上午 0:03.09 redis-server *:26379 [sentinel] longtao 73541 0.2 0.0 4348072 2292 ?? Ss 10:18上午 0:04.79 redis-server *:6379 longtao 75521 0.0 0.0 4286728 728 s008 S+ 10:39上午 0:00.00 grep --color=auto --exclude-dir=.bzr --exclude-dir=CVS --exclude-dir=.git -- exclude-dir=.hg --exclude-dir=.svn 6379 longtao 74836 0.0 0.0 4289844 944 s006 S+ 10:32上午 0:00.01 redis-cli -p 26379 ? ~ kill -9 73541如果?剛殺掉瞬間?在哨兵節點中執行?info?命令來查看,會發現主節點還沒有切換過來,因為哨兵發現主節點故障并轉移需要一段時間:
# 第一時間查看哨兵節點發現并未轉移,還在 6379 端口 127.0.0.1:26379> info Sentinel # Sentinel sentinel_masters:1 sentinel_tilt:0 sentinel_running_scripts:0 sentinel_scripts_queue_length:0 sentinel_simulate_failure_flags:0 master0:name=mymaster,status=ok,address=127.0.0.1:6379,slaves=2,sentinels=3一段時間之后你再執行?info?命令,查看,你就會發現主節點已經切換成了?6381?端口的從節點:
# 過一段時間之后在執行,發現已經切換了 6381 端口 127.0.0.1:26379> info Sentinel # Sentinel sentinel_masters:1 sentinel_tilt:0 sentinel_running_scripts:0 sentinel_scripts_queue_length:0 sentinel_simulate_failure_flags:0 master0:name=mymaster,status=ok,address=127.0.0.1:6381,slaves=2,sentinels=3但同時還可以發現,哨兵節點認為新的主節點仍然有兩個從節點 (上方 slaves=2),這是因為哨兵在將?6381?切換成主節點的同時,將?6379?節點置為其從節點。雖然?6379?從節點已經掛掉,但是由于?哨兵并不會對從節點進行客觀下線,因此認為該從節點一直存在。當?6379?節點重新啟動后,會自動變成6381?節點的從節點。
另外,在故障轉移的階段,哨兵和主從節點的配置文件都會被改寫:
- 對于主從節點: 主要是?slaveof?配置的變化,新的主節點沒有了?slaveof?配置,其從節點則slaveof?新的主節點。
- 對于哨兵節點: 除了主從節點信息的變化,紀元(epoch) (記錄當前集群狀態的參數) 也會變化,紀元相關的參數都 +1 了。
2)客戶端訪問哨兵系統代碼演示
上面我們在?快速體驗?中主要感受到了服務端自己對于當前主從節點的自動化治理,下面我們以 Java 代碼為例,來演示一下客戶端如何訪問我們的哨兵系統:
public static void testSentinel() throws Exception { String masterName = "mymaster"; Set<String> sentinels = new HashSet<>(); sentinels.add("127.0.0.1:26379"); sentinels.add("127.0.0.1:26380"); sentinels.add("127.0.0.1:26381"); // 初始化過程做了很多工作 JedisSentinelPool pool = new JedisSentinelPool(masterName, sentinels); Jedis jedis = pool.getResource(); jedis.set("key1", "value1"); pool.close(); }①、客戶端原理
Jedis 客戶端對哨兵提供了很好的支持。如上述代碼所示,我們只需要向 Jedis 提供哨兵節點集合和?masterName?,構造?JedisSentinelPool?對象,然后便可以像使用普通 Redis 連接池一樣來使用了:通過?pool.getResource()?獲取連接,執行具體的命令。
在整個過程中,我們的代碼不需要顯式的指定主節點的地址,就可以連接到主節點;代碼中對故障轉移沒有任何體現,就可以在哨兵完成故障轉移后自動的切換主節點。之所以可以做到這一點,是因為在?JedisSentinelPool?的構造器中,進行了相關的工作;主要包括以下兩點:
3)新的主服務器是怎樣被挑選出來的?
故障轉移操作的第一步?要做的就是在已下線主服務器屬下的所有從服務器中,挑選出一個狀態良好、數據完整的從服務器,然后向這個從服務器發送?slaveof no one?命令,將這個從服務器轉換為主服務器。但是這個從服務器是怎么樣被挑選出來的呢?
簡單來說 Sentinel 使用以下規則來選擇新的主服務器:
4.Redis 集群
上圖 展示了?Redis Cluster?典型的架構圖,集群中的每一個 Redis 節點都?互相兩兩相連,客戶端任意直連?到集群中的?任意一臺,就可以對其他 Redis 節點進行?讀寫?的操作。
1)基本原理
Redis 集群中內置了?16384?個哈希槽。當客戶端連接到 Redis 集群之后,會同時得到一份關于這個?集群的配置信息,當客戶端具體對某一個?key?值進行操作時,會計算出它的一個 Hash 值,然后把結果對?16384?求余數,這樣每個key?都會對應一個編號在?0-16383?之間的哈希槽,Redis 會根據節點數量?大致均等?的將哈希槽映射到不同的節點。
再結合集群的配置信息就能夠知道這個?key?值應該存儲在哪一個具體的 Redis 節點中,如果不屬于自己管,那么就會使用一個特殊的?MOVED?命令來進行一個跳轉,告訴客戶端去連接這個節點以獲取數據:
GET x -MOVED 3999 127.0.0.1:6381MOVED?指令第一個參數?3999?是?key?對應的槽位編號,后面是目標節點地址,?MOVED?命令前面有一個減號,表示這是一個錯誤的消息。客戶端在收到?MOVED?指令后,就立即糾正本地的?槽位映射表,那么下一次再訪問?key?時就能夠到正確的地方去獲取了。
2)集群的主要作用
3)快速體驗
①、第一步:創建集群節點配置文件
首先我們找一個地方創建一個名為?redis-cluster?的目錄:
mkdir -p ~/Desktop/redis-cluster然后按照上面的方法,創建六個配置文件,分別命名為:redis_7000.conf?/?redis_7001.conf?…?redis_7005.conf,然后根據不同的端口號修改對應的端口值就好了:
# 后臺執行 daemonize yes # 端口號 port 7000 # 為每一個集群節點指定一個 pid_file pidfile ~/Desktop/redis-cluster/redis_7000.pid # 啟動集群模式 cluster-enabled yes # 每一個集群節點都有一個配置文件,這個文件是不能手動編輯的。確保每一個集群節點的配置文件不通 cluster-config-file nodes-7000.conf # 集群節點的超時時間,單位:ms,超時后集群會認為該節點失敗 cluster-node-timeout 5000 # 最后將 appendonly 改成 yes(AOF 持久化) appendonly yes記得把對應上述配置文件中根端口對應的配置都修改掉 (port/ pidfile/ cluster-config-file)。
②、第二步:分別啟動 6 個 Redis 實例
redis-server ~/Desktop/redis-cluster/redis_7000.conf redis-server ~/Desktop/redis-cluster/redis_7001.conf redis-server ~/Desktop/redis-cluster/redis_7002.conf redis-server ~/Desktop/redis-cluster/redis_7003.conf redis-server ~/Desktop/redis-cluster/redis_7004.conf redis-server ~/Desktop/redis-cluster/redis_7005.conf然后執行?ps -ef | grep redis?查看是否啟動成功:
可以看到?6?個 Redis 節點都以集群的方式成功啟動了,但是現在每個節點還處于獨立的狀態,也就是說它們每一個都各自成了一個集群,還沒有互相聯系起來,我們需要手動地把他們之間建立起聯系。
③、第三步:建立集群
執行下列命令:
redis-cli --cluster create --cluster-replicas 1 127.0.0.1:7000 127.0.0.1:7001 127.0.0.1:7002 127.0.0.1:7003 127.0.0.1:7004 127.0.0.1:7005- 這里稍微解釋一下這個?--replicas 1?的意思是:我們希望為集群中的每個主節點創建一個從節點。
觀察控制臺輸出:
看到?[OK]?的信息之后,就表示集群已經搭建成功了,可以看到,這里我們正確地創建了三主三從的集群。
④、第四步:驗證集群
我們先使用?redic-cli?任意連接一個節點:
redis-cli -c -h 127.0.0.1 -p 7000 127.0.0.1:7000>- -c?表示集群模式;-h?指定 ip 地址;?-p?指定端口。
然后隨便 set 一些值觀察控制臺輸入:
127.0.0.1:7000> SET name wmyskxz -> Redirected to slot [5798] located at 127.0.0.1:7001 OK 127.0.0.1:7001>可以看到這里 Redis 自動幫我們進行了Redirected操作跳轉到了7001這個實例上。
我們再使用?cluster info?(查看集群信息) 和?cluster nodes?(查看節點列表) 來分別看看:(任意節點輸入均可)
127.0.0.1:7001> CLUSTER INFO cluster_state:ok cluster_slots_assigned:16384 cluster_slots_ok:16384 cluster_slots_pfail:0 cluster_slots_fail:0 cluster_known_nodes:6 cluster_size:3 cluster_current_epoch:6 cluster_my_epoch:2 cluster_stats_messages_ping_sent:1365 cluster_stats_messages_pong_sent:1358 cluster_stats_messages_meet_sent:4 cluster_stats_messages_sent:2727 cluster_stats_messages_ping_received:1357 cluster_stats_messages_pong_received:1369 cluster_stats_messages_meet_received:1 cluster_stats_messages_received:2727 127.0.0.1:7001> CLUSTER NODES 56a04742f36c6e84968cae871cd438935081e86f 127.0.0.1:7003@17003 slave 4ec8c022e9d546c9b51deb9d85f6cf867bf73db6 0 1584428884000 4 connected 4ec8c022e9d546c9b51deb9d85f6cf867bf73db6 127.0.0.1:7000@17000 master - 0 1584428884000 1 connected 0-5460 e2539c4398b8258d3f9ffa714bd778da107cb2cd 127.0.0.1:7005@17005 slave a3406db9ae7144d17eb7df5bffe8b70bb5dd06b8 0 1584428885222 6 connected d31cd1f423ab1e1849cac01ae927e4b6950f55d9 127.0.0.1:7004@17004 slave 236cefaa9cdc295bc60a5bd1aed6a7152d4f384d 0 1584428884209 5 connected 236cefaa9cdc295bc60a5bd1aed6a7152d4f384d 127.0.0.1:7001@17001 myself,master - 0 1584428882000 2 connected 5461-10922 a3406db9ae7144d17eb7df5bffe8b70bb5dd06b8 127.0.0.1:7002@17002 master - 0 1584428884000 3 connected 10923-16383 127.0.0.1:7001>5)數據分區方案簡析
最后
給大家送一個小福利
資料附送高清腦圖,高清知識點講解教程,以及一些面試真題及答案解析。送給需要的提升技術、準備面試跳槽、自身職業規劃迷茫的朋友們。
CodeChina開源項目:【一線大廠Java面試題解析+核心總結學習筆記+最新講解視頻】
7.0.0.1:7002@17002 master - 0 1584428884000 3 connected 10923-16383
127.0.0.1:7001>
總結
以上是生活随笔為你收集整理的Java中高级核心知识全面解析(3),美团Java面试算法题的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java怎么销毁session_【Jav
- 下一篇: Java中数组的写法