最近看左耳听风,看到了如下的描述
然后想了想,自己之前的公司对api出错的方式也是选择一律返回 200 状态码并自定义消息。我认为的一些原因:
1. 统一的错误处理机制
希望简化客户端的错误处理逻辑,通过统一返回 200 状态码和自定义的错误消息,避免客户端根据不同的 HTTP 状态码来处理不同的错误情况。
2. 历史遗留问题
项目开始时可能并未严格遵循 RESTful API 的设计原则,随着项目的演进,这种做法逐渐固化下来,修改起来可能涉及较大范围的调整和测试,因而继续沿用。
3. 简化前端开发
通过统一返回 200 状态码,可以让前端开发人员不必处理各种不同的 HTTP 状态码,从而简化前端代码的逻辑处理。但这种做法其实是将复杂性转移到了另一个地方,并不是最佳实践。
4. 不同的设计哲学
基于某种设计哲学或特定的业务需求,认为自定义消息可以提供更详细的错误信息,而不是依赖于标准的 HTTP 状态码。
而返回正确的 HTTP 错误码也确实更方便监控服务的运行状况,因为标准的 HTTP 状态码可以直接被监控工具识别和处理。以下是如何利用 HTTP 状态码来监控服务,以及常用的监控工具。
为什么返回 HTTP 错误码方便监控
-
标准化: 监控工具可以直接解析 HTTP 状态码,识别出哪些请求成功(2xx)、哪些请求客户端有误(4xx)、哪些请求服务器有问题(5xx)。
-
快速定位问题: 使用标准的 HTTP 状态码可以帮助开发人员和运维人员快速定位问题。比如,频繁的 500 错误可能意味着服务器有问题,而频繁的 404 错误可能意味着客户端在请求不存在的资源。
-
自动化告警: 许多监控系统可以基于 HTTP 状态码设置自动化告警。例如,当特定时间内 5xx 错误超过某个阈值时,触发告警。
常用的监控工具
1. Prometheus 和 Grafana
Prometheus 是一个强大的监控和报警工具,Grafana 是一个可视化工具,两者常常结合使用。
- Prometheus:负责数据采集和存储。通过 HTTP 状态码监控 API,可以定义查询和报警规则。
- Grafana:负责数据展示。可以创建图表、仪表盘等,实时展示 API 的健康状态。
2. ELK Stack
ELK Stack 包括 Elasticsearch、Logstash 和 Kibana,是另一套流行的日志分析和监控解决方案。
- Elasticsearch:用于存储和搜索数据。
- Logstash:用于数据收集和处理,可以将日志数据送入 Elasticsearch。
- Kibana:用于数据可视化,可以展示 HTTP 请求的状态码分布等。
返回适当的 HTTP 状态码不仅是 RESTful API 的最佳实践,还能显著提升监控的效率和准确性。通过结合使用 Prometheus、Grafana、ELK Stack 或 New Relic 等工具,可以实现对 API 服务的全面监控和快速问题定位。
但是结合国内现状来说,很多公司的项目虽然说是分布式微服务,其实并没有并未严格遵循 RESTful API 的设计原则,而且使用http状态码也有一些缺点,不符合国内的约定俗成。
- 灵活性有限:标准的 HTTP 状态码数量有限,可能无法精确表达业务层面的各种错误。
- 一致性问题:在分布式系统中,可能存在部分服务返回 HTTP 状态码,部分服务返回自定义错误码,导致一致性问题。
对了如果你使用统一 200 状态码返回错误还想进行监控和告警的话可以参考以下方式,仅供参考,不保证正确性
方法一:使用 APM 工具
借助 APM 工具(如 New Relic、Datadog),可以创建自定义指标记录错误信息。例如,在 Spring Boot 应用中使用 New Relic:
import com.newrelic.api.agent.NewRelic;
@GetMapping("/example")
public ResponseEntity<ApiResponse<String>> example(@RequestParam(required = false) String param) {
if (param == null) {
NewRelic.noticeError("Custom Error Code: 4001 - Parameter missing");
return new ResponseEntity<>(new ApiResponse<>(4001, "Parameter missing", null), HttpStatus.OK);
}
try {
return new ResponseEntity<>(new ApiResponse<>(0, "Success", "data"), HttpStatus.OK);
} catch (Exception e) {
NewRelic.noticeError("Custom Error Code: 5001 - Internal server error");
return new ResponseEntity<>(new ApiResponse<>(5001, "Internal server error", null), HttpStatus.OK);
}
}
方法二:使用日志记录和分析
通过在日志中记录自定义的错误码和错误信息,并使用 ELK Stack 进行分析。
方法三:使用 Prometheus 和 Grafana
在 Spring Boot 应用中使用 Micrometer 和 Prometheus 记录自定义的错误码,并在 Grafana 中进行可视化:
import io.micrometer.core.instrument.Counter;
import io.micrometer.core.instrument.MeterRegistry;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class MonitoringService {
@Autowired
private MeterRegistry meterRegistry;
public void recordErrorCode(int errorCode) {
Counter.builder("custom.error.codes")
.tag("error_code", String.valueOf(errorCode))
.register(meterRegistry)
.increment();
}
}
在控制器中使用 MonitoringService 记录错误码:
@Autowired
private MonitoringService monitoringService;
@GetMapping("/example")
public ResponseEntity<ApiResponse<String>> example(@RequestParam(required = false) String param) {
if (param == null) {
monitoringService.recordErrorCode(4001);
return new ResponseEntity<>(new ApiResponse<>(4001, "Parameter missing", null), HttpStatus.OK);
}
try {
return new ResponseEntity<>(new ApiResponse<>(0, "Success", "data"), HttpStatus.OK);
} catch (Exception e) {
monitoringService.recordErrorCode(5001);
return new ResponseEntity<>(new ApiResponse<>(5001, "Internal server error", null), HttpStatus.OK);
}
}
在 Grafana 中,通过 Prometheus 查询语言(PromQL)查询和展示自定义错误码的数据:
sum by (error_code) (rate(custom_error_codes[5m]))
结论
在接口错误码的返回方式上,使用 HTTP 状态码和统一 200 状态码各有优缺点。使用 HTTP 状态码标准化且便于监控,而统一 200 状态码灵活性更高但增加了监控和调试的复杂度。无论采用哪种方式,都应确保在监控和告警方面的完善,以快速响应和处理系统中的异常情况。结合使用 APM 工具、日志分析和 Prometheus 等监控工具,可以在统一 200 状态码的情况下实现有效的监控和告警。