下面是“SpringBoot实现接口幂等性的4种方案”的完整攻略:
什么是接口幂等性?
接口幂等性指的是对于同一请求,多次调用接口所产生的结果是一致的。常见的应用场景包括支付、订单创建等需要保证数据一致性的场景。
在实际开发中,由于应用的多实例部署,以及网络延迟等原因,可能会导致接口被重复调用,进而产生数据不一致的问题。因此,保证接口幂等性非常重要。
SpringBoot实现接口幂等性的4种方案
SpringBoot提供了四种方案实现接口幂等性:
1. 使用Token机制
在第一次请求时生成一个token,将token存储到服务端,并将token作为响应返回给客户端。客户端在下一次请求时需要携带该token。如果服务端已经处理过该token,就不再处理。
// Java代码示例
@GetMapping("/token")
public String token() {
String token = UUID.randomUUID().toString();
// 将token存储到服务端
cache.put(token, true);
return token;
}
@PostMapping("/submit")
public String submit(@RequestParam String token, @RequestBody RequestBody body) {
// 如果服务端已经处理过该token,就不再处理
if (cache.getIfPresent(token) != null) {
return "success";
}
// 处理请求
// ...
return "success";
}
2. 使用数据库乐观锁机制
在接口对应的数据表中新增一个version字段,并在版本变化时更新该字段。每次请求时,服务端先查询该记录对应的版本号,判断版本号是否一致,如果一致则处理请求,否则返回幂等性校验失败的结果。
-- MySQL代码示例
CREATE TABLE user (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(32),
version BIGINT NOT NULL DEFAULT 0
);
-- Java代码示例
@PostMapping("/submit")
@Transactional(rollbackFor = Exception.class)
public String submit(@RequestBody User user) {
// 查询该记录对应的版本号
User existingUser = userRepository.findById(user.getId()).orElseThrow(() -> new RuntimeException("User not found"));
Long existingVersion = existingUser.getVersion();
// 比较版本号
if (existingVersion.equals(user.getVersion())) {
// 更新用户信息
user.setVersion(existingVersion + 1);
userRepository.save(user);
return "success";
} else {
return "failure";
}
}
3. 使用Redis实现分布式锁机制
在接口调用时,先获取分布式锁,如果获取锁成功,则处理请求并释放锁;如果获取锁失败,则不处理请求。
// Java代码示例
@PostMapping("/submit")
public String submit(@RequestBody RequestBody body) {
String key = "submit:" + body.getId();
String requestId = UUID.randomUUID().toString();
try {
// 获取分布式锁
Boolean lock = redisTemplate.opsForValue().setIfAbsent(key, requestId, 10, TimeUnit.SECONDS);
if (lock != null && lock) {
// 处理请求
// ...
return "success";
} else {
return "failure";
}
} finally {
// 释放分布式锁
String lockRequestId = redisTemplate.opsForValue().get(key);
if (lockRequestId != null && requestId.equals(lockRequestId)) {
redisTemplate.delete(key);
}
}
}
4. 使用请求参数加签机制
在第一次请求时,客户端生成一个请求参数的哈希值,并将哈希值存储到服务端。在后续请求时,客户端需要携带该哈希值。服务端在接收到请求时,先计算哈希值,然后判断服务端是否已经处理过该哈希值,如果已经处理则不再处理。
// Java代码示例
@GetMapping("/sign")
public String sign() {
String sign = UUID.randomUUID().toString();
// 将哈希值存储到服务端
cache.put(sign, true);
return sign;
}
@PostMapping("/submit")
public String submit(@RequestParam String sign, @RequestBody RequestBody body) {
// 计算哈希值
String bodyStr = JSON.toJSONString(body);
String hash = DigestUtils.md5Hex(bodyStr + "secret");
// 如果服务端已经处理过该哈希值,就不再处理
if (cache.getIfPresent(hash) != null) {
return "success";
}
// 处理请求
// ...
return "success";
}
以上四种方案均可以实现接口幂等性,根据不同的场景选择不同的方案即可。
示例
下面我们以使用Redis实现分布式锁机制为例,演示如何实现接口幂等性。
1. 添加依赖
在pom.xml文件中添加以下依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
<version>3.0.8</version>
</dependency>
2. 配置Redis
在application.yml文件中添加以下配置:
spring:
redis:
host: localhost
port: 6379
password: password
caffeine:
cache:
spec: maximumSize=10000, expireAfterWrite=5m
3. 实现幂等性接口
在Controller中添加幂等性接口:
@RestController
public class IdempotentController {
@Autowired
private RedisTemplate<String, String> redisTemplate;
@Autowired
private Cache<Object, Object> cache;
@PostMapping("/idempotent")
public String idempotent(@RequestBody Object body) {
String key = "idempotent:" + DigestUtils.md5Hex(JSON.toJSONString(body));
String requestId = UUID.randomUUID().toString();
try {
// 获取分布式锁
Boolean lock = redisTemplate.opsForValue().setIfAbsent(key, requestId, 10, TimeUnit.SECONDS);
if (lock != null && lock) {
// 处理请求
System.out.println("handle request " + body);
return "success";
} else {
return "failure";
}
} finally {
// 释放分布式锁
String lockRequestId = redisTemplate.opsForValue().get(key);
if (lockRequestId != null && requestId.equals(lockRequestId)) {
redisTemplate.delete(key);
}
}
}
}
4. 测试接口
使用curl工具模拟多次请求接口:
curl -H "Content-Type:application/json" -X POST --data '{"id":1}' http://localhost:8080/idempotent
curl -H "Content-Type:application/json" -X POST --data '{"id":1}' http://localhost:8080/idempotent
curl -H "Content-Type:application/json" -X POST --data '{"id":1}' http://localhost:8080/idempotent
输出如下:
handle request {"id":1}
failure
failure
可以看到,第一次请求获得了处理结果,而第二次和第三次请求由于已经处理过,所以返回了失败结果。
这就是使用Redis实现接口幂等性的方法。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:SpringBoot实现接口幂等性的4种方案 - Python技术站