自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

分布式進階:Springboot自定義注解優(yōu)雅的實現(xiàn)Redisson分布式鎖

開發(fā) 架構
在本篇博客中,我們深入探討了如何在Spring Boot應用中借助自定義注解來實現(xiàn)分布式鎖,為分布式環(huán)境下的并發(fā)問題提供了優(yōu)雅且高效的解決方案。通過自定義注解,我們成功地將分布式鎖的復雜邏輯進行了封裝,使得在業(yè)務代碼中只需簡單地使用注解,便能實現(xiàn)分布式鎖的獲取和釋放。

一、前言

在這個微服務多節(jié)點、多線程的環(huán)境中,多個任務可能會同時競爭訪問共享資源,從而導致數(shù)據(jù)錯誤和不一致。一般的JVM層面的加鎖顯然無法滿足多個節(jié)點的情況!分布式鎖就出現(xiàn)了,在redis官網推薦Java使用Redisson去實現(xiàn)分布式鎖!

這是基本api調用,今天我們使用自定義注解來完成,一勞永逸,減少出錯!

二、Redisson簡介

Redisson是一個用于Java應用程序的開源的、基于Redis的分布式和高性能數(shù)據(jù)結構服務庫。它提供了一系列的分布式對象和服務,幫助開發(fā)人員更輕松地在分布式環(huán)境中使用Java編程語言。Redisson通過封裝Redis的功能,使得開發(fā)者能夠更方便地利用分布式特性,同時提供了許多額外的功能和工具。

比setnx簡單的加鎖機制,Redisson會提供更完善的加鎖機制,比如:

「到期方法沒有執(zhí)行完成,引入看門狗機制自動續(xù)期,內部使用Lua腳本保證原子性!」

「提供眾多的鎖:」

  • 可重入鎖(Reentrant Lock)
  • 公平鎖(Fair Lock)
  • 聯(lián)鎖(MultiLock)
  • 紅鎖(RedLock)
  • 讀寫鎖(ReadWriteLock)

對于今天的注解形式,只能實現(xiàn)可重入鎖、公平鎖兩種形式,不過也滿足大部分業(yè)務場景!

今天以實戰(zhàn)為主,這些信息可以去官網看一下詳細的文檔:

Redisson文檔:https://github.com/redisson/redisson/wiki/1.-Overview。

三、實戰(zhàn)

1、導入依賴

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson</artifactId>
    <version>3.12.0</version>
</dependency>

2、配置文件

server:
  port: 8087
spring:
  redis:
    password: 123456
    # 一定要加redis://
    address: redis://127.0.0.1:6379
  datasource:
    #使用阿里的Druid
    type: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://127.0.0.1:3306/test?serverTimeznotallow=UTC
    username: root
    password:

3、RedissonClient配置

/**
 * @author wangzhenjun
 * @date 2022/2/9 9:57
 */
@Configuration
public class MyRedissonConfig {

    @Value("${spring.redis.password}")
    private String password;
    @Value("${spring.redis.address}")
    private String address;

    /**
     * 所有對redisson的使用都是通過RedissonClient來操作的
     * @return
     */
    @Bean(destroyMethod="shutdown")
    public RedissonClient redissonClient(){
        // 1. 創(chuàng)建配置
        Config config = new Config();
        // 一定要加redis://
        config.useSingleServer().setAddress(address);
        config.useSingleServer().setPassword(password);
        // 2. 根據(jù)config創(chuàng)建出redissonClient實例
        RedissonClient redissonClient = Redisson.create(config);
        return redissonClient;
    }
}

4、Redis序列化配置

/**
 * @author wangzhenjun
 * @date 2022/11/17 15:20
 */
@Configuration
public class RedisConfig {

    @Bean
    @SuppressWarnings(value = { "unchecked", "rawtypes" })
    public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory connectionFactory)
    {
        RedisTemplate<Object, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(connectionFactory);
        Jackson2JsonRedisSerializer serializer = new Jackson2JsonRedisSerializer(Object.class);

        // 使用StringRedisSerializer來序列化和反序列化redis的key值
        template.setKeySerializer(new StringRedisSerializer());
        template.setValueSerializer(serializer);

        // Hash的key也采用StringRedisSerializer的序列化方式
        template.setHashKeySerializer(new StringRedisSerializer());
        template.setHashValueSerializer(serializer);

        template.afterPropertiesSet();
        return template;
    }
}

5、自定義注解

我們自定義注解,key支持el表達式!這里的參數(shù)可以再加一個key的前綴或者鎖的類型,根據(jù)類型判斷:可重入鎖(RLock getLock(String name))、公平鎖(RLock getFairLock(String name);)這兩種的加鎖!等待鎖超時時間、自動解鎖時間、時間單位這是可選擇的,大家按需,需要看門狗的有的就不需要,現(xiàn)在是有兩種加鎖機制,后面也是看大家的選擇!

/**
 * @author wangzhenjun
 * @date 2023/8/30 10:45
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
public @interface RedisLock {

    /**
     * 分布式鎖的 key,必須:請保持唯一性,支持 spring el表達式
     *
     */
    String value();

    /**
     * 等待鎖超時時間,默認30
     *
     */
    long waitTime() default 30;

    /**
     * 自動解鎖時間,自動解鎖時間一定得大于方法執(zhí)行時間,否則會導致鎖提前釋放,默認100(根據(jù)場景配置)
     * 對時間沒有把握可以使用默認的看門狗會自動續(xù)期
     */
    long leaseTime() default 100;

    /**
     * 時間單位,默認為秒
     *
     */
    TimeUnit timeUnit() default TimeUnit.SECONDS;
}

6、定義切片

現(xiàn)在有兩種加鎖方式,我們來詳細說一下區(qū)別,大家按需選擇:

「lock.tryLock():」

這是一個非阻塞的方法。如果獲取鎖成功,會立即返回 true,如果獲取鎖失敗,會立即返回 false。

當然你可以添加等待時間,超過這個時間仍然沒有獲取到鎖才會返回false。tryLock(long time, TimeUnit unit)tryLock(long waitTime, long leaseTime, TimeUnit unit)

如果你想嘗試獲取鎖,但「不希望在獲取失敗時被阻塞」,可以使用這個方法。

這個方法通常用于獲取鎖后執(zhí)行一個短時間的任務,避免長時間的等待。

「lock.lock():」

這是一個阻塞的方法,如果獲取鎖失敗,它會阻塞當前線程,直到獲取到鎖或超時。因此要確保你的鎖的使用不會導致長時間的等待,避免影響系統(tǒng)性能。

也可以添加鎖的過期時間,一旦獲取鎖成功,鎖會在指定的時間后自動釋放。如果在這段時間內任務未完成,鎖會自動釋放,避免長時間的占用。這個時間要考慮清除,如果執(zhí)行時間不可控建議還是不要傳過期時間,默認會有看門狗來自動續(xù)期,防止方法執(zhí)行中鎖被釋放了!

lock(long leaseTime, TimeUnit unit)

如果你希望一定能夠獲取鎖,而且「不希望在獲取失敗時立即返回」,可以使用這個方法。

這個方法通常用于獲取鎖后需要執(zhí)行一個相對耗時的任務,以及希望避免鎖被長時間占用而引發(fā)的問題。

小編這里建議使用第一種,有鎖正在執(zhí)行,應該返回信息給用戶,不應該讓用戶長時間等待造成不好的影響!

如果是第一個方法,我們需要判斷返回值,加鎖失敗返回給用戶!異常大家可以專門定義一個加鎖失敗異常,小編這里就使用業(yè)務異常了!

/**
 * 分布式鎖切片
 * @author wangzhenjun
 * @date 2023/8/31 9:28
 */
@Slf4j
@Aspect
@RequiredArgsConstructor
@Component
public class RedisLockAspect {

    private final RedissonClient redissonClient;
    private final SpelExpressionParser spelExpressionParser = new SpelExpressionParser();
    private final DefaultParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();


    /**
     * 環(huán)繞切片
     */
    @Around("@annotation(redisLock)")
    public Object aroundRedisLock(ProceedingJoinPoint point, RedisLock redisLock) throws Throwable {
        log.info("=====請求來排隊嘗試獲取鎖=====");
        String value = redisLock.value();
        Assert.hasText(value, "@RedisLock key不能為空!");
        boolean el = redisLock.isEl();
        String lockKey;
        if (el) {
            lockKey = evaluateExpression(value, point);
        } else {
            lockKey = value;
        }
        log.info("========解析后的lockKey :{}", lockKey);
        long waitTime = redisLock.waitTime();
        long leaseTime = redisLock.leaseTime();
        TimeUnit timeUnit = redisLock.timeUnit();

        RLock lock = redissonClient.getLock(lockKey);
//        lock.tryLock(waitTime, leaseTime, timeUnit);
//        lock.lock(leaseTime, timeUnit);
//        lock.lock();
        boolean tryLock = lock.tryLock();
        if (!tryLock) {
            throw new ServiceException("鎖被占用,請稍后提交!");
        }
        try {
            return point.proceed();
        } catch (Throwable throwable) {
            log.error("方法執(zhí)行失敗:", throwable.getMessage());
            throw throwable;
        } finally {
            lock.unlock();
        }
    }

    /**
     * 解析el表達式
     * @param expression
     * @param point
     * @return
     */
    private String evaluateExpression(String expression, ProceedingJoinPoint point) {
        // 獲取目標對象
        Object target = point.getTarget();
        // 獲取方法參數(shù)
        Object[] args = point.getArgs();
        MethodSignature methodSignature = (MethodSignature) point.getSignature();
        Method method = methodSignature.getMethod();

        EvaluationContext context = new MethodBasedEvaluationContext(target, method, args, parameterNameDiscoverer);
        Expression exp = spelExpressionParser.parseExpression(expression);
        return exp.getValue(context, String.class);
    }
}

7、測試

我們測試一個el表達式的,模擬方法執(zhí)行15s,方便我們測試!

@SneakyThrows
@RedisLock("#id")
@GetMapping("/listTest")
public Result listTest(@RequestParam("id") Long id){
    System.out.println("=====方法執(zhí)行中");
    Thread.sleep(150000);
    System.out.println("=====方法執(zhí)行完成");
    return Result.success("成功");
}

我們調用兩次這個方法,看到控制臺有報錯信息,返回結果也是沒有問題的!

四、總結

在本篇博客中,我們深入探討了如何在Spring Boot應用中借助自定義注解來實現(xiàn)分布式鎖,為分布式環(huán)境下的并發(fā)問題提供了優(yōu)雅且高效的解決方案。通過自定義注解,我們成功地將分布式鎖的復雜邏輯進行了封裝,使得在業(yè)務代碼中只需簡單地使用注解,便能實現(xiàn)分布式鎖的獲取和釋放。這不僅讓代碼更具可讀性,還提升了開發(fā)效率,讓開發(fā)人員能夠更專注于業(yè)務邏輯的實現(xiàn)。

相信大家已經能夠對Spring Boot中使用自定義注解實現(xiàn)分布式鎖有一個清晰的理解,加鎖的方式大家可以按需選擇!

責任編輯:姜華 來源: 小王博客基地
相關推薦

2022-08-04 08:45:50

Redisson分布式鎖工具

2024-01-02 13:15:00

分布式鎖RedissonRedis

2023-08-27 22:13:59

Redisson分布式緩存

2019-06-19 15:40:06

分布式鎖RedisJava

2025-01-07 08:37:35

2023-01-13 07:39:07

2021-07-06 08:37:29

Redisson分布式

2024-11-28 15:11:28

2019-02-26 09:51:52

分布式鎖RedisZookeeper

2021-07-02 08:51:09

Redisson分布式鎖公平鎖

2022-11-06 19:28:02

分布式鎖etcd云原生

2021-06-30 14:56:12

Redisson分布式公平鎖

2018-07-17 08:14:22

分布式分布式鎖方位

2022-01-06 10:58:07

Redis數(shù)據(jù)分布式鎖

2023-08-21 19:10:34

Redis分布式

2021-10-25 10:21:59

ZK分布式鎖ZooKeeper

2021-09-17 07:51:24

RedissonRedis分布式

2021-02-28 07:49:28

Zookeeper分布式

2017-01-16 14:13:37

分布式數(shù)據(jù)庫

2018-04-03 16:24:34

分布式方式
點贊
收藏

51CTO技術棧公眾號