SpringCloud 分布式跟踪
分布式跟踪
分布式跟踪是一种监视和分析应用程序的技术,尤其是使用微服务体系结构构建的应用程序。也称为分布式请求跟踪。开发人员使用分布式跟踪来调试和优化代码。
分布式跟踪提供了一个地方,我们可以看到"特定请求正在发生什么?这很重要,因为微服务涉及许多组件。
如果我们要解决问题或调试问题,则需要一个中央服务器。因此,出现了 分布式跟踪一词。
在本节中,我们将 Spring Cloud Sleuth 与 Zipkin 。 Spring Cloud Sleuth为我们提出的每个请求分配一个 唯一ID 。我们可以在所有组件中基于唯一的ID跟踪所有请求。
Spring Cloud Sleuth
Spring Cloud Sleuth是 Spring Cloud库 strong>通过在适当的HTTP请求标头上添加 trace 和 span ID 来提供跟踪后续微服务进度的功能。 Sleuth库基于 MDC (映射的诊断上下文)概念,我们可以在其中轻松提取值,放入上下文并在日志中显示它们。
Zipkin
Zipkin是基于Java的开源 分布式跟踪系统。它具有一个管理控制台,该控制台提供了一种机制,用于 发送,接收,存储和 可视化跟踪后续服务的详细信息。
借助在Zipkin服务器中,我们可以将所有组件的所有日志都放在 MQ (RabbitMQ)中。我们将日志发送到合并日志的Zipkin服务器。完成此操作后,我们可以监视不同的请求。我们还可以找到特定请求的状态吗?
使用Spring Cloud Sleuth实现分布式跟踪
在这一步中,我们将为所有请求添加Spring Cloud Sleuth微服务。它为所有请求添加唯一的ID。它用于生成 跟踪ID , 跨度ID 并将其附加到日志,以便工具(Zipkin)可以使用这些ID。
Spring Cloud Sleuth令牌具有以下组件:
- 应用程序名称: 在属性文件中定义的应用程序的名称。
- 跟踪ID: 侦探将添加跟踪ID。对于给定的请求,在所有服务中都保持不变。
- 跨度ID: 侦探还添加了跨度ID。对于一个给定的请求,它在一个工作单元中保持不变,但是对于不同的服务而言却是不同的。
- Zipkin导出标志: 表示布尔值。它可以是 true 或
下图显示了Spring Cloud Sleuth令牌。
让我们在项目中实施 Spring Cloud Sleuth 。
步骤1: 选择项目 netflix-zuul -api-gateway-server 。
步骤2: 打开 pom.xml 并添加 Sleuth 依赖性。
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-sleuth</artifactId> </dependency>
现在,我们需要跟踪所有请求。如果要跟踪所有请求,则需要创建 ALWAYS_SAMPLE 。我们可以使用 Bean 创建一个Sampler。
Sampler
分布式跟踪可能包含大量数据,因此,采样在分布式跟踪中很重要。 Spring Cloud Sleuth提供了 采样器策略。借助Sampler,我们可以实施提供算法控制的采样算法。默认情况下,如果 span (相关性: 是一个单独的操作)已经处于活动状态,我们将获得一个连续执行跟踪的过程。
新创建的跨度始终标记为 不可导出。如果所有应用程序都与Sampler一起运行,则可以在日志中看到跟踪(由跨度组成的端到端延迟图),而不是在任何远程位置。默认情况下,Spring Cloud Sleuth会将所有范围设置为 不可导出。
当我们将范围数据导出到 Zipkin 或 Spring Cloud Stream时,Spring Cloud Sleuth提供了 AlwaysSampler 类,该类将所有内容导出到Zipkin。它还提供了一个 PercentageBasedSampler 类,该类对固定范围的跨度进行采样。
记住: 如果您使用的是 Spring 2.0.0 或更高版本,请使用以下采样器。之所以使用相同的名称,是因为我们使用的是Spring版本 2.2.1。
@Bean public Sampler defaultSampler() { return Sampler.ALWAYS_SAMPLE; }
步骤3: 打开 NetflixZuulApiGatewayServerApplication.java 文件并定义一个 Bean 。
NetflixZuulApiGatewayServerApplication。 Java
package com.aizws.microservices.netflixzuulapigatewayserver; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.cloud.netflix.zuul.EnableZuulProxy; import org.springframework.context.annotation.Bean; import brave.sampler.Sampler; @SpringBootApplication @EnableDiscoveryClient @EnableZuulProxy public class NetflixZuulApiGatewayServerApplication { public static void main(String[] args) { SpringApplication.run(NetflixZuulApiGatewayServerApplication.class, args); } //creating a bean @Bean //creating a sampler called public Sampler defaultSampler() { return Sampler.ALWAYS_SAMPLE; } }
在上面的代码中,我们已将Spring Cloud Sleuth添加到Zuul API Gateway服务器。
现在,我们必须在 currency-exchange-service 和<
步骤4: 打开 currency-exchange-service的 pom.xml 并添加 Sleuth 依赖项。
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-sleuth</artifactId> </dependency>
步骤5: 打开 CurrencyExchangeServiceApplication.java 文件并定义一个 Bean 。
CurrencyExchangeServiceApplication。 Java
package com.aizws.microservices.currencyexchangeservice; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.context.annotation.Bean; import brave.sampler.Sampler; @SpringBootApplication @EnableDiscoveryClient public class CurrencyExchangeServiceApplication { public static void main(String[] args) { SpringApplication.run(CurrencyExchangeServiceApplication.class, args); } @Bean //creating a sampler called always sampler public Sampler defaultSampler() { return Sampler.ALWAYS_SAMPLE; } }
步骤6: 类似地,打开 currency-conversion-service 的 pom.xml 并添加Sleuth依赖项。
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-sleuth</artifactId> </dependency>
步骤7: 打开 CurrencyConversionServiceApplication.java 文件并定义一个 Bean 。
CurrencyConversionServiceApplication。 Java
package com.aizws.microservices.currencyconversionservice; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.cloud.openfeign.EnableFeignClients; import org.springframework.context.annotation.Bean; import brave.sampler.Sampler; @SpringBootApplication @EnableFeignClients("com.aizws.microservices.currencyconversionservice") @EnableDiscoveryClient public class CurrencyConversionServiceApplication { public static void main(String[] args) { SpringApplication.run(CurrencyConversionServiceApplication.class, args); } @Bean //creating a sampler called always sampler public Sampler defaultSampler() { return Sampler.ALWAYS_SAMPLE; } }
现在我们有三个连接到Spring Cloud Sleuth的应用程序。
步骤8: 按以下顺序启动应用程序:
netflix-eureka-naming-server
- 打开浏览器并调用URL http://localhost:8761 。它返回Eureka界面,如下所示。
currency-exchange-service (在端口8000上)
currency-conversion-service
netflix-zuul-api-gateway-server
记住: 启动每个服务后,刷新 Eureka服务器。
它显示当前已在Eureka服务器上注册的所有实例。
步骤9: 打开 CurrencyExchangeController.java 文件,并在其中添加 logger 。
CurrencyExchangeController.java
package com.aizws.microservices.currencyexchangeservice; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.core.env.Environment; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RestController; @SpringBootApplication @RestController public class CurrencyExchangeController { private Logger logger=LoggerFactory.getLogger(this.getClass()); @Autowired private Environment environment; @Autowired private ExchangeValueRepository repository; @GetMapping("/currency-exchange/from/{from}/to/{to}") //where {from} and {to} are path variable public ExchangeValue retrieveExchangeValue(@PathVariable String from, @PathVariable String to) //from map to USD and to map to INR { ExchangeValue exchangeValue = repository.findByFromAndTo(from, to); //setting the port exchangeValue.setPort(Integer.parseInt(environment.getProperty("local.server.port"))); logger.info("{}", exchangeValue); return exchangeValue; } }
类似地,我们将记录器添加到CurrencyConversionContoller。
步骤10: 打开 CurrencyConversionContoller 。 java 文件并在其中添加 logger 。
CurrencyConversionContoller 。 java
package com.aizws.microservices.currencyconversionservice; import java.math.BigDecimal; import java.util.HashMap; import java.util.Map; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; @RestController public class CurrencyConversionController { private Logger logger=LoggerFactory.getLogger(this.getClass()); @Autowired private CurrencyExchangeServiceProxy proxy; @GetMapping("/currency-converter/from/{from}/to/{to}/quantity/{quantity}") //where {from} and {to} represents the column //returns a bean back public CurrencyConversionBean convertCurrency(@PathVariable String from, @PathVariable String to, @PathVariable BigDecimal quantity) { //setting variables to currency exchange service Map<String, String> uriVariables=new HashMap<>(); uriVariables.put("from", from); uriVariables.put("to", to); //calling the currency exchange service ResponseEntity<CurrencyConversionBean> responseEntity=new RestTemplate().getForEntity("http://localhost:8000/currency-exchange/from/{from}/to/{to}", CurrencyConversionBean.class, uriVariables); CurrencyConversionBean response=responseEntity.getBody(); //creating a new response bean and getting the response back and taking it into Bean return new CurrencyConversionBean(response.getId(), from, to, response.getConversionMultiple(), quantity, quantity.multiply(response.getConversionMultiple()), response.getPort()); } //mapping for currency-converter-feign service @GetMapping("/currency-converter-feign/from/{from}/to/{to}/quantity/{quantity}") //where {from} and {to} represents the column //returns a bean public CurrencyConversionBean convertCurrencyFeign(@PathVariable String from, @PathVariable String to, @PathVariable BigDecimal quantity) { CurrencyConversionBean response=proxy.retrieveExchangeValue(from, to); logger.info("{}", response); //creating a new response bean //getting the response back and taking it into Bean return new CurrencyConversionBean(response.getId(), from, to, response.getConversionMultiple(), quantity, quantity.multiply(response.getConversionMultiple()), response.getPort()); } }
步骤12: 执行请求 http://localhost:8100/currency-converter-feign/from/EUR/to/INR/quantity/100 。它将返回以下响应,如下所示。
让我们在控制台中查看 currency-conversion-service 的日志。currency-conversion-service显示以下日志:
我们还可以查看 currency-exchange-service 的日志。货币兑换服务显示以下日志:
类似地,我们可以看到 netflix-zuul-api-gateway-server的日志。
让我们仔细看一下以上三种不同服务的日志。我们发现这三个服务都具有相同的 跟踪ID(533f8d3966d8f4e7)。
Spring Cloud Sleuth将跟踪ID分配给请求。我们可以使用此ID跨多个组件跟踪请求。但是存在一个问题,该日志分布在多个位置。我们使用 Zipkin 来解决此问题。借助Zipkin,我们可以将日志集中在一个地方。
使用Zipkin进行分布式跟踪
但是,我们在跟踪方面面临挑战。如果要跟踪请求,则必须检查单个应用程序的日志。解决此问题的方法称为 集中式日志。
我们需要集中所有微服务中的所有日志。我们可以搜索Spring Cloud Sleuth分配的ID。在集中的地方,我们将能够搜索并找出特定请求的状况。
有以下集中日志记录的解决方案:
- ELK Stack (Elastic Search)
- Kibana
- Zipkin
在此分布式跟踪中,我们将使用 Zipkin分发跟踪服务器。它为我们提供了所有微服务的统一视图。我们从单个微服务获取所有日志消息。 Zipkin服务器收集日志消息。所有微服务都将日志消息放在名为 RabbitMQ 的队列中,然后Zipkin从RabbitMQ中选择这些日志消息。 Zipkin跟踪服务器与数据库连接。
在本例中,我们使用内存数据库。我们将从数据库中提取日志消息。下一步,我们将安装RabbitMQ。
下一章:SpringCloud RabbitMQ
RabbitMQ RabbitMQ 是广泛部署的开源 消息代理软件,该软件可实现 高级消息队列协议。它轻巧且易于在云中部署。它支持多种消息传递协议。可以将其部署在分布式环境中,以满足 大规模和 高可 ...