场景模拟:还是和第一篇的场景模拟是一样的。转账操作。下面是我的代码:系统A--------转账到---------->系统B。
方式二、我是使用redis去做的。而且为了不影响业务这个操作完全是那个接口需要我在那个接口,去加上一行代码去做成分布式的幂等。
1、请看代码:(系统A的代码我就不写了和上篇的一致)
package com.assets.service.feture;
import com.apibase.rest.response.ResponseObject;
/**
* 实现幂等性所需要的实现类接口
*/
public interface IdempotentService {
/**
* 定义执行方法
*/
public static interface Handler<T> {
ResponseObject<T> excute();
}
/**判断RequestID是否已存在,如果不存在保存RequestID
* @param requestID
* @return
*/
boolean existRequest(String requestID);
/**保存返回值
* @param requestID
* @param resp
*/
void saveResponse(String requestID, ResponseObject<?> resp);
/**获取返回值
* @param requestID
* @param cls
* @return
*/
<T> ResponseObject<T> getResponse(String requestID, Class<T> cls);
/**核心接口,使用内部类定义执行方法,提高复用性
* @param requestID
* @param handler
* @return
*/
<T> ResponseObject<T> excuteHandler(Handler<T> handler);
}
2、这步需要连接redis的配置。配置我就不多说了,网上的轮子很多。。。
用的时候会报错,因为缺少一个返回值的类。这个类。可以根据自己需要的返回值自己去定义格式。异常的捕获,这个也需要自己去定义。那俩个异常是我这边自己要用到的。根据自己的需求格式来。我这边只提供解决上述问题。(如果有不会写的可以给我留言。)
package com.assets.service.feture.Impl;
import java.util.concurrent.TimeUnit;
import javax.annotation.Resource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import com.apibase.excep.ParamInvalidException;
import com.apibase.excep.ServerErrorException;
import com.apibase.rest.response.ResponseObject;
import com.assets.service.feture.IdempotentService;
import com.assets.web.filter.IdempotentTool;
@Service
public class IdemopotentServiceImpl implements IdempotentService {
private Logger logger = LoggerFactory.getLogger(this.getClass());
@Resource(name="redisTemplate4Object")
private RedisTemplate<String, Object> redisCache;
private String getKey(String requestID){
return "&idemop_"+requestID;
}
@Override
public boolean existRequest(String requestID) {
if(requestID==null){
throw new ParamInvalidException("requestId", " can not Found");
}
String key = this.getKey(requestID);
boolean ifset = redisCache.opsForValue().setIfAbsent(key,"");
if(ifset){
redisCache.expire(key, 30, TimeUnit.SECONDS); //设置超时时间
return false;
}else{
return true;
}
}
@SuppressWarnings("unchecked")
@Override
public <T> ResponseObject<T> getResponse(String requestID, Class<T> cls) {
String key = this.getKey(requestID);
Object resp = null;
long time1 = System.nanoTime();
while(true){
Object value = redisCache.opsForValue().get(key);
if(value==null){
resp = null;
break;
}else if(!"".equals(value)){
resp = value;
break;//获取到返回值,推出
}
long time2 = System.nanoTime();
if(time2-time1>2000){
throw new ServerErrorException("AccessTimeOut",null);
}
//循环等待
try {Thread.sleep(200);}
catch (InterruptedException e){}
}
if(resp!=null){
return (ResponseObject<T>)resp;
}else{
throw new ServerErrorException("ReponseDataLost",null);
}
}
@Override
public void saveResponse(String requestID, ResponseObject<?> resp) {
String key = this.getKey(requestID);
redisCache.opsForValue().set(key, resp);
redisCache.expire(key, 30, TimeUnit.SECONDS);
}
@Override
public <T> ResponseObject<T> excuteHandler(Handler<T> handler) {
String requestID = IdempotentTool.getRequestId();
boolean exit = this.existRequest(requestID);//判断是否存在
if(exit){
ResponseObject<T> resp = this.getResponse(requestID,null);
return resp;
}else{
ResponseObject<T> resp = null;
try {
resp = handler.excute();
this.saveResponse(requestID,resp);//保存结果
return resp;
} catch (RuntimeException e) {
logger.error(e.getMessage());
resp = ResponseObject.failRuntimeException(e);
this.saveResponse(requestID,resp);//保存结果
return resp;
}
}
}
}
3、分布式幂等的使用方式:
package com.assets.web.rest;
import java.math.BigDecimal;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RestController;
import com.apibase.excep.BusinessException;
import com.apibase.rest.response.ResponseObject;
import com.assets.service.feture.IdempotentService;
import com.bsts.assets.constant.BitstarException;
import com.bsts.assets.rest.TransferServiceRest;
import com.bsts.assets.service.TransferService;
@RestController
public class TransferServiceRestImpl implements TransferServiceRest {
private Logger logger = LoggerFactory.getLogger(TransferServiceRestImpl.class);
@Autowired
private TransferService transferService;
//这里注入幂等操作的入口
@Autowired
private IdempotentService idempotentService;
@Override
public ResponseObject<?> transferMainToOther(Integer userId, BigDecimal amount, Integer coinTypeId, Integer status,
Integer recordId, String callbackId, Integer mode) {
//这里是幂等的调用方式,这样就可以解决只是某一个接口需要幂等操作
ResponseObject<Long> handler = idempotentService.excuteHandler(new IdempotentService.Handler<Long>() {
@Override
public ResponseObject<java.lang.Long> excute() {
try {
if (userId == 0 || amount.compareTo(BigDecimal.ZERO) <= 0 || coinTypeId == 0 || recordId == 0) {
throw new BitstarException("UF001", "date is null!", null);
}
//没有转账的类型不允许转账
if (mode==null) {
throw new BitstarException("UF307","No transfer is allowed without the type of transfe!",null);
}
transferService.transferAssetToFun(userId ,amount,coinTypeId, status,recordId,callbackId,mode);
return ResponseObject.success();
} catch (BusinessException e1) {
e1.printStackTrace();
logger.error(e1.getMessage(), e1);
return ResponseObject.failBusiException(e1);
}
}
} );
return handler;
}
@Override
public ResponseObject<?> transferOtherToMain(Integer userId, BigDecimal amount, Integer coinTypeId, Integer status,
Integer recordId, String callbackId, Integer mode) {
ResponseObject<Long> excuteHandler = idempotentService.excuteHandler(new IdempotentService.Handler<Long>() {
@Override
public ResponseObject<Long> excute() {
try {
if (userId == 0 || amount.compareTo(BigDecimal.ZERO) <= 0 || coinTypeId == 0 || recordId == 0) {
throw new BitstarException("UF001", "date is null!", null);
}
//没有转账的类型不允许转账
if (mode==null) {
throw new BitstarException("UF307","No transfer is allowed without the type of transfe!",null);
}
transferService.transferOtherToAsset(userId ,amount,coinTypeId, status,recordId,callbackId,mode);
return ResponseObject.success();
} catch (BusinessException e1) {
e1.printStackTrace();
logger.error(e1.getMessage(), e1);
return ResponseObject.failBusiException(e1);
}
}
});
return excuteHandler;
}
}