SpringBoot
是為了簡化 Spring
應用的創建、運行、調試、部署等一系列問題而誕生的產物,自動裝配的特性讓我們可以更好的關注業務本身而不是外部的XML配置,我們只需遵循規范,引入相關的依賴就可以輕易的搭建出一個 WEB 工程
在 一起來學SpringBoot | 第二十二篇:輕松搞定重復提交(一) 一文中介紹了單機版的重復提交解決方案,在如今這個分布式與集群橫行的世道中,那怎么夠用呢,所以本章重點來了....
重復提交(分布式)
單機版中我們用的是Guava Cache
,但是這玩意存在集群的時候就涼了,所以我們還是要借助類似Redis
、ZooKeeper
之類的中間件實現分布式鎖。
本章目標
利用 自定義注解
、Spring Aop
、Redis Cache
實現分布式鎖,你想鎖表單鎖表單,想鎖接口鎖接口….
具體代碼
也很簡單…
導入依賴
在 pom.xml
中添加上 starter-web
、starter-aop
、starter-data-redis
的依賴即可
1 2 3 4 5 6 7 8 9 10 11 12 13 14
|
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> </dependencies>
|
屬性配置
在 application.properites
資源文件中添加 redis
相關的配置項
1 2 3
|
spring.redis.host=localhost spring.redis.port=6379 spring.redis.password=battcn
|
CacheLock 注解
創建一個 CacheLock
注解,本章內容都是實戰使用過的,所以屬性配置會相對完善了,話不多說注釋都給各位寫齊全了….
prefix:
緩存中 key 的前綴
expire:
過期時間,此處默認為 5 秒
timeUnit:
超時單位,此處默認為秒
delimiter:
key 的分隔符,將不同參數值分割開來
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
|
package com.battcn.annotation;
import java.lang.annotation.*; import java.util.concurrent.TimeUnit;
|
CacheParam 注解
上一篇中給說過 key 的生成規則是自己定義的,如果通過表達式語法自己得去寫解析規則還是比較麻煩的,所以依舊是用注解的方式…
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
|
package com.battcn.annotation;
import java.lang.annotation.*;
|
Key 生成策略(接口)
創建一個 CacheKeyGenerator
具體實現由使用者自己去注入
Key 生成策略(實現)
解析過程雖然看上去優點繞,但認真閱讀或者調試就會發現,主要是解析帶 CacheLock
注解的屬性,獲取對應的屬性值,生成一個全新的緩存 Key
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
|
package com.battcn.interceptor;
import com.battcn.annotation.CacheLock; import com.battcn.annotation.CacheParam; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.reflect.MethodSignature; import org.springframework.util.ReflectionUtils; import org.springframework.util.StringUtils;
import java.lang.annotation.Annotation; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Parameter;
|
Lock 攔截器(AOP)
熟悉 Redis
的朋友都知道它是線程安全的,我們利用它的特性可以很輕松的實現一個分布式鎖,如 opsForValue().setIfAbsent(key,value)
它的作用就是如果緩存中沒有當前 Key 則進行緩存同時返回 true
反之亦然;當緩存后給 key
在設置個過期時間,防止因為系統崩潰而導致鎖遲遲不釋放形成死鎖; 那么我們是不是可以這樣認為當返回 true
我們認為它獲取到鎖了,在鎖未釋放的時候我們進行異常的拋出….
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
|
package com.battcn.interceptor;
import com.battcn.annotation.CacheLock; import com.battcn.utils.RedisLockHelper; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.reflect.MethodSignature; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.util.StringUtils;
import java.lang.reflect.Method; import java.util.UUID;
|
RedisLockHelper
通過封裝成 API 方式調用,靈活度更加高
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120
|
package com.battcn.utils;
import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.connection.RedisStringCommands; import org.springframework.data.redis.core.RedisCallback; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.data.redis.core.types.Expiration; import org.springframework.util.StringUtils;
import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import java.util.regex.Pattern;
|