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

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

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

一、概述

什么是分布式鎖

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

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

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

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

分布式鎖滿足條件

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

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

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

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

二、環(huán)境搭建

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

依賴

<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

啟動(dòng)及配置類

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(){
        // 單機(jī)模式
        Config config = new Config();
        config.useSingleServer().setAddress("redis://192.144.228.170:6379").setDatabase(0);
        return (Redisson) Redisson.create(config);
    }
}

三、模擬一個(gè)庫存扣減的場(chǎng)景

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ā)場(chǎng)景下,這樣肯定不行
        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,因?yàn)槿绻?wù)掛了,鎖還得釋放
        String clientId = UUID.randomUUID().toString();
        try {
            // 相當(dāng)于加鎖
            // Boolean absent = redisTemplate.opsForValue().setIfAbsent(lockKey, "zxs");
            // 上下兩行不能分開寫,如果這中間報(bào)異常了,依然出現(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è)置超時(shí)時(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():初步實(shí)現(xiàn)方式,在單線程環(huán)境下可以使用,但是在分布式高并發(fā)場(chǎng)景下,毫無疑問是肯定不行的
  • @GetMapping("/minus1") public ResponseEntity<String> minusInventory1():進(jìn)階實(shí)現(xiàn)方式,在一般的不拘小節(jié)的公司,勉強(qiáng)夠用,但是你需要考慮一下,鎖過期時(shí)間,到底設(shè)置多少才能完美呢?
  • @GetMapping("/minus2") public ResponseEntity<String> minusInventory2():終極實(shí)現(xiàn)方式,redisson幫我們解決了上面的實(shí)現(xiàn)方式出現(xiàn)的尷尬情況

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

相關(guān)文章

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

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

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

    詳解Spring如何整合Mybatis

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

    Spring如何在xml文件中配置Bean

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

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

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

    java中的HashMap多層嵌套

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

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

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

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

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

    Java8 日期、時(shí)間操作代碼

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

    SpringBoot2.動(dòng)態(tài)@Value的實(shí)現(xiàn)方式

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

    java實(shí)現(xiàn)幸運(yùn)抽獎(jiǎng)功能

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

最新評(píng)論