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) { /* ... */ } }
关键注意事项
- 幂等性保障:确保
createProduct和updateProduct方法是幂等的,即使客户端重试也不会产生重复创建、重复更新等副作用。 - 锁超时调整:根据业务处理的实际耗时调整锁的持有超时时间,避免锁提前释放导致并发问题。
- 异常处理:所有锁操作必须放在
try-finally块中,确保锁能被正常释放,避免死锁。
内容的提问来源于stack exchange,提问作者theyesn
相关产品推荐
相关产品推荐

