Dubbo隐式参数
简介
在dubbo应用互相调用过程中,某些dubbo方法可能需要一些非业务属性的参数,例如traceId、userInfo、token等,如果在方法签名上增加这些参数,那么代码看上去会有些不优雅。那么有没有更好的方法呢?
Dubbo框架通过RpcContext#attachments
提供了隐式参数传递的功能。
服务消费方可以通过RpcContext.getContext().setAttachment()
方法设置附加属性键值对,服务提供方可以通过RpcContext.getContext().getAttachment()
在服务方法内获取设置的附加属性键值对。通常用于框架集成、不建议用于传递常规业务参数(影响代码可读性等原因)。
用法示例
在服务消费方端设置隐式参数
@Test public void test() { // 隐式传参,会隐式将这些参数发送到服务器端 RpcContext.getContext().setAttachment("userId", "1"); RpcContext.getContext().setAttachment("traceId", "Hemx4j"); String result = xxxService.sayHello("隐式参数"); System.out.println("result = " + result); }复制代码
在服务提供方端获取隐式参数
@Override public String sayHello(String name) { // 获取服务消费方传入的隐式参数 Map<String, String> attachments = RpcContext.getContext().getAttachments(); System.out.println("attachments = " + attachments); String userId = RpcContext.getContext().getAttachment("userId"); String traceId = (String) RpcContext.getContext().getAttachment("traceId"); System.out.println("userId = " + userId); System.out.println("traceId = " + traceId); return "Hello, " + name; }复制代码
源码解析
服务消费方绑定并传递隐式参数
在服务消费方,发起远程调用时,会从RpcContext获取到附加属性键值对,然后放入到RpcInvocation#attachments中,最后经过网络传递到服务提供端。
org.apache.dubbo.rpc.cluster.support.AbstractClusterInvoker#invoke
org.apache.dubbo.rpc.RpcInvocation#addAttachments
@Override public Result invoke(final Invocation invocation) throws RpcException { checkWhetherDestroyed(); // binding attachments into invocation. Map<String, String> contextAttachments = RpcContext.getContext().getAttachments(); if (contextAttachments != null && contextAttachments.size() != 0) { ((RpcInvocation) invocation).addAttachments(contextAttachments); } ... return doInvoke(invocation, invokers, loadbalance); } public void addAttachments(Map<String, String> attachments) { if (attachments == null) { return; } if (this.attachments == null) { this.attachments = new HashMap<String, String>(); } this.attachments.putAll(attachments); }复制代码
服务提供方获取并回收隐式参数
在服务提供方,接受到远程请求时,会通过ContextFilter对请求进行拦截,然后从RpcInvocation#attachments获取到附加属性键值对,并将键值对设置到RpcContext中。后续提供方如果需要使用到attachments值,只需要从RpcContext获取即可。
org.apache.dubbo.rpc.filter.ContextFilter#invoke
@Override public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException { // 从Invocation获取附加属性键值对attachments Map<String, String> attachments = invocation.getAttachments(); ... // 将键值对设置到RpcContext中 if (attachments != null) { if (RpcContext.getContext().getAttachments() != null) { RpcContext.getContext().getAttachments().putAll(attachments); } else { RpcContext.getContext().setAttachments(attachments); } } ... try { return invoker.invoke(invocation); } finally { // 清空当前线程的RpcContext对象,隐式参数不会继续传递给下一个应用 RpcContext.removeContext(); RpcContext.removeServerContext(); } }
作者:逍遥道长
链接:https://juejin.cn/post/7022261378102591495