mirror of
				https://gitee.com/hhyykk/ipms-sjy.git
				synced 2025-10-31 10:18:42 +08:00 
			
		
		
		
	增加 Redis 分布式锁
This commit is contained in:
		| @@ -38,6 +38,8 @@ | |||||||
| 1. Redis 监控:监控 Redis 数据库的使用情况,使用的 Redis Key 管理 | 1. Redis 监控:监控 Redis 数据库的使用情况,使用的 Redis Key 管理 | ||||||
| 1. Java 监控:基于 Spring Boot Admin 实现 Java 应用的监控 | 1. Java 监控:基于 Spring Boot Admin 实现 Java 应用的监控 | ||||||
| 1. 链路追踪:基于 SkyWalking 实现性能监控,特别是链路的追踪 | 1. 链路追踪:基于 SkyWalking 实现性能监控,特别是链路的追踪 | ||||||
|  | 1. 分布式锁:基于 Redis 实现分布式锁,满足并发场景 | ||||||
|  | 1. 幂等组件:基于 Redis 实现幂等组件,解决重复请求问题 | ||||||
|  |  | ||||||
| ### 研发工具 | ### 研发工具 | ||||||
|  |  | ||||||
| @@ -45,7 +47,6 @@ | |||||||
| 1. 代码生成:前后端代码的生成(Java、Vue、SQL),支持 CRUD 下载 | 1. 代码生成:前后端代码的生成(Java、Vue、SQL),支持 CRUD 下载 | ||||||
| 1. 系统接口:基于 Swagger 自动生成相关的 RESTful API 接口文档 | 1. 系统接口:基于 Swagger 自动生成相关的 RESTful API 接口文档 | ||||||
| 1. 数据库文档:基于 Screw 自动生成数据库文档 | 1. 数据库文档:基于 Screw 自动生成数据库文档 | ||||||
| 1. 幂等组件:基于 Redis 实现幂等组件,解决重复请求问题 |  | ||||||
|  |  | ||||||
| ## 在线体验 | ## 在线体验 | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,6 +1,6 @@ | |||||||
| { | { | ||||||
|   "local": { |   "local": { | ||||||
|     "baseUrl": "http://127.0.0.1:8080/api", |     "baseUrl": "http://127.0.0.1:48080/api", | ||||||
|     "token": "test1" |     "token": "test1" | ||||||
|   } |   } | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										15
									
								
								pom.xml
									
									
									
									
									
								
							
							
						
						
									
										15
									
								
								pom.xml
									
									
									
									
									
								
							| @@ -30,6 +30,10 @@ | |||||||
|         <druid.version>1.2.4</druid.version> |         <druid.version>1.2.4</druid.version> | ||||||
|         <mybatis-plus.version>3.4.1</mybatis-plus.version> |         <mybatis-plus.version>3.4.1</mybatis-plus.version> | ||||||
|         <redisson.version>3.14.1</redisson.version> |         <redisson.version>3.14.1</redisson.version> | ||||||
|  |         <!-- Config 配置中心相关 --> | ||||||
|  |         <apollo.version>1.7.0</apollo.version> | ||||||
|  |         <!-- 服务保障相关 --> | ||||||
|  |         <lock4j.version>2.2.0</lock4j.version> | ||||||
|         <!-- 监控相关 --> |         <!-- 监控相关 --> | ||||||
|         <skywalking.version>8.3.0</skywalking.version> |         <skywalking.version>8.3.0</skywalking.version> | ||||||
|         <spring-boot-admin.version>2.3.1</spring-boot-admin.version> |         <spring-boot-admin.version>2.3.1</spring-boot-admin.version> | ||||||
| @@ -125,7 +129,7 @@ | |||||||
|         <dependency> |         <dependency> | ||||||
|             <groupId>com.ctrip.framework.apollo</groupId> |             <groupId>com.ctrip.framework.apollo</groupId> | ||||||
|             <artifactId>apollo-client</artifactId> <!-- 引入 Apollo Client 库,实现内嵌的配置中心 --> |             <artifactId>apollo-client</artifactId> <!-- 引入 Apollo Client 库,实现内嵌的配置中心 --> | ||||||
|             <version>1.7.0</version> |             <version>${apollo.version}</version> | ||||||
|         </dependency> |         </dependency> | ||||||
|  |  | ||||||
|         <!-- Job 定时任务相关 --> |         <!-- Job 定时任务相关 --> | ||||||
| @@ -134,6 +138,13 @@ | |||||||
|             <artifactId>spring-boot-starter-quartz</artifactId> |             <artifactId>spring-boot-starter-quartz</artifactId> | ||||||
|         </dependency> |         </dependency> | ||||||
|  |  | ||||||
|  |         <!-- 服务保障相关 --> | ||||||
|  |         <dependency> | ||||||
|  |             <groupId>com.baomidou</groupId> | ||||||
|  |             <artifactId>lock4j-redisson-spring-boot-starter</artifactId> | ||||||
|  |             <version>${lock4j.version}</version> | ||||||
|  |         </dependency> | ||||||
|  |  | ||||||
|         <!-- 监控相关 --> |         <!-- 监控相关 --> | ||||||
|         <dependency> |         <dependency> | ||||||
|             <groupId>org.apache.skywalking</groupId> |             <groupId>org.apache.skywalking</groupId> | ||||||
| @@ -217,7 +228,7 @@ | |||||||
|  |  | ||||||
|         <dependency> |         <dependency> | ||||||
|             <groupId>cn.smallbun.screw</groupId> |             <groupId>cn.smallbun.screw</groupId> | ||||||
|             <artifactId>screw-core</artifactId> |             <artifactId>screw-core</artifactId> <!-- 实现数据库文档 --> | ||||||
|             <version>${screw.version}</version> |             <version>${screw.version}</version> | ||||||
|         </dependency> |         </dependency> | ||||||
|  |  | ||||||
|   | |||||||
| @@ -29,7 +29,8 @@ public interface GlobalErrorCodeConstants { | |||||||
|     ErrorCode INTERNAL_SERVER_ERROR = new ErrorCode(500, "系统异常"); |     ErrorCode INTERNAL_SERVER_ERROR = new ErrorCode(500, "系统异常"); | ||||||
|  |  | ||||||
|     // ========== 自定义错误段 ========== |     // ========== 自定义错误段 ========== | ||||||
|     ErrorCode REPEATED_REQUESTS = new ErrorCode(900, "重复请求"); |     ErrorCode REPEATED_REQUESTS = new ErrorCode(900, "重复请求,请稍后重试"); // 重复请求 | ||||||
|  |     ErrorCode CONCURRENCY_REQUESTS = new ErrorCode(901, "请求失败,请稍后重试"); // 并发请求,不允许 | ||||||
|  |  | ||||||
|     ErrorCode UNKNOWN = new ErrorCode(999, "未知错误"); |     ErrorCode UNKNOWN = new ErrorCode(999, "未知错误"); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -0,0 +1,23 @@ | |||||||
|  | package cn.iocoder.dashboard.framework.lock4j.config; | ||||||
|  |  | ||||||
|  | import cn.hutool.core.util.ClassUtil; | ||||||
|  | import cn.iocoder.dashboard.framework.lock4j.core.DefaultLockFailureStrategy; | ||||||
|  | import cn.iocoder.dashboard.framework.lock4j.core.Lock4jRedisKeyConstants; | ||||||
|  | import org.springframework.context.annotation.Bean; | ||||||
|  | import org.springframework.context.annotation.Configuration; | ||||||
|  |  | ||||||
|  | @Configuration | ||||||
|  | public class Lock4jConfiguration { | ||||||
|  |  | ||||||
|  |     static { | ||||||
|  |         // 手动加载 Lock4jRedisKeyConstants 类,因为它不会被使用到 | ||||||
|  |         // 如果不加载,会导致 Redis 监控,看到它的 Redis Key 枚举 | ||||||
|  |         ClassUtil.loadClass(Lock4jRedisKeyConstants.class.getName()); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Bean | ||||||
|  |     public DefaultLockFailureStrategy lockFailureStrategy() { | ||||||
|  |         return new DefaultLockFailureStrategy(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | } | ||||||
| @@ -0,0 +1,20 @@ | |||||||
|  | package cn.iocoder.dashboard.framework.lock4j.core; | ||||||
|  |  | ||||||
|  | import cn.iocoder.dashboard.common.exception.ServiceException; | ||||||
|  | import cn.iocoder.dashboard.common.exception.enums.GlobalErrorCodeConstants; | ||||||
|  | import com.baomidou.lock.LockFailureStrategy; | ||||||
|  | import lombok.extern.slf4j.Slf4j; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * 自定义获取锁失败策略,抛出 {@link cn.iocoder.dashboard.common.exception.ServiceException} 异常 | ||||||
|  |  */ | ||||||
|  | @Slf4j | ||||||
|  | public class DefaultLockFailureStrategy implements LockFailureStrategy { | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public void onLockFailure(String key, long acquireTimeout, int acquireCount) { | ||||||
|  |         log.debug("[onLockFailure][线程:{} 获取锁失败,key:{} 获取超时时长:{} ms]", Thread.currentThread().getName(), key, acquireTimeout); | ||||||
|  |         throw new ServiceException(GlobalErrorCodeConstants.CONCURRENCY_REQUESTS); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | } | ||||||
| @@ -0,0 +1,19 @@ | |||||||
|  | package cn.iocoder.dashboard.framework.lock4j.core; | ||||||
|  |  | ||||||
|  | import cn.iocoder.dashboard.framework.redis.core.RedisKeyDefine; | ||||||
|  | import org.redisson.api.RLock; | ||||||
|  |  | ||||||
|  | import static cn.iocoder.dashboard.framework.redis.core.RedisKeyDefine.KeyTypeEnum.HASH; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Lock4j Redis Key 枚举类 | ||||||
|  |  * | ||||||
|  |  * @author 芋道源码 | ||||||
|  |  */ | ||||||
|  | public interface Lock4jRedisKeyConstants { | ||||||
|  |  | ||||||
|  |     RedisKeyDefine LOCK4J = new RedisKeyDefine("分布式锁", | ||||||
|  |             "lock4j:%s", // 参数来自 DefaultLockKeyBuilder 类 | ||||||
|  |             HASH, RLock.class, RedisKeyDefine.TimeoutTypeEnum.DYNAMIC); // Redisson 的 Lock 锁,使用 Hash 数据结构 | ||||||
|  |  | ||||||
|  | } | ||||||
| @@ -0,0 +1,4 @@ | |||||||
|  | /** | ||||||
|  |  * 分布式锁组件,使用 https://gitee.com/baomidou/lock4j 开源项目 | ||||||
|  |  */ | ||||||
|  | package cn.iocoder.dashboard.framework.lock4j; | ||||||
| @@ -8,7 +8,7 @@ import java.time.Duration; | |||||||
| import static cn.iocoder.dashboard.framework.redis.core.RedisKeyDefine.KeyTypeEnum.STRING; | import static cn.iocoder.dashboard.framework.redis.core.RedisKeyDefine.KeyTypeEnum.STRING; | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * Redis Key 枚举类 |  * System Redis Key 枚举类 | ||||||
|  * |  * | ||||||
|  * @author 芋道源码 |  * @author 芋道源码 | ||||||
|  */ |  */ | ||||||
|   | |||||||
| @@ -0,0 +1,3 @@ | |||||||
|  | ### 请求 /get-permission-info 接口 => 成功 | ||||||
|  | GET {{baseUrl}}/tool/test-demo/get?id=1 | ||||||
|  | Authorization: Bearer {{token}} | ||||||
| @@ -1,5 +1,6 @@ | |||||||
| package cn.iocoder.dashboard.modules.tool.controller.test; | package cn.iocoder.dashboard.modules.tool.controller.test; | ||||||
|  |  | ||||||
|  | import cn.hutool.core.thread.ThreadUtil; | ||||||
| import cn.iocoder.dashboard.common.pojo.CommonResult; | import cn.iocoder.dashboard.common.pojo.CommonResult; | ||||||
| import cn.iocoder.dashboard.common.pojo.PageResult; | import cn.iocoder.dashboard.common.pojo.PageResult; | ||||||
| import cn.iocoder.dashboard.framework.excel.core.util.ExcelUtils; | import cn.iocoder.dashboard.framework.excel.core.util.ExcelUtils; | ||||||
| @@ -8,6 +9,7 @@ import cn.iocoder.dashboard.modules.tool.controller.test.vo.*; | |||||||
| import cn.iocoder.dashboard.modules.tool.convert.test.ToolTestDemoConvert; | import cn.iocoder.dashboard.modules.tool.convert.test.ToolTestDemoConvert; | ||||||
| import cn.iocoder.dashboard.modules.tool.dal.dataobject.test.ToolTestDemoDO; | import cn.iocoder.dashboard.modules.tool.dal.dataobject.test.ToolTestDemoDO; | ||||||
| import cn.iocoder.dashboard.modules.tool.service.test.ToolTestDemoService; | import cn.iocoder.dashboard.modules.tool.service.test.ToolTestDemoService; | ||||||
|  | import com.baomidou.lock.annotation.Lock4j; | ||||||
| import io.swagger.annotations.Api; | import io.swagger.annotations.Api; | ||||||
| import io.swagger.annotations.ApiImplicitParam; | import io.swagger.annotations.ApiImplicitParam; | ||||||
| import io.swagger.annotations.ApiOperation; | import io.swagger.annotations.ApiOperation; | ||||||
| @@ -21,6 +23,7 @@ import javax.validation.Valid; | |||||||
| import java.io.IOException; | import java.io.IOException; | ||||||
| import java.util.Collection; | import java.util.Collection; | ||||||
| import java.util.List; | import java.util.List; | ||||||
|  | import java.util.concurrent.TimeUnit; | ||||||
|  |  | ||||||
| import static cn.iocoder.dashboard.common.pojo.CommonResult.success; | import static cn.iocoder.dashboard.common.pojo.CommonResult.success; | ||||||
| import static cn.iocoder.dashboard.framework.logger.operatelog.core.enums.OperateTypeEnum.EXPORT; | import static cn.iocoder.dashboard.framework.logger.operatelog.core.enums.OperateTypeEnum.EXPORT; | ||||||
| @@ -62,7 +65,11 @@ public class ToolTestDemoController { | |||||||
|     @ApiOperation("获得测试示例") |     @ApiOperation("获得测试示例") | ||||||
|     @ApiImplicitParam(name = "id", value = "编号", required = true, dataTypeClass = Long.class) |     @ApiImplicitParam(name = "id", value = "编号", required = true, dataTypeClass = Long.class) | ||||||
|     @PreAuthorize("@ss.hasPermission('tool:test-demo:query')") |     @PreAuthorize("@ss.hasPermission('tool:test-demo:query')") | ||||||
|  |     @Lock4j // 分布式锁 | ||||||
|     public CommonResult<ToolTestDemoRespVO> getTestDemo(@RequestParam("id") Long id) { |     public CommonResult<ToolTestDemoRespVO> getTestDemo(@RequestParam("id") Long id) { | ||||||
|  |         if (true) { // 测试分布式锁 | ||||||
|  |             ThreadUtil.sleep(5, TimeUnit.SECONDS); | ||||||
|  |         } | ||||||
|         ToolTestDemoDO testDemo = testDemoService.getTestDemo(id); |         ToolTestDemoDO testDemo = testDemoService.getTestDemo(id); | ||||||
|         return success(ToolTestDemoConvert.INSTANCE.convert(testDemo)); |         return success(ToolTestDemoConvert.INSTANCE.convert(testDemo)); | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -81,6 +81,13 @@ apollo: | |||||||
|     username: ${spring.datasource.username} |     username: ${spring.datasource.username} | ||||||
|     password: ${spring.datasource.password} |     password: ${spring.datasource.password} | ||||||
|  |  | ||||||
|  | --- #################### 服务保障相关配置 #################### | ||||||
|  |  | ||||||
|  | # Lock4j 配置项 | ||||||
|  | lock4j: | ||||||
|  |   acquire-timeout: 3000 # 获取分布式锁超时时间,默认为 3000 毫秒 | ||||||
|  |   expire: 30000 # 分布式锁的超时时间,默认为 30 毫秒 | ||||||
|  |  | ||||||
| --- #################### 监控相关配置 #################### | --- #################### 监控相关配置 #################### | ||||||
|  |  | ||||||
| # Actuator 监控端点的配置项 | # Actuator 监控端点的配置项 | ||||||
|   | |||||||
| @@ -81,6 +81,13 @@ apollo: | |||||||
|     username: ${spring.datasource.username} |     username: ${spring.datasource.username} | ||||||
|     password: ${spring.datasource.password} |     password: ${spring.datasource.password} | ||||||
|  |  | ||||||
|  | --- #################### 服务保障相关配置 #################### | ||||||
|  |  | ||||||
|  | # Lock4j 配置项 | ||||||
|  | lock4j: | ||||||
|  |   acquire-timeout: 3000 # 获取分布式锁超时时间,默认为 3000 毫秒 | ||||||
|  |   expire: 30000 # 分布式锁的超时时间,默认为 30 毫秒 | ||||||
|  |  | ||||||
| --- #################### 监控相关配置 #################### | --- #################### 监控相关配置 #################### | ||||||
|  |  | ||||||
| # Actuator 监控端点的配置项 | # Actuator 监控端点的配置项 | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 YunaiV
					YunaiV