注:记录开发,自己总结,随便写写,不喜勿喷。
问题描述
开发需求的时候发现接口的公共参数特别多(各种信息、各种版本号),还需要进行服务间的传递,直接写个base类,里面包含所有公共参数,让所有传参对象都去继承它似乎也行,但这种公共的东西我就是不想跟业务参数耦合在一起。
今天想讲一种方案,基于dubbo拦截器实现公共参数的传递。
解决方案
之前讲过dubbo拦截器扩展:https://mp.csdn.net/mp_blog/creation/editor/new/128960789
这次还是基于拦截器实现这个功能
首先定义一个公共类,封装公共参数
@DATA
public class RequestCommonParam{
private String versionCode;
private String imei;
private String ouid;
......
}
在公共包新增如下文件
@Activate(
group = {Constants.CONSUMER}
)
public class RequestContextConsumerFilter implements Filter {
private static final Logger logger = LoggerFactory.getLogger(RequestContextConsumerFilter.class);
private static final String COMMON_PARAM_KEY = "common_param_key";
@Override public Result invoke(
Invoker<?> invoker, Invocation invocation) throws RpcException {
RequestCommonParam requestParam = RequestContextManager.get();
try {
if (requestContext != null) {
String string = JSON.toJSONString(requestParam);
if (invocation instanceof RpcInvocation) {
RpcInvocation rpcInvocation = (RpcInvocation)invocation;
rpcInvocation.setAttachment(COMMON_PARAM_KEY, string);
}
}
} catch (Throwable throwable) {
logger.warn("设置requestParam异常 ", throwable);
}
return invoker.invoke(invocation);
}
}
@Activate(
group = {Constants.PROVIDER}
)
public class RequestContextProviderFilter implements Filter {
private static final Logger logger = LoggerFactory.getLogger(RequestContextProviderFilter.class);
@Override public Result invoke(
Invoker<?> invoker, Invocation invocation) throws RpcException {
String attachment = invocation.getAttachment(”common_param_key”);
logger.info("invocation : {}, attachment : {}, url : {}", invocation, attachment, invoker.getUrl().getProtocol());
RequestCommonParam requestContext = null;
try {
if (attachment != null) {
requestContext = JSON.parseObject(attachment, RequestCommonParam.class);
}
} catch (Throwable throwable) {
logger.warn("RequestCommonParam 反序列化异常 : {}", attachment, throwable);
}
try {
if (requestContext != null) {
RequestContextManager.begin(requestContext);
}
return invoker.invoke(invocation);
} finally {
if (!LOCAL_PROTOCOL.equals(invoker.getUrl().getProtocol())) {
RequestContextManager.remove();
}
}
}
}
META-INF.dubbo.org.apache.dubbo.rpc.Filter新增配置
providerRequestContext=com.oppo.usercenter.common.easydubbo.apache.filter.RequestContextProviderFilter
consumerRequestContext=com.oppo.usercenter.common.easydubbo.apache.filter.RequestContextConsumerFilter
注解@Activate是自动生效的,引入这个公共包的依赖就行了。
这只是dubbo服务间的参数传递,那最开始的前端http请求怎么处理呢?其实上面的consumer的拦截器里有个RequestContextManager,这是啥呢?
public class RequestContextManager {
private static final Logger logger = LoggerFactory.getLogger(RequestContextManager.class);
private static final TransmittableThreadLocal<RequestCommonParam> contextLocal = new TransmittableThreadLocal() {
protected RequestCommonParam initialValue() {
return new RequestCommonParam();
}
};
public RequestContextManager() {
}
public static void begin(RequestCommonParam requestContext) {
contextLocal.set(requestContext);
}
public static RequestCommonParam get() {
return (RequestCommonParam)contextLocal.get();
}
public static void remove() {
contextLocal.remove();
}
public static String toJson() {
return JSON.toJSONString(get());
}
}
这是封装了一个threadlocal的类,可以存放RequestCommonParam,用完记得删,如上面的remove方法,因为线程是复用的,不删后果自负。那我们在什么时候存放进去呢?
那肯定是客户端请求进来的时候就要直接放进去。怎么放?当然是aop,看代码:
@Aspect
@Component
@Order(1)
public class RequestCommonParamAOP {
private static final Logger LOGGER = LoggerFactory.getLogger(RequestCommonParamAOP.class);
public static final String REPORT_CONTEXT = "Report-Context";
public RequestContextAOP() {
LOGGER.info("--------------------------------RequestContextAOP--------------------------------");
}
@Pointcut("@annotation(org.apache.springframework.web.bind.annotation.RequestMapping)")
public void pointcutRequest() {
// pointcutRequest
}
/**
* 切点配置
* @param proceedingJoinPoint
* @return Object
* @throws Throwable
*/
@Around("pointcutRequest()")
public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
AsyncRequest request = RequestResponseHolder.getRequest();
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("request url : {},headrs : {}", request.path(), request.headers());
}
RequestCommonParam requestContext = new RequestCommonParam();
try {
setRequestContextValue(request, requestContext);
} catch (Exception e) {
LOGGER.warn("traceId={},解析context异常", traceId, e);
}
RequestContextManager.begin(requestContext);
Object responseInfo = null;
try {
responseInfo = proceedingJoinPoint.proceed();
return responseInfo;
} catch (Exception e) {
throw e;
} finally {
MDC.remove("traceId");
RequestContextManager.remove();
}
}
private void setRequestContextValue(AsyncRequest request, RequestCommonParam requestContext){
/、需要啥,从request里拿就好了
}
}
除了aop,也可以使用其它相关的拦截器,都是可以的哈。
至此,全链路都有这些公共参数了。问题来了,怎么用呢?或者说怎么获取这个参数呢?其实上面代码已经写好了,参数是放在threadlocal包装类中的,用法代码如下:
RequestCommonParam requestParam = RequestContextManager.get();
然后从requestParam直接拿公共参数就好了。
自己试试吧!