Java接口幂等性的几种实现方法

0- 什么是幂等性

举个例子:

投资理财的案例,用户可以充值、投资、提现,使用第三方支付进行充值,过程如下:

step1:用户网站中输入充值金额

step2:后端创建充值订单入库,此时订单是待支付状态

step3:跳转到第三方支付页面,输入银行卡,然后确认支付

step4:第三方支付通过我方提供的回调接口异步将充值结果告知我方

问题出在了step4,逻辑如下:

//返回通知处理结果,true:处理成功;false:处理失败,第三方会继续重试
public boolean rechargeNotice(第三方支付充值结果){
   
   
    try{
   
   
        //第三方充值结果中包含了我方的订单id,从db中获取充值订单信息
        OrderModel order = this.getOrderById(订单id); //@1
        //判断订单状态是否是待支付状态
        if(订单状态 == 待支付状态){
   
    //@2
            //将订单状态置为充值成功
            order.status(充值成功);
            orderService.update(order);
            //用户账户可用余额增加
            this.accountService.incrBalance(用户id,充值金额);
            return tru
### Java实现接口幂等性方法 为了确保接口幂等性,在 Java 应用程序中通常会采用多种策略和技术来防止重复提交请求或执行操作。下面介绍几种常见的技术手段并提供相应的代码示例。 #### 1. 使用唯一标识符验证幂等性 当客户端发起请求时,服务器端可以通过校验唯一标识符(如 UUID 或订单号)来判断该请求是否已经被处理过。如果已经存在,则不再重新处理而是返回之前的结果。 ```java public class IdempotentService { private final Map<String, String> processedRequests = new ConcurrentHashMap<>(); public synchronized Response handleRequest(String uniqueId, Request request) { if (processedRequests.containsKey(uniqueId)) { return new Response(processedRequests.get(uniqueId)); } else { // 处理业务逻辑... String result = processBusinessLogic(request); // 将结果缓存起来以便后续查询 processedRequests.putIfAbsent(uniqueId, result); return new Response(result); } } } ``` 此段代码展示了如何利用 `ConcurrentHashMap` 来存储已处理过的请求及其对应的响应数据[^3]。 #### 2. 利用数据库事务与乐观锁控制并发访问 对于涉及数据库更新的操作来说,可以借助于版本字段配合 SQL 的 `UPDATE ... WHERE version = ?` 语句以及设置合适的隔离级别来达到防重的效果。这种方式被称为“乐观锁定”。 ```sql -- 假设有一个表 t_order(id INT PRIMARY KEY, status VARCHAR(20), version INT) UPDATE t_order SET amount=amount+?,version=version+1 WHERE id=? AND version=? ``` 在应用程序层面: ```java @Transactional(isolation = Isolation.READ_COMMITTED) public void updateOrderAmount(Long orderId, BigDecimal increment) throws Exception{ Order order = orderRepository.findById(orderId).orElseThrow(() -> new RuntimeException("Order not found")); int rowsAffected = jdbcTemplate.update( "UPDATE t_order SET amount=?,version=version+1 WHERE id=? AND version=?", order.getAmount().add(increment), orderId, order.getVersion() ); if(rowsAffected == 0){ throw new OptimisticLockingFailureException(); } } ``` 上述例子中,一旦检测到记录被其他事务修改则抛出异常终止当前流程,从而避免了脏读等问题的发生[^4]。 #### 3. 分布式环境下的全局唯一 ID 和分布式锁 在一个分布式的微服务体系结构里,可能还会遇到跨多个实例间的竞争条件。此时除了要保证本地服务内的等外还需要考虑整个集群范围的一致性问题。一种解决方案就是引入像 Redis 这样的中间件作为协调者生成全局唯一的序列号或者是基于它构建简单的分布式互斥锁机制。 ```java // 获取Redis连接对象 Jedis jedis = pool.getResource(); try { // 设置key的有效期为5秒,并尝试加锁 Long lockResult = jedis.setnx(lockKey, Thread.currentThread().getName()); Boolean expireSuccess = jedis.expire(lockKey, 5); if ((lockResult != null && lockResult.equals(1L))) { try { // 执行具体业务逻辑... } finally { // 解除锁定 jedis.del(lockKey); } } else { log.warn("{} failed to acquire the distributed lock",Thread.currentThread().getName()); } } catch(Exception e){ ... }finally{ jedis.close(); } ``` 这段代码片段实现了通过 Redis 实现简单版的分布式锁功能,有效解决了高并发场景下资源争抢的问题[^2]。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值