Application publishes events:
在事件驱动的微服务架构中,为了可靠性,服务必须自动发布任何有关它们状态发生变化的事件。
跨数据库和消息代理来使用分布式事务是不太可能的。

那么我们如何在状态发生变化时自动可靠的发布事件呢?
2PC 不是一个可用的选项。
解决方案:应用程序向EVENTS表插入数据作为本地事务的一部分,另外一个单独进程从EVENTS表中获取这些
记录并发布它们到一个消息中间件。
好处是,高水平的域事件,非2PC实现。
我们需要在实现时注意追踪避免重复发布事件,追踪管理事件发布的正确顺序。
===================
Event Sourcing:
事件发射源保存一个业务实体的状态,比如一个订单或者一个客户作为一系列状态变化事件。
当业务实体状态发生变化时,一个新的事件被添加到该事件列表。因为保存一个事件是一个单一的操作,
所以它是内在原子性。应用程序依赖事件来重构实体当前状态。

应用程序保存事件到一个事件存储数据库,该存储装置应该有一个API用于添加和获取实体的事件。
事件存储应该有类似消息中间件的行为。提供一个API让服务来预定事件,当服务将一个事件保存到事件存储时,
它会将事件分发给所有预定该事件的预订者。

有些实体,比如客户,可能会有大量的事件存在。为了优化事件的加载,应用程序可以周期性的保存一个
实体当前状态的快照,用于重构当前状态时使用。应用程序在重构实体当前状态时,会查找该实体的最近的快照
和自从该快照以后发生的事件执行即可。这样可以避免太多的事件被回放。

实例:
Customer 和 Orders ,我们使用Event Sourcing模式和CQRS。
应用程序使用Spring Boot和Java开发,使用Eventuate编译,它是一个机遇事件源和CQRS的应用程序平台。

Order Service 操作ORDER table,将每一次状态改变的操作都产生相应的事件存储到事件存储装置,
该数据库以Order 2345 为记录记录其OrderCreated,OrderApproved...OrderShipped等事件。
该事件存储装置提供API接受Order Service的 add和find事件操作,和来自Customer Service的预定事件。

不只是简单的将每一个订单的当前状态存储为ORDERS表的一行,应用程序还会持久化每一个Order为一个事件序列。
CustomerService会预定这些订单事件并更新它自己的状态。

public class order extends ReflectiveMutableCommandProcessingAggregate<Order,OrderCommand>{
     private OrderState state;
     private String customerId;
     public OrderState getState(){
         return state;
     }
     
     public List<Event> process(CreateOrderCommand cmd){
         return EventUtil.events(new OrderCreatedEvent(cmd.getCustomerId(),cmd.getOrderTotal()));
     }
     
     public List<Event> process(ApproveOrderCommand cmd){
         return EventUtil.events(new OrderApprovedEvent(customerId);
     }
     
     public List<Event> process(RejectedOrderCommand cmd){
         return EventUtil.events(new OrederRejectedEvent(customerId);
     }
     
     public void apply(OrderCreatedEvent event){
         this.state = OrderState.CREATED;
         this.customerId = event.getCustomerId();
     }
     
     public void apply(OrderApprovedEvent event) {
         this.state = OrderState.APPROVED;
     }     public void apply(OrderRejectedEvent event) {
         this.state = OrderState.REJECTED;
     }
 }

在CustomerService里预定Order事件的事件处理器:

@EventSubscriber(id="customerWorkflow")
 public class CustomerWorkflow{
     @EventHandlerMethod
     public CompleteFuture<EntityWithIdAndVersion<Customer>> reserveCredit(
         EventHandlerContext<OrderCreatedEvent> ctx){
             OrderCreatedEvent event = ctx.getEvent();
             Money orderTotal = event.getOrderTotal();
             String customerId = event.getCustomerId();
             String orderId = ctx.getEntity();
             
             return ctx.update(Customer.class,customerId,new ReserveCreditCommand(orderTotal,orderId));
     }
 }


上面的代码通过试着保持信用额度来处理一个OrderCreated事件。

事件源模式在事件驱动架构中解决了一个关键问题,使得它可靠的发布事件成为可能。
因为它保存的是事件而非域对象,最大可能的避免了对象关系数据库不统一问题。
它提供了100%可靠的发生在业务实体上的变化audit log。
它使得在任何时间和任意点上实现临时状态查询成为可能

==================================================
API Gateway/Backend for Front-End 
我们在是用微服务架构创建一个在线商店时,为了实现一个产品详细页面,我们需要开发多个
版本的产品详细内容用户界面。
HTML5/JavaScript开发的桌面和移动浏览器使用的页面,它需要有服务端的Web应用提供生成HTML。
原生的Android和苹果客户端,则需要跟服务器通过REST APIs交换
同时我们的在线商店还应该考虑通过REST API暴露一些产品详细介绍给第三方应用程序。

假设我们卖一本书,则需要涉及的详细内容有:
关于书的基本信息,比如书名,作者,定价等。
用户购买的历史信息
可购买数
购买选项
其它跟本书一起可购买的选项
买了这本书的人还会买什么
客户预览
卖家评级
。。。

我们采用微服务架构设计时,制作这样的详细界面需要多个微服务来实现。
产品信息服务,提供产品的基本信息
定价服务,提供产品价格
其它服务提供产品历史
库存服务,提供产品可供购买数
浏览历史服务,提供客户回顾页面
。。。

那我们的微服务架构的应用程序如何来访问这些单独的微服务呢?
我们知道微服务提供的内容通常是跟我们页面需要的内容是不完全一致的。
微服务通常提供充分授权的API,这也就意味着客户端需要跟多个微服务交互。
不同的客户端需要不同的数据,比如桌面浏览器版产品详细页面通常比移动版的包含更多内容。
不同的客户端网络性能也不同。
微服务的实例和它们运行位置(主机+端口)可能是动态变化的
随着时间的变化,服务分区也会发生变化,这些必须对于客户端来说是透明的。

解决方案:
实现一个API网关作为所有客户端的访问入口。
API网关处理请求,有些请求只是被proxied/routed代理并路由到相应的服务,
通过它将请求扩散给其它处理器。
 
 API 网关会提供各种不同的服务API样式供不同的客户端使用。比如Netflix API网关
 它会运行各种客户端不同的适配器代码,提供不同客户端适合的API调用。
 
 API网关还会实现安全内容,比如检查客户端请求的身份认证执行。
 
 API网关的变种:Backend for front-end
 它为每一个客户端定义了各自的API 网关。
 比如 Web app API 网关,Mobile API网关,Public API网关等。
 
 典型的实例是Netflix API gateway
 http://techblog.netflix.com/2013/01/optimizing-netflix-api.html  https://github.com/cer/event-sourcing-examples/tree/master/java-spring/api-gateway-service
 https://github.com/cer/event-sourcing-examples
 
 如此一来,可以将客户端和应用程序的微服务分离设计隔离开来
 在决定使用哪个位置的微服务实例时也不需要客户端关心
 为每个客户端提供了更加优化的API
 减少请求和来回请求的次数
 简化了客户端对微服务的调用逻辑
 
 如何实现API网关呢?
 现在基于JVM,NIO的类库比如Netty,Spring Reactor等都是可用的。另外NodeJS也是一个选项。
 
 关联的设计模式:
 它是创建微服务架构的必须模式
 API网关必须使用Client-side Discovery 模式或者Server-side Discovery模式来路由请求到可用的微服务实例。
 API网关可能授权用户并传递包含用户信息的Access Token给服务。
 API网关将使用Circuit Breaker模式调用服务。
 
 ============================
 Publish events using database triggers
 使用数据库触发器来发布事件:
这是解决如何可靠稳定的发布状态变化事件问题。
使用一个或者多个数据库事件触发器来向EVENTS表里,该表会有单独的流程来处理发布存储的事件。
===============================================
Transaction log tailing
事务日志追踪:
追踪数据库事务日志来将其每个变化都发布成一个事件。
Eventuate Local(https://blog.eventuate.io/2016/10/06/eventuate-local-event-sourcing-and-cqrs-with-spring-boot-apache-kafka-and-mysql/)
使用了事务日志追踪方式。
该模式是非2PC方式,不需要应用程序变化追踪,保证了较高的准确性。
缺点在于相对晦涩,只能针对特定数据库开发,需要追踪避免重复发布,因为它发生在底层数据库
很难判断对业务层级的事件影响。
==============================================
Audit Logging 审计日志
将用户活动记录到数据库。

=============================================
Circuit Breaker 断路器模式
微服务架构中服务之间有时候需要协作处理请求,当一个服务同时调用其它服务时总是可能存在要调用的服务不可用或者显示高延迟最后不能使用。
前面的资源比如线程可能可能在等待其它服务回复时被调用者使用。
这些都会导致资源被耗尽,使得被调用服务无法再为其它请求服务。
一个服务的失败可能潜在的影响到整个应用程序的其它服务。

那么如何阻止一个网络或者服务的失败级联影响到其它服务呢?

方案是:每个服务客户端必须通过一个代理调用一个远程的服务。
这种代理的功能类似断路器。electrical circuit breaker
当一个连环的consecutive失败穿越一个阈值时(threshold),断路器断开,在一个周期内让所有这期间试图调用
该远程服务的请求都立刻失败。在超出一定时间后,断路器允许有限数量的测试请求通过。
如果这些测试请求成功,断路器会回复正常。否则,如果还有失败发生,则再次断开。

实例:
RegistrationServiceProxy 就是一个使用Scala写的处理远程服务调用失败的断路器。

@Component
 class RegistrationServiceProxy @Autowired()(restTemplate: RestTemplate) extends RegistrationService {  @Value("${user_registration_url}")
   var userRegistrationUrl: String = _  @HystrixCommand(commandProperties=Array(new HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds", value="800")))
   override def registerUser(emailAddress: String, password: String): Either[RegistrationError, String] = {
     try {
       val response = restTemplate.postForEntity(userRegistrationUrl,
         RegistrationBackendRequest(emailAddress, password),
         classOf[RegistrationBackendResponse])
       response.getStatusCode match {
         case HttpStatus.OK =>
           Right(response.getBody.id)
       }
     } catch {
       case e: HttpClientErrorException if e.getStatusCode == HttpStatus.CONFLICT =>
         Left(DuplicateRegistrationError)
     }
   }
 }

这里的@HystrixCommand 指定使用断路器来调用registerUser()服务
@EnableCircuitBreaker声明用于启动断路器功能:
@EnableCircuitBreaker
class UserRegistrationConfiguration {

使用该模式的挑战在于 选择合适的超时时间值 打到不产生误报(false positive)
或者带来执行的延迟问题。

与其相关的模式
Microservice Chassis可能实现该模式
API Gateway会使用该模式调用服务
Server-side discovery 路由器会使用该模式调用服务

Netflix Hystrix 是实现该模式的一个类库。
===================================================================
Microservice Chassis 微服务底架
我们在开发一个应用程序时总是花费大量的时间来处理跨切面的内容。比如
Externalized configuration外部配置,包括credentials证书,外部服务的网络地址,比如数据库和消息中间件。
Logging 日志-配置系统日志框架,比如log4j 或者 logback
Health checks健康检查,一个监控服务可以执行“ping”的url,来判断应用程序的健康状况。
Metrics-矩阵,能够提供一些测量维度来从内部说明应用程序做了什么以及如何做的。
Distributed tracing分布式追踪,使用设备服务代码可以为外部请求在服务之间穿行时添加唯一标识。

与这些跨切面内容对应的另外一些跨切面内容是跟应用程序使用的特定的技术关联的。
应用程序使用的基础架构服务比如数据库或者消息中间件需要对其引用进行配置才能使用。
比如我们应用程序如果要使用关系型数据库,则必须配置一个数据库连接池。Web应用程序要处理HTTP请求
也需要引用配置。

要建立这种机制可能需要花费一天或者两天甚至更长时间。
Microservice Chassis 框架有:
Java语言方面:Spring Boot + Spring Cloud
Dropwizard(http://www.dropwizard.io/1.1.0/docs/)
Go语言方面:Gizmo,Micro,Go kit等

关联的模式:
Self Registration--微服务的底盘通常需要负责注册服务和服务寄存器registry
Client-side discovery--微服务的底盘框架通常要负责客户端服务发现
Circuit Breaker--微服务的底盘可能需要实现断路器模式
Distributed tracing--微服务的底盘框架可能设备代码

=========================================================
Client-side service discovery:
应用系统中服务通常需要调用其它的服务,在一个大型集成应用程序中,服务调用通过语言级别的方法或者过程来实现调用。
在传统的分布式应用系统部署中,服务是运行在固定的,众所周知的位置上(主机+端口),很容易通过HTTP/REST或者一些RPC机制调用。
但是现在基于微服务的应用通常运行在一个虚拟的或者容器化的环境里,有大量的服务实例同时运行,并且他们的位置都是在动态变化的。
这时就需要我们必须实现一种机制来让客户端服务将请求发送给一个动态变化的短暂存在的服务实例。

问题是:客户端服务-API 网关或者其它服务如何发现一个服务实例的位置?
每个服务暴露一个远程API比如HTTP/REST,或者 Thrift等在特定的位置(主机+端口)
这些服务的实例和它们的位置是动态变化的
运行这些服务的虚拟机和容器通常被赋予动态IP地址
服务实例的数量也是动态变化的,比如EC2 Autoscaling Group会基于加载来调整实例的数量。

解决方案:
在请求一个服务时,客户端会首先查询Service Registry来获取一个服务实例的位置。
服务寄存器(Service Registry)是存放所有服务的地方。
首先服务会注册到Service Registry,包括主机和端口信息。
客户端服务 Registryaware HTTP客户端首先会 查询它,然后通过负载均衡去调用具体服务。

实例:使用Spring Boot和Spring Cloud作为微服务应用底盘,使用Scala语言开发
这里RegistrationServiceProxy是应用程序的一个组件,为了注册一个用户我们使用SpringFramework的RestTemplate
来调用其它服务。

@Component
 class RegistrationServiceProxy @Autowired()(restTemplate: RestTemplate) extends RegistrationService{
     @Value("${user_registration_url")
     var userRegistration_url=_
     
     override def registerUser(emailAddress:String, password:String):Either[RegistrationError,String]={
         val response = restTemplate.postForEntity(userRegistrationUrl,RegistrationBackendRequest(emailAddress,password),classOf[RegistrationBackendResponse])
         ...
 }

在该类里我们注入了RestTemplate和指定REST 终端的user_registration_url
当应用程序部署后,user_registration_url被设置到URL http://REGISTRATION-SERVICE/user
查看docker-compose.yml文件。
REGISTRATION-SERVICE是本地服务名,它会被客户端发现解析成一个网络位置。
服务发现使用Netflix OSS组件实现,该组件提供Eureka和Ribbon,Eureka是一个Service Registry。
Ribbon是一个HTTP客户端用于查询Eureka以将请求路由到可用的服务实例上。

使用各种Spring Cloud声明来配置客户端服务发现:

@Configuration
 @EnableEurekaClient
 @Profile(Array("enableEurable"))
 class EurekaClientConfiguration{
     @Bean
     @LoadBalanced
     def restTemplate(scalaObjectMapper:ScalaObjectMapper):RestTemplate = {
         var restTemplate = new RestTemplate()
         restTemplate.getMessageConverters foreach{
             case mc: MappingJackson2HttpMessageConverter =>
                 mc.setObjectMapper(scalaObjectMapper)
             case _=>
         }
         restTemplate
     }
 }

@EnableEurekaClient 指定Eureka客户端
@LoadBalanced 配置RestTemplate使用Ribbon,我们需要为Ribbon配置使用Eureka客户端来执行服务发现。
结果就是RestTemplate将通过查询Eureka来找到可用的服务实例的网络位置,然后用于处理对http://REGISTRATION-SERVICE/user端点的请求。

占用更少的网络流量
该模式会使客户端和Service Registry产生耦合
我们需要为每种编程语言或者框架都实现客户端服务发现逻辑,比如Java/Scala,JavaScript/NodeJS等
Netflix Prana(https://github.com/Netflix/Prana)为非JVM客户端提供了一个基于HTTP 代理发现服务。

关联模式:
Service Registry--服务注册表,是服务发现的一个组成部分
Microservice chassis--客户端发现是微服务底架的主要功能
Server Side Discovery--是解决同一个问题的另一方案
==========================================================
Server-client service discovery
当我们请求一个服务时,客户端会发送一个请求给我们众所周知的一个路由地址,路由器会查询服务注册表,这个服务注册表有可能是嵌入在路由器内部的。
然后将我们的请求路由到可用的服务实例上进行处理。

实例:
AWS Elastic Load Balancer(ELB)就是一个服务端服务发现的典型例子。当客户端发送HTTP请求(或者打开TCP链接)到ELB时,它会在一堆的EC2实例中间平衡载入。
ELB可以平衡载入来自互联网的外部接入,当我们部署到VPC时,还能平衡载入内部通信量。 ELB还充当了服务注册表的作用。
EC2实例注册到ELB后,既可以通过显式API调用,也可以作为自动扩容的一部分。

一些集群方案比如Kubernetes和Marathon都是在每个节点主机上运行一个代理程序来负责服务端发现路由。
为了访问一个服务,客户端首先使用配置给该服务的端口连接其本地代理,代理将请求发送给位于集群中某处节点上的服务实例。

跟Client-side discovery机制相比,客户端发现机制相对比较简单,因为它不需要处理发现。 一些云环境都实现这些服务发现功能。
除了作为云环境的一部分,否则路由器是必须另外安装配置的一个系统组件,它也需要根据需要复制扩容。
路由器必须支持必须的通讯协议比如HTTP,gRPC,Thrift等,当然基于TCP的路由除外。

===============================================================

Service Registry 服务注册表
一个服务的客户端可以使用客户端发现也可以使用服务端发现来断定要调用的服务实例的位置信息。

我们实现一个服务注册表,它其实是一个服务的数据库,保存服务的实例和位置。
服务实例为在启动时注册到该数据库,并在关闭时注销。
客户端服务发现 或者 服务端路由发现都会查询该服务注册表来查找可用的服务实例。
服务注册表会调用每个服务实例的健康检查API(http://microservices.io/patterns/observability/health-check-api.html)
来检验服务能否被用来处理请求。

实例:
Eureka Server 是一个简单的Spring Boot应用程序

@SpringBootApplication
 @EnableEurekaServer
 public class EurekaServer{
     public static void main(String[] args){
         new SpringApplicationBuilder(EurekaServer.class).web(true).run(args);
     }
 }

它使用Docker进行部署:

eureka:
  image: java:openjdk-8u91-jdk
  working_dir: /app
  volumes:
     - ./eureka-server/build/libs:/app
  command: java -jar /app/eureka-server.jar --server.port=8761
  ports:
     - "8761:8761"

其它实现了服务注册表的例子包括
Apache Zookeeper
Consul(https://www.consul.io/)
Etcd(https://github.com/coreos/etcd)
另外像Kubernetes,Marathon和AWS ELB都隐式的实现了服务注册表模式。

除非服务注册表被基础架构实现,它是一个重要的基础组件。尽管客户端需要缓存有服务注册表提供的数据,
如果服务注册表失败了,数据最终就将过期,总之,服务注册表必须具备高可用性。

我们需要考虑如何在服务注册表中注册服务。这里有两个选项
Self Registration pattern--服务实体注册它们自己
3rd party registration pattern -- 第三方注册服务实例

注册服务的服务客户端需要知道服务注册表实例的位置,服务注册表必须部署到固定的服务器拥有众所周知的IP。
服务客户端需要配置这一地址。

Netflix Eureka服务实例通常使用弹性IP地址部署,我们在服务端配置是使用属性文件或者DNS来指定弹性IP地址。
当Eureka实例启动时,它会询问配置来觉得哪个弹性地址可用。
Eureka 客户端也需要配置 弹性地址池。

关联模式:
Client-side discovery 和Server-side discovery 的创建都需要服务注册表
Self registration 和 3rd party registration是服务注册表两种不同的服务实例注册方式
Health Check API 服务注册表调用它的一个实例来检查服务是否可以用来处理请求。

==================================================================

Log aggregation 日志集成
微服务架构中,应用程序有多个服务和服务实例构成,它们运行在多台服务器上,请求通常活跨越多个服务。
每个服务实例都会生成关于它所有操作的日志到一个标准格式的日志文件中。这些日志中包含错误,警告,提示和调试信息等。
如何理解应用程序的行为以及排除问题呢?
使用收集日志服务从各个服务实例上收集日志,用户可以查询和分析这些日志。还可以配置当特定的消息在日志中出现时出发警报。

比如 AWS Cloud Watch

关联模式:
Distributed tracing -- 在日志消息里包含外部请求的id
Exception tracking -- 也就是日志异常,将它们报告给一个异常追踪服务。

=======================================================
Self Registration
无论我们采用Client-side Service Discovery 还是Server-side Service Discovery模式,我们都必须在启动时将服务实体注册到Service registry,
让它们能够找到并在关闭时撤销。
那么服务实体是如何注册到服务注册表又是如何从其上面注销的呢?
服务实体必须在启动时注册到服务注册表,在关闭时从服务注册表注销。
服务实体崩溃时必须从服务注册表中注销。
服务实体依然在运行但已无法提供请求处理必须从服务注册表注销

解决方案:
服务实例自己负责将自己注册到服务注册表。在服务实体启动时,将自己的主机IP和端口信息注册到服务注册表。服务实体的必须定期的重新注册自己
让服务注册表知道自己还活着。在服务实体关闭时,需要将自己从服务注册表中注销。

实例:使用SpringBoot和SpringCloud作为Microservice Chassis,用Scala语言编写,该应用程序使用Eureka Service Registry,它是Netflix OSS的一个组件。

在Java配置类上使用@EnableEurekaClient 来让服务实体注册到Eureka

@Configuration
 @EnableEurekaClient
 class EurekaClientConfiguration {}
 ============================================================
 Health Check API


我们在使用微服务架构时,有时候一个服务实体已经没有处理请求的能力了但是还依然在运行,比如它可能连接数据库超时,当这样的事情发生时,监控系统应该
生成一个警报,载入平衡或者服务注册表不应该再路由请求给它。
那么我们如何判断一个服务实例是否可以处理请求呢?
当服务实体失败时应该发出警报
所有的请求都应该发送给可用的服务实例。
解决方案:
服务有一个健康监测API终端,可以返回服务的健康状况给客户端。
服务实例跟基础服务的连接状态
主机的运行状态,比如硬盘空间等
应用程序的特定逻辑是否正常

监控服务,服务注册表或者负载平衡会周期性的调用这些健康监测内容来检查服务实例的健康状况。

实例:该实例的终端由SpringBoot Actuator模块实现,它配置了/health HTTP端点调用扩展健康检查逻辑。
首先添加对actuator的依赖

dependencies {
   compile "org.springframework.boot:spring-boot-starter-actuator"
 }


然后,启动Spring Boot 自动配置

@SpringBootApplication
 class UserRegistrationConfiguration{}


至此,我们应用程序已经具备了默认行为的健康监测终端
我们可以通过定义一个或者多个实现了HealthIndicator接口的 Spring beans来自定义监测行为

class UserRegistrationConfiguration{
     @Bean
     def discoveryHealthIndicator(discoveryClient: EurekaClient) : HealthIndicator = new DiscoveryHealthIndicator(discoveryClient)
 }


HealthIndicator 接口定义了一个必须实现的方法health(),该方法返回一个Health值。

服务注册表会调用健康监测终端。
==================================================================
Exception Tracking
在微服务架构中,运行在多个机器上的服务实例在发生错误时,会抛出异常,异常为包含错误消息和追踪堆栈信息
如何理解应用程序并排除异常?
异常信息必须是非重复的,被记录,开发者可以检查以及底层问题解析。
任何方案都必须有最小的运行负载。

解决方案:
将所有的异常都上报给指定的集中异常追踪服务,由该服务来汇总并追踪异常然后上报给开发者。

一般采用日志汇总的方案来实现异常汇总。
================================================================
Distributed Tracing
分布式追踪

在微服务架构中,请求通常会跨多个服务,每个服务通过执行一个或多个操作来处理请求,比如数据库查询,发布消息等。
那么如何理解应用程序的行为并排除问题呢?
外部监控只能告诉我们整体的回复时间和调用次数,不能看到每个操作的情况
解决方案:服务仪器代码
为每一个外部请求赋予一个外部请求ID
将外部请求ID传递给所有服务包括处理该请求的服务
将外部请求的ID包含到所有相关日志信息里
记录关于请求的信息(比如开始时间,结束时间)和操作执行。

实例:
我们使用Spring Cloud Sleuth(https://cloud.spring.io/spring-cloud-sleuth/)
该组件提供分布式追踪支持。它测量Spring组件来收集追踪信息并提交给一个Zipkin Server,该服务负责收集和展示追踪内容。

添加相关依赖:

dependencies {
   compile "org.springframework.cloud:spring-cloud-sleuth-stream"
   compile "org.springframework.cloud:spring-cloud-starter-sleuth"
   compile "org.springframework.cloud:spring-cloud-stream-binder-rabbit"
 }


RabbitMQ用于交付追踪信息到Zipkin
在docker-compose.yml文件中配置Spring Cloud Sleuth相关的环境变量

environment:
     SPRING_RABBITMQ_HOST: rabbitmq
     SPRING_SLEUTH_ENABLED: "true"
     SPRING_SLEUTH_SAMPLER_PERCENTAGE: 1
     SPRING_SLEUTH_WEB_SKIPPATTERN: "/api-docs.*|/autoconfig|/configprops|/dump|/health|/info|/metrics.*|/mappings|/trace|/swagger.*|.*\\.png|.*\\.css|.*\\.js|/favicon.ico|/hystrix.stream"


该属性文件配置启用Spring Cloud Sleuth并配置它来简化所有请求,同时告诉它通过运行在rabbitmq主机上的RabbitMQ中间件分发trace到Zipkin。

Zipkin定义

@SpringBootApplication
 @EnableZipkinStreamServer
 public class ZipkinServer {
   public static void main(String[] args) {
     SpringApplication.run(ZipkinServer.class, args);
   }
 }

Docker部署配置

zipkin:
   image: java:openjdk-8u91-jdk
   working_dir: /app
   volumes:
     - ./zipkin-server/build/libs:/app
   command: java -jar /app/zipkin-server.jar --server.port=9411
   links:
     - rabbitmq
   ports:
     - "9411:9411"
   environment:
     RABBIT_HOST: rabbitmq


    
它能够提供一些有用的深入到系统内部的行为,找到延迟的来源。
让开发这能够看到单个请求是如何被处理。
汇集和存储这些追踪信息需要大量的基础设备。

Open Zipkin 用于记录和显示追踪信息
Open Tracing 分布式追踪的标准API