Dubbo RPC 序列化问题记录
Dubbo RPC 序列化问题:List.of() 导致的 “Fail to decode request” 错误
问题背景
在开发过程中,遇到了一个 Dubbo RPC 调用失败的问题。当在请求参数中设置 revises
字段时,会出现以下错误:
com.alibaba.dubbo.rpc.RpcException: Failed to invoke the method jobSearch in the service com.techwolf.oceanus.api.search.job.JobSearchService$Iface.
Tried 1 times of the providers [10.57.121.172:28000] (1/1) from the registry ceres-qa.weizhipin.com:8091 on the consumer 172.16.26.127 using the dubbo version 2.6.17-ZP.
Last error is: Failed to invoke remote method: jobSearch, provider: dubbo://10.57.121.172:28000/com.techwolf.oceanus.api.search.job.JobSearchService$Iface
...
Caused by: com.alibaba.dubbo.remoting.RemotingException: Fail to decode request due to: RpcInvocation [methodName=jobSearch, parameterTypes=[class com.techwolf.oceanus.api.search.job.JobSearchRequest], arguments=null, attachments={path=com.techwolf.oceanus.api.search.job.JobSearchService$Iface, input=1650, dubbo=2.0.2, version=1.0.0}, attributes={serialization_id=2}]
问题分析
1. 错误现象
- 当不设置
revises
参数时,RPC 调用正常 - 当设置
revises
参数时,出现序列化失败错误 - 错误信息显示 “Fail to decode request”,表明是序列化/反序列化问题
2. 代码定位
问题出现在 buildEXTFilter
方法中:
private Revise buildEXTFilter(long positionId, long parentId) {ReviseTag curTag = new ReviseTag();curTag.setCode("posi:" + positionId);ReviseTag parentTag = new ReviseTag();parentTag.setCode("posi:" + parentId);parentTag.setContents(List.of(curTag)); // 问题所在ReviseTag rootTag = new ReviseTag();rootTag.setContents(List.of(parentTag)); // 问题所在rootTag.setCode("posi:1");Map<ReviseType, ReviseContent> reviseContents = new HashMap<>();ReviseContent content = new ReviseContent();content.setType(ReviseType.EXT_FILTER);content.setTags(List.of(rootTag)); // 问题所在reviseContents.put(ReviseType.EXT_FILTER, content);Revise revise = new Revise();revise.setReviseContents(reviseContents);return revise;
}
3. 根本原因
- 使用了
List.of()
方法创建列表 List.of()
是 Java 9 引入的方法,返回不可变列表- Dubbo 使用 Hessian2 序列化,对不可变列表的序列化支持存在问题
- 不可变列表在某些序列化场景下可能导致序列化失败
解决方案
方案一:使用 Arrays.asList() 替换 List.of()
private Revise buildEXTFilter(long positionId, long parentId) {ReviseTag curTag = new ReviseTag();curTag.setCode("posi:" + positionId);ReviseTag parentTag = new ReviseTag();parentTag.setCode("posi:" + parentId);parentTag.setContents(Arrays.asList(curTag)); // 修改后ReviseTag rootTag = new ReviseTag();rootTag.setContents(Arrays.asList(parentTag)); // 修改后rootTag.setCode("posi:1");Map<ReviseType, ReviseContent> reviseContents = new HashMap<>();ReviseContent content = new ReviseContent();content.setType(ReviseType.EXT_FILTER);content.setTags(Arrays.asList(rootTag)); // 修改后reviseContents.put(ReviseType.EXT_FILTER, content);Revise revise = new Revise();revise.setReviseContents(reviseContents);return revise;
}
方案二:使用 new ArrayList<>() 创建可变列表
private Revise buildEXTFilter(long positionId, long parentId) {ReviseTag curTag = new ReviseTag();curTag.setCode("posi:" + positionId);ReviseTag parentTag = new ReviseTag();parentTag.setCode("posi:" + parentId);parentTag.setContents(new ArrayList<>(Arrays.asList(curTag)));ReviseTag rootTag = new ReviseTag();rootTag.setContents(new ArrayList<>(Arrays.asList(parentTag)));rootTag.setCode("posi:1");Map<ReviseType, ReviseContent> reviseContents = new HashMap<>();ReviseContent content = new ReviseContent();content.setType(ReviseType.EXT_FILTER);content.setTags(new ArrayList<>(Arrays.asList(rootTag)));reviseContents.put(ReviseType.EXT_FILTER, content);Revise revise = new Revise();revise.setReviseContents(reviseContents);return revise;
}
技术要点
1. List.of() vs Arrays.asList() 对比
特性 | List.of() | Arrays.asList() |
---|---|---|
Java 版本 | Java 9+ | Java 1.2+ |
可变性 | 不可变 | 半可变(不支持结构修改) |
序列化兼容性 | 部分场景有问题 | 兼容性更好 |
性能 | 略好 | 略差 |
空值支持 | 不支持 null | 支持 null |
2. Dubbo 序列化机制
- Dubbo 默认使用 Hessian2 序列化
- Hessian2 对某些 Java 集合类型的序列化支持有限
- 不可变集合在某些场景下可能导致序列化失败
3. 最佳实践建议
- 在 Dubbo RPC 调用中,优先使用
Arrays.asList()
或new ArrayList<>()
- 避免在序列化场景中使用
List.of()
、Set.of()
等不可变集合 - 如果必须使用不可变集合,需要测试序列化兼容性
验证结果
修改后重新测试:
- ✅ RPC 调用成功
- ✅ 序列化/反序列化正常
- ✅ 功能逻辑保持不变
总结
这个问题提醒我们在使用 Java 新特性时要注意兼容性问题,特别是在分布式系统、RPC 调用等场景下。List.of()
虽然提供了更好的性能和不可变性保证,但在某些序列化场景下可能存在兼容性问题。在实际开发中,需要根据具体的使用场景选择合适的集合创建方式。
相关链接
- Java 9 List.of() 官方文档
- Dubbo 序列化机制
- Hessian2 序列化
记录时间:2025年1月
问题类型:Dubbo RPC 序列化兼容性问题
解决状态:已解决