1. 如何優(yōu)雅的實(shí)現(xiàn)分布式鎖

        共 4690字,需瀏覽 10分鐘

         ·

        2020-08-18 10:18

        概述

        提到分布式鎖大家都會(huì)想到如下兩種:

        • 基于 Redisson組件,使用redlock算法實(shí)現(xiàn)
        • 基于 Apache Curator,利用Zookeeper的臨時(shí)順序節(jié)點(diǎn)模型實(shí)現(xiàn)

        今天我們來(lái)說(shuō)說(shuō)第三種,使用 Spring Integration 實(shí)現(xiàn),也是我個(gè)人比較推薦的一種。

        Spring Integration在基于Spring的應(yīng)用程序中實(shí)現(xiàn)輕量級(jí)消息傳遞,并支持通過(guò)聲明適配器與外部系統(tǒng)集成。Spring Integration的主要目標(biāo)是提供一個(gè)簡(jiǎn)單的模型來(lái)構(gòu)建企業(yè)集成解決方案,同時(shí)保持關(guān)注點(diǎn)的分離,這對(duì)于生成可維護(hù),可測(cè)試的代碼至關(guān)重要。我們熟知的 Spring Cloud Stream的底層就是Spring Integration。

        官方地址:https://github.com/spring-projects/spring-integration

        Spring Integration提供的全局鎖目前為如下存儲(chǔ)提供了實(shí)現(xiàn):

        • Gemfire
        • JDBC
        • Redis
        • Zookeeper

        它們使用相同的API抽象,這意味著,不論使用哪種存儲(chǔ),你的編碼體驗(yàn)是一樣的。試想一下你目前是基于zookeeper實(shí)現(xiàn)的分布式鎖,哪天你想換成redis的實(shí)現(xiàn),我們只需要修改相關(guān)依賴(lài)和配置就可以了,無(wú)需修改代碼。下面是你使用 Spring Integration 實(shí)現(xiàn)分布式鎖時(shí)需要關(guān)注的方法:

        方法名描述
        lock()Acquires the lock. ? 加鎖,如果已經(jīng)被其他線程鎖住或者當(dāng)前線程不能獲取鎖則阻塞
        lockInterruptibly()Acquires the lock unless the current thread is interrupted. 加鎖,除非當(dāng)前線程被打斷。
        tryLock()Acquires the lock only if it is free at the time of invocation. 嘗試加鎖,如果已經(jīng)有其他鎖鎖住,獲取當(dāng)前線程不能加鎖,則返回false,加鎖失?。患渔i成功則返回true
        tryLock(long time, TimeUnit unit)Acquires the lock if it is free within the given waiting time and the current thread has not been interrupted. 嘗試在指定時(shí)間內(nèi)加鎖,如果已經(jīng)有其他鎖鎖住,獲取當(dāng)前線程不能加鎖,則返回false,加鎖失敗;加鎖成功則返回true
        unlock()Releases the lock. 解鎖

        實(shí)戰(zhàn)

        話不多說(shuō),我們看看使用 Spring Integration ?如何基于redis和zookeeper快速實(shí)現(xiàn)分布式鎖,至于Gemfire 和 Jdbc的實(shí)現(xiàn)大家自行實(shí)踐。

        基于Redis實(shí)現(xiàn)

        • 引入相關(guān)組件
        <dependency>
        ?<groupId>org.springframework.bootgroupId>
        ?<artifactId>spring-boot-starter-integrationartifactId>
        dependency>

        <dependency>
        ?<groupId>org.springframework.integrationgroupId>
        ?<artifactId>spring-integration-redisartifactId>
        dependency>

        <dependency>
        ?<groupId>org.springframework.bootgroupId>
        ?<artifactId>spring-boot-starter-data-redisartifactId>
        dependency>
        • 在application.yml中添加redis的配置
        spring:
        ?redis:
        ??host:?172.31.0.149
        ??port:?7111
        • 建立配置類(lèi),注入 RedisLockRegistry
        @Configuration
        public?class?RedisLockConfiguration?{

        ????@Bean
        ????public?RedisLockRegistry?redisLockRegistry(RedisConnectionFactory?redisConnectionFactory){
        ????????return?new?RedisLockRegistry(redisConnectionFactory,?"redis-lock");
        ????}

        }
        • 編寫(xiě)測(cè)試代碼
        @RestController
        @RequestMapping("lock")
        @Log4j2
        public?class?DistributedLockController?{
        ????@Autowired
        ????private?RedisLockRegistry?redisLockRegistry;

        ????@GetMapping("/redis")
        ????public?void?test1()?{
        ????????Lock?lock?=?redisLockRegistry.obtain("redis");
        ????????try{
        ????????????//嘗試在指定時(shí)間內(nèi)加鎖,如果已經(jīng)有其他鎖鎖住,獲取當(dāng)前線程不能加鎖,則返回false,加鎖失?。患渔i成功則返回true
        ????????????if(lock.tryLock(3,?TimeUnit.SECONDS)){
        ????????????????log.info("lock?is?ready");
        ????????????????TimeUnit.SECONDS.sleep(5);
        ????????????}
        ????????}?catch?(InterruptedException?e)?{
        ????????????log.error("obtain?lock?error",e);
        ????????}?finally?{
        ????????????lock.unlock();
        ????????}
        ????}
        }
        • 測(cè)試
          啟動(dòng)多個(gè)實(shí)例,分別訪問(wèn) /lock/redis 端點(diǎn),一個(gè)正常秩序業(yè)務(wù)邏輯,另外一個(gè)實(shí)例訪問(wèn)出現(xiàn)如下錯(cuò)誤說(shuō)明第二個(gè)實(shí)例沒(méi)有拿到鎖,證明了分布式鎖的存在。

        注意,如果使用新版Springboot進(jìn)行集成時(shí)需要使用Redis4版本,否則會(huì)出現(xiàn)下面的異常告警,主要是 unlock() 釋放鎖時(shí)使用了UNLINK命令,這個(gè)需要Redis4版本才能支持。


        2020-05-14?11:30:24,781?WARN??RedisLockRegistry:339?-?The?UNLINK?command?has?failed?(not?supported?on?the?Redis?server?);?falling?back?to?the?regular?DELETE?command
        org.springframework.data.redis.RedisSystemException:?Error?in?execution;?nested?exception?is?io.lettuce.core.RedisCommandExecutionException:?ERR?unknown?command?'UNLINK'

        基于Zookeeper實(shí)現(xiàn)

        • 引入組件
        <dependency>
        ?<groupId>org.springframework.bootgroupId>
        ?<artifactId>spring-boot-starter-integrationartifactId>
        dependency>

        ?<dependency>
        ?<groupId>org.springframework.integrationgroupId>
        ?<artifactId>spring-integration-zookeeperartifactId>
        dependency>
        • 在application.yml中添加zookeeper的配置
        zookeeper:??
        ????host:?172.31.0.43:2181
        • 建立配置類(lèi),注入 ZookeeperLockRegistry
        @Configuration
        public?class?ZookeeperLockConfiguration?{
        ????@Value("${zookeeper.host}")
        ????private?String?zkUrl;


        ????@Bean
        ????public?CuratorFrameworkFactoryBean?curatorFrameworkFactoryBean(){
        ????????return?new?CuratorFrameworkFactoryBean(zkUrl);
        ????}

        ????@Bean
        ????public?ZookeeperLockRegistry?zookeeperLockRegistry(CuratorFramework?curatorFramework){
        ????????return?new?ZookeeperLockRegistry(curatorFramework,"/zookeeper-lock");
        ????}
        }
        • 編寫(xiě)測(cè)試代碼
        @RestController
        @RequestMapping("lock")
        @Log4j2
        public?class?DistributedLockController?{

        ????@Autowired
        ????private?ZookeeperLockRegistry?zookeeperLockRegistry;

        ????@GetMapping("/zookeeper")
        ????public?void?test2()?{
        ????????Lock?lock?=?zookeeperLockRegistry.obtain("zookeeper");
        ????????try{
        ????????????//嘗試在指定時(shí)間內(nèi)加鎖,如果已經(jīng)有其他鎖鎖住,獲取當(dāng)前線程不能加鎖,則返回false,加鎖失??;加鎖成功則返回true
        ????????????if(lock.tryLock(3,?TimeUnit.SECONDS)){
        ????????????????log.info("lock?is?ready");
        ????????????????TimeUnit.SECONDS.sleep(5);
        ????????????}
        ????????}?catch?(InterruptedException?e)?{
        ????????????log.error("obtain?lock?error",e);
        ????????}?finally?{
        ????????????lock.unlock();
        ????????}
        ????}
        }
        • 測(cè)試
          啟動(dòng)多個(gè)實(shí)例,分別訪問(wèn) /lock/zookeeper 端點(diǎn),一個(gè)正常執(zhí)行業(yè)務(wù)邏輯,另外一個(gè)實(shí)例訪問(wèn)出現(xiàn)如下錯(cuò)誤:說(shuō)明第二個(gè)實(shí)例沒(méi)有拿到鎖,證明了分布式鎖的存在。



        如果本文對(duì)你有幫助,
        別忘記給我個(gè)三連:
        點(diǎn)贊,轉(zhuǎn)發(fā),評(píng)論
        。
        咱們下期見(jiàn)!

        收藏?等于白嫖,點(diǎn)贊?才是真情!

        1.?人人都能看懂的 6 種限流實(shí)現(xiàn)方案!

        2.?一個(gè)空格引發(fā)的“慘案“

        3.?大型網(wǎng)站架構(gòu)演化發(fā)展歷程

        4.?Java語(yǔ)言“坑爹”排行榜TOP 10

        5. 我是一個(gè)Java類(lèi)(附帶精彩吐槽)

        6. 看完這篇Redis緩存三大問(wèn)題,保你能和面試官互扯

        7. 程序員必知的 89 個(gè)操作系統(tǒng)核心概念

        8. 深入理解 MySQL:快速學(xué)會(huì)分析SQL執(zhí)行效率

        9. API 接口設(shè)計(jì)規(guī)范

        10. Spring Boot 面試,一個(gè)問(wèn)題就干趴下了!



        掃碼二維碼關(guān)注我


        ·end·

        —如果本文有幫助,請(qǐng)分享到朋友圈吧—

        我們一起愉快的玩耍!



        你點(diǎn)的每個(gè)贊,我都認(rèn)真當(dāng)成了喜歡

        瀏覽 67
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        評(píng)論
        圖片
        表情
        推薦
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
          
          

            1. 爱搞视频在线观看 | 天天爱夜夜草 | 淫男乱女之小雄性事 | 色黄视频在线播放 | 黄工厂精品免费观看 |