This website requires JavaScript.

Spring-Cloud-Gateway-Sleuth全链路监控

1 2021-11-17 15:33:45 72

全链路监控

只要是微服务开发,没有全链路监控系统的有一个算一个全是坑。

维护的一个老项目里面日志打印格式那叫一个牛,还没有全链路监控,排查问题那叫一个爽,生怕你能定位到问题。

所以,痛定思痛赶紧把监控安排上

使用Spring提供的Spring-Cloud-Gateway-Sleuth 组件即可实现基础的链路追踪功能,Sleuth 提供基础的traceId和spanId,但是这两个对于一般的业务够用,但是稍微大点的系统是远远不够的。

比如现在我有一个需求:我需要知道当前请求的用户名、企业名、携带的参数,并且当这条链路请求完成后,我需要返回给调用方当前请求的traceId、spanId以及logid,可以参考微信公众平台、饿了么公众平台接口定义,如下图:

Untitled

后面的1234533425就是用户Id,这里只是演示,更多字段可以根据需求来增加。

实现以上功能我们可以基于Sleuth 提供的Brave实现,我们直接找到Sleuth 官方文档 ,然后搜索cuctom定位到下面这张图,Sleuth 告诉我们如果需要自定义更高级的,不用自定义属性,使用@Bean来配置自定义字段

https://yd-note.oss-cn-beijing.aliyuncs.com/spring/cloud/Sleuth.png

一共四段配置,直接上代码:

        
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
@Configuration(proxyBeanMethods = false) public class TracerConfig { BaggageField requestRequestUri = BaggageField.create(ClassicConstants.REQUEST_REQUEST_URI); BaggageField requestQueryString = BaggageField.create(ClassicConstants.REQUEST_QUERY_STRING); BaggageField requestMethod = BaggageField.create(ClassicConstants.REQUEST_METHOD); BaggageField userId = BaggageField.create("USER-ID"); /** * 响应前响应后过滤器 */ @Bean WebFilter tracerWebFilter(Optional<Tracer> tracer) { return (exchange, chain) -> { exchange.getResponse().beforeCommit(() -> { Optional.ofNullable(exchange.<String>getAttribute(ServerWebExchange.LOG_ID_ATTRIBUTE)).ifPresent(logId -> exchange.getResponse().getHeaders().add("logId", logId)); return Mono.empty(); }); return chain.filter(exchange) // 执行完请求后将相关traceId、spanID返回前端,方便排查 .doOnSubscribe(subscription -> tracer.ifPresent(t -> { Optional.ofNullable(t.currentSpan()).map(Span::context).ifPresent(context -> exchange.getResponse().beforeCommit(() -> { Optional.ofNullable(context.traceId()).ifPresent(traceId -> exchange.getResponse().getHeaders().add("traceId", traceId)); Optional.ofNullable(context.spanId()).ifPresent(spanId -> exchange.getResponse().getHeaders().add("spanId", spanId)); Optional.ofNullable(context.parentId()).ifPresent(parentId -> exchange.getResponse().getHeaders().add("parentId", parentId)); return Mono.empty(); })); // 执行请求前传播字段 t.createBaggage(ClassicConstants.REQUEST_REQUEST_URI, exchange.getRequest().getURI().getPath()); t.createBaggage(ClassicConstants.REQUEST_QUERY_STRING, exchange.getRequest().getURI().getQuery()); t.createBaggage(ClassicConstants.REQUEST_METHOD, exchange.getRequest().getMethodValue()); String authorization = exchange.getRequest().getHeaders().getFirst(HttpHeaders.AUTHORIZATION); if (StringUtils.isNotEmpty(authorization) & StringUtils.startsWith(authorization,JWTAuthentication.BEARER)){ JSONObject jsonObject = JWTAuthentication.parseJwtToClaimsAsJSONObject(authorization); t.createBaggage("USER-ID",jsonObject.getStr("user_name")); } })); }; } /** * 传播字段定制器,SingleBaggageField.remote请求时携带请求头 */ @Bean BaggagePropagationCustomizer baggagePropagationCustomizer() { return builder -> builder .add(BaggagePropagationConfig.SingleBaggageField.remote(requestRequestUri)) .add(BaggagePropagationConfig.SingleBaggageField.remote(requestQueryString)) .add(BaggagePropagationConfig.SingleBaggageField.remote(userId)) .add(BaggagePropagationConfig.SingleBaggageField.remote(requestMethod)); } /** * 相关范围定制器 */ @Bean CorrelationScopeCustomizer correlationScopeCustomizer() { return builder -> builder .add(CorrelationScopeConfig.SingleCorrelationField.create(BaggageFields.PARENT_ID)) .add(CorrelationScopeConfig.SingleCorrelationField.create(BaggageFields.SAMPLED)) .add(CorrelationScopeConfig.SingleCorrelationField.newBuilder(requestRequestUri).flushOnUpdate().build()) .add(CorrelationScopeConfig.SingleCorrelationField.newBuilder(requestQueryString).flushOnUpdate().build()) .add(CorrelationScopeConfig.SingleCorrelationField.newBuilder(requestMethod).flushOnUpdate().build()) .add(CorrelationScopeConfig.SingleCorrelationField.newBuilder(userId).flushOnUpdate().build()); } }

上面这段配置就完美实现了我们的需求,对于每一个请求进行全链路追踪且返回logId,对于问题排查效率大大增加。

Sleuth原理就是基于请求头,需要对feign进行处理,feign调用接口时需要将请求头也携带过去,具体可以参考 GitHub

最后

关于微服务我想说的几个点

  1. 团队太小了,后端团队都只有个位数,就不要玩微服务了,还不够人解决环境、部署、测试等问题的。
  2. 正在构建未经证实的项目,没人玩过,没有成功的案例参考。这种情况下谁都说不清楚后面会需要用到的哪些技术。
  3. 团队本身没有微服务开发经验,而且找不到有成功架构微服务经验的人做指导,那就老老实实用单体吧。

免责声明

本站提供Hack区的一切软件、教程和仅限用于学习和研究目的;不得将其用于商业或者非法用途,否则,一切后果由用户自己承担 您必须在下载后的24个小时之内, 从您的电脑中彻底删除。如果条件支持,请支持正版,得到更好的服务。 另如有侵权请邮件与我 联系处理。敬请谅解!

本文于   2021/11/17 下午  发布 

永久地址: https://madaoo.com/article/1460994574154600448

版权声明: 自由转载-署名-非商业性使用   |   Creative Commons BY-NC 3.0 CN