服务间公共参数传递解决方案

注:记录开发,自己总结,随便写写,不喜勿喷。

问题描述

开发需求的时候发现接口的公共参数特别多(各种信息、各种版本号),还需要进行服务间的传递,直接写个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直接拿公共参数就好了。

自己试试吧!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值