使用公司RPC中间件,发现一个问题,最终定位到序列化问题,进行记录
1.起因
- 公司内部RPC框架,服务端报错,但客户端却不会立刻报错,而是等到Timeout后,报超时错误。
组内使用内部RPC框架暴露多个服务,其他服务在Provider报错后,Consumer可以立刻响应错误,但唯独使用ElasticSearch的服务不行,怀疑方向为ES自定义的Exception与内部中间件不匹配
在中间件团队的支持下,定位到Netty,isSent为flase,没有发送成功,报错信息为序列化失败。
具体内容为 suppressed Exception (org.elasticsearch.client.Response 不支持序列化)
2.相关联知识点
Java的序列化
Java序列化对象要实现Serializable接口
1.属性为基本类型,默认支持序列化
2.属性为引用对象,则引用对象也要支持序列化Suppressed Exception
这个是Java 1.7引用的异常,常伴随着 try with resource语法使用
try with resource
public static void main(String[] args) {
try (FileInputStream inputStream = new FileInputStream(new File("test"))) {
System.out.println(inputStream.read());
} catch (IOException e) {
throw new RuntimeException(e.getMessage(), e);
}
}
try-with-resource并不是JVM虚拟机的新增功能,只是JDK实现了一个语法糖。当你将上面代码反编译后会发现,其实对JVM虚拟机而言,它看到的依然是之前的写法:
public static void main(String[] args) {
try {
FileInputStream inputStream = new FileInputStream(new File("test"));
Throwable var2 = null;
try {
System.out.println(inputStream.read());
} catch (Throwable var12) {
var2 = var12;
throw var12;
} finally {
if (inputStream != null) {
if (var2 != null) {
try {
inputStream.close();
} catch (Throwable var11) {
var2.addSuppressed(var11);
}
} else {
inputStream.close();
}
}
}
} catch (IOException var14) {
throw new RuntimeException(var14.getMessage(), var14);
}
}
Java最终只能抛出一个Exception,但如果try中爆出了错误1,在finally中报错了错误2,这怎么办,在jdk 1.7以前,你只能通过代码自己解决包装。但jdk1.7以后,提供了addSuppressed方法,可以让你将其他错误,放在一个Exception里进行抛出。
问题最终解释
暴露的服务A使用ES服务,ES服务报错,最终报错 ElasticsearchStatusException 支持序列化 ,但是内部的addSuppressed添加的org.elasticsearch.client.Response 不支持序列化,由于公司内部的RPC框架会对整个对象进行序列化,导致Netty发送失败
解决方案
自己在此服务处,catch Exception进行包装后抛出