一、什麼是冪等
冪等指的是相同的請求執行一次和執行多次的效果是一致的,不會因為多次請求產生副作用。
對於一些數據更新操作,例如修改用戶信息、下單等操作,如果發生網路故障或者客戶端異常,造成了多次請求,如果系統沒有冪等措施,可能導致數據多次更新,最終產生異常數據。
冪等可以有效解決重複執行的問題。
二、為什麼需要冪等
網路通信中常常出現不穩定的情況,例如網路中斷、超時等問題,而相同操作請求多次則會產生意料之外的結果。在銀行支付、物流發貨、物資調撥等場景中,重複操作可能會導致金錢流、庫存等數據的不一致,因此在這些場景中需要通過冪等來保證數據的一致性、正確性和安全性。
三、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/zh-tw/n/301302.html
微信掃一掃
支付寶掃一掃