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

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

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

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

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

Lock注解

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

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 "";
 
    /**
     * 超時時間
     *
     * @return 超時時間
     */
    long leaseTime() default 30L;
 
    /**
     * 等待時間
     *
     * @return 等待時間
     */
    long waitTime() default 0L;
 
    /**
     * 超時時間單位(默認秒)
     *
     * @return 超時時間單位
     */
    TimeUnit leaseTimeTimeUnit() default TimeUnit.SECONDS;
 
    /**
     * 等待時間單位(默認秒)
     *
     * @return 等待時間單位
     */
    TimeUnit waitTimeTimeUnit() default TimeUnit.SECONDS;
 
}

LockAspect切面

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

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

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("正在處理中,請稍后再試");
        });
        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ù)中獲取到對應的值,默認使用lockName作為key", lockName, key);
        }
        return lockName;
    }
 
}

RedisLockUtils工具類

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

這個類會使用Redisson客戶端來簡化分布式鎖的操作。

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           鎖超時時間
     * @param waitTimeMs            獲取鎖等待時間
     * @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;
    }
 
}

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

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

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

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

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

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

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

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

總結(jié)

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

相關文章

  • Quarkus篇入門創(chuàng)建項目搭建debug環(huán)境

    Quarkus篇入門創(chuàng)建項目搭建debug環(huán)境

    這篇文章主要為大家介紹了Quarkus篇入門創(chuàng)建項目搭建debug環(huán)境,先來一套hello?world,來搭建基本的運行及調(diào)試環(huán)境吧
    2022-02-02
  • Tomcat安裝配置及Eclipse配置詳解

    Tomcat安裝配置及Eclipse配置詳解

    給大家介紹一下Tomcat安裝配置及Eclipse配置的全部圖文過程,如果你對這個還有不明白,一起跟著小編學習下。
    2017-11-11
  • java 設計模型之單例模式詳解

    java 設計模型之單例模式詳解

    本文主要介紹了java 單例模式,單例對象(Singleton)是一種常用的設計模式。在Java應用中,單例對象能保證在一個JVM中,該對象只有一個實例存在,希望能幫助有需要的同學
    2016-07-07
  • why在重寫equals時還必須重寫hashcode方法分享

    why在重寫equals時還必須重寫hashcode方法分享

    首先我們先來看下String類的源碼:可以發(fā)現(xiàn)String是重寫了Object類的equals方法的,并且也重寫了hashcode方法
    2013-10-10
  • 詳解Java設計模式之備忘錄模式的使用

    詳解Java設計模式之備忘錄模式的使用

    這篇文章主要介紹了Java設計模式之備忘錄模式的使用,備忘錄模式中的發(fā)起者和管需要的朋友可以參考下
    2016-02-02
  • java尋找迷宮路徑的簡單實現(xiàn)示例

    java尋找迷宮路徑的簡單實現(xiàn)示例

    這篇文章主要介紹了java尋找迷宮路徑的簡單實現(xiàn)示例,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2020-10-10
  • 淺談Hibernate n+1問題

    淺談Hibernate n+1問題

    這篇文章主要介紹了淺談Hibernate n+1問題,怎么解決n+1問題,文中也作了簡要分析,小編覺得還是挺不錯的,具有一定借鑒價值,需要的朋友可以參考下
    2018-02-02
  • Java語言打印九九乘法表

    Java語言打印九九乘法表

    這篇文章主要為大家詳細介紹了Java語言打印九九乘法表的相關代碼,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2016-06-06
  • 原來Java中有兩個ArrayList

    原來Java中有兩個ArrayList

    原來Java中有兩個ArrayList,本文就帶著大家一起探究Java中的ArrayList,感興趣的小伙伴們可以參考一下
    2016-01-01
  • Apache?Maven3.6.0的下載安裝和環(huán)境配置(圖文教程)

    Apache?Maven3.6.0的下載安裝和環(huán)境配置(圖文教程)

    本文主要介紹了Apache?Maven3.6.0的下載安裝和環(huán)境配置,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2022-07-07

最新評論