博主以前寫了一篇Redis哨兵搭建,並無對哨兵進行講解,本篇填坑。html
同時,也爲博主寫Redis分佈式鎖(二)作一些前置知識。java
挖坑位置:Redis集羣搭建(哨兵)node
在講redis哨兵前,須要先簡單講解一下redis主從。web
俗話說,雞蛋放在一個籃子裏容易碎,那就把雞蛋複製一份,放到其餘籃子裏。全部的高可用基本都是這個思路。redis
上一篇文章講主從配置的時候,講到一個配置屬性slaveofspring
# 這個配置是redis-1中沒有的,須要在redis-2中新增 # 這裏的IP是redis-1的IP地址,端口是redis-1 6379.conf配置文件中port的值,默認值是6379 slaveof 1.1.1.1 6379
這個屬性就是配置redis主從的。apache
這裏分析如下上面這張圖,能夠發現如下幾個特色json
這種redis架構解決了如下問題springboot
存在如下幾個問題架構
基於以上的一些問題,咱們引出了redis哨兵
主從中存在一些問題是咱們不能接受的,好比,主庫宕機=沒法寫入。咱們固然指望,宕機一個節點的時候,仍然能夠對外服務,這纔是高可用嘛~
假如不能自動切換主庫,咱們該怎麼作呢?運維童鞋,先無論主庫了(已經宕機了),在從庫中選擇一個做爲新的主庫啓動,優先提供服務嘛~至於主庫,稍後再分析宕機緣由,解決問題。
本着軟件能解決的問題,就不使用人力,咱們可不能夠下一個軟件,來代替運維童鞋的這些操做?
這個就是哨兵的功能雛形了。
哨兵監視主從redis,一旦redis主庫宕機,哨兵切換主庫,客戶端再寫入數據的時候,向新的主庫中寫入。在切換的時候,給運維童鞋一條通知,運維童鞋再處理。是否是很6?
從上面能夠看出,咱們只有一個哨兵,若是這個哨兵宕機了,那咱們的保障就沒有了。
咱們能夠把哨兵也集羣起來,優化結構以下:
你能夠把哨兵理解爲zookeeper或者是eureka,哨兵至關於一個註冊中心(固然不單單是註冊中心的功能),客戶端也從哨兵讀取redis主從庫信息。三個哨兵組成了一個集羣的註冊中心,當有一個哨兵宕機,還有其餘哨兵存活,依然能夠服務。
這樣哨兵Sentinel也集羣起來了,redis也有主從,咱們這個時候能夠說,提供了一套高可用的redis。
但這樣的架構並非絕對完美的,仍然存在一些問題。
依舊不能解決主從中的一個問題「主庫寫入數據成功,還沒來得及同步到從庫,主庫宕機」。
例如主庫上寫入了一把鎖,還沒來得及把鎖的信息同步到從庫,主庫掛了,從庫切換爲了主庫,然而這個新的主庫上並無鎖。引發了鎖失效。
如何解決呢?
手動調整數據吧,少年。
若是是鎖的場景,能夠用zookeeper來代替redis分佈式鎖來解決。
博主使用的springboot來演示,使用工具包lettuce
<!-- data-redis中集成了lettuce --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <!-- redis連接池 --> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-pool2</artifactId> </dependency> <!-- alibaba json --> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.72</version> </dependency>
server: port: 80 spring: redis: password: 密碼 sentinel: # 這個配置在 哨兵配置文件中 master: 你的集羣名稱 # 26379 端口是哨兵的默認端口 nodes: 10.101.36.19:26379,10.101.36.20:26379,10.101.36.21:26379 lettuce: pool: # 最大連接數 max-active: 30 # 連接池中最大空閒連接數 max-idle: 15 # 最大阻塞等待連接時長 默認不限制 -1 max-wait: 2000 # 最小空閒連接數 min-idle: 10 # 連接超時時長 shutdown-timeout: 10000
import com.alibaba.fastjson.support.spring.GenericFastJsonRedisSerializer; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.serializer.StringRedisSerializer; /** * redis 配置類 將RedisTemplate交給spring託管 */ @Configuration public class RedisConfig { @Bean public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory){ RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>(); redisTemplate.setConnectionFactory(redisConnectionFactory); StringRedisSerializer stringRedisSerializer = new StringRedisSerializer(); GenericFastJsonRedisSerializer genericFastJsonRedisSerializer = new GenericFastJsonRedisSerializer(); redisTemplate.setKeySerializer(stringRedisSerializer); redisTemplate.setValueSerializer(genericFastJsonRedisSerializer); redisTemplate.setHashKeySerializer(stringRedisSerializer); redisTemplate.setHashValueSerializer(genericFastJsonRedisSerializer); redisTemplate.afterPropertiesSet(); return redisTemplate; } }
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.concurrent.TimeUnit; @RestController @RequestMapping("test") public class TestController { @Autowired private RedisTemplate redisTemplate; @GetMapping public String Test(){ redisTemplate.opsForValue().set("xujp", "sentinel"); String rst = (String) redisTemplate.opsForValue().get("xujp"); return rst; } }
postman直接get請求便可
在redis鏈接工具中查看
主庫:
從庫:
這個時候把主庫手動關閉
查看哨兵日誌
能夠看到主節點已切換完成
26721:X 20 Jul 2020 15:08:40.941 # +switch-master mymaster 10.101.36.20 6379 10.101.36.19 6379
這時候,在postman中再次發送請求
請求成功。
有興趣的童鞋能夠再測試一下內容:
這裏博主再挖一坑,redis哨兵模式切換主庫的時候,如何通知運維人員呢?
本文挖坑: