在工作中,置信大家最怕的一件事就是听到有人在工作群艾特你:某某性能报错啦。。。
而后你就得屁颠屁颠的去服务器看日志,日志量少还好点,多的话找起来太麻烦了。不太容易间接定位到要害中央。
东找找西找找,好不容易找到了报错的信息,却不晓得过后的参数信息是什么,也不太好复现,太难了。。
改完还得写故障报告,美妙的一天又没了。
要解决这类的痛点须要做上面几件事件:
- 日志收集
- 异样告警
- 日志减少链路
- API 响应减少 traceId
- 异样时打印以后报错办法的参数
- 反对调试模式
日志收集
要解决的第一个问题就是日志的集中管理,不然报错了你得去多台服务上找错误信息,效率太低了。当然也能够用 ansible 这种工具来做,最好的还是日志统计收集起来,通过 Web 页面就能够搜寻查看。
日志收集的标杆就是 ELK 啦,不做过多解说。像咱们是用的云服务,收集起来更不便,间接页面上点点就搞定了。
日志减少链路
日志减少链路跟踪性能分为两个步骤,首先零碎要有链路跟踪,而后将链路信息集成到日志中就能够了。
我用的是 Spring Cloud Sleuth,次要是 Sleuth 对很多开源的框架都反对了,也集成了 logback 这样的日志框架,用起来十分不便。
Sleuth 默认加强的日志格局如下:
[${spring.zipkin.service.name:${spring.application.name:-}},%X{X-B3-TraceId:-},%X{X-B3-SpanId:-},%X{X-Span-Export:-}]
别离是服务名,链路 ID, 工作单元 ID,是否导入到 zipkin 中。对于日志来说最重要的就是 traceId 了,有了 traceId 就能将所有零碎的日志串连起来了。
咱们也能够本人扩大,减少一些其余的信息放入日志中。比方:
%X{X-REST-API:-},%X{X-RPC-SERVICE:-},%X{X-ORIGIN-INFO:-},%X{X-USER-ID:-},%X{X-BIZ-NAME:-},%X{X-BIZ-ID:-}
- X-REST-API:入口 API, 全局透传
- X-RPC-SERVICE:入口 RPC, 每个服务入口处新增
- X-ORIGIN-INFO:起源信息(调用方利用名:IP: 服务名)
- X-USER-ID:用户 ID, 全局透传
- X-BIZ-NAME:业务名称, 全局透传, 利用内可替换
- X-BIZ-ID:业务 ID, 全局透传, 利用内可替换
有了这些扩大的信息就能够间接从日志中晓得以后申请的入口 API 是哪个,也晓得整个申请通过了哪些服务。
如果我是订单服务的负责人,当我去排查问题的时候依据日志就晓得以后这个谬误是上游哪个零碎和哪个接口调用导致的。
日志中还带上了用户信息,晓得是哪个用户的申请。
BIZ-ID 和 BIZ-NAME 能够用于业务场景的问题排查,比方下单后就晓得订单 ID 了,后续就能够将订单 ID 追加到日志中,BIZ-NAME=order, BIZ-ID=20102121212121。
后续有领取,发货,退款等等订单相干的操作,日志中都有订单 ID,须要排查的时候间接依据订单 ID 就能够看到整个订单相干的日志信息,前提是打了这些信息。
异样告警
除了用户反馈性能异样,开发也应该在第一工夫晓得出问题了。所以异样告警肯定要做。
个别咱们的利用分为:服务利用,Job 利用,异步生产利用
服务利用咱们能够在对立的异样解决中进行告警,Job 利用也能够在对立调度的入口进行告警,异步生产的也是一样。
能够通过音讯队列告警,也能够通过制订日志的格局,通过记录日志的形式,让日志收集到日志平台,而后配置各种规定进行告警。
异样告警的时候就能够带上 traceId,这样在发现异常的时候,间接通过 traceId 去日志平台搜寻,就能够看到这个 traceId 相干的所有日志,对排错很有帮忙,前提是你打印了要害的日志信息。
API 响应减少 traceId
能够通过 ResponseBodyAdvice 对响应后果进行对立定制,减少 traceId 响应。这样在出问题的时候能够通过响应的 traceId 间接去日志平台进行日志的搜寻。
除了疾速排查异样问题之外,对于性能优化的时候,咱们也能够间接依据 traceId 去查看这个 API 对应的耗时状况,前提是这个 traceId 就是跟你们的 APM 零碎统一就行。
异样时打印以后报错办法的参数
通过后面的操作,咱们曾经能够在异样的时候获取一个 traceId 去排查相干错误信息,也不必去多台机器随机找日志了,极大的进步了问题解决的速度。
只能说这些操作对咱们的问题排查起了一半的帮忙作用,比如说我当初收到一个告警,而后我去日志平台查了相干的日志,发现某行报错了。
这个时候也只能是猜想这个中央是有问题的,因为我不晓得过后是什么参数导致这行报错了。所以如果能在报错的时候将以后报错办法的参数打印到日志中,也就相当于保留了出问题时的现场,解决起问题来就是分分钟的事。
具体实现计划没有固定的,最简略的形式就是写一个 Aspect 切到所有业务办法上,当办法抛出异样的时候记录参数信息,切记只在异样的时候做这个记录的操作,否则对性能影响很大。
成果:
com.xxx.biz.service.impl.GoodsSkuServiceImpl.createSku 异样, 参数信息:{"cspuId":1, stock:10, price:100}
Caused by: java.util.NoSuchElementException: No value present
at java.util.Optional.get(Optional.java:135)
at com.xxx.biz.service.impl.GoodsSkuServiceImpl.createSku(GoodsSkuServiceImpl.java:682)
反对调试模式
反对调试模式指的是在某些场景下,咱们能够复现出谬误,然而除了过后异样时记录的参数信息,还想晓得整个申请链路的参数和响应。也就是从入口处通过的所有办法都可能打印出申请和响应数据。
能够定义一个特定的申请头,在复现问题的时候带上这个申请头,由对立的框架去接管这个申请头,而后在整个链路上进行透传。再联合异样的那个 Aspect 将参数和后果进行日志输入即可。
成果:
xxx.xxxController.makeOrder 参数:xxx
xxx.xxxRpcService.makeOrder 参数:xxx
xxx.xxxStockRpcService.lockStock 参数:xxx
xxx.xxxStockRpcService.lockStock 响应:xxx
xxx.xxxRpcService.makeOrder 响应:xxx
xxx.xxxController.makeOrder 响应:xxx
对于作者 :尹吉欢,简略的技术爱好者,《Spring Cloud 微服务 - 全栈技术与案例解析》,《Spring Cloud 微服务 入门 实战与进阶》作者, 公众号 猿天地 发起人。