<kbd id="5sdj3"></kbd>
<th id="5sdj3"></th>

  • <dd id="5sdj3"><form id="5sdj3"></form></dd>
    <td id="5sdj3"><form id="5sdj3"><big id="5sdj3"></big></form></td><del id="5sdj3"></del>

  • <dd id="5sdj3"></dd>
    <dfn id="5sdj3"></dfn>
  • <th id="5sdj3"></th>
    <tfoot id="5sdj3"><menuitem id="5sdj3"></menuitem></tfoot>

  • <td id="5sdj3"><form id="5sdj3"><menu id="5sdj3"></menu></form></td>
  • <kbd id="5sdj3"><form id="5sdj3"></form></kbd>

    SpringBoot 2.x 整合 Redis Sentinel 集群

    共 8009字,需瀏覽 17分鐘

     ·

    2022-05-10 15:31

    ?SpringBoot 2.x 整合 Redis Sentinel 集群實現(xiàn)自動故障轉(zhuǎn)移.

    上一篇我們講解了 Redis Sentinel 集群搭建的詳細過程, 作為一個 Java 程序員, 我們還是需要從代碼層面來操作 Redis 的.

    1. 環(huán)境準(zhǔn)備

    • 192.168.3.26: Master 實例, Sentinel 1

    • 192.168.3.27: Slave1 實例, Sentinel 2

    • 192.168.3.28:Slave2 實例, Sentinel 3

    1.1 Redis Sentinel 集群(一主二從三個 Sentinel)

    Redis Sentinel 集群的搭建過程可以參考帥帥之前的文章 Redis 高可用專題-- Sentinel 哨兵模式

    注意: 目前的 master 是 192.168.3.27, 即經(jīng)過故障轉(zhuǎn)移后 slave1 目前是 Master

    1.2 SpringBoot 環(huán)境

    • SpringBoot 版本: 2.3.2.RELEASE

    2. 引入依賴

             

    org.springframework.boot
    spring-boot-starter-data-redis




    org.apache.commons
    commons-pool2



    org.springframework.boot
    spring-boot-starter-web



    org.springframework.boot
    spring-boot-starter-test
    test


    org.junit.vintage
    junit-vintage-engine


    SpringBoot 2.x 的 Redis 客戶端已經(jīng)從 Jedis 切換到了 Lettuce

    3. 配置

    • application.yml 配置文件中編輯 Sentinel 信息


    spring:
    application:
    name: sb-redis-sentinel

    # redis 配置
    redis:
    database: 1
    # 數(shù)據(jù)節(jié)點密碼
    password: javaFamily123!!

    sentinel:
    master: mymaster
    # Sentinel 連接密碼, 如果沒配置就不寫
    # password:
    nodes:
    - 192.168.3.26:26379
    - 192.168.3.27:26379
    - 192.168.3.28:26379

    # 配置 lettuce 客戶端連接池信息
    lettuce:
    pool:
    # 連接池中的最小空閑連接
    min-idle: 5
    # 連接池中的最大空閑連接
    max-idle: 10
    # 連接池的最大數(shù)據(jù)庫連接數(shù)
    max-active: 100
    # 連接池最大阻塞等待時間(使用負值表示沒有限制)
    max-wait: -1

    這里只需要配置 Sentinel 節(jié)點.?客戶端會自動從 sentinel 獲取數(shù)據(jù)節(jié)點信息.

    • 配置 Redis 連接池、RedisTemplate

    package club.javafamily.sbredissentinel.conf;

    import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
    import org.springframework.beans.factory.annotation.Qualifier;
    import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.data.redis.connection.RedisNode;
    import org.springframework.data.redis.connection.RedisSentinelConfiguration;
    import org.springframework.data.redis.connection.lettuce.*;
    import org.springframework.data.redis.core.RedisTemplate;
    import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
    import org.springframework.data.redis.serializer.StringRedisSerializer;

    import java.util.ArrayList;
    import java.util.List;

    /**
    * @author Jack Li
    * @date 2022/5/8 下午6:42
    * @description Redis 哨兵配置
    */
    @Configuration
    public class RedisConfig {

    private final RedisProperties redisProperties;

    public RedisConfig(RedisProperties redisProperties) {
    this.redisProperties = redisProperties;
    }

    /**
    * 創(chuàng)建 Redis 連接池配置
    */
    @Bean
    public GenericObjectPoolConfig poolConfig() {
    GenericObjectPoolConfig config = new GenericObjectPoolConfig();
    config.setMinIdle(redisProperties.getLettuce().getPool().getMinIdle());
    config.setMaxIdle(redisProperties.getLettuce().getPool().getMaxIdle());
    config.setMaxTotal(redisProperties.getLettuce().getPool().getMaxActive());
    config.setMaxWaitMillis(redisProperties.getLettuce().getPool().getMaxWait().toMillis());

    return config;
    }

    /**
    * 聲明哨兵配置, 將哨兵信息放到配置中
    */
    @Bean
    public RedisSentinelConfiguration sentinelConfig() {
    RedisSentinelConfiguration redisConfig = new RedisSentinelConfiguration();
    redisConfig.setMaster(redisProperties.getSentinel().getMaster());
    redisConfig.setPassword(redisProperties.getPassword());

    if(redisProperties.getSentinel().getNodes()!=null) {
    List sentinelNode = new ArrayList();

    for(String sen : redisProperties.getSentinel().getNodes()) {
    String[] arr = sen.split(":");
    sentinelNode.add(new RedisNode(arr[0],Integer.parseInt(arr[1])));
    }

    redisConfig.setSentinels(sentinelNode);
    }

    return redisConfig;
    }

    /**
    * 創(chuàng)建連接工廠 LettuceConnectionFactory
    */
    @Bean("lettuceConnectionFactory")
    public LettuceConnectionFactory lettuceConnectionFactory(
    @Qualifier("poolConfig") GenericObjectPoolConfig config,
    @Qualifier("sentinelConfig") RedisSentinelConfiguration sentinelConfig)
    {
    LettuceClientConfiguration clientConfiguration
    = LettucePoolingClientConfiguration.builder()
    .poolConfig(config)
    .build();

    return new LettuceConnectionFactory(sentinelConfig, clientConfiguration);
    }

    /**
    * 創(chuàng)建 RedisTemplate
    */
    @Bean("stringObjectRedisTemplate")
    public RedisTemplate stringObjectRedisTemplate(
    @Qualifier("lettuceConnectionFactory") LettuceConnectionFactory connectionFactory)
    {
    RedisTemplate redisTemplate = new RedisTemplate<>();
    redisTemplate.setConnectionFactory(connectionFactory);

    StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
    GenericJackson2JsonRedisSerializer genericJackson2JsonRedisSerializer
    = new GenericJackson2JsonRedisSerializer();

    //設(shè)置序列化器
    redisTemplate.setKeySerializer(stringRedisSerializer);
    redisTemplate.setValueSerializer(genericJackson2JsonRedisSerializer);
    redisTemplate.setHashKeySerializer(stringRedisSerializer);
    redisTemplate.setHashValueSerializer(genericJackson2JsonRedisSerializer);
    redisTemplate.afterPropertiesSet();

    return redisTemplate;
    }
    }

    4. 正常測試

    • 讀寫測試

    package club.javafamily.sbredissentinel;

    import org.junit.jupiter.api.*;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.test.context.SpringBootTest;
    import org.springframework.data.redis.core.RedisTemplate;
    import org.springframework.data.redis.core.ValueOperations;

    @SpringBootTest
    class SbRedisSentinelApplicationTests {

    @Autowired
    private RedisTemplate redisTemplate;

    ValueOperations opsForValue;

    @BeforeEach
    public void init() {
    opsForValue = redisTemplate.opsForValue();
    }

    @Test
    void contextLoads() {
    Assertions.assertNotNull(redisTemplate);
    }

    @Test
    void writeTest() {
    opsForValue.set("tsb1", "springboot");
    }

    @Test
    void readTest() {
    final String value = (String) opsForValue.get("tsb1");

    System.out.println(value);
    }

    }

    • 在 slave 節(jié)點讀取

    可以看到主從復(fù)制及讀寫都沒問題.

    5. 主節(jié)點 Master 宕機測試

    哨兵的一大作用即就是自動故障轉(zhuǎn)移, 因此, 我們測試一下主節(jié)點宕機后?SpringBoot 應(yīng)用不重啟的情況下, SpringBoot 應(yīng)用是否還可以繼續(xù)正常工作.

    • 寫一個 Controller 接收讀寫請求, 啟動并讓 SpringBoot 處于運行狀態(tài)

    package club.javafamily.sbredissentinel.controller;

    import org.springframework.data.redis.core.RedisTemplate;
    import org.springframework.web.bind.annotation.*;

    /**
    * @author Jack Li
    * @date 2022/5/8 下午11:43
    * @description
    */
    @RestController
    public class RedisTestController {

    private final RedisTemplate redisTemplate;

    public RedisTestController(RedisTemplate redisTemplate) {
    this.redisTemplate = redisTemplate;
    }

    @GetMapping("/write")
    public String write(@RequestParam("key") String key,
    @RequestParam("value") String val)
    {
    // 寫入
    redisTemplate.opsForValue().set(key, val);

    // 讀取并返回
    return (String) redisTemplate.opsForValue().get(key);
    }
    }

    • 下線當(dāng)前的 master

    通過 info 指令可以看到當(dāng)前集群的 master 是 slave1 節(jié)點(192.168.3.27)

    執(zhí)行下線

    觀察 SpringBoot 后臺也會重新連接

    • 再次執(zhí)行寫入

    • 查看從節(jié)點數(shù)據(jù)

    目前 master 為 slave 節(jié)點, slave1 已經(jīng)下線, slave2 為 master

    至此, SpringBoot 連接 Sentinel 集群及自動故障轉(zhuǎn)移測試就講完了, 這里再補充一個知識點, 也是一道字節(jié)面試題哦:

    哨兵集群完成了主從切換,客戶端如何感知?

    我們知道Redis有pub/sub機制,為了便于外部知道當(dāng)前的切換進度,哨兵提供了多個訂閱頻道。其中就有一個新主庫切換頻道,(switch-master)

    SUBSCRIBE +switch-master

    訂閱對應(yīng)頻道,就可以獲得切換后的新主庫ip、port,并與之建立連接,繼續(xù)使用 Redis 服務(wù)。

    ? ? ? ? 如果有任何相關(guān)的問題都可以加入 QQ/微信群一起討論, 學(xué)習(xí), 進步. 此外如果有任何對于本公眾號的意見和建議也歡迎大家留言積極批評指正, 最后, 愿你我都能成為更好的自己.?


    ? ? ? ? 我是帥帥, 一個集帥氣, 幽默與內(nèi)涵, 并且熱愛編程, 擁抱開源, 喜歡烹飪與旅游的暖男, 我們下期再見.?拜了個拜!

    ? ? ? ??老規(guī)矩別忘了哦,?點擊原文鏈接跳轉(zhuǎn)到我們官方的博客平臺哦.



    悄悄話




    每文一句



    Don't aim for success if you really want it. Just stick to what you love and believe in, and it will come naturally.

    少一些功利主義的追求, 多一些不為什么的堅持.


    日常求贊

    ? ? ? 你們白漂的力量就是我拖更的史詩級動力, 點贊, 評論, 再看, 贊賞, 看都看到這了, 隨便點一個咯.



    關(guān)注加好友


    拉你進大佬交流群





    瀏覽 128
    點贊
    評論
    收藏
    分享

    手機掃一掃分享

    分享
    舉報
    評論
    圖片
    表情
    推薦
    點贊
    評論
    收藏
    分享

    手機掃一掃分享

    分享
    舉報

    <kbd id="5sdj3"></kbd>
    <th id="5sdj3"></th>

  • <dd id="5sdj3"><form id="5sdj3"></form></dd>
    <td id="5sdj3"><form id="5sdj3"><big id="5sdj3"></big></form></td><del id="5sdj3"></del>

  • <dd id="5sdj3"></dd>
    <dfn id="5sdj3"></dfn>
  • <th id="5sdj3"></th>
    <tfoot id="5sdj3"><menuitem id="5sdj3"></menuitem></tfoot>

  • <td id="5sdj3"><form id="5sdj3"><menu id="5sdj3"></menu></form></td>
  • <kbd id="5sdj3"><form id="5sdj3"></form></kbd>
    国产精品做受 | 草比| 抽插熟女 | 久久99久久99精品免费看小说 | 自拍成人午夜视频 |