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

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

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

一、前言

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

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

二、Redisson簡介

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

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

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

「提供眾多的鎖:」

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

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

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

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

三、實戰(zhàn)

1、導(dǎo)入依賴

<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表達(dá)式!這里的參數(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表達(dá)式
     *
     */
    String value();

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

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

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

6、定義切片

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

「lock.tryLock():」

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

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

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

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

「lock.lock():」

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

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

lock(long leaseTime, TimeUnit unit)

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

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

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

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

/**
 * 分布式鎖切片
 * @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表達(dá)式
     * @param expression
     * @param point
     * @return
     */
    private String evaluateExpression(String expression, ProceedingJoinPoint point) {
        // 獲取目標(biāo)對象
        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表達(dá)式的,模擬方法執(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("成功");
}

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

四、總結(jié)

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

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

責(zé)任編輯:姜華 來源: 小王博客基地
相關(guā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

2022-11-06 19:28:02

分布式鎖etcd云原生

2021-07-02 08:51:09

Redisson分布式鎖公平鎖

2021-06-30 14:56:12

Redisson分布式公平鎖

2022-01-06 10:58:07

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

2023-08-21 19:10:34

Redis分布式

2021-10-25 10:21:59

ZK分布式鎖ZooKeeper

2018-07-17 08:14:22

分布式分布式鎖方位

2021-02-28 07:49:28

Zookeeper分布式

2018-04-03 16:24:34

分布式方式

2017-01-16 14:13:37

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

2017-04-13 10:51:09

Consul分布式
點贊
收藏

51CTO技術(shù)棧公眾號

主站蜘蛛池模板: 在线91 | 欧美8一10sex性hd| 国产精品视频一区二区三 | 久久精品久久久久久 | 一级片免费视频 | 奇米久久 | 欧美小视频在线观看 | 亚洲视频免费在线观看 | 国产精品夜夜春夜夜爽久久电影 | 国产专区在线 | 亚洲精品成人网 | 男人的天堂一级片 | 久久国产精品网 | 中文字幕不卡 | 久久国产成人精品国产成人亚洲 | 久久国产精品首页 | 日韩欧美国产不卡 | 日本小电影网站 | 一级黄色片在线免费观看 | 日韩精品极品视频在线观看免费 | 日韩精品成人 | 亚洲成人高清 | 色综合一区二区三区 | 中国一级大毛片 | 五月天激情电影 | 久久久.com| 国产精品激情在线 | 99久久国产 | 日韩看片 | 中文字幕国产精品 | 天天拍夜夜爽 | 亚洲国产精品va在线看黑人 | 国产在线观看福利 | 男女精品网站 | 国产成人一区二区三区 | 欧美一区二区三区久久精品 | 精品91久久| 看亚洲a级一级毛片 | 亚洲国产aⅴ成人精品无吗 亚洲精品久久久一区二区三区 | 这里精品| 亚洲国产一区二区三区在线观看 |