成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

分布式進階:Springboot自定義注解優雅的實現Redisson分布式鎖

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

一、前言

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

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

二、Redisson簡介

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

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

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

「提供眾多的鎖:」

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

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

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

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

三、實戰

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. 創建配置
        Config config = new Config();
        // 一定要加redis://
        config.useSingleServer().setAddress(address);
        config.useSingleServer().setPassword(password);
        // 2. 根據config創建出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表達式!這里的參數可以再加一個key的前綴或者鎖的類型,根據類型判斷:可重入鎖(RLock getLock(String name))、公平鎖(RLock getFairLock(String name);)這兩種的加鎖!等待鎖超時時間、自動解鎖時間、時間單位這是可選擇的,大家按需,需要看門狗的有的就不需要,現在是有兩種加鎖機制,后面也是看大家的選擇!

/**
 * @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;

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

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

6、定義切片

現在有兩種加鎖方式,我們來詳細說一下區別,大家按需選擇:

「lock.tryLock():」

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

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

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

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

「lock.lock():」

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

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

lock(long leaseTime, TimeUnit unit)

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

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

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

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

/**
 * 分布式鎖切片
 * @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();


    /**
     * 環繞切片
     */
    @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("方法執行失敗:", throwable.getMessage());
            throw throwable;
        } finally {
            lock.unlock();
        }
    }

    /**
     * 解析el表達式
     * @param expression
     * @param point
     * @return
     */
    private String evaluateExpression(String expression, ProceedingJoinPoint point) {
        // 獲取目標對象
        Object target = point.getTarget();
        // 獲取方法參數
        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表達式的,模擬方法執行15s,方便我們測試!

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

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

四、總結

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

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

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

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

2022-11-06 19:28:02

分布式鎖etcd云原生

2021-07-02 08:51:09

Redisson分布式鎖公平鎖

2021-06-30 14:56:12

Redisson分布式公平鎖

2023-08-21 19:10:34

Redis分布式

2022-01-06 10:58:07

Redis數據分布式鎖

2021-10-25 10:21:59

ZK分布式鎖ZooKeeper

2018-07-17 08:14:22

分布式分布式鎖方位

2021-02-28 07:49:28

Zookeeper分布式

2021-09-17 07:51:24

RedissonRedis分布式

2017-01-16 14:13:37

分布式數據庫

2022-04-08 08:27:08

分布式鎖系統
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 在线观看免费av网站 | 91操操操 | h在线播放 | 亚洲欧美视频一区二区 | 色吧综合 | 日韩中文字幕在线观看视频 | 色桃网| 一区二区三区四区在线 | 亚洲视频一区在线观看 | 亚洲视频在线一区 | 国产在线中文字幕 | 波多野结衣一区二区三区在线观看 | 特级做a爰片毛片免费看108 | 亚洲高清在线 | 成人欧美日韩一区二区三区 | 99re99| 中文字幕97 | 狠狠操电影 | 日本午夜免费福利视频 | 亚洲麻豆 | 国产精品视频在 | 夜夜夜久久 | 日本精品一区二区在线观看 | 久久高清国产 | 成人国产精品 | 日本久久一区 | 日韩久草 | 亚洲一区二区三区视频 | 国产一卡二卡三卡 | 日韩在线免费电影 | 97av视频| 亚洲一二三区不卡 | av网站免费 | 激情网站在线观看 | 国产一区二区三区色淫影院 | 亚洲精品乱码 | 中文字幕日韩三级 | 99reav| 99视频在线免费观看 | 中文字幕一区在线观看视频 | 欧美日韩国产精品一区 |