需求
为什么要写这么一个小问题呢,可能有同学说了,发生异常后不是自动就体现到日志里面去了吗?有啥可捕获的?
想想这么一个场景:
生产环境中众多日志中发现了如下异常信息:
[New I/O worker #1][Log4j2Logger.java:73] -
Data length too large: 1895857664, max payload: 8388608,
channel: NettyChannel [channel=[id: 0xbe23f2b5,
/172.31.5.38:36596 => /172.31.2.196:21880]]\n\"\
用过Dubbo的都知道,这是因为 RPC接口的返回值过大,超出了设定的响应最大值,可以通过修改 payload 参数来解决。
通过这个信息你大致可以知道是 哪两个服务 交互时发生的异常,但是无法知道具体是哪个接口、哪个方法、什么参数情况下产生的这个异常,里面也没有logId。
这就是老吕要捕获它的原因,我要把产生这个错误时对应的 logId,接口名称,方法名称,和参数值 全部记录下来,方便业务开发的同学进行优化改进。
logId在每一个业务线程中都会绑定,发生业务异常时日志中就会自动记录下 logId,开发人员可以方便的检索 分布式链路日志解决问题。但是这个IO异常是产生的IO线程上的,大家都知道在NIO架构中IO线程是所有业务线程共享的,这就意味着 IO线程上不可能绑定业务logId这个东西,所以它出来的异常中是没有logId这个东西的。
解决方案
关于Dubbo IO线程模型的先不说了,下面直接给出一个针对此问题的可行的解决方案:
由于这个IO异常的根源产生在提供者端,我一直想在提供者端捕获,最终证明不太可行(有知道的同学可以通知我下),还是要到消费者端捕获。
我分别写了2个过滤器,一个是提供者端过滤器,一个是消费者端过滤器,看里面的注释就知道怎么做了,如下:
##提供者过滤器(无法感知Duboo IO异常,这里只是为了说明问题)
public class MyProviderFilter implements Filter {
@Override
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
long startTime = System.currentTimeMillis();
try {
Result result = invoker.invoke(invocation);
if (result.hasException()) {
//是否有异常,异常处理,这里可以感知所有的业务线程异常,但无法感知Dubbo的IO异常
}
return result;
}catch (Throwable e){
//这里也无法感知IO异常
e.printStackTrace();
throw e;
}finally {
String log = invoker.getInterface().getCanonicalName()+","+invocation.getMethodName();
System.out.println(log+","+(System.currentTimeMillis()-startTime));
}
}
}
##消费者过滤器(可以感知Dubbo IO异常,但是也需要特殊处理才行)
public class MyConsumerFilter implements Filter {
@Override
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
long startTime = System.currentTimeMillis();
try {
Result result = invoker.invoke(invocation);
if (result.hasException()) {
//是否有异常,异常处理,这里无法感知Dubbo IO异常
}
return result;
}catch (Throwable e){
//这里可以感知IO异常
dubboRpcLog(invoker,invocation,e.getMessage());
throw e;
}finally {
String log = invoker.getInterface().getCanonicalName()+","+invocation.getMethodName();
System.out.println(log+","+(System.currentTimeMillis()-startTime));
}
}
/**
* 记录调用日志
* @param invoker
* @param invocation
*/
private void dubboRpcLog(Invoker<?> invoker, Invocation invocation,String errorMsg) {
StringBuilder message = new StringBuilder();
message.append("dubboUrl:").append(invoker.getUrl()).append(";");
message.append("interface:").append(invoker.getInterface().getName()).append(";");
message.append("methodName:").append(invocation.getMethodName()).append(";");
Class<?>[] parameterTypes = invocation.getParameterTypes();
Object[] arguments = invocation.getArguments();
if (parameterTypes!=null||parameterTypes.length>0) {
message.append("arguments:");
try {
message.append(JSON.json(arguments));
} catch (IOException e) {
e.printStackTrace();
}
}
System.out.println(message.toString());
}
}
今天就到这里,希望对你有所帮助
扫码加微信技术群