You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java中如何暂存含已处理ItemID的POST请求?

解决方案:基于ItemID的串行化处理

针对同一ItemID的并发POST请求导致的冲突问题,核心思路是为每个ItemID分配独立的锁,保证同一ID的请求串行处理,同时完全兼容第三方API不支持多参数的限制。以下是两种场景下的具体实现:

单实例部署:Guava Striped锁

利用Guava的Striped锁,通过哈希将不同ItemID映射到不同锁,既保证同一ID的请求串行执行,又避免全局锁带来的性能瓶颈。

步骤1:引入依赖(Maven)

<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>31.1-jre</version> <!-- 使用最新稳定版 -->
</dependency>

步骤2:改造代码

import com.google.common.util.concurrent.Striped;
import javax.ws.rs.Consumes;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import java.util.concurrent.locks.Lock;
import org.codehaus.jettison.json.JSONArray;
import org.codehaus.jettison.json.JSONObject;

public class ProductResource {
    // 创建100个锁的分片,平衡锁粒度与竞争
    private final Striped<Lock> itemLocks = Striped.lock(100);

    @POST
    @Path("ObjectItem")
    @Consumes(MediaType.APPLICATION_JSON)
    @Produces(MediaType.APPLICATION_JSON)
    public JSONObject processObjectItem(String JSON) throws Exception {
        JSONObject jData = new JSONObject(JSON);
        JSONArray jItems = jData.getJSONArray("Items");
        JSONObject productItem = jItems.getJSONObject(0);
        Long itemId = productItem.getLong("ItemID");
        
        // 获取当前ItemID对应的锁
        Lock lock = itemLocks.get(itemId);
        lock.lock();
        try {
            // 原业务逻辑,现在已保证串行执行
            Asset product = getProductById(itemId);
            if (product == null) {
                createProduct(itemId, productItem);
            } else {
                updateProduct(itemId, productItem);
            }

            JSONObject result = new JSONObject();
            result.put("status", "success");
            result.put("message", "Items processed successfully");
            return result;
        } finally {
            // 确保锁释放,避免死锁
            lock.unlock();
        }
    }

    // 原有业务方法(保持不变)
    private Asset getProductById(Long id) { /* ... */ }
    private void createProduct(Long id, JSONObject productItem) { /* ... */ }
    private void updateProduct(Long id, JSONObject productItem) { /* ... */ }
}

集群部署:Redis分布式锁

如果服务是多节点集群,需要用分布式锁保证全局串行。推荐使用Redisson实现,它内置了锁的自动续期、超时处理等机制。

步骤1:引入依赖(Maven)

<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson</artifactId>
    <version>3.23.3</version> <!-- 使用最新稳定版 -->
</dependency>

步骤2:配置Redisson客户端(示例)

import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class RedissonConfig {
    @Bean
    public RedissonClient redissonClient() {
        Config config = new Config();
        config.useSingleServer()
              .setAddress("redis://localhost:6379")
              .setDatabase(0);
        return Redisson.create(config);
    }
}

步骤3:改造代码

import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import javax.annotation.Resource;
import javax.ws.rs.Consumes;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import java.util.concurrent.TimeUnit;
import org.codehaus.jettison.json.JSONArray;
import org.codehaus.jettison.json.JSONObject;

public class ProductResource {
    @Resource
    private RedissonClient redissonClient;

    @POST
    @Path("ObjectItem")
    @Consumes(MediaType.APPLICATION_JSON)
    @Produces(MediaType.APPLICATION_JSON)
    public JSONObject processObjectItem(String JSON) throws Exception {
        JSONObject jData = new JSONObject(JSON);
        JSONArray jItems = jData.getJSONArray("Items");
        JSONObject productItem = jItems.getJSONObject(0);
        Long itemId = productItem.getLong("ItemID");
        
        // 构造分布式锁唯一Key
        String lockKey = "product:lock:" + itemId;
        RLock lock = redissonClient.getLock(lockKey);
        
        // 尝试获取锁:最多等待5秒,持有锁30秒(可根据业务调整)
        if (lock.tryLock(5, 30, TimeUnit.SECONDS)) {
            try {
                Asset product = getProductById(itemId);
                if (product == null) {
                    createProduct(itemId, productItem);
                } else {
                    updateProduct(itemId, productItem);
                }

                JSONObject result = new JSONObject();
                result.put("status", "success");
                result.put("message", "Items processed successfully");
                return result;
            } finally {
                lock.unlock();
            }
        } else {
            // 获取锁失败,返回忙状态提示客户端重试
            JSONObject result = new JSONObject();
            result.put("status", "busy");
            result.put("message", "Item is being processed, please retry later");
            return result;
        }
    }

    // 原有业务方法(保持不变)
    private Asset getProductById(Long id) { /* ... */ }
    private void createProduct(Long id, JSONObject productItem) { /* ... */ }
    private void updateProduct(Long id, JSONObject productItem) { /* ... */ }
}

关键注意事项

  1. 幂等性保障:确保createProduct和updateProduct方法是幂等的,即使客户端重试也不会产生重复创建、重复更新等副作用。
  2. 锁超时调整:根据业务处理的实际耗时调整锁的持有超时时间,避免锁提前释放导致并发问题。
  3. 异常处理:所有锁操作必须放在try-finally块中,确保锁能被正常释放,避免死锁。

内容的提问来源于stack exchange,提问作者theyesn

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.24 00:47:43