Redisson自定义注解解决重复提交问题

在这里插入图片描述

/**
 * <p>
 *  redis配置类
 * </p>
 */
@Configuration
public class RedisConfig {

	@Bean
	public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
		StringRedisTemplate redisTemplate = new StringRedisTemplate();
		redisTemplate.setConnectionFactory(redisConnectionFactory);
		return redisTemplate;
	}

	@Bean
	public RedisTemplate redisTemplate(RedisConnectionFactory factory) {
		RedisTemplate<String, Object> template = new RedisTemplate<>();
		template.setConnectionFactory(factory);

		Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
		ObjectMapper objectMapper = new ObjectMapper();
		objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
		objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
		jackson2JsonRedisSerializer.setObjectMapper(objectMapper);

		StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
		// key采用String的序列化方式
		template.setKeySerializer(stringRedisSerializer);
		// hash的key也采用String的序列化方式
		template.setHashKeySerializer(stringRedisSerializer);
		// value序列化方式采用jackson
		template.setValueSerializer(jackson2JsonRedisSerializer);
		// hash的value序列化方式采用jackson
		template.setHashValueSerializer(jackson2JsonRedisSerializer);
		template.afterPropertiesSet();
		return template;
	}
}

/**
 * <p>
 *  redission配置类
 * </p>
 */
@Configuration
@ConfigurationProperties(prefix = "redisson.singleserverconfig")
public class RedissonSpringDataConfig {

	private static final Logger log = LoggerFactory.getLogger(RedissonSpringDataConfig.class);

    private String address;
    private int database;
    private String password;

    @Bean
    public RedissonConnectionFactory redissonConnectionFactory(RedissonClient redisson) {
        return new RedissonConnectionFactory(redisson);
    }

    @Bean(destroyMethod = "shutdown")
    public RedissonClient redisson() throws JsonProcessingException {
		log.debug("[RedissonSpringDataConfig][redisson]>>>> address: {}, database: {}, password: {}", address, database, password);
        Config config = new Config();
        SingleServerConfig sconfig= config.useSingleServer()
            .setAddress(address)
			.setDatabase(database);
        // 如果redis设置了密码,这里不设置密码就会报“org.redisson.client.RedisAuthRequiredException: NOAUTH Authentication required”错误。
        if(StringUtils.hasText(password)){
            sconfig.setPassword(password);
        }
        return Redisson.create(config);
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    public int getDatabase() {
        return database;
    }

    public void setDatabase(int database) {
        this.database = database;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
}

@TableName("tb_user")
@ApiModel(value = "User对象", description = "")
public class User implements Serializable {

    private static final long serialVersionUID = 1L;

    @ApiModelProperty("主键id")
    @TableId(value = "id", type = IdType.AUTO)
    private Long id;

    @ApiModelProperty("姓名")
    private String name;

    @ApiModelProperty("年龄")
    private String age;

    @ApiModelProperty("性别,1-男,0-女。")
    private String sex;

    @ApiModelProperty("住址")
    private String address;

    @ApiModelProperty("创建时间")
    private LocalDateTime createdAt;

    @ApiModelProperty("创建人")
    private String createdBy;

    @ApiModelProperty("更新时间")
    private LocalDateTime updatedAt;

    @ApiModelProperty("更新人")
    private String updatedBy;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
    public String getAge() {
        return age;
    }

    public void setAge(String age) {
        this.age = age;
    }
    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }
    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }
    public LocalDateTime getCreatedAt() {
        return createdAt;
    }

    public void setCreatedAt(LocalDateTime createdAt) {
        this.createdAt = createdAt;
    }
    public String getCreatedBy() {
        return createdBy;
    }

    public void setCreatedBy(String createdBy) {
        this.createdBy = createdBy;
    }
    public LocalDateTime getUpdatedAt() {
        return updatedAt;
    }

    public void setUpdatedAt(LocalDateTime updatedAt) {
        this.updatedAt = updatedAt;
    }
    public String getUpdatedBy() {
        return updatedBy;
    }

    public void setUpdatedBy(String updatedBy) {
        this.updatedBy = updatedBy;
    }

    @Override
    public String toString() {
        return "User{" +
            "id=" + id +
            ", name=" + name +
            ", age=" + age +
            ", sex=" + sex +
            ", address=" + address +
            ", createdAt=" + createdAt +
            ", createdBy=" + createdBy +
            ", updatedAt=" + updatedAt +
            ", updatedBy=" + updatedBy +
        "}";
    }
}

/**
 * <p>
 * 加锁解锁工具类
 * </p>
 */
@Component
public class RedisLock {
	private static final Logger log = LoggerFactory.getLogger(RedisLock.class);

	// todo  待优化,最好使用自定义的线程池,自定义工作队列和最大线程数。
	private static final ScheduledExecutorService EXECUTOR_SERVICE = Executors.newScheduledThreadPool(4);
	@Resource
	private Redisson redisson;

	/**
	 * Redission获取锁
	 *
	 * @param lockKey      锁名
	 * @param uuid         唯一标识
	 * @param delaySeconds 过期时间
	 * @param unit         单位
	 * @return 是否获取成功
	 */
	public boolean Rlock(String lockKey, final String uuid, long delaySeconds, final TimeUnit unit) {
		RLock rLock = redisson.getLock(lockKey);
		boolean success = false;
		try {
			// log.debug("===lock thread id is :{}", Thread.currentThread().getId());
			success = rLock.tryLock(0, delaySeconds, unit);
		} catch (InterruptedException e) {
			log.error("[RedisLock][Rlock]>>>> 加锁异常: ", e);
		}
		return success;
	}

	/**
	 * Redission释放锁
	 *
	 * @param lockKey 锁名
	 */
	public void Runlock(String lockKey) {
		RLock rLock = redisson.getLock(lockKey);
		log.debug("[RedisLock][Rlock]>>>> {}, status: {} === unlock thread id is: {}", rLock.isHeldByCurrentThread(), rLock.isLocked(),
				Thread.currentThread().getId());
		rLock.unlock();
	}

	/**
	 * Redission延迟释放锁
	 *
	 * @param lockKey 锁名
	 * @param delayTime 延迟时间
	 * @param unit 单位
	 */
	public void delayUnlock(final String lockKey, long delayTime, TimeUnit unit) {
		if (!StringUtils.hasText(lockKey)) {
			return;
		}
		if (delayTime <= 0) {
			Runlock(lockKey);
		} else {
			EXECUTOR_SERVICE.schedule(() -> Runlock(lockKey), delayTime, unit);
		}
	}

}

在这里插入图片描述

/**
 * <p>
 * 防重复提交注解的实现,使用AOP。
 * </p>

 */
@Aspect
@Component
public class LockMethodAOP {
	private static final Logger log = LoggerFactory.getLogger(LockMethodAOP.class);

	@Resource
	private RedisLock redisLock;

	/**
	 * 这里注意,我的注解写在同一个包下,如果换目录,要改为@annotation(com.xxx.NotResubmit).
	 */
	@Around("execution(public * *(..)) && @annotation(NotResubmit)")
	public Object interceptor(ProceedingJoinPoint pjp) throws Throwable {
		// 获取到这个注解
		MethodSignature signature = (MethodSignature) pjp.getSignature();
		Method method = signature.getMethod();
		NotResubmit lock = method.getAnnotation(NotResubmit.class);

		final String lockKey = generateKey(pjp);

		// 上锁
		final boolean success = redisLock.Rlock(lockKey, null, lock.delaySeconds(), TimeUnit.SECONDS);
		if (!success) {
			// 这里也可以改为自己项目自定义的异常抛出
			return ResponseEntity.badRequest().body(ResultEntity.fail(ResponseCodeEnum.FAIL.getCode(), "操作太频繁"));
		}
		return pjp.proceed();
	}

	private String generateKey(ProceedingJoinPoint pjp) {
		StringBuilder sb = new StringBuilder();
		Signature signature = pjp.getSignature();
		MethodSignature methodSignature = (MethodSignature) signature;
		Method method = methodSignature.getMethod();
		sb.append(pjp.getTarget().getClass().getName())//类名
				.append(method.getName());//方法名
		for (Object o : pjp.getArgs()) {
			sb.append(o.toString());
		}
		return DigestUtils.md5DigestAsHex(sb.toString().getBytes(Charset.defaultCharset()));
	}

}

在这里插入图片描述
在这里插入图片描述

可以使用Redisson提供的分布式限流工具来实现自定义注解接口限流。具体实现步骤如下: 1. 定义一个注解,例如@RateLimiter,用于标记需要进行限流的接口方法。 2. 在注解中定义相关属性,例如限流的速率、限流的时间单位等。 3. 使用AOP技术,在接口方法执行前判断当前请求是否超过了限流速率,如果超过则拒绝请求。 4. 在AOP切面中使用Redisson提供的分布式锁来实现限流。 下面是一个简单的示例代码: ```java @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface RateLimiter { int rate() default 10; // 速率 TimeUnit timeUnit() default TimeUnit.SECONDS; // 时间单位 } @Aspect @Component public class RateLimiterAspect { private final RedissonClient redissonClient; public RateLimiterAspect(RedissonClient redissonClient) { this.redissonClient = redissonClient; } @Around("@annotation(rateLimiter)") public Object rateLimit(ProceedingJoinPoint joinPoint, RateLimiter rateLimiter) throws Throwable { String key = "rate_limiter:" + joinPoint.getSignature().toLongString(); RRateLimiter limiter = redissonClient.getRateLimiter(key); limiter.trySetRate(RateType.OVERALL, rateLimiter.rate(), rateLimiter.timeUnit()); if (limiter.tryAcquire()) { return joinPoint.proceed(); } else { throw new RuntimeException("接口访问过于频繁,请稍后再试!"); } } } ``` 在上面的代码中,我们定义了一个@RateLimiter注解,并在AOP切面中使用Redisson提供的RRateLimiter来实现限流。在接口方法执行前,我们会先获取到当前接口方法的签名,然后使用签名作为Redisson的key,创建一个RRateLimiter对象,并设置限流速率。最后,我们使用tryAcquire()方法来尝试获取锁,如果获取成功则执行接口方法,否则抛出异常。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

time丶sand

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值