最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

分布式鎖在Spring Boot應(yīng)用中的實(shí)現(xiàn)過程

 更新時(shí)間:2025年08月02日 10:43:43   作者:csdn_Ty  
文章介紹在SpringBoot中通過自定義Lock注解、LockAspect切面和RedisLockUtils工具類實(shí)現(xiàn)分布式鎖,確保多實(shí)例并發(fā)操作的數(shù)據(jù)一致性,注解聲明式管理鎖,切面處理獲取與釋放,工具類基于Redisson操作Redis,簡(jiǎn)化鎖機(jī)制集成

在現(xiàn)代微服務(wù)架構(gòu)中,分布式鎖是一種常用的技術(shù)手段,用于確保在分布式系統(tǒng)中,同一時(shí)間只有一個(gè)服務(wù)實(shí)例能夠執(zhí)行某個(gè)特定的操作。

這對(duì)于防止并發(fā)問題、保證數(shù)據(jù)一致性至關(guān)重要。在Spring Boot應(yīng)用中,我們可以通過自定義注解和切面的方式,來實(shí)現(xiàn)一個(gè)既簡(jiǎn)潔又強(qiáng)大的分布式鎖機(jī)制。

Lock注解

首先,我們定義一個(gè)Lock注解,用于標(biāo)記需要加鎖的方法。這個(gè)注解包含了鎖的鍵值、超時(shí)時(shí)間和等待時(shí)間等信息。

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.concurrent.TimeUnit;
 
/**
 * @author tangzx
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Lock {
 
    /**
     * 鎖的鍵值
     *
     * @return 鎖的鍵值
     */
    String value() default "";
 
    /**
     * 鎖的鍵值
     *
     * @return 鎖的鍵值
     */
    String key() default "";
 
    /**
     * 超時(shí)時(shí)間
     *
     * @return 超時(shí)時(shí)間
     */
    long leaseTime() default 30L;
 
    /**
     * 等待時(shí)間
     *
     * @return 等待時(shí)間
     */
    long waitTime() default 0L;
 
    /**
     * 超時(shí)時(shí)間單位(默認(rèn)秒)
     *
     * @return 超時(shí)時(shí)間單位
     */
    TimeUnit leaseTimeTimeUnit() default TimeUnit.SECONDS;
 
    /**
     * 等待時(shí)間單位(默認(rèn)秒)
     *
     * @return 等待時(shí)間單位
     */
    TimeUnit waitTimeTimeUnit() default TimeUnit.SECONDS;
 
}

LockAspect切面

接下來,我們創(chuàng)建一個(gè)LockAspect切面類,用于處理Lock注解。

這個(gè)切面會(huì)在方法執(zhí)行前嘗試獲取鎖,如果獲取成功,則執(zhí)行方法體;如果獲取失敗,則執(zhí)行相應(yīng)的失敗邏輯。

import com.lock.core.exception.AppException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;
 
import java.util.concurrent.atomic.AtomicReference;
 
/**
 * @author tangzx
 */
@Slf4j
@Aspect
@Component
public class LockAspect {
 
    @Around("@annotation(lock)")
    public Object around(ProceedingJoinPoint joinPoint, Lock lock) throws Throwable {
        String value = lock.value();
        String key = lock.key();
        long leaseTimeMs = lock.leaseTimeTimeUnit().toMillis(lock.leaseTime());
        long waitTimeMs = lock.waitTimeTimeUnit().toMillis(lock.waitTime());
        String lockKey = resolveLockKey(value, key, joinPoint);
        AtomicReference<Object> result = new AtomicReference<>(null);
        AtomicReference<Throwable> throwable = new AtomicReference<>(null);
        RedisUtils.LockOps.execute(lockKey, leaseTimeMs, waitTimeMs, () -> {
            try {
                result.set(joinPoint.proceed());
            } catch (Throwable t) {
                throwable.set(t);
            }
        }, () -> {
            AppLogger.append("未獲取到Lock鎖[{}]", lockKey);
            throw new AppException("正在處理中,請(qǐng)稍后再試");
        });
        if (null != throwable.get()) {
            throw throwable.get();
        }
        return result.get();
    }
 
    public String resolveLockKey(String lockName, String key, ProceedingJoinPoint joinPoint) {
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        String[] parameterNames = methodSignature.getParameterNames();
        Object[] args = joinPoint.getArgs();
 
        ExpressionParser parser = new SpelExpressionParser();
        Expression expression = parser.parseExpression(key);
        StandardEvaluationContext context = new StandardEvaluationContext();
        for (int i = 0; i < args.length; i++) {
            context.setVariable(parameterNames[i], args[i]);
        }
        String value = expression.getValue(context, String.class);
        if (StringUtils.isNotBlank(value)) {
            return lockName + ":" + value;
        }
        if (log.isWarnEnabled()) {
            log.warn("lockName={},根據(jù)規(guī)則[key={}],未在參數(shù)中獲取到對(duì)應(yīng)的值,默認(rèn)使用lockName作為key", lockName, key);
        }
        return lockName;
    }
 
}

RedisLockUtils工具類

最后,我們實(shí)現(xiàn)一個(gè)RedisLockUtils工具類,用于與Redis交互,實(shí)現(xiàn)鎖的獲取和釋放。

這個(gè)類會(huì)使用Redisson客戶端來簡(jiǎn)化分布式鎖的操作。

import com.redis.utils.ServiceLocator;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
 
import java.util.Optional;
import java.util.concurrent.TimeUnit;
 
/**
 * @author :Tzx
 * @date :Created in 2021/8/2 18:09
 * @description: redis鎖
 * @version: 1.0
 */
@Slf4j
public class RedisLockUtils {
 
    private final static String REDIS_LOCK_HANDLER_PREFIX = RedisLockUtils.class.getSimpleName().toLowerCase() + ":";
 
    private static volatile RedissonClient redissonClient;
 
    /**
     * 獲取分布式鎖執(zhí)行
     *
     * @param redisKey      redisKey
     * @param codeToExecute 獲取鎖執(zhí)行
     */
    public static void execute(String redisKey, Runnable codeToExecute) {
        execute(redisKey, null, null, codeToExecute, null);
    }
 
    /**
     * 獲取分布式鎖執(zhí)行
     *
     * @param redisKey              redisKey
     * @param codeToExecute         獲取鎖執(zhí)行
     * @param codeIfLockNotAcquired 未獲取到鎖執(zhí)行
     */
    public static void execute(String redisKey, Runnable codeToExecute, Runnable codeIfLockNotAcquired) {
        execute(redisKey, null, null, codeToExecute, codeIfLockNotAcquired);
    }
 
    /**
     * 獲取分布式鎖執(zhí)行
     *
     * @param key                   redisKey
     * @param leaseTimeMs           鎖超時(shí)時(shí)間
     * @param waitTimeMs            獲取鎖等待時(shí)間
     * @param codeToExecute         獲取鎖執(zhí)行
     * @param codeIfLockNotAcquired 未獲取到鎖執(zhí)行
     */
    public static void execute(String key, Long leaseTimeMs, Long waitTimeMs, Runnable codeToExecute, Runnable codeIfLockNotAcquired) {
        waitTimeMs = Optional.ofNullable(waitTimeMs).orElse(0L);
 
        String lockKey = REDIS_LOCK_HANDLER_PREFIX + key;
        RLock lock = getRedissonClient().getLock(lockKey);
        boolean tryLock = false;
        try {
            if (null != leaseTimeMs && leaseTimeMs > 0L) {
                tryLock = lock.tryLock(waitTimeMs, leaseTimeMs, TimeUnit.MILLISECONDS);
            } else {
                tryLock = lock.tryLock(waitTimeMs, TimeUnit.MILLISECONDS);
            }
        } catch (InterruptedException interruptedException) {
            log.warn("獲取鎖異常", interruptedException);
            Thread.currentThread().interrupt();
        }
        if (tryLock) {
            try {
                codeToExecute.run();
                return;
            } finally {
                if (lock.isHeldByCurrentThread()) {
                    lock.unlock();
                }
            }
        }
        if (log.isDebugEnabled()) {
            log.debug("未獲取到鎖[{}]", key);
        }
        Optional.ofNullable(codeIfLockNotAcquired).ifPresent(Runnable::run);
    }
 
    private static RedissonClient getRedissonClient() {
        if (null == redissonClient) {
            synchronized (RedisLockUtils.class) {
                if (null == redissonClient) {
                    redissonClient = ServiceLocator.getService(RedissonClient.class);
                }
            }
        }
        return redissonClient;
    }
 
}

下面是一個(gè)使用Lock注解的示例,展示了如何在Spring Boot應(yīng)用中實(shí)現(xiàn)分布式鎖。

假設(shè)我們有一個(gè)OrderService服務(wù),其中包含一個(gè)方法createOrder,這個(gè)方法需要保證在多服務(wù)實(shí)例中同時(shí)只有一個(gè)能夠被執(zhí)行,以防止創(chuàng)建重復(fù)的訂單。

import org.springframework.stereotype.Service;
@Service
public class OrderService {
    @Lock(value = "order", key = "#orderId", leaseTime = 10, waitTime = 5)
    public void createOrder(String orderId) {
        // 業(yè)務(wù)邏輯,比如創(chuàng)建訂單、保存訂單等
        System.out.println("Creating order: " + orderId);
    }
}

在這個(gè)示例中,createOrder方法使用了Lock注解。當(dāng)這個(gè)方法被調(diào)用時(shí),LockAspect切面會(huì)攔截這個(gè)調(diào)用,并嘗試獲取一個(gè)分布式鎖。鎖的鍵值是由key屬性的SpEL表達(dá)式計(jì)算得出的,這里使用了方法的參數(shù)orderId。leaseTimewaitTime分別設(shè)置了鎖的超時(shí)時(shí)間和等待時(shí)間。

當(dāng)多個(gè)服務(wù)實(shí)例嘗試同時(shí)創(chuàng)建同一個(gè)訂單時(shí),由于分布式鎖的存在,只有一個(gè)實(shí)例能夠成功執(zhí)行createOrder方法,其他實(shí)例將會(huì)在等待一段時(shí)間后失敗,或者執(zhí)行Lock注解中定義的失敗邏輯。

這種使用注解的方式,使得分布式鎖的集成變得非常簡(jiǎn)單和直觀。開發(fā)者不需要關(guān)心鎖的具體實(shí)現(xiàn)細(xì)節(jié),只需要在需要加鎖的方法上添加Lock注解,并設(shè)置相應(yīng)的參數(shù)即可。

通過這三個(gè)組件,我們可以在Spring Boot應(yīng)用中非常優(yōu)雅地實(shí)現(xiàn)分布式鎖。Lock注解提供了一種聲明式的方式,讓開發(fā)者可以輕松地為方法添加分布式鎖。LockAspect切面確保了鎖的邏輯在方法執(zhí)行前后被正確地處理。而RedisLockUtils工具類則負(fù)責(zé)與Redis交互,確保鎖的原子性和一致性。

在實(shí)現(xiàn)這些組件時(shí),我們還需要注意一些細(xì)節(jié),比如如何處理鎖的鍵值解析、如何處理鎖獲取失敗的情況、如何確保鎖的釋放等。

總結(jié)

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • 為什么Java單例模式一定要加?volatile

    為什么Java單例模式一定要加?volatile

    這篇文章主要介紹了為什么Java單例一定要加volatile,指的是為什么懶漢模式中的私有變量要加volatile?帶著疑問一起學(xué)習(xí)下面文章內(nèi)容吧
    2022-05-05
  • Spring Bean常用的的裝配方式詳解

    Spring Bean常用的的裝配方式詳解

    這篇文章主要介紹了Spring Bean常用的的裝配方式詳解,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-07-07
  • Spring Cloud EureKa Ribbon 服務(wù)注冊(cè)發(fā)現(xiàn)與調(diào)用

    Spring Cloud EureKa Ribbon 服務(wù)注冊(cè)發(fā)現(xiàn)與調(diào)用

    這篇文章主要介紹了Spring Cloud EureKa Ribbon 服務(wù)注冊(cè)發(fā)現(xiàn)與調(diào)用,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2018-02-02
  • Java多線程編程之ThreadLocal詳解

    Java多線程編程之ThreadLocal詳解

    這篇文章主要介紹了Java多線程編程之ThreadLocal詳解,ThreadLocal是Java中的一個(gè)線程局部變量,該變量在多線程并發(fā)執(zhí)行時(shí),為每個(gè)線程都提供了一個(gè)獨(dú)立的副本,簡(jiǎn)單來說,ThreadLocal提供了一種在多線程環(huán)境中,使每個(gè)線程綁定自己獨(dú)立的變量的方法,需要的朋友可以參考下
    2023-09-09
  • 解決RabbitMq queue異常導(dǎo)致consumer停止問題

    解決RabbitMq queue異常導(dǎo)致consumer停止問題

    該問題為RabbitMQ隊(duì)列異常導(dǎo)致消費(fèi)者停止消費(fèi),錯(cuò)誤碼404表示隊(duì)列不存在或broker不允許使用該隊(duì)列,原因可能為RabbitMQ服務(wù)異?;蚺渲脝栴},建議檢查RabbitMQ服務(wù)狀態(tài),確認(rèn)隊(duì)列名稱正確,并適當(dāng)調(diào)整消費(fèi)者重試策略
    2026-05-05
  • SpringBoot事務(wù)源碼從注解到數(shù)據(jù)庫的全解析

    SpringBoot事務(wù)源碼從注解到數(shù)據(jù)庫的全解析

    本文深度解析SpringBoot事務(wù)源碼:從@Transactional注解觸發(fā)AOP代理,到TransactionInterceptor攔截,本文結(jié)合實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧
    2026-03-03
  • mybatis某些字段無法映射成功的解決

    mybatis某些字段無法映射成功的解決

    這篇文章主要介紹了mybatis某些字段無法映射成功的解決方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2020-11-11
  • 詳解SpringMVC 基礎(chǔ)教程 簡(jiǎn)單入門實(shí)例

    詳解SpringMVC 基礎(chǔ)教程 簡(jiǎn)單入門實(shí)例

    這篇文章主要介紹了詳解SpringMVC 基礎(chǔ)教程 簡(jiǎn)單入門實(shí)例,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2016-12-12
  • 教你用JAVA寫文本編輯器(二)

    教你用JAVA寫文本編輯器(二)

    之前介紹了JAVA寫文本編輯器的基本思路,下面這篇文章就來給大家介紹了關(guān)于用JAVA寫文本編輯器的相關(guān)資料,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2021-11-11
  • Java線程創(chuàng)建的四種方式總結(jié)

    Java線程創(chuàng)建的四種方式總結(jié)

    這篇文章主要介紹了Java線程創(chuàng)建的四種方式,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-09-09

最新評(píng)論

历史| 吴忠市| 平和县| 太湖县| 新宁县| 当雄县| 嘉峪关市| 循化| 荣昌县| 财经| 饶阳县| 肇源县| 成武县| 会同县| 德庆县| 高唐县| 平湖市| 乐安县| 诸城市| 阿尔山市| 唐山市| 集安市| 尼勒克县| 呼图壁县| 英山县| 衡阳县| 浮山县| 阿图什市| 永仁县| 铅山县| 门源| 庆阳市| 蓝山县| 德化县| 独山县| 庆安县| 临夏市| 福州市| 巴马| 太仓市| 府谷县|