小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

手把手教你 SpringBoot 分布式鎖的實現(xiàn)

 鷹兔牛熊眼 2020-06-27

你知道的越多,不知道的就越多,業(yè)余的像一棵小草!

你來,我們一起精進(jìn)!你不來,我和你的競爭對手一起精進(jìn)!

編輯:業(yè)余草

來源:https:///632yIv

推薦:https://www./?p=5035

前言

前段時間面試了一個高級程序員,問他了一些分布式鎖的知識,回答的還不錯。后來進(jìn)了我們的團(tuán)隊,剛好讓他接手一個新項目,需要用到分布式鎖,我說這是你的強項,你來實現(xiàn)。

誰知,項目上線后,各種問題接連爆發(fā)出來。后來我找他談話,主要是說,千萬不要重復(fù)造輪子,直接吧 A 項目中的 Redisson 用法實現(xiàn)復(fù)制過來一份不就行了,干嘛非要自己搞一套,還搞出問題。。。

今天,我們一起來手把手來實現(xiàn)一個高效的 SpringBoot 分布式鎖!本文通過 Spring Boot 整合 redisson 來實現(xiàn)分布式鎖,并結(jié)合 demo 測試結(jié)果。

分析設(shè)計要點

當(dāng)我們在設(shè)計分布式鎖的時候,我們應(yīng)該考慮分布式鎖至少要滿足的一些條件,同時考慮如何高效的設(shè)計分布式鎖,這里我認(rèn)為以下幾點是必須要考慮的。

1、互斥

在分布式高并發(fā)的條件下,我們最需要保證,同一時刻只能有一個線程獲得鎖,這是最基本的一點。

2、防止死鎖

在分布式高并發(fā)的條件下,比如有個線程獲得鎖的同時,還沒有來得及去釋放鎖,就因為系統(tǒng)故障或者其它原因使它無法執(zhí)行釋放鎖的命令,導(dǎo)致其它線程都無法獲得鎖,造成死鎖。

所以分布式非常有必要設(shè)置鎖的有效時間,確保系統(tǒng)出現(xiàn)故障后,在一定時間內(nèi)能夠主動去釋放鎖,避免造成死鎖的情況。

3、性能

對于訪問量大的共享資源,需要考慮減少鎖等待的時間,避免導(dǎo)致大量線程阻塞。

所以在鎖的設(shè)計時,需要考慮兩點。

1、鎖的顆粒度要盡量小。比如你要通過鎖來減庫存,那這個鎖的名稱你可以設(shè)置成是商品的ID,而不是任取名稱。這樣這個鎖只對當(dāng)前商品有效,鎖的顆粒度小。

2、鎖的范圍盡量要小。比如只要鎖2行代碼就可以解決問題的,那就不要去鎖10行代碼了。

4、重入

我們知道ReentrantLock是可重入鎖,那它的特點就是:同一個線程可以重復(fù)拿到同一個資源的鎖。重入鎖非常有利于資源的高效利用。關(guān)于這點之后會做演示。

針對以上Redisson都能很好的滿足,下面就來使用它。

首先看下常規(guī)Redis鎖的處理圖

代碼實現(xiàn)
添加依賴
  1. <!--redis-->

  2. <dependency>

    1. <groupId>org.springframework.boot</groupId>

    2. <artifactId>spring-boot-starter-data-redis</artifactId>

  3. </dependency>

  4. <!--redisson-->

  5. <dependency>

    1. <groupId>org.redisson</groupId>

    2. <artifactId>redisson-spring-boot-starter</artifactId>

    3. <version>3.10.6</version>

  6. </dependency>

配置信息

  1. spring:

  2. # redis

  3. redis:

  4. host: 47.103.5.190

  5. port: 6379

  6. jedis:

  7. pool:

  8. # 連接池最大連接數(shù)(使用負(fù)值表示沒有限制)

  9. max-active: 100

  10. # 連接池中的最小空閑連接

  11. max-idle: 10

  12. # 連接池最大阻塞等待時間(使用負(fù)值表示沒有限制)

  13. max-wait: -1

  14. # 連接超時時間(毫秒)

  15. timeout: 5000

  16. #默認(rèn)是索引為0的數(shù)據(jù)庫

  17. database: 0

配置類

  1. /**

  2. * redisson 配置,下面是單節(jié)點配置:

  3. *

  4. * @author gourd

  5. */

  6. @Configuration

  7. publicclassRedissonConfig{

  8. @Value('${spring.redis.host}')

  9. privateString host;

  10. @Value('${spring.redis.port}')

  11. privateString port;

  12. @Value('${spring.redis.password:}')

  13. privateString password;

  14. @Bean

  15. publicRedissonClient redissonClient() {

  16. Config config = newConfig();

  17. //單節(jié)點

  18. config.useSingleServer().setAddress('redis://'+ host + ':'+ port);

  19. if(StringUtils.isEmpty(password)) {

  20. config.useSingleServer().setPassword(null);

  21. } else{

  22. config.useSingleServer().setPassword(password);

  23. }

  24. //添加主從配置

  25. // config.useMasterSlaveServers().setMasterAddress('').setPassword('').addSlaveAddress(new String[]{'',''});

  26. // 集群模式配置 setScanInterval()掃描間隔時間,單位是毫秒, //可以用'rediss://'來啟用SSL連接

  27. // config.useClusterServers().setScanInterval(2000).addNodeAddress('redis://127.0.0.1:7000', 'redis://127.0.0.1:7001').addNodeAddress('redis://127.0.0.1:7002');

  28. returnRedisson.create(config);

  29. }

  30. }

Redisson 工具類

  1. /**

  2. * redis分布式鎖幫助類

  3. *

  4. * @author gourd

  5. *

  6. */

  7. publicclassRedisLockUtil{

  8. privatestaticDistributedLocker distributedLocker = SpringContextHolder.getBean('distributedLocker',DistributedLocker.class);

  9. /**

  10. * 加鎖

  11. * @param lockKey

  12. * @return

  13. */

  14. publicstaticRLocklock(String lockKey) {

  15. return distributedLocker.lock(lockKey);

  16. }

  17. /**

  18. * 釋放鎖

  19. * @param lockKey

  20. */

  21. publicstaticvoid unlock(String lockKey) {

  22. distributedLocker.unlock(lockKey);

  23. }

  24. /**

  25. * 釋放鎖

  26. * @param lock

  27. */

  28. publicstaticvoid unlock(RLocklock) {

  29. distributedLocker.unlock(lock);

  30. }

  31. /**

  32. * 帶超時的鎖

  33. * @param lockKey

  34. * @param timeout 超時時間 單位:秒

  35. */

  36. publicstaticRLocklock(String lockKey, int timeout) {

  37. return distributedLocker.lock(lockKey, timeout);

  38. }

  39. /**

  40. * 帶超時的鎖

  41. * @param lockKey

  42. * @param unit 時間單位

  43. * @param timeout 超時時間

  44. */

  45. publicstaticRLocklock(String lockKey, int timeout,TimeUnit unit ) {

  46. return distributedLocker.lock(lockKey, unit, timeout);

  47. }

  48. /**

  49. * 嘗試獲取鎖

  50. * @param lockKey

  51. * @param waitTime 最多等待時間

  52. * @param leaseTime 上鎖后自動釋放鎖時間

  53. * @return

  54. */

  55. publicstaticboolean tryLock(String lockKey, int waitTime, int leaseTime) {

  56. return distributedLocker.tryLock(lockKey, TimeUnit.SECONDS, waitTime, leaseTime);

  57. }

  58. /**

  59. * 嘗試獲取鎖

  60. * @param lockKey

  61. * @param unit 時間單位

  62. * @param waitTime 最多等待時間

  63. * @param leaseTime 上鎖后自動釋放鎖時間

  64. * @return

  65. */

  66. publicstaticboolean tryLock(String lockKey, TimeUnit unit, int waitTime, int leaseTime) {

  67. return distributedLocker.tryLock(lockKey, unit, waitTime, leaseTime);

  68. }

  69. /**

  70. * 獲取計數(shù)器

  71. *

  72. * @param name

  73. * @return

  74. */

  75. publicstaticRCountDownLatch getCountDownLatch(String name){

  76. return distributedLocker.getCountDownLatch(name);

  77. }

  78. /**

  79. * 獲取信號量

  80. *

  81. * @param name

  82. * @return

  83. */

  84. publicstaticRSemaphore getSemaphore(String name){

  85. return distributedLocker.getSemaphore(name);

  86. }

  87. }

底層封裝

  1. /**

  2. * @author gourd

  3. */

  4. publicinterfaceDistributedLocker{

  5. RLocklock(String lockKey);

  6. RLocklock(String lockKey, int timeout);

  7. RLocklock(String lockKey, TimeUnit unit, int timeout);

  8. boolean tryLock(String lockKey, TimeUnit unit, int waitTime, int leaseTime);

  9. void unlock(String lockKey);

  10. void unlock(RLocklock);

  11. }

  12. /**

  13. * @author gourd

  14. */

  15. @Component

  16. publicclassRedisDistributedLockerimplementsDistributedLocker{

  17. @Autowired

  18. privateRedissonClient redissonClient;

  19. @Override

  20. publicRLocklock(String lockKey) {

  21. RLocklock= redissonClient.getLock(lockKey);

  22. lock.lock();

  23. returnlock;

  24. }

  25. @Override

  26. publicRLocklock(String lockKey, int leaseTime) {

  27. RLocklock= redissonClient.getLock(lockKey);

  28. lock.lock(leaseTime, TimeUnit.SECONDS);

  29. returnlock;

  30. }

  31. @Override

  32. publicRLocklock(String lockKey, TimeUnit unit ,int timeout) {

  33. RLocklock= redissonClient.getLock(lockKey);

  34. lock.lock(timeout, unit);

  35. returnlock;

  36. }

  37. @Override

  38. publicboolean tryLock(String lockKey, TimeUnit unit, int waitTime, int leaseTime) {

  39. RLocklock= redissonClient.getLock(lockKey);

  40. try{

  41. returnlock.tryLock(waitTime, leaseTime, unit);

  42. } catch(InterruptedException e) {

  43. returnfalse;

  44. }

  45. }

  46. @Override

  47. publicvoid unlock(String lockKey) {

  48. RLocklock= redissonClient.getLock(lockKey);

  49. lock.unlock();

  50. }

  51. @Override

  52. publicvoid unlock(RLocklock) {

  53. lock.unlock();

  54. }

  55. }


測試
模擬并發(fā)測試
  1. /**

  2. * redis分布式鎖控制器

  3. * @author gourd

  4. * @since 2019-07-30

  5. */

  6. @RestController

  7. @Api(tags = 'redisson', description = 'redis分布式鎖控制器')

  8. @RequestMapping('/redisson')

  9. @Slf4j

  10. publicclassRedissonLockController{

  11. /**

  12. * 鎖測試共享變量

  13. */

  14. privateInteger lockCount = 10;

  15. /**

  16. * 無鎖測試共享變量

  17. */

  18. privateInteger count = 10;

  19. /**

  20. * 模擬線程數(shù)

  21. */

  22. privatestaticint threadNum = 10;

  23. /**

  24. * 模擬并發(fā)測試加鎖和不加鎖

  25. * @return

  26. */

  27. @GetMapping('/test')

  28. @ApiOperation(value = '模擬并發(fā)測試加鎖和不加鎖')

  29. publicvoidlock(){

  30. // 計數(shù)器

  31. finalCountDownLatch countDownLatch = newCountDownLatch(1);

  32. for(int i = 0; i < threadNum; i ++) {

  33. MyRunnable myRunnable = newMyRunnable(countDownLatch);

  34. Thread myThread = newThread(myRunnable);

  35. myThread.start();

  36. }

  37. // 釋放所有線程

  38. countDownLatch.countDown();

  39. }

  40. /**

  41. * 加鎖測試

  42. */

  43. privatevoid testLockCount() {

  44. String lockKey = 'lock-test';

  45. try{

  46. // 加鎖,設(shè)置超時時間2s

  47. RedisLockUtil.lock(lockKey,2, TimeUnit.SECONDS);

  48. lockCount--;

  49. log.info('lockCount值:'+lockCount);

  50. }catch(Exception e){

  51. log.error(e.getMessage(),e);

  52. }finally{

  53. // 釋放鎖

  54. RedisLockUtil.unlock(lockKey);

  55. }

  56. }

  57. /**

  58. * 無鎖測試

  59. */

  60. privatevoid testCount() {

  61. count--;

  62. log.info('count值:'+count);

  63. }

  64. publicclassMyRunnableimplementsRunnable{

  65. /**

  66. * 計數(shù)器

  67. */

  68. finalCountDownLatch countDownLatch;

  69. publicMyRunnable(CountDownLatch countDownLatch) {

  70. this.countDownLatch = countDownLatch;

  71. }

  72. @Override

  73. publicvoid run() {

  74. try{

  75. // 阻塞當(dāng)前線程,直到計時器的值為0

  76. countDownLatch.await();

  77. } catch(InterruptedException e) {

  78. log.error(e.getMessage(),e);

  79. }

  80. // 無鎖操作

  81. testCount();

  82. // 加鎖操作

  83. testLockCount();

  84. }

  85. }

  86. }

調(diào)用接口后打印值:

測試結(jié)果
根據(jù)打印結(jié)果可以明顯看到,未加鎖的 count-- 后值是亂序的,而加鎖后的結(jié)果和我們預(yù)期的一樣。
本文只是實戰(zhàn)項目中復(fù)制出來的部分代碼,實際使用中封裝成了 jar,內(nèi)部系統(tǒng)只需要引用即可。最后,希望對大家有幫助。如有錯誤之處,歡迎指正!

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多