欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

SpringBoot基于Redis的分布式鎖實現(xiàn)過程記錄

 更新時間:2022年01月07日 11:17:11   作者:Freelance?developer  
Redis是一套 key-value 高性能數(shù)據(jù)庫,使用它可以大大提高我們的開發(fā)效率,在SpringBoot中,自動配置也幫我們節(jié)約了大量的配置,下面這篇文章主要給大家介紹了關(guān)于SpringBoot基于Redis的分布式鎖實現(xiàn)的相關(guān)資料,需要的朋友可以參考下

一、概述

什么是分布式鎖

在單機環(huán)境中,一般在多并發(fā)多線程場景下,出現(xiàn)多個線程去搶占一個資源,這個時候會出現(xiàn)線程同步問題,造成執(zhí)行的結(jié)果沒有達(dá)到預(yù)期。我們會用線程間加鎖的方式,比如synchronized,lock,volatile,以及JVM并發(fā)包中提供的其他工具類去處理此問題。

但是隨著技術(shù)的發(fā)展,分布式系統(tǒng)的出現(xiàn),各個應(yīng)用服務(wù)都部署在不同節(jié)點,由各自的JVM去操控,資源已經(jīng)不是在 線程 之間的共享,而是變成了 進(jìn)程 之間的共享,以上解決線程同步問題的辦法已經(jīng)無法滿足。

因此,引入了分布式鎖的概念。

分布式鎖,既在分布式部署的環(huán)境下,通過在外部設(shè)置鎖,讓客戶端之間互斥,當(dāng)多應(yīng)用發(fā)生對共享資源的搶占時,該資源同一時刻只能一個應(yīng)用訪問,從而保證數(shù)據(jù)一致性

分布式鎖滿足條件

  • 互斥性:即同一時刻只能一個客戶端獲得鎖,其他客戶端必須等待獲取鎖的客戶端主動釋放鎖或鎖超時后再次對資源進(jìn)行搶占。
  • 避免死鎖:這把鎖在一段有限的時間之后,一定會被釋放(正常釋放或異常釋放),否則當(dāng)一個客戶端線程獲得鎖后沒有主動釋放,也沒有設(shè)置超時時間,中途宕機等原因,其他線程就會一直獲取不了鎖
  • 具備可重入特性:同一個線程可重復(fù)可遞歸調(diào)用的鎖,在外層使用鎖之后,在內(nèi)層仍然可以使用,如果沒有可重入鎖的支持,在第二次嘗試獲得鎖時將會進(jìn)入死鎖狀態(tài)。
  • 高可用:獲取或釋放鎖的機制必須高可用且性能佳

分布式鎖的重要性不言而喻,原因不在贅述,每一位菜鳥都有理由掌握它。提到分布式鎖,解決方案更是烏泱烏泱的,如:

  • 直接通過關(guān)系型數(shù)據(jù)庫實現(xiàn)
  • 基于Redission實現(xiàn)
  • 基于Apache Curator實現(xiàn)

本文暫時先介紹一種,基于Redission實現(xiàn)的方式

二、環(huán)境搭建

有一個簡單的SpringBoot環(huán)境即可,便于測試:

依賴

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
    <dependency>
        <groupId>org.redisson</groupId>
        <artifactId>redisson</artifactId>
        <version>3.6.5</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

配置

server:
  port: 7077
spring:
  redis:
    host: 192.144.228.170
    database: 0

啟動及配置類

package com.ideax.distributed;

import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;

@SpringBootApplication
public class DistributedApplication {
    public static void main(String[] args) {
        SpringApplication.run(DistributedApplication.class,args);
    }

    /**
     * 配置redisson客戶端
     * @return org.redisson.Redisson
     * @author zhangxs
     * @date 2022-01-06 10:01
     */
    @Bean
    public Redisson redisson(){
        // 單機模式
        Config config = new Config();
        config.useSingleServer().setAddress("redis://192.144.228.170:6379").setDatabase(0);
        return (Redisson) Redisson.create(config);
    }
}

三、模擬一個庫存扣減的場景

package com.ideax.distributed.controller;

import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Objects;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * 庫存 前端控制器
 * @author zhangxs
 * @date 2022-01-06 09:46
 */
@RequestMapping("/inventory")
@RestController
public class InventoryController {
    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    private Redisson redisson;

    @GetMapping("/minus")
    public ResponseEntity<String> minusInventory(){
        // 分布式高并發(fā)場景下,這樣肯定不行
        synchronized (this) {
            int stock = Integer.parseInt(Objects.requireNonNull(redisTemplate.opsForValue().get("stock")));
            if (stock > 0) {
                int currentStock = stock - 1;
                redisTemplate.opsForValue().set("stock", currentStock + "");
                System.out.println("扣減成功,當(dāng)前庫存為" + currentStock);
            } else {
                System.out.println("庫存不足,扣減失?。?);
            }
        }
        return ResponseEntity.ok("success");
    }

    @GetMapping("/minus1")
    public ResponseEntity<String> minusInventory1(){
        // 相當(dāng)于setnx命令
        String lockKey = "lockKey";
        // 務(wù)必加try-finally,因為如果服務(wù)掛了,鎖還得釋放
        String clientId = UUID.randomUUID().toString();
        try {
            // 相當(dāng)于加鎖
            // Boolean absent = redisTemplate.opsForValue().setIfAbsent(lockKey, "zxs");
            // 上下兩行不能分開寫,如果這中間報異常了,依然出現(xiàn)死鎖
            // redisTemplate.expire(lockKey,10, TimeUnit.SECONDS);
            Boolean absent = redisTemplate.opsForValue().setIfAbsent(lockKey, clientId,30,TimeUnit.SECONDS);
            if (!absent) {
                return ResponseEntity.notFound().build();
            }

            int stock = Integer.parseInt(Objects.requireNonNull(redisTemplate.opsForValue().get("stock")));
            if (stock > 0) {
                int currentStock = stock - 1;
                redisTemplate.opsForValue().set("stock", currentStock + "");
                System.out.println("扣減成功,當(dāng)前庫存為" + currentStock);
            } else {
                System.out.println("庫存不足,扣減失?。?);
            }
        } finally {
            // 如果系統(tǒng)掛了呢,finally也不起作用了,因此還需要設(shè)置超時時間
            // 釋放鎖之前,判斷一下,務(wù)必釋放的鎖是自己持有的鎖
            if (clientId.equals(redisTemplate.opsForValue().get(lockKey))) {
                redisTemplate.delete(lockKey);
            }
        }
        return ResponseEntity.ok("success");
    }

    /**
     * 終極方案
     */
    @GetMapping("/minus2")
    public ResponseEntity<String> minusInventory2(){
        // redisson解決方案
        String lockKey = "lockKey";
        RLock lock = redisson.getLock(lockKey);
        try {
            // 加鎖
            lock.lock();
            int stock = Integer.parseInt(Objects.requireNonNull(redisTemplate.opsForValue().get("stock")));
            if (stock > 0) {
                int currentStock = stock - 1;
                redisTemplate.opsForValue().set("stock", currentStock + "");
                System.out.println("扣減成功,當(dāng)前庫存為" + currentStock);
            } else {
                System.out.println("庫存不足,扣減失??!");
            }
        } finally {
            // 釋放鎖
            lock.unlock();
        }
        return ResponseEntity.ok("success");
    }
}

四、總結(jié)

  • @GetMapping("/minus") public ResponseEntity<String> minusInventory():初步實現(xiàn)方式,在單線程環(huán)境下可以使用,但是在分布式高并發(fā)場景下,毫無疑問是肯定不行的
  • @GetMapping("/minus1") public ResponseEntity<String> minusInventory1():進(jìn)階實現(xiàn)方式,在一般的不拘小節(jié)的公司,勉強夠用,但是你需要考慮一下,鎖過期時間,到底設(shè)置多少才能完美呢?
  • @GetMapping("/minus2") public ResponseEntity<String> minusInventory2():終極實現(xiàn)方式,redisson幫我們解決了上面的實現(xiàn)方式出現(xiàn)的尷尬情況

到此這篇關(guān)于SpringBoot基于Redis的分布式鎖實現(xiàn)的文章就介紹到這了,更多相關(guān)SpringBoot基于Redis的分布式鎖內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java面向?qū)ο笾^承的概念詳解

    Java面向?qū)ο笾^承的概念詳解

    這篇文章主要介紹了Java面向?qū)ο笾^承的概念詳解,Java是一種面向?qū)ο蟮木幊陶Z言,繼承是實現(xiàn)面向?qū)ο缶幊痰幕A(chǔ)之一。通過繼承,我們可以使代碼更具可讀性、可重用性和可維護性,從而提高程序的效率和可靠性,需要的朋友可以參考下
    2023-04-04
  • 詳解Spring如何整合Mybatis

    詳解Spring如何整合Mybatis

    今天給大家?guī)淼氖顷P(guān)于Java的相關(guān)知識,文章圍繞著Spring如何整合Mybatis展開,文中有非常詳細(xì)的介紹及代碼示例,需要的朋友可以參考下
    2021-06-06
  • Spring如何在xml文件中配置Bean

    Spring如何在xml文件中配置Bean

    這篇文章主要介紹了Spring如何在xml文件中配置Bean的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2019-07-07
  • JAVA多線程實現(xiàn)生產(chǎn)者消費者的實例詳解

    JAVA多線程實現(xiàn)生產(chǎn)者消費者的實例詳解

    這篇文章主要介紹了JAVA多線程實現(xiàn)生產(chǎn)者消費者的實例詳解的相關(guān)資料,需要的朋友可以參考下
    2017-06-06
  • java中的HashMap多層嵌套

    java中的HashMap多層嵌套

    這篇文章主要介紹了java中的HashMap多層嵌套問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-09-09
  • IDEA中多行注釋及取消注釋的快捷鍵分享

    IDEA中多行注釋及取消注釋的快捷鍵分享

    這篇文章主要介紹了IDEA中多行注釋及取消注釋的快捷鍵分享,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2021-02-02
  • java實現(xiàn)簡單的給sql語句賦值的示例

    java實現(xiàn)簡單的給sql語句賦值的示例

    這篇文章主要介紹了java實現(xiàn)簡單的給sql語句賦值的示例,需要的朋友可以參考下
    2014-05-05
  • Java8 日期、時間操作代碼

    Java8 日期、時間操作代碼

    在Java8之前,日期時間API一直被開發(fā)者詬病,包括:java.util.Date是可變類型,SimpleDateFormat非線程安全等問題。故此,Java8引入了一套全新的日期時間處理API,新的API基于ISO標(biāo)準(zhǔn)日歷系統(tǒng)
    2021-09-09
  • SpringBoot2.動態(tài)@Value的實現(xiàn)方式

    SpringBoot2.動態(tài)@Value的實現(xiàn)方式

    這篇文章主要介紹了SpringBoot2.動態(tài)@Value的實現(xiàn)方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-07-07
  • java實現(xiàn)幸運抽獎功能

    java實現(xiàn)幸運抽獎功能

    這篇文章主要為大家詳細(xì)介紹了java實現(xiàn)幸運抽獎功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-03-03

最新評論