异常处理与排障
2026年9月14日 上午5:32:44
先判断调用失败发生在哪一层,再决定看哪里。Nami 源码的调用链大致是:
接口代理 → 解析上游 → 选择 Channel → 发请求 → Result → assertSuccess → Decoder
1、 常见异常类型
| 阶段 | 常见现象 | 首查内容 |
|---|---|---|
| 上游解析 | 找不到服务实例、upstream 为 null | name、group、Discovery/LoadBalance |
| Channel | There is no channel available | URL scheme、nami-channel-* |
| 连接 | 连接拒绝、连接超时 | 服务是否启动、端口、网络、防火墙 |
| 响应 | NamiResponseException | HTTP 状态码、服务端路由和业务日志 |
| 解码 | NamiDecodeException 或字段转换失败 | Content-Type、coder/serialization、DTO |
| 业务 | 返回的异常被包装 | 服务端异常堆栈、接口语义和幂等性 |
2、 处理 HTTP 错误
Result.assertSuccess() 在状态码 >= 400 时抛出 NamiResponseException。它包含状态码和响应描述:
import org.noear.nami.exception.NamiResponseException;
try {
User user = userService.getById(1L);
} catch (NamiResponseException e) {
int status = e.getCode();
String description = e.getDescription();
// 记录 status、description、接口和 traceId
}
响应体被读取为字符串后会被清理,排查时不要反复假设还能读取同一 body。连接、配置和通道问题通常是 NamiException 或带 cause 的包装异常,日志应保留完整异常链。
3、 固定 URL 优先排查
推荐严格按以下顺序恢复功能:
- 服务端是否以
Solon.start启动,端口是否监听。 - 用固定 URL 调用,暂时去掉服务发现和 LoadBalance。
- 对照
@Mapping检查完整路径、尾斜杠和 HTTP 方法。 - 检查请求参数:query、表单、
@NamiBody/@Body是否一致。 - 检查
http/https/tcp/ws与已注册 channel 是否一致。 - 检查 Content-Type、Accept 以及双方编解码组件。
- 最后恢复发现、过滤器和自定义负载策略。
4、 一个实用的最小日志字段
traceId, service/group, resolved upstream, path, action, timeout, cost, status
不要记录 Token、Cookie、完整个人信息和大请求体。过滤器中可以统计耗时,但必须确保最终调用 inv.invoke():
Filter filter = inv -> {
long start = System.currentTimeMillis();
try {
return inv.invoke();
} finally {
LOG.debug("url=" + inv.url + ", cost="
+ (System.currentTimeMillis() - start));
}
};
5、 超时与重试
超时只说明客户端在期限内没有拿到结果,不代表服务端没有执行。对写操作盲目重试可能产生重复数据;重试前要有幂等键或明确的幂等设计。服务发现异常也不要通过无限重试掩盖配置错误。