Java幂等

一、什么是幂等

幂等指的是相同的请求执行一次和执行多次的效果是一致的,不会因为多次请求产生副作用。

对于一些数据更新操作,例如修改用户信息、下单等操作,如果发生网络故障或者客户端异常,造成了多次请求,如果系统没有幂等措施,可能导致数据多次更新,最终产生异常数据。

幂等可以有效解决重复执行的问题。

二、为什么需要幂等

网络通信中常常出现不稳定的情况,例如网络中断、超时等问题,而相同操作请求多次则会产生意料之外的结果。在银行支付、物流发货、物资调拨等场景中,重复操作可能会导致金钱流、库存等数据的不一致,因此在这些场景中需要通过幂等来保证数据的一致性、正确性和安全性。

三、java中的实现方法

1、Token检测

Token是服务器返回的一段随机生成的字符串,客户端在每次请求时都必须带上这个Token,服务器在接收到Token后将Token与之前存储的Token比较,如果相同,则代表请求已经被处理,是重复请求,直接返回结果即可。

代码示例:

public class IdempotentTokenInterceptor extends HandlerInterceptorAdapter {
    private TokenProvider tokenProvider;

    public IdempotentTokenInterceptor(TokenProvider tokenProvider) {
        this.tokenProvider = tokenProvider;
    }

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String token = request.getHeader("X-api-token");

        if(token == null) {
            throw new BusinessException("Missing X-api-token header!");
        }

        if(!tokenProvider.validateToken(token)) {
            throw new BusinessException("Invalid X-api-token!");
        }

        return true;
    }
}

public class TokenProvider {
    public String generateToken() {
        String token = UUID.randomUUID().toString();
        //save to cache or database
        return token;
    }

    public boolean validateToken(String token) {
        //从缓存中获取,并删除Token
        return true;
    }
}

public class BusinessController {
    private TokenProvider tokenProvider;

    public BusinessController(TokenProvider tokenProvider) {
        this.tokenProvider = tokenProvider;
    }

    @PostMapping("/api")
    public ApiResponse businessApi() {
        String token = tokenProvider.generateToken();

        //save token to cache or database

        //process business logic

        return ApiResponse.ok();
    }
}

2、数据库唯一约束

在数据库表中设置一个唯一约束,例如唯一索引,保证相同的请求只处理一次,后续的请求会抛出数据库异常。

代码示例:

public class BusinessController {
    @Autowired
    private BusinessService businessService;

    @PostMapping("/api")
    public ApiResponse businessApi(@RequestBody BusinessRequest request) {
        businessService.processData(request);
        return ApiResponse.ok();
    }
}

@Service
public class BusinessService {
    @Autowired
    private BusinessDao businessDao;

    public void processData(BusinessRequest request) {
        //检查是否已经处理
        boolean processed = businessDao.checkIfProcessed(request);

        if(processed) {
            throw new BusinessException("Request has been processed!");
        }

        //处理请求
        businessDao.process(request);
    }
}

@Repository
public class BusinessDao {
    @Autowired
    private JdbcTemplate jdbcTemplate;

    public boolean checkIfProcessed(BusinessRequest request) {
        String sql = "SELECT COUNT(*) FROM business_table WHERE request_id=?";
        int count = jdbcTemplate.queryForObject(sql, new Object[]{request.getRequestId()}, Integer.class);
        return count > 0;
    }

    public void process(BusinessRequest request) {
        String sql = "INSERT INTO business_table (request_id, data) VALUES (?,?)";
        jdbcTemplate.update(sql, request.getRequestId(), request.getData());
    }
}

3、拦截器

拦截器是一种在请求被处理之前或之后,拦截并处理请求的机制。通过在拦截器中实现幂等处理,可以保证相同请求只处理一次。

代码示例:

public class IdempotentInterceptor extends HandlerInterceptorAdapter {
    @Autowired
    private IdempotentKeyGenerator idempotentKeyGenerator;

    @Autowired
    private CacheManager cacheManager;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //获取幂等Key
        String idempotentKey = idempotentKeyGenerator.generate(request);

        if(StringUtils.isNotBlank(idempotentKey)) {
            Cache cache = cacheManager.getCache("idempotent");
            ValueWrapper valueWrapper = cache.get(idempotentKey);

            if(valueWrapper != null && valueWrapper.get() != null) {
                throw new BusinessException("Request has been processed!");
            }

            cache.put(idempotentKey, idempotentKey);
        }

        return true;
    }
}

public interface IdempotentKeyGenerator {
    String generate(HttpServletRequest request);
}

@Component
public class DefaultIdempotentKeyGenerator implements IdempotentKeyGenerator {
    @Override
    public String generate(HttpServletRequest request) {
        return request.getHeader("X-idempotent-key");
    }
}

@Configuration
public class CacheConfig {
    @Bean
    public CacheManager cacheManager() {
        SimpleCacheManager cacheManager = new SimpleCacheManager();

        Cache idempotentCache = new ConcurrentMapCache("idempotent", false);

        cacheManager.setCaches(Arrays.asList(idempotentCache));

        return cacheManager;
    }
}

@Controller
public class BusinessController {
    @Autowired
    private BusinessService businessService;

    @PostMapping("/api")
    public ApiResponse businessApi(@RequestBody BusinessRequest request) {
        businessService.processData(request);
        return ApiResponse.ok();
    }
}

@Service
public class BusinessService {
    public void processData(BusinessRequest request) {
        //处理请求
    }
}

四、总结

幂等可以帮助我们解决重复操作的问题,确保请求的正确性和唯一性,提高系统的稳定性和安全性。在Java中,有多种实现幂等的方式,例如Token检测、数据库唯一约束、拦截器等,我们可以根据实际需求和场景选择合适的方式。

原创文章,作者:小蓝,如若转载,请注明出处:https://www.506064.com/n/301302.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
小蓝小蓝
上一篇 2024-12-30 16:08
下一篇 2024-12-30 16:08

相关推荐

  • Java JsonPath 效率优化指南

    本篇文章将深入探讨Java JsonPath的效率问题,并提供一些优化方案。 一、JsonPath 简介 JsonPath是一个可用于从JSON数据中获取信息的库。它提供了一种DS…

    编程 2025-04-29
  • java client.getacsresponse 编译报错解决方法

    java client.getacsresponse 编译报错是Java编程过程中常见的错误,常见的原因是代码的语法错误、类库依赖问题和编译环境的配置问题。下面将从多个方面进行分析…

    编程 2025-04-29
  • Java腾讯云音视频对接

    本文旨在从多个方面详细阐述Java腾讯云音视频对接,提供完整的代码示例。 一、腾讯云音视频介绍 腾讯云音视频服务(Cloud Tencent Real-Time Communica…

    编程 2025-04-29
  • Java Bean加载过程

    Java Bean加载过程涉及到类加载器、反射机制和Java虚拟机的执行过程。在本文中,将从这三个方面详细阐述Java Bean加载的过程。 一、类加载器 类加载器是Java虚拟机…

    编程 2025-04-29
  • Java Milvus SearchParam withoutFields用法介绍

    本文将详细介绍Java Milvus SearchParam withoutFields的相关知识和用法。 一、什么是Java Milvus SearchParam without…

    编程 2025-04-29
  • Java 8中某一周的周一

    Java 8是Java语言中的一个版本,于2014年3月18日发布。本文将从多个方面对Java 8中某一周的周一进行详细的阐述。 一、数组处理 Java 8新特性之一是Stream…

    编程 2025-04-29
  • Java判断字符串是否存在多个

    本文将从以下几个方面详细阐述如何使用Java判断一个字符串中是否存在多个指定字符: 一、字符串遍历 字符串是Java编程中非常重要的一种数据类型。要判断字符串中是否存在多个指定字符…

    编程 2025-04-29
  • VSCode为什么无法运行Java

    解答:VSCode无法运行Java是因为默认情况下,VSCode并没有集成Java运行环境,需要手动添加Java运行环境或安装相关插件才能实现Java代码的编写、调试和运行。 一、…

    编程 2025-04-29
  • Java任务下发回滚系统的设计与实现

    本文将介绍一个Java任务下发回滚系统的设计与实现。该系统可以用于执行复杂的任务,包括可回滚的任务,及时恢复任务失败前的状态。系统使用Java语言进行开发,可以支持多种类型的任务。…

    编程 2025-04-29
  • Java 8 Group By 会影响排序吗?

    是的,Java 8中的Group By会对排序产生影响。本文将从多个方面探讨Group By对排序的影响。 一、Group By的概述 Group By是SQL中的一种常见操作,它…

    编程 2025-04-29

发表回复

登录后才能评论