本文已参与「掘力星计划」,赢取创作大礼包,挑战创作激励金。
简介
在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();
}
}