在不改变消费者代码的前提下,生产者怎样获取消费者的applicationName,ip?
[apache/dubbo]在不改变消费者代码的前提下,生产者怎样获取消费者的applicationName,ip?
回答
dubbo有现成的api开放出来,如下 //获取远程ip RpcContext context = RpcContext.getContext(); String remoteHost = context.getRemoteHost(); System.out.println(remoteHost); //应用名 String applicationName= context.getUrl().getParameter("application"); System.out.println(applicationName); 通过获取RpcContext的实例,该上下文实例,在当前线程内,是一个全局变量。
我知道有这个api,但是这个api在生产者方调用只会获取生产者的信息。remoteHost和 aplicationName都是生产者的,不是消费者的。
` @Override protected void encodeRequestData(Channel channel, ObjectOutput out, Object data) throws IOException { RpcInvocation inv = (RpcInvocation) data;
out.writeUTF(inv.getAttachment(Constants.DUBBO_VERSION_KEY, DUBBO_VERSION));
out.writeUTF(inv.getAttachment(Constants.PATH_KEY));
out.writeUTF(inv.getAttachment(Constants.VERSION_KEY));
out.writeUTF(inv.getMethodName());
out.writeUTF(ReflectUtils.getDesc(inv.getParameterTypes()));
Object[] args = inv.getArguments();
if (args != null)
for (int i = 0; i < args.length; i++){
out.writeObject(encodeInvocationArgument(channel, inv, i));
}
out.writeObject(inv.getAttachments());
}`
看了dubbo源码,在DubboCodec类的encodeRequestData中,压根就没有向生产者发送自己的applicationName 和ip. 所以生产者是获取不到的
applicationName真有问题; 不过IP是没有问题。 两台服务器之间通信,IP在理论上是互知的。并不需要客户端显式传值;如果不考虑多级转发的问题,服务端获取的客户端的地址,就是消费者的;实际上,多级转发比如A调用B,B调用C,这和用户通过浏览器经过多级代理,不一样;这里,更多滴看作是服务端之间的调用,IP控制的话,只对上一级负责就可以了。当然,要看你自己的实际需求了。
ip地址:RpcContext.getRemoteAddress() appName: 目前dubbo框架层面是没有传递appname到provider端的,需要使用者通过attachments等自己传递
ip可以,不过ip不是通过业务参数传过去的,是通过通讯层的机制实现的。applicationName是不行的。 不知道为什么dubbo不传这些参数,然道真的是为了实现所谓的无状态,透明?
需要在消费者端自己实现一个 Filter
@Activate(group = Constants.CONSUMER, order = -10000)
public class DubboContextFilter implements Filter {
@Override
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
String application = RpcContext.getContext().getUrl().getParameter("application");
RpcContext.getContext().setAttachment("application", application);
return invoker.invoke(invocation);
}
}
需要配置声明一下这个filter(com.alibaba.dubbo.rpc.Filter)以遍能扫描到:
然后配置一下filter:
<dubbo:consumer application="pdl-gateway" layer="gateway" filter="dubboContextFilter" />
然后再服务端就可以通过RpcContext.getContext().getAttachment("application")
拿到了
思路:provider端的filter中扩展获取
途径:
- IP可以通过RemoteHost直接获取
- 应用名是无法直接获取的,需要consumer传入,所以consumer也需要扩展一个filter。
以下是一个完整可用的,记录ip,appName,耗时的一对filter,请自行取用
@Activate(group = Constants.CONSUMER)
public class LogTraceConsumerFilter implements Filter {
private static final Logger LOG = LoggerFactory.getLogger(LogTraceConsumerFilter.class);
@Override
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
//手动设置consumer的应用名进attachment
String application = invoker.getUrl().getParameter(Constants.APPLICATION_KEY);
if (application != null) {
RpcContext.getContext().setAttachment("dubboApplication", application);
}
Result result = null;
String serverIp = null;
long startTime = System.currentTimeMillis();
try {
result = invoker.invoke(invocation);
serverIp = RpcContext.getContext().getRemoteHost();//这次返回结果是哪个ip
return result;
} finally {
Throwable throwable = (result == null) ? null : result.getException();
Object resultObj = (result == null) ? null : result.getValue();
long costTime = System.currentTimeMillis() - startTime;
LOG.info("[TRACE] Call {}, {}.{}() param:{}, return:{}, exception:{}, cost:{} ms!", serverIp, invoker.getInterface(), invocation.getMethodName(), invocation.getArguments(), resultObj, throwable, costTime);
}
}
}
@Activate(group = Constants.PROVIDER)
public class LogTraceProviderFilter implements Filter {
private static final Logger LOG = LoggerFactory.getLogger(LogTraceProviderFilter.class);
@Override
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
//上游如果手动设置了consumer的应用名进attachment,则取出来打印
String clientIp = RpcContext.getContext().getRemoteHost();//这次请求来自哪个ip
String application = RpcContext.getContext().getAttachment("dubboApplication");
String from = clientIp;
if (!StringUtils.isEmpty(application)) {
from = application+"("+clientIp+")";
}
LOG.info("[Trace]From {}, {}.{}() param:{}", from, invoker.getInterface(), invocation.getMethodName(), invocation.getArguments());
return invoker.invoke(invocation);
}
}
关于更多详情我写了一个博客:http://jaskey.github.io/blog/2020/05/18/dubbo-filter-trace-consumer/