微服务设计(四)---微服务网关与链路追踪

一、微服务网关概述

  如下图:

  不同的微服务一般会有不同的网络地址,客户端在访问这些微服务时必须记住几十甚至几百个地址,这对于客户端方来说太复杂也难以维护。如果让客户端直接与各个微服务通讯,可能会有很多问题: 客户端会请求多个不同的服务,需要维护不同的请求地址,增加开发难度,在某些场景下存在跨域请求的问题,加大身份认证的难度,每个微服务需要独立认证。因此,需要一个微服务网关,介于客户端与服务器之间的中间层,所有的外部请求都会先经过微服务网关。客户端只需要与网关交互,只知道一个网关地址即可,这样简化了开发,还有以下优点: 1、易于监控 2、易于认证 3、减少了客户端与各个微服务之间的交互次数。

1、服务网关的概念

   API网关是一个服务器,是系统对外的唯一入口。API网关封装了系统内部架构,为每个客户端提供 一个定制的API。API网关方式的核心要点是,所有的客户端和消费端都通过统一的网关接入微服务,在网关层处理所有的非业务功能。通常,网关也是提供REST/HTTP的访问API。服务端通过API-GW注册和管理服务。

2、作用和应用场景

  网关具有的职责,如身份验证、监控、负载均衡、缓存、请求分片与管理、静态响应处理。当然,最主要的职责还是与“外界联系”。

3、常见的API网关实现方式

  Kong

  基于Nginx+Lua开发,性能高,稳定,有多个可用的插件(限流、鉴权等等)可以开箱即用。 问题:只支持Http协议;二次开发,自由扩展困难;提供管理API,缺乏更易用的管控、配置方式。

  Zuul

  Netflix开源,功能丰富,使用JAVA开发,易于二次开发;需要运行在web容器中,如Tomcat。 问题:缺乏管控,无法动态配置;依赖组件较多;处理Http请求依赖的是Web容器,性能不如 Nginx;

  Traefik

  Go语言开发;轻量易用;提供大多数的功能:服务路由,负载均衡等等;提供WebUI 问题:二进制文件部署,二次开发难度大;UI更多的是监控,缺乏配置、管理能力;

  Spring Cloud Gateway

  SpringCloud提供的网关服务 Nginx+lua实现,使用Nginx的反向代理和负载均衡可实现对api服务器的负载均衡及高可用问题:自注册的问题和网关本身的扩展性。

4、基于Nginx的网关实现

(1) Nginx介绍

(2) Nginx反向代理

  多个客户端给服务器发送的请求,Nginx服务器接收到之后,按照一定的规则分发给了后端的业务处理服务器进行处理了。此时~请求的来源也就是客户端是明确的,但是请求具体由哪台服务器处理的并不明确了,Nginx扮演的就是一个反向代理角色。客户端是无感知代理的存在的,反向代理对外都是透明的,访问者并不知道自己访问的是一个代理。因为客户端不需要任何配置就可以访问。反向代理,"它代理的是服务端,代服务端接收请求",主要用于服务器集群分布式部署的情况下,反向代理隐藏了服务器的信息,如果只是单纯的需要一个最基础的具备转发功能的网关,那么使用Ngnix是一个不错的选择。

5、 微服务网关Zuul

(1)Zuul简介

  Zuul是Netflix开源的微服务网关,它可以和Eureka、Ribbon、Hystrix等组件配合使用,Zuul组件的核心是一系列的过滤器,这些过滤器可以完成以下功能:

  动态路由:动态将请求路由到不同后端

  集群压力测试:逐渐增加指向集群的流量,以了解性能

  负载分配:为每一种负载类型分配对应容量,并弃用超出限定值的请求

  静态响应处理:边缘位置进行响应,避免转发到内部集群

  身份认证和安全: 识别每一个资源的验证要求,并拒绝那些不符的请求。

Spring Cloud对Zuul进行了整合和增强。 

(2)搭建Zuul网关服务器

   1)创建工程导入依赖

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
<version>2.1.0.RELEASE</version>
</dependency>

  2)编写启动类

@SpringBootApplication
@EnableZuulProxy // 开启Zuul的网关功能
public class ZuulServerApplication {
public static void main(String[] args) {
SpringApplication.run(ZuulServerApplication.class, args);
}
}

@EnableZuulProxy : 通过 @EnableZuulProxy 注解开启Zuul网管功能

  3)编写配置

创建配置文件 application.yml ,并添加相应配置

server:
port: 8080 #服务端口
spring:
application:
name: api-gateway #指定服务名

(3)Zuul中的路由转发

  “路由”是指根据请求URL,将请求分配到对应的处理程序。在微服务体系中,Zuul负责接收所有的请求。根据不同的URL匹配规则,将不同的请求转发到不同的微服务处理。

zuul:
routes:
product-service: # 这里是路由id,随意写
path: /product-service/** # 这里是映射路径
url: http://127.0.0.1:9002 # 映射路径对应的实际url地址
sensitiveHeaders: #默认zuul会屏蔽cookie,cookie不会传到下游服务,这里设置为空则取
消默认的黑名单,如果设置了具体的头信息则不会传到下游服务

  只需要在application.yml文件中配置路由规则即可:

  product-service:配置路由id,可以随意取名 u

  rl:映射路径对应的实际url地址

  path:配置映射路径,这里将所有请求前缀为/product-service/的请求,转发到http://127.0.0.1: 9002处理 配置好Zuul路由之后启动服务。

(4)面向服务的路由

  微服务一般是由几十、上百个服务组成,对于一个URL请求,最终会确认一个服务实例进行处理。如果对每个服务实例手动指定一个唯一访问地址,然后根据URL去手动实现请求匹配,这样做显然就不合理。 Zuul支持与Eureka整合开发,根据ServiceID自动的从注册中心中获取服务地址并转发请求,这样做的好处不仅可以通过单个端点来访问应用的所有服务,而且在添加或移除服务实例的时候不用修改Zuul的 路由配置。

  1)添加Eureka客户端依赖

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

  2)开启Eureka客户端发现功能

@SpringBootApplication
@EnableZuulProxy // 开启Zuul的网关功能
@EnableDiscoveryClient
public class ZuulServerApplication {
public static void main(String[] args) {
SpringApplication.run(ZuulServerApplication.class, args);
}
}

  3)添加Eureka配置,获取服务信息

eureka:
client:
serviceUrl:
defaultZone: http://127.0.0.1:8761/eureka/
registry-fetch-interval-seconds: 5 # 获取服务列表的周期:5s
instance:
preferIpAddress: true
ip-address: 127.0.0.1

  4)修改映射配置,通过服务名称获取

  因为已经有了Eureka客户端,我们可以从Eureka获取服务的地址信息,因此映射时无需指定IP地址,而是通过服务名称来访问,而且Zuul已经集成了Ribbon的负载均衡功能。

#配置路由规则
zuul:
routes:
product-service: # 这里是路由id,随意写
path: /product-service/** # 这里是映射路径
serviceId: shop-service-product #配置转发的微服务名称

serviceId: 指定需要转发的微服务实例名称

(5)Zuul加入后的架构

(6)Zuul中的过滤器

  Zuul它包含了两个核心功能:对请求的路由和过滤。其中路由功能负责将外部请求转发到具体的微服务实例上,是实现外部访问统一入口的基础;而过滤器功能则负责对请求的处理过程进行干预,是实现请求校验、服务聚合等功能的基础。其实,路由功能在真正运行时,它的路由映射和请求转发同样也由几个不同的过滤器完成的。所以,过滤器可以说是Zuul实现API网关功能最为核心的部件,每一个进入Zuul的HTTP请求都会经过一系列的过滤器处理链得到请求响应并返回给客户端。

 1)ZuulFilter简介

  Zuul 中的过滤器跟我们之前使用的 javax.servlet.Filter 不一样,javax.servlet.Filter 只有一种类型,可以通过配置 urlPatterns 来拦截对应的请求。而 Zuul 中的过滤器总共有 4 种类型,且每种类型都有对 应的使用场景。

  1. PRE:这种过滤器在请求被路由之前调用。我们可利用这种过滤器实现身份验证、在集群中选择请 求的微服务、记录调试信息等。

  2. ROUTING:这种过滤器将请求路由到微服务。这种过滤器用于构建发送给微服务的请求,并使用 Apache HttpClient或Netfilx Ribbon请求微服务。

  3. POST:这种过滤器在路由到微服务以后执行。这种过滤器可用来为响应添加标准的HTTP Header、收集统计信息和指标、将响应从微服务发送给客户端等。

  4. ERROR:在其他阶段发生错误时执行该过滤器。

  Zuul提供了自定义过滤器的功能实现起来也十分简单,只需要编写一个类去实现zuul提供的接口

public abstract ZuulFilter implements IZuulFilter{
abstract public String filterType();
abstract public int filterOrder();
boolean shouldFilter();// 来自IZuulFilter
Object run() throws ZuulException;// IZuulFilter
}

  ZuulFilter是过滤器的顶级父类。在这里我们看一下其中定义的4个最重要的方法

  shouldFilter :返回一个 Boolean 值,判断该过滤器是否需要执行。返回true执行,返回false 不执行。

  run :过滤器的具体业务逻辑。

  filterType :返回字符串,代表过滤器的类型。包含以下4种:

    pre :请求在被路由之前执行

    routing :在路由请求时调用

    post :在routing和errror过滤器之后调用

    error :处理请求时发生错误调用

  filterOrder :通过返回的int值来定义过滤器的执行顺序,数字越小优先级越高。

(7) 生命周期

  正常流程: 请求到达首先会经过pre类型过滤器,而后到达routing类型,进行路由,请求就到达真正的 服务提供者,执行请求,返回结果后,会到达post过滤器。而后返回响应。 异常流程: 整个过程中,pre或者routing过滤器出现异常,都会直接进入error过滤器,再error处理完毕 后,会将请求交给POST过滤器,最后返回给用户。 如果是error过滤器自己出现异常,最终也会进入POST过滤器,而后返回。 如果是POST过滤器出现异常,会跳转到error过滤器,但是与pre和routing不同的时,请求不会再到达POST过滤器了。 不同过滤器的场景: 请求鉴权:一般放在pre类型,如果发现没有访问权限,直接就拦截了 异常处理:一般会在error类型和post类型过滤器中结合来处理。 服务调用时长统计:pre和post结合使用。

(8)Zuul网关存在的问题

  在实际使用中我们会发现直接使用Zuul会存在诸多问题,包括: 性能问题 Zuul1x版本本质上就是一个同步Servlet,采用多线程阻塞模型进行请求转发。简单讲,每来 一个请求,Servlet容器要为该请求分配一个线程专门负责处理这个请求,直到响应返回客户 端这个线程才会被释放返回容器线程池。如果后台服务调用比较耗时,那么这个线程就会被阻塞,阻塞期间线程资源被占用,不能干其它事情。我们知道Servlet容器线程池的大小是有限制的,当前端请求量大,而后台慢服务比较多时,很容易耗尽容器线程池内的线程,造成容器无法接受新的请求。 不支持任何长连接,如websocket

(9) Zuul网关的替换方案

  Zuul2.x版本 SpringCloud Gateway

5 、微服务网关GateWay

  Zuul 1.x 是一个基于阻塞 IO 的 API Gateway 以及 Servlet;直到 2018 年 5 月,Zuul 2.x(基于 Netty,也是非阻塞的,支持长连接)才发布,但 Spring Cloud 暂时还没有整合计划。Spring Cloud Gateway 比 Zuul 1.x 系列的性能和功能整体要好。

(1)Gateway简介

  Spring Cloud Gateway 是 Spring 官方基于 Spring 5.0,Spring Boot 2.0 和 Project Reactor 等技术开发的网关,旨在为微服务架构提供一种简单而有效的统一的 API 路由管理方式,统一访问接口。Spring Cloud Gateway 作为 Spring Cloud 生态系中的网关,目标是替代 Netflix ZUUL,其不仅提供统一的路由方式,并且基于Filter链的方式提供了网关基本的功能,例如:安全,监控/埋点,和限流等。它是基于Nttey的响应式开发模式。

     当客户端向Spring Cloud Gateway发出请求时,如果网关处理程序判断请求与路由规则相匹配,则将请求继续下发到网关Web处理节点,由Web处理节点将其下发到过滤器链。过滤器被虚线划分的原因是过滤器可以在发送代理请求之前或之后执行逻辑。执行所有PRE阶段过滤器逻辑,然后进行代理请求发送到真正的业务处理模块。在发出代理请求之后,执行POST过滤器逻辑对响应结果进行过滤,如图

  

上表为Spring Cloud Gateway与Zuul的性能对比,从结果可知,Spring Cloud Gateway的RPS是Zuul 的1.6倍

(2)核心概念

  1. 路由(route) 路由是网关最基础的部分,路由信息由一个ID、一个目的URL、一组断言工厂和一 组Filter组成。如果断言为真,则说明请求URL和配置的路由匹配。

  2. 断言(predicates) Java8中的断言函数,Spring Cloud Gateway中的断言函数输入类型是 Spring5.0框架中的ServerWebExchange。Spring Cloud Gateway中的断言函数允许开发者去定 义匹配来自Http Request中的任何信息,比如请求头和参数等。

  3. 过滤器(filter) 一个标准的Spring webFilter,Spring Cloud Gateway中的Filter分为两种类型, 分别是Gateway Filter和Global Filter。过滤器Filter可以对请求和响应进行处理。

(3) 入门案例

  1) 创建工程导入依赖

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>

注意SpringCloud Gateway使用的web框架为webflux,和SpringMVC不兼容。引入的限流组件是 hystrix。redis底层不再使用jedis,而是lettuce。

  2) 配置启动类

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

  3) 编写配置文件

创建 application.yml 配置文件

server:
port: 8080 #服务端口
spring:
application:
name: api-gateway #指定服务名
cloud:
gateway:
routes:
- id: product-service
uri: http://127.0.0.1:9002
predicates:
- Path=/product/**

id:我们自定义的路由 ID,保持唯一;

uri:目标服务地址;

predicates:路由条件,Predicate 接受一个输入参数,返回一个布尔值结果,该接口包含多种默认方法来将 Predicate 组合成其他复杂的逻辑(比如:与,或,非);

filters:过滤规则。

(4)路由规则

  Spring Cloud Gateway 的功能很强大,内置了很多 Predicates 功能。在 Spring Cloud Gateway 中 Spring 利用 Predicate 的特性实现了各种路由匹配规则,有通过 Header、请求参数等不同的条件来进行作为条件匹配到对应的路由。

eg:

#路由断言之后匹配
spring:
cloud:
gateway:
routes:
- id: after_route
uri: https://xxxx.com
#路由断言之前匹配
predicates:
- After=xxxxx
#路由断言之前匹配
spring:
cloud:
gateway:
routes:
- id: before_route
uri: https://xxxxxx.com
predicates:
- Before=xxxxxxx
#路由断言之间
spring:
cloud:
gateway:
routes:
- id: between_route
uri: https://xxxx.com
predicates:
- Between=xxxx,xxxx
#路由断言Cookie匹配,此predicate匹配给定名称(chocolate)和正则表达式(ch.p)
spring:
cloud:
gateway:
routes:
- id: cookie_route
uri: https://xxxx.com
predicates:
- Cookie=chocolate, ch.p
#路由断言Header匹配,header名称匹配X-Request-Id,且正则表达式匹配\d+
spring:
cloud:
gateway:
routes:
- id: header_route
uri: https://xxxx.com
predicates:
- Header=X-Request-Id, \d+
#路由断言匹配Host匹配,匹配下面Host主机列表,**代表可变参数
spring:
cloud:
gateway:
routes:
- id: host_route
uri: https://xxxx.com
predicates:
- Host=**.somehost.org,**.anotherhost.org
#路由断言Method匹配,匹配的是请求的HTTP方法
spring:
cloud:
gateway:
routes:
- id: method_route
uri: https://xxxx.com
predicates:
- Method=GET
#路由断言匹配,{segment}为可变参数
spring:
cloud:
gateway:
routes:
- id: host_route
uri: https://xxxx.com
predicates:
- Path=/foo/{segment},/bar/{segment}
#路由断言Query匹配,将请求的参数param(baz)进行匹配,也可以进行regexp正则表达式匹配 (参数包含
foo,并且foo的值匹配ba.)
spring:
cloud:
gateway:
routes:
- id: query_route
uri: https://xxxx.com
predicates:
- Query=baz 或 Query=foo,ba.
#路由断言RemoteAddr匹配,将匹配192.168.1.1~192.168.1.254之间的ip地址,其中24为子网掩码位
数即255.255.255.0
spring:
cloud:
gateway:
routes:
- id: remoteaddr_route
uri: https://example.org
predicates:
- RemoteAddr=192.168.1.1/24

(5)过滤器

  Spring Cloud Gateway除了具备请求路由功能之外,也支持对请求的过滤。通过Zuul网关类似,也是通过过滤器的形式来实现的。

(6)过滤器基础

  1) 过滤器的生命周期

  Spring Cloud Gateway 的 Filter 的生命周期不像 Zuul 的那么丰富,它只有两个:“pre” 和 “post”。

  PRE: 这种过滤器在请求被路由之前调用。我们可利用这种过滤器实现身份验证、在集群中选择请求的微服务、记录调试信息等。

  POST:这种过滤器在路由到微服务以后执行。这种过滤器可用来为响应添加标准的 HTTP Header、收集统计信息和指标、将响应从微服务发送给客户端等。

  2) 过滤器类型 Spring Cloud Gateway 的 Filter 从作用范围可分为另外两种GatewayFilter 与 GlobalFilter。

  GatewayFilter:应用到单个路由或者一个分组的路由上。

  GlobalFilter:应用到所有的路由上。

(7) 局部过滤器

  局部过滤器(GatewayFilter),是针对单个路由的过滤器。可以对访问的URL过滤,进行切面处理。在 Spring Cloud Gateway中通过GatewayFilter的形式内置了很多不同类型的局部过滤器。

  每个过滤器工厂都对应一个实现类,并且这些类的名称必须以GatewayFilterFactory结尾,这是Spring Cloud Gateway的一个约定,例如 AddRequestHeader 对应的实现类为 AddRequestHeaderGatewayFilterFactory 。对于这些过滤器的使用方式可以参考官方文档

(8)全局过滤器

  全局过滤器(GlobalFilter)作用于所有路由,Spring Cloud Gateway 定义了Global Filter接口,用户可以自定义实现自己的Global Filter。通过全局过滤器可以实现对权限的统一校验,安全性验证等功能,并且全局过滤器也是程序员使用比较多的过滤器。 Spring Cloud Gateway内部也是通过一系列的内置全局过滤器对整个路由转发进行处理如下:

(9)统一鉴权

  内置的过滤器已经可以完成大部分的功能,但是对于企业开发的一些业务功能处理,还是需要编写过滤器来实现的。

(10) 鉴权逻辑

  当客户端第一次请求服务时,服务端对用户进行信息认证(登录) 认证通过,将用户信息进行加密形成token,返回给客户端,作为登录凭证以后每次请求,客户端都携带认证的token 服务端对token进行解密,判断是否有效。

如上图,对于验证用户是否已经登录鉴权的过程可以在网关层统一检验。检验的标准就是请求中是否携 带token凭证以及token的正确性。

(11)网关限流

   常见的限流算法

  1) 计数器 计数器限流算法是最简单的一种限流实现方式。其本质是通过维护一个单位时间内的计数器,每次请求 计数器加1,当单位时间内计数器累加到大于设定的阈值,则之后的请求都被拒绝,直到单位时间已经 过去,再将计数器重置为零

  2) 漏桶算法

  漏桶算法可以很好地限制容量池的大小,从而防止流量暴增。漏桶可以看作是一个带有常量服务时间的 单服务器队列,如果漏桶(包缓存)溢出,那么数据包会被丢弃。 在网络中,漏桶算法可以控制端口的 流量输出速率,平滑网络上的突发流量,实现流量整形,从而为网络提供一个稳定的流量。

为了更好的控制流量,漏桶算法需要通过两个变量进行控制:一个是桶的大小,支持流量突发增多时可 以存多少的水(burst),另一个是水桶漏洞的大小(rate)。

  3) 令牌桶算法

  令牌桶算法是对漏桶算法的一种改进,桶算法能够限制请求调用的速率,而令牌桶算法能够在限制调用 的平均速率的同时还允许一定程度的突发调用。在令牌桶算法中,存在一个桶,用来存放固定数量的令 牌。算法中存在一种机制,以一定的速率往桶中放令牌。每次请求调用需要先获取令牌,只有拿到令 牌,才有机会继续执行,否则选择选择等待可用的令牌、或者直接拒绝。放令牌这个动作是持续不断的 进行,如果桶中令牌数达到上限,就丢弃令牌,所以就存在这种情况,桶中一直有大量的可用令牌,这 时进来的请求就可以直接拿到令牌执行,比如设置qps为100,那么限流器初始化完成一秒后,桶中就 已经有100个令牌了,这时服务还没完全启动好,等启动完成对外提供服务时,该限流器可以抵挡瞬时 的100个请求。所以,只有桶中没有令牌时,请求才会进行等待,最后相当于以一定的速率执行。

(12)基于Filter的限流

  SpringCloudGateway官方就提供了基于令牌桶的限流支持。基于其内置的过滤器工厂 RequestRateLimiterGatewayFilterFactory 实现。在过滤器工厂中是通过Redis和lua脚本结合的方式进行流量控制。

(13)基于Sentinel的限流

  Sentinel 支持对 Spring Cloud Gateway、Zuul 等主流的 API Gateway 进行限流。

  从 1.6.0 版本开始,Sentinel 提供了 Spring Cloud Gateway 的适配模块,可以提供两种资源维度的限流:

  route 维度:即在 Spring 配置文件中配置的路由条目,资源名为对应的 routeId 自定义;

  API 维度:用户可以利用 Sentinel 提供的 API 来自定义一些 API 分组 ;

Sentinel 1.6.0 引入了 Sentinel API Gateway Adapter Common 模块,此模块中包含网关限流的规则和自定义API的实体和管理逻辑:

   GatewayFlowRule :网关限流规则,针对 API Gateway 的场景定制的限流规则,可以针对不同 route 或自定义的 API 分组进行限流,支持针对请求中的参数、Header、来源 IP 等进行定制化的限流。

  ApiDefinition :用户自定义的 API 定义分组,可以看做是一些URL匹配的组合。比如可以定义一个API叫 my_api ,请求path模式为 /foo/** 和 /baz/** 的都归到my_api这个API分组下面。限流的时候可以针对这个自定义的API分组维度进行限流。

  基于Sentinel 的Gateway限流是通过其提供的Filter来完成的,使用时只需注入对应的SentinelGatewayFilter实例以及 SentinelGatewayBlockExceptionHandler实例即可。 @PostConstruct定义初始化的加载方法,用于指定资源的限流规则。这里资源的名称为 orderservice ,统计时间是1秒内,限流阈值是1。表示每秒只能访问一个请求。

(14)网关高可用

  高可用HA(High Availability)是分布式系统架构设计中必须考虑的因素之一,它通常是指,通过设计减少系统不能提供服务的时间。我们都知道,单点是系统高可用的大敌,应该尽量在系统设计的过程中避免单点。方法论上,高可用保证的原则是“集群化”,或者叫“冗余”:只有一个单点,挂了服务会受影响;如果有冗余备份,挂了还有其他backup能够顶上。

  实际使用 Spring Cloud Gateway 的方式如上图,不同的客户端使用不同的负载将请求分发到后端 的 Gateway,Gateway 再通过HTTP调用后端服务,最后对外输出。因此为了保证 Gateway 的高可用性,前端可以同时启动多个 Gateway 实例进行负载,在Gateway的前端使用Nginx或者F5进行负载 转发以达到高可用性。

(15) 执行流程分析

  Spring Cloud Gateway 核心处理流程如上图所示,Gateway的客户端向 Spring Cloud Gateway 发 送请求,请求首先被 HttpWebHandlerAdapter 进行提取组装成网关上下文,然后网关的上下文会传递到 DispatcherHandler 。 DispatcherHandler 是所有请求的分发处理器, DispatcherHandler 主要负责分发请求对应的处理器。比如请求分发到对应的 RoutePredicateHandlerMapping (路由断言处 理映射器)。路由断言处理映射器主要作用用于路由查找,以及找到路由后返回对应的 FilterWebHandler 。 FilterWebHandler 主要负责组装Filter链并调用Filter执行一系列的Filter处理, 然后再把请求转到后端对应的代理服务处理,处理完毕之后将Response返回到Gateway客户端。

二、微服务的链路追踪概述

1、微服务架构下的问题

   在大型系统的微服务化构建中,一个系统会被拆分成许多模块。这些模块负责不同的功能,组合成系统,最终可以提供丰富的功能。在这种架构中,一次请求往往需要涉及到多个服务。互联网应用构建在不同的软件模块集上,这些软件模块,有可能是由不同的团队开发、可能使用不同的编程语言来实现、 有可能布在了几千台服务器,横跨多个不同的数据中心,也就意味着这种架构形式也会存在一些问题: 如何快速发现问题? 如何判断故障影响范围? 如何梳理服务依赖以及依赖的合理性? 如何分析链路性能问题以及实时容量规划? 分布式链路追踪(Distributed Tracing),就是将一次分布式请求还原成调用链路,进行日志记录,性能监控并将 一次分布式请求的调用情况集中展示。比如各个服务节点上的耗时、请求具体到达哪台机器 上、每个服务节点的请求状态等等。 目前业界比较流行的链路追踪系统如:Twitter的Zipkin,阿里的鹰眼,美团的Mtrace,大众点评的cat等,大部分都是基于google发表的Dapper。Dapper阐述了分布式系统,特别是微服务架构中链路 追踪的概念、数据表示、埋点、传递、收集、存储与展示等技术细节。

2、 Sleuth概述

     Spring Cloud Sleuth 主要功能就是在分布式系统中提供追踪解决方案,并且兼容支持了 zipkin,只需要在pom文件中引入相应的依赖即可。

(1)相关概念

  Spring Cloud Sleuth 为Spring Cloud提供了分布式根据的解决方案。它大量借用了Google Dapper的设计。

Sleuth中的术语和相关概念:

Span:基本工作单元,例如,在一个新建的span中发送一个RPC等同于发送一个回应请求给 RPC,span通过一个64位ID唯一标识,trace以另一个64位ID表示,span还有其他数据信息,比如摘要、时间戳事件、关键值注释(tags)、span的ID、以及进度ID(通常是IP地址) span在不断的启动和停止,同时记录了时间信息,当你创建了一个span,必须在未来的某个时刻停止它。

Trace:一系列spans组成的一个树状结构,例如,如果正在跑一个分布式大数据工程,可能需要创建一个trace。

Annotation:用来及时记录一个事件的存在,一些核心annotations用来定义一个请求的开始和结束 cs - Client Sent -客户端发起一个请求,这个annotion描述了这个span的开始 sr - Server Received -服务端获得请求并准备开始处理它,如果将其sr减去cs时间戳便可得到网络延迟ss - Server Sent -注解表明请求处理的完成(当请求返回客户端),如果ss减去sr时间戳便可得 到服务端需要的处理请求时间 cr - Client Received -表明span的结束,客户端成功接收到服务端的回复,如果cr减去cs时间戳便可得到客户端从服务端获取回复的所有所需时间。

(2) 链路追踪Sleuth入门

  查看日志文件并不是一个很好的方法,当微服务越来越多日志文件也会越来越多,通过Zipkin可以将日志聚合,并进行可视化展示和全文检索。

(3)Zipkin的概述

  Zipkin 是 Twitter 的一个开源项目,它基于 Google Dapper 实现,它致力于收集服务的定时数据,以解决微服务架构中的延迟问题,包括数据的收集、存储、查找和展现。 可以使用它来收集各个服务 器上请求链路的跟踪数据,并通过它提供的 REST API 接口来辅助我们查询跟踪数据以实现对分布式系 统的监控程序,从而及时地发现系统中出现的延迟升高问题并找出系统性能瓶颈的根源。除了面向开发 的 API 接口之外,它也提供了方便的 UI 组件来帮助我们直观的搜索跟踪信息和分析请求链路明细,比 如:可以查询某段时间内各用户请求的处理时间等。 Zipkin 提供了可插拔数据存储方式:InMemory、MySql、Cassandra 以及 Elasticsearch。

 上图展示了 Zipkin 的基础架构,它主要由 4 个核心组件构成:

Collector:收集器组件,它主要用于处理从外部系统发送过来的跟踪信息,将这些信息转换为 Zipkin 内部处理的 Span 格式,以支持后续的存储、分析、展示等功能。

Storage:存储组件,它主要对处理收集器接收到的跟踪信息,默认会将这些信息存储在内存中, 我们也可以修改此存储策略,通过使用其他存储组件将跟踪信息存储到数据库中。

RESTful API:API 组件,它主要用来提供外部访问接口。比如给客户端展示跟踪信息,或是外接系统访问以实现监控等。

Web UI:UI 组件,基于 API 组件实现的上层应用。通过 UI 组件用户可以方便而有直观地查询和 分析跟踪信息。

Zipkin 分为两端,一个是 Zipkin 服务端,一个是 Zipkin 客户端,客户端也就是微服务的应用。 客户端会配置服务端的 URL 地址,一旦发生服务间的调用的时候,会被配置在微服务里面的 Sleuth 的监听器监听,并生成相应的 Trace 和 Span 信息发送给服务端。 发送的方式主要有两种,一种是 HTTP 报文的方式,还有一种是消息总线的方式如 RabbitMQ。不论哪种方式,我们都需要: 一个 Eureka 服务注册中心,这里我们就用之前的 eureka 项目来当注册中心。 一个 Zipkin 服务端。 多个微服务,这些微服务中配置Zipkin 客户端。

(4)基于消息中间件收集数据

  在默认情况下,Zipkin客户端和Server之间是使用HTTP请求的方式进行通信(即同步的请求方式),在网络波动,Server端异常等情况下可能存在信息收集不及时的问题。Zipkin支持与rabbitMQ整合完成异步消息传输。 加了MQ之后,通信过程如下图所示:

(5)存储跟踪数据

  Zipkin Server默认时间追踪数据信息保存到内存,这种方式不适合生产环境。因为一旦Service关闭重启或者服务崩溃,就会导致历史数据消失。Zipkin支持将追踪数据持久化到mysql数据库或者存储到elasticsearch中。

 

 感谢阅读,借鉴了不少大佬资料,如需转载,请注明出处,谢谢!https://www.cnblogs.com/huyangshu-fs/p/13888664.html

 

posted on 2022-09-25 23:48  ys-fullStack  阅读(1990)  评论(2编辑  收藏  举报