<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

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

          共 4690字,需瀏覽 10分鐘

           ·

          2020-08-18 10:18

          概述

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

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

          今天我們來說說第三種,使用 Spring Integration 實現(xiàn),也是我個人比較推薦的一種。

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

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

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

          • Gemfire
          • JDBC
          • Redis
          • Zookeeper

          它們使用相同的API抽象,這意味著,不論使用哪種存儲,你的編碼體驗是一樣的。試想一下你目前是基于zookeeper實現(xiàn)的分布式鎖,哪天你想換成redis的實現(xiàn),我們只需要修改相關(guān)依賴和配置就可以了,無需修改代碼。下面是你使用 Spring Integration 實現(xiàn)分布式鎖時需要關(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,加鎖失敗;加鎖成功則返回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. 嘗試在指定時間內(nèi)加鎖,如果已經(jīng)有其他鎖鎖住,獲取當(dāng)前線程不能加鎖,則返回false,加鎖失敗;加鎖成功則返回true
          unlock()Releases the lock. 解鎖

          實戰(zhàn)

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

          基于Redis實現(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
          • 建立配置類,注入 RedisLockRegistry
          @Configuration
          public?class?RedisLockConfiguration?{

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

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

          ????@GetMapping("/redis")
          ????public?void?test1()?{
          ????????Lock?lock?=?redisLockRegistry.obtain("redis");
          ????????try{
          ????????????//嘗試在指定時間內(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();
          ????????}
          ????}
          }
          • 測試
            啟動多個實例,分別訪問 /lock/redis 端點,一個正常秩序業(yè)務(wù)邏輯,另外一個實例訪問出現(xiàn)如下錯誤說明第二個實例沒有拿到鎖,證明了分布式鎖的存在。

          注意,如果使用新版Springboot進行集成時需要使用Redis4版本,否則會出現(xiàn)下面的異常告警,主要是 unlock() 釋放鎖時使用了UNLINK命令,這個需要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實現(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
          • 建立配置類,注入 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");
          ????}
          }
          • 編寫測試代碼
          @RestController
          @RequestMapping("lock")
          @Log4j2
          public?class?DistributedLockController?{

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

          ????@GetMapping("/zookeeper")
          ????public?void?test2()?{
          ????????Lock?lock?=?zookeeperLockRegistry.obtain("zookeeper");
          ????????try{
          ????????????//嘗試在指定時間內(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();
          ????????}
          ????}
          }
          • 測試
            啟動多個實例,分別訪問 /lock/zookeeper 端點,一個正常執(zhí)行業(yè)務(wù)邏輯,另外一個實例訪問出現(xiàn)如下錯誤:說明第二個實例沒有拿到鎖,證明了分布式鎖的存在。



          如果本文對你有幫助,
          別忘記給我個三連:
          點贊,轉(zhuǎn)發(fā),評論
          咱們下期見!

          收藏?等于白嫖點贊?才是真情!

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

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

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

          4.?Java語言“坑爹”排行榜TOP 10

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

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

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

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

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

          10. Spring Boot 面試,一個問題就干趴下了!



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


          ·end·

          —如果本文有幫助,請分享到朋友圈吧—

          我們一起愉快的玩耍!



          你點的每個贊,我都認真當(dāng)成了喜歡

          瀏覽 67
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  五月激情婷婷影音娱乐 | 午夜欧美精品久久久久久久 | 91精品久久久久久久不卡 | 婷婷五月天综合久久 | 51精品人人搡人人妻 |