使用SpringBoot、Redis和拦截器实现接口自动幂等

3 下载量 62 浏览量 更新于2024-09-01 1 收藏 323KB PDF 举报
SpringBoot+Redis+Interceptor+自定义Annotation实现接口自动幂等 在实际的开发项目中,一个对外暴露的接口往往会面临很多次请求,这种情况下,如何保证接口的幂等性成为一个非常重要的问题。幂等性是指任意多次执行所产生的影响均与一次执行的影响相同。在本篇文章中,我们将介绍使用 SpringBoot 和 Redis 实现接口自动幂等的方法。 首先, lets 回顾一下幂等性的概念。幂等性是指任意多次执行所产生的影响均与一次执行的影响相同。这意味着,对数据库的影响只能是一次性的,不能重复处理。那么,如何保证幂等性呢?通常有以下几种方法: 1. 数据库建立唯一性索引,可以保证最终插入数据库的只有一条数据。 2. Token 机制,每次接口请求前先获取一个 Token,然后再下次请求的时候在请求的 Header 体中加上这个 Token,后台进行验证,如果验证通过删除 Token,下次请求再次判断 Token。 3. 悲观锁或者乐观锁,悲观锁可以保证每次 for update 的时候其他 SQL 无法 update 数据(在数据库引擎是 InnoDB 的时候,select 的条件必须是唯一索引,防止锁全表)。 4. 先查询后判断,首先通过查询数据库是否存在数据,如果存在证明已经请求过了,直接拒绝该请求,如果没有存在,就证明是第一次进来,直接放行。 下面,我们将介绍使用 SpringBoot 和 Redis 实现接口自动幂等的方法。 使用 Redis 实现自动幂等的原理图: 目录 一、搭建 Redis 的服务 API 首先,我们需要搭建 Redis 服务器,这个之前搭过了,就不赘述了。详情可参考:https://www.cnblogs.com/wyq178/p/10340234.html 引入 SpringBoot 中的 Redis 的 starter,或者 Spring 封装的 Jedis 也可以,后面主要用到的 API 就是它的 set 方法和 exists 方法,这里我们使用 SpringBoot 的封装好的 RedisTemplate。 Redis 工具类: @Component public class RedisService { @Autowired private RedisTemplate redisTemplate; / * 写入缓存 * @param key * @param value * @return */ public boolean set(final String key, final String value) { return redisTemplate.opsForValue().set(key, value); } // ... Interceptor 的实现 使用自定义的 Annotation,我们可以实现接口自动幂等的功能。首先,我们需要定义一个 Annotation: @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface Idempotent { String value(); } 然后,我们需要实现一个 Interceptor,用于拦截接口的请求: @Component public class IdempotentInterceptor implements HandlerInterceptor { @Autowired private RedisService redisService; @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 获取 Token String token = request.getHeader("token"); if (token == null) { throw new RuntimeException("Token 不能为空"); } // 检查 Token 是否存在 if (redisService.exists(token)) { // 如果 Token 存在,证明已经请求过了,直接拒绝该请求 throw new RuntimeException("已经请求过了"); } else { // 如果 Token 不存在,证明是第一次进来,直接放行 redisService.set(token, "1"); } return true; } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { // 删除 Token String token = request.getHeader("token"); redisService.delete(token); } } 使用上述实现,我们可以在 Controller 中使用 @Idempotent 注解来标记需要幂等的接口: @Idempotent("token") @RequestMapping("/api/test") public String test() { // ... } 这样,我们就可以使用 SpringBoot 和 Redis 实现接口自动幂等的功能。