1. 程式人生 > >SpringBoot專案開發(十五):redisson實現分散式鎖

SpringBoot專案開發(十五):redisson實現分散式鎖

1.為什麼要使用分散式鎖?
      在分散式場景下為了保證資料最終一致性。在單程序的系統中,存在多個執行緒可以同時改變某個變數(可變共享變數)時,就需要對變數或程式碼塊做同步(lock—synchronized),使其在修改這種變數時能夠線性執行消除併發修改變數。但分散式系統是多部署、多程序的,開發語言提供的併發處理API在此場景下就無能為力了。

2.分散式鎖的使用場景
      電商網站用下單操作時需要使用,秒殺活動更是如此,否則會出現超賣(庫存100,秒殺活動時庫存變負數了)現象

3.分散式鎖的實現方式
      大概有三種:1.基於關係型資料庫,2.基於快取,3基於zookeeper
      大部分網站使用的是基於快取的,有更好的效能,而快取一般是以叢集方式部署,保證了高可用性

4.基於快取redis,使用開源 redisson 實現分散式鎖
  4.1新建SpringBoot專案,引入依賴:redisson依賴 netty 、jackson-core、jackson-databind

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!--start redis distributed lock-->
<!-- https://mvnrepository.com/artifact/org.redisson/redisson --> <dependency> <groupId>org.redisson</groupId> <artifactId>redisson</artifactId> <version>3.6.5</version> </dependency> <dependency> <groupId>io.netty</groupId>
<artifactId>netty-all</artifactId> <version>4.1.25.Final</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-core</artifactId> <version>2.9.0</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.9.0</version> </dependency> <!--end redis distributed lock-->

  4.2在啟動類中注入 redisson 的bean,可以在配置檔案中新增redis的連線地址、引數,然後在程式碼中引入配置檔案

@SpringBootApplication
public class RedissonApplication {
    public static void main(String[] args) {
        SpringApplication.run(RedissonApplication.class, args);
    }
    @Bean
    Redisson redissonSentinel() {
        //支援單機,主從,哨兵,叢集等模式
        //此為哨兵模式
        Config config = new Config();
        config.useSentinelServers()
                .setMasterName("mymaster")
                .addSentinelAddress("redis://192.168.1.1:26379")
                .setPassword("123456");
        return (Redisson)Redisson.create(config);
    }
}

  4.3到此,就完成了,只要測試了,是不是很簡單。模擬秒殺場景,有一個商品aaa,庫存100個,2個併發請求110次,每請求一次庫存減1,看最後商品數有沒有變為負數,
  4.4在redis中,設定一個庫存數量為100

@Test
public void test() throws Exception {
    //設定一個key,aaa商品的庫存數量為100
    stringRedisTemplate.opsForValue().set("aaa","100");
    Assert.assertEquals("100", stringRedisTemplate.opsForValue().get("aaa"));
}

  4.5把專案複製一份,2個專案,分別使用下面的測試方法,同時啟動,因庫存為100,每次請求庫存減1,啟動2個測試方法各迴圈55次,總計110次請求,看庫存列印結果會不會變負數

String lockKey = "testRedisson";//分散式鎖的key
@Test
public void testDistributed(){
    //執行的業務程式碼
    for(int i=0; i < 55; i++){
        RLock lock = redisson.getLock(lockKey);
        lock.lock(60, TimeUnit.SECONDS); //設定60秒自動釋放鎖  (預設是30秒自動過期)
        int stock = Integer.parseInt(stringRedisTemplate.opsForValue().get("aaa").toString());
        if(stock > 0){
            stringRedisTemplate.opsForValue().set("aaa",(stock-1)+"");
            System.out.println("test2_:lockkey:"+lockKey+",stock:"+(stock-1)+"");
        }
        lock.unlock(); //釋放鎖
    }
}

測試結果:正確,redis的值沒有變負數,說明被鎖住了
這裡寫圖片描述
再看看第一個專案列印的資料,可以看到第70,39被第二個專案的鎖獲得了
這裡寫圖片描述
第二個專案列印的資料
這裡寫圖片描述
測試成功