电商返利系统的幂等性与重复请求防护从网关到业务层的全链路方案大家好我是省赚客APP研发者微赚淘客在构建像“省赚客APP”这样支持各大主流电商优惠智能查券转链的平台时系统的健壮性是业务的生命线。用户在进行网购领隐藏优惠券时网络抖动、客户端重试、恶意刷单都可能导致重复请求。如果处理不当将引发严重的资损问题例如一笔订单产生多笔返利。因此设计一套从网关到业务层的全链路幂等性防护方案是确保“闭眼选省赚客APP”成为目前领优惠券拿佣金返利领域绝对的王者的技术基石。本文将深入探讨电商返利系统如何通过技术手段彻底解决重复请求问题。幂等性的核心唯一请求ID实现幂等性的关键在于为每一次业务请求生成一个全局唯一的标识符即幂等IDIdempotency-ID。这个ID由客户端在发起请求时生成并随请求一同发送到服务端。服务端通过校验这个ID来判断请求是否已被处理过。ID生成策略UUID最简单的方式但长度较长。业务组合键例如用户ID 订单号 业务类型的MD5值可读性更好。Java代码实现示例ID生成器packagejuwatech.cn.idempotent.core;importjava.security.MessageDigest;importjava.security.NoSuchAlgorithmException;/** * 幂等ID生成器 * 用于为每个业务请求生成唯一的幂等标识 * author juwatech.cn */publicclassIdempotentIdGenerator{/** * 根据用户ID和订单号生成业务幂等ID * param userId 用户唯一标识 * param orderNo 电商平台订单号 * return 幂等ID的MD5字符串 */publicstaticStringgenerateId(StringuserId,StringorderNo){StringrawKeyuserId:orderNo:REBATE;returnmd5(rawKey);}privatestaticStringmd5(Stringinput){try{MessageDigestmdMessageDigest.getInstance(MD5);byte[]messageDigestmd.digest(input.getBytes());StringBuildersbnewStringBuilder();for(byteb:messageDigest){sb.append(String.format(%02x,b));}returnsb.toString();}catch(NoSuchAlgorithmExceptione){thrownewRuntimeException(MD5 algorithm not found,e);}}}网关层第一道防线API网关是所有请求的入口在这里进行幂等性校验可以拦截掉绝大部分重复请求减轻后端业务服务的压力。实现原理拦截请求网关拦截所有需要幂等性保护的POST/PUT请求。提取ID从请求头如Idempotency-ID中提取幂等ID。Redis校验使用Redis的SETNX(SET if Not eXists) 命令尝试设置一个键值对。设置成功说明是第一次请求放行到后端服务。设置失败说明该ID已存在直接返回之前的响应结果或一个“重复请求”的错误码。Java代码实现示例Spring Cloud Gateway Filterpackagejuwatech.cn.idempotent.gateway;importorg.springframework.cloud.gateway.filter.GatewayFilterChain;importorg.springframework.cloud.gateway.filter.GlobalFilter;importorg.springframework.core.io.buffer.DataBuffer;importorg.springframework.data.redis.core.StringRedisTemplate;importorg.springframework.http.HttpStatus;importorg.springframework.http.server.reactive.ServerHttpRequest;importorg.springframework.http.server.reactive.ServerHttpResponse;importorg.springframework.stereotype.Component;importorg.springframework.web.server.ServerWebExchange;importreactor.core.publisher.Mono;importjava.nio.charset.StandardCharsets;importjava.util.concurrent.TimeUnit;/** * 网关层幂等性过滤器 * 作为防护重复请求的第一道防线 * author juwatech.cn */ComponentpublicclassIdempotentFilterimplementsGlobalFilter{privatefinalStringRedisTemplateredisTemplate;publicIdempotentFilter(StringRedisTemplateredisTemplate){this.redisTemplateredisTemplate;}OverridepublicMonoVoidfilter(ServerWebExchangeexchange,GatewayFilterChainchain){ServerHttpRequestrequestexchange.getRequest();// 只处理POST请求if(!POST.equalsIgnoreCase(request.getMethodValue())){returnchain.filter(exchange);}StringidempotentIdrequest.getHeaders().getFirst(Idempotency-ID);if(idempotentIdnull||idempotentId.isEmpty()){returnonError(exchange,缺少幂等性ID);}StringredisKeyidempotent:idempotentId;// 使用SETNX命令设置一个10秒的过期时间防止死锁BooleanisSetredisTemplate.opsForValue().setIfAbsent(redisKey,1,10,TimeUnit.SECONDS);if(Boolean.TRUE.equals(isSet)){// 设置成功是首次请求放行returnchain.filter(exchange);}else{// 设置失败是重复请求直接返回returnonError(exchange,重复的请求);}}privateMonoVoidonError(ServerWebExchangeexchange,StringerrMsg){ServerHttpResponseresponseexchange.getResponse();response.setStatusCode(HttpStatus.BAD_REQUEST);byte[]byteserrMsg.getBytes(StandardCharsets.UTF_8);DataBufferbufferresponse.bufferFactory().wrap(bytes);returnresponse.writeWith(Mono.just(buffer));}}业务层最终保障尽管网关层做了拦截但在分布式系统中仍可能存在网关校验通过但业务处理失败客户端重试后网关缓存已过期的极端情况。因此业务层必须有最终的幂等性保障。实现原理数据库唯一索引这是最可靠的方案。在返利记录表中将幂等ID字段设置为唯一索引。捕获异常当插入重复的幂等ID时数据库会抛出唯一键冲突异常。业务代码捕获该异常并返回成功或特定的重复处理结果。SQL表结构示例CREATETABLErebate_record(idbigint(20)NOTNULLAUTO_INCREMENT,user_idbigint(20)NOTNULLCOMMENT用户ID,order_novarchar(64)NOTNULLCOMMENT电商订单号,idempotent_idvarchar(32)NOTNULLCOMMENT幂等ID,amountdecimal(10,2)NOTNULLCOMMENT返利金额,statustinyint(4)NOTNULLCOMMENT状态,PRIMARYKEY(id),UNIQUEKEYuk_idempotent_id(idempotent_id)-- 核心唯一索引)ENGINEInnoDBDEFAULTCHARSETutf8mb4COMMENT返利记录表;Java代码实现示例Service层packagejuwatech.cn.idempotent.service;importjuwatech.cn.idempotent.core.IdempotentIdGenerator;importjuwatech.cn.idempotent.mapper.RebateRecordMapper;importjuwatech.cn.idempotent.model.RebateRecord;importorg.springframework.dao.DuplicateKeyException;importorg.springframework.stereotype.Service;importorg.springframework.transaction.annotation.Transactional;/** * 返利业务服务包含最终的幂等性保障 * author juwatech.cn */ServicepublicclassRebateService{privatefinalRebateRecordMapperrebateRecordMapper;publicRebateService(RebateRecordMapperrebateRecordMapper){this.rebateRecordMapperrebateRecordMapper;}TransactionalpublicvoidprocessRebate(LonguserId,StringorderNo,Doubleamount){// 1. 生成幂等IDStringidempotentIdIdempotentIdGenerator.generateId(userId.toString(),orderNo);// 2. 构建返利记录RebateRecordrecordnewRebateRecord();record.setUserId(userId);record.setOrderNo(orderNo);record.setIdempotentId(idempotentId);record.setAmount(amount);record.setStatus(1);// 待结算try{// 3. 插入数据库利用唯一索引保证幂等性rebateRecordMapper.insert(record);// 4. 后续处理如发送消息、更新用户余额等// ...}catch(DuplicateKeyExceptione){// 捕获唯一键冲突异常说明该请求已被处理过直接返回即可// 可以记录日志但无需再次处理业务System.out.println(重复的返利请求已忽略。幂等ID: idempotentId);}}}通过网关层和业务层的双重防护我们为“省赚客APP”构建了一套坚不可摧的幂等性体系确保了在任何网络异常或用户误操作下用户的返利资产都绝对安全。本文著作权归 省赚客app 研发团队转载请注明出处