Spring Schedule + Redisson 構建分布式任務調度


在Spring Schedule進行定時關單任務時候呢,由於項目涉及到分布式,導致遇到多線程上的問題,
最后在Spring Schedule定時關單快速入門(三)的最后也得到了解決。但是解決的方法相對原生。

至此,我們引入Redisson框架進行優化出v4版本的定時器。

1.首先我們要在項目中的pom.xml文件中引入Redisson框架。

<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson</artifactId>
    <version>2.9.0</version>
</dependency>

2.在項目中的pom.xml文件中引入Redisson框架的依賴文件。

<dependency>
    <groupId>com.fasterxml.jackson.dataformat</groupId>
    <artifactId>jackson-dataformat-avro</artifactId>
    <version>2.9.0</version>
</dependency>

3.將Redisson進行初始化

    A:首先創建Redisson類,在類上添加上Spring的注解(@Component)使該類被Spring管理。

    B:創建兩個主要類成員(config)(redisson)和對應Redis的類成員(RedisIP)(RedisPort)
         config為配置文件,負責加載上Redis的類成員(RedisIP)(RedisPort)
         redisson為主要成員,負責加載上配置文件(config)

    C:添加初始化方法,為方法添加上@PostConstruct注解使該方法在spring加載此類組件(Component)的時候調用此方法。

    D:為主要成員(redisson)提供外界訪問方式(getter方法)

Redisson初始化代碼如下:

  package com.mmall.common;

  import com.mmall.util.PropertiesUtil;
  import lombok.extern.slf4j.Slf4j;
  import org.redisson.Redisson;
  import org.redisson.config.Config;
  import org.springframework.stereotype.Component;

  import javax.annotation.PostConstruct;


  @Component //組件
  @Slf4j
  public class RedissonManager {

      private Config config = new Config();
  
      private Redisson redisson = null;
  
      public Redisson getRedisson() { //提供外界訪問使用
          return redisson;
      }
      private static String redis1Ip  = PropertiesUtil.getProperty("redis1.ip");//RedisIP
      private static Integer redis1Port = Integer.parseInt(PropertiesUtil.getProperty("redis1.port"));//RedisPort
      private static String redis2Ip  = PropertiesUtil.getProperty("redis2.ip");
      private static Integer redis2Port = Integer.parseInt(PropertiesUtil.getProperty("redis2.port"));



      @PostConstruct //在spring加載此類組件(Component)的時候調用此方法。
      private void init(){//初始化方法
          //setAddress 需要跟進源碼,源碼里頭有該方法的使用介紹,里頭詳細的介紹了參數該如何傳遞
          try {
              config.useSingleServer().setAddress(new StringBuilder().append(redis1Ip).append(":").append(redis1Port).toString());
  
              redisson = (Redisson) Redisson.create(config);

              log.info("初始化Redisson結束");
          } catch (Exception e) {
              log.error("redisson init error",e);
              e.printStackTrace();
          }

      }
     
    //如果不添加下面這個方法,那項目在關閉Tomcat的時候會有線程未關閉。導致Tomcat無法順利關閉。(重點)
    @PreDestroy  //被@PreConstruct修飾的方法會在服務器卸載Servlet的時候運行,並且只會被服務器調用一次,類似於Servlet的destroy()方法。被@PreConstruct修飾的方法會在destroy()方法之后運行,在Servlet被徹底卸載之前。
    private void close(){
         redisson.shutdown();
          log.info("Redisson關閉");
    }


  }

定時關單器V4版本(代碼)

@Scheduled(cron = "0 */1 * * * ?")//每1分鍾(每個1分鍾的整數倍)調用一次
public void closeOrderTaskV4(){
    RLock lock = redissonManager.getRedisson().getLock(Const.REDIS_LOCK.CLOSE_ORDER_TASK_LOCK);//獲取到鎖  參數傳遞鎖的名字
    boolean getLock = false;
    try {                     //在參數1中的等待時間中,我們應該預估定時器代碼的執行而定,
        //如果項目中定的等待時間是2秒,但是定時器的代碼在1秒的時候執行完后釋放了鎖
        //此時另一個Tomcat的定時器發現在等待2秒的期間發現鎖釋放了,隨即進入了鎖定時器。
        //所以在我們不知道定時器代碼執行的大概時間時,我們盡可能將等待時間設置為0
        if(getLock = lock.tryLock(0,5, TimeUnit.SECONDS)){//參數1:等待時間2 參數2:工作時間5  參數3:單位秒

            log.info("Redisson獲取到分布式鎖:{},ThreadName:{}",Const.REDIS_LOCK.CLOSE_ORDER_TASK_LOCK,Thread.currentThread().getName());
            int hour = Integer.parseInt(PropertiesUtil.getProperty("close.order.task.time.hour","2"));
            //iOrderService.closeOrder(hour);
        }else{
            log.info("Redisson沒有獲取到分布式鎖:{},ThreadName:{}",Const.REDIS_LOCK.CLOSE_ORDER_TASK_LOCK,Thread.currentThread().getName());
        }
    } catch (InterruptedException e) {
        log.error("Redisson分布式鎖獲取異常",e);
    }finally {
        if(!getLock){
            return;
        }
        lock.unlock();//釋放鎖
        log.info("Redisson分布式鎖釋放鎖");
    }
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM