1、微服务网关概述

在学习完前面的知识后,微服务架构已经初具雏形。但还有一些问题:不同的微服务一般会有不同的网络地址,客户端在访问这些微服务时必须记住几十甚至几百个地址,这对于客户端方来说太复杂也难以维护。如下图:![

如果让客户端直接与各个微服务通讯,可能会有很多问题:

  • 客户端会请求多个不同的服务,需要维护不同的请求地址,增加开发难度。
  • 在某些场景下存在跨域请求的问题
  • 加大身份认证的难度,每个微服务需要独立认证
    因此,我们需要一个微服务网关,介于客户端与服务器之间的中间层,所有的外部请求都会先经过微服务网关。客户端只需要与网关交互,只知道一个网关地址即可,这样简化了开发还有以下优点:
  • 易于监控
  • 易于认证
  • 减少了客户端与各个微服务之间的交互次数

1.1 服务网关的概念

1.1.1 什么是微服务网关

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

1.1.2 作用和应用场景

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

1.1.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服务器的负载均衡及高可用
    问题:自注册的问题和网关本身的扩展性

zuul网关(了解)

1.1介绍

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

  • 动态路由:动态将请求路由到不同后端集群
  • 压力测试:逐渐增加指向集群的流量,以了解性能
  • 负载分配:为每一种负载类型分配对应容量,并弃用超出限定值的请求
  • 静态响应处理:边缘位置进行响应,避免转发到内部集群
  • 身份认证和安全: 识别每一个资源的验证要求,并拒绝那些不符的请求。Spring Cloud对Zuul进行了整合和增强。

1.2 搭建zuul网关服务器

  1. 创建工程导入坐标
        <dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-zuul</artifactId></dependency>
  1. 配置启动类,开启网关服务器功能
//开启Zuul网关功能
@EnableZuulProxy
  1. 配置文件
server:port: 8080 #端口spring:application:name: api-zuul-server

1.3 路由

1.3.1 基础路由配置

根据请求的URL将请求分配给相应的微服务中处理

zuul:routes:product-service: # 给路由起个名字,随便写path: /product-service/** #映射路径url: http://127.0.0.1:9001

1.3.2 面向服务的路由配置

  1. 添加eureka的依赖
<!--引入EurekaClient--><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-client</artifactId></dependency>
  1. 开启eureka的的客户端服务发现
//Eureka的服务发现
@EnableDiscoveryClient
  1. 在zuul网关服务中配置eureka注册中心相关信息
#配置Eureka
eureka:client:service-url:defaultZone: http://localhost:9000/eureka/instance:prefer-ip-address: true #使用ip地址注册
  1. 修改路由映射中的相关配置
# 配置路由信息
zuul:routes:product-service: # 给路由起个名字,随便写path: /product-service/** #映射路径
#      url: http://127.0.0.1:9001serviceId: service-product #从Eureka中获取的服务Id

1.3.3 简化路由配置

zuul:routes:shop-service-product: /product-service/**

1.3.5 路由转发示意图

1.4 过滤器

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

1.4.1 Zuul过滤器简介

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

  • PRE:这种过滤器在请求被路由之前调用。我们可利用这种过滤器实现身份验证、在集群中选择请求的微服务、记录调试信息等。
  • ROUTING:这种过滤器将请求路由到微服务。这种过滤器用于构建发送给微服务的请求,并使用
    Apache HttpClient或Netfilx Ribbon请求微服务。
  • POST:这种过滤器在路由到微服务以后执行。这种过滤器可用来为响应添加标准的HTTPHeader、收集统计信息和指标、将响应从微服务发送给客户端等。
  • ERROR:在其他阶段发生错误时执行该过滤器。
public abstract ZuulFilter implements IZuulFilter{abstract public String filterType();abstract public int filterOrder();boolean shouldFilter();// 来自IZuulFilterObject run() throws ZuulException;// IZuulFilter
}

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

  • shouldFilter :返回一个 Boolean 值,判断该过滤器是否需要执行。返回true执行,返回false不执行。
  • run :过滤器的具体业务逻辑。
  • filterType :返回字符串,代表过滤器的类型。包含以下4种:
  1. pre :请求在被路由之前执行
  2. routing :在路由请求时调用
  3. post :在routing和errror过滤器之后调用
  4. error :处理请求时发生错误调用
  5. filterOrder :通过返回的int值来定义过滤器的执行顺序,数字越小优先级越高。
package com.runze.zool.filter;import com.netflix.zuul.exception.ZuulException;
import org.springframework.stereotype.Component;@Component
public class ZuulFilter extends com.netflix.zuul.ZuulFilter {/***@ClassName:Filter的类型 pre routing post error*@Param*@Author 李润泽*@Return*@Time*/@Overridepublic String filterType() {return "pre";}/***@ClassName: 过滤器执行顺序,返回值越小,执行顺序越高*@Param*@Author 李润泽*@Return*@Time*/@Overridepublic int filterOrder() {return 1;}/***@ClassName: 当前过滤器是否生效*@Param*@Author 李润泽*@Return*@Time*/@Overridepublic boolean shouldFilter() {return true;}/***@ClassName:执行过滤器中的业务逻辑*@Param*@Author 李润泽*@Return*@Time*/@Overridepublic Object run() throws ZuulException {System.out.println("执行了过滤器");return null;}
}

登录验证小案例

        //获取Zuul请求提供的请求上下文对象RequestContext ctx = RequestContext.getCurrentContext();//获取请求HttpServletRequest request = ctx.getRequest();//获取tokenString token = request.getParameter("access-token");if(token == null){//如果token为空,拦截ctx.setSendZuulResponse(false);//返回状态码ctx.setResponseStatusCode(404);}

1.5 内部源码

1.6 zuul的不足

在实际使用中我们会发现直接使用Zuul会存在诸多问题,包括:

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

springcloud gateway网关

1.1 GateWay简介

  1. 简介
    Spring Cloud Gateway 是 Spring 官方基于 Spring 5.0,Spring Boot 2.0 和 Project Reactor 等技术开发的网关,旨在为微服务架构提供一种简单而有效的统一的 API 路由管理方式,统一访问接口。SpringCloud Gateway 作为 Spring Cloud 生态系中的网关,目标是替代 Netflix ZUUL,其不仅提供统一的路由方式,并且基于 Filter 链的方式提供了网关基本的功能,例如:安全,监控/埋点,和限流等。它是基于Nttey的响应式开发模式。
    上表为Spring Cloud Gateway与Zuul的性能对比,从结果可知,Spring Cloud Gateway的RPS是Zuul
    的1.6倍
  2. 核心概念
  • 路由(route) 路由是网关最基础的部分,路由信息由一个ID、一个目的URL、一组断言工厂和一组Filter组成。如果断言为真,则说明请求URL和配置的路由匹配。
  • 断言(predicates) Java8中的断言函数,Spring Cloud Gateway中的断言函数输入类型是 Spring5.0框架中的ServerWebExchange。Spring Cloud Gateway中的断言函数允许开发者去定义匹配来自Http Request中的任何信息,比如请求头和参数等。
  • 过滤器(filter) 一个标准的Spring webFilter,Spring Cloud Gateway中的Filter分为两种类型,分别是Gateway Filter和Global Filter。过滤器Filter可以对请求和响应进行处理。

1.2 路由配置

1.2.1入门案例

  1. 导入坐标(该坐标与web的坐标存在冲突,所以在父类项目中不要导入web坐标,哪个子类需要导入到哪里)
        <dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-gateway</artifactId></dependency>
  1. 配置启动类
package com.runze.gateway;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication
public class GatewayApplication {public static void main(String[] args) {SpringApplication.run(GatewayApplication.class,args);}
}
  1. 配置文件
server:port: 8080 #端口spring:application:name: api-gateway-server# 配置SpringCloud gateway的路由cloud:gateway:routes:# 配置路由:路由id,uri,断言(判断条件)- id: product-serviceuri: http://127.0.0.1:9001predicates:- Path=/product/**

1.2.2路由规则

示例

#路由断言之后匹配
spring:cloud:gateway:routes:- id: after_routeuri: https://xxxx.com#路由断言之前匹配predicates:- After=xxxxx
#路由断言之前匹配
spring:cloud:gateway:routes:- id: before_routeuri: https://xxxxxx.compredicates:- Before=xxxxxxx
#路由断言之间
spring:cloud:gateway:routes:- id: between_routeuri: https://xxxx.compredicates:- Between=xxxx,xxxx
#路由断言Cookie匹配,此predicate匹配给定名称(chocolate)和正则表达式(ch.p)
spring:cloud:gateway:routes:- id: cookie_routeuri: https://xxxx.compredicates:- Cookie=chocolate, ch.p
#路由断言Header匹配,header名称匹配X-Request-Id,且正则表达式匹配\d+
spring:cloud:gateway:routes:- id: header_routeuri: https://xxxx.compredicates:- Header=X-Request-Id, \d+
#路由断言匹配Host匹配,匹配下面Host主机列表,**代表可变参数
spring:cloud:gateway:routes:- id: host_routeuri: https://xxxx.compredicates:- Host=**.somehost.org,**.anotherhost.org
#路由断言Method匹配,匹配的是请求的HTTP方法
spring:cloud:gateway:routes:- id: method_routeuri: https://xxxx.compredicates:- Method=GET
#路由断言匹配,{segment}为可变参数
spring:cloud:gateway:routes:- id: host_routeuri: https://xxxx.compredicates:- Path=/foo/{segment},/bar/{segment}
#路由断言Query匹配,将请求的参数param(baz)进行匹配,也可以进行regexp正则表达式匹配 (参数包含
foo,并且foo的值匹配ba.)
spring:cloud:gateway:routes:- id: query_routeuri: https://xxxx.compredicates:- 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_routeuri: https://example.orgpredicates:- RemoteAddr=192.168.1.1/24

1.2.3 动态路由(**)

  1. 引入坐标
        <dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-server</artifactId></dependency>
  1. 引入eureka的服务发现
@EnableDiscoveryClient
  1. 修改配置文件
# 配置SpringCloud gateway的路由cloud:gateway:routes:# 配置路由:路由id,uri,断言(判断条件)- id: product-service
#        uri: http://127.0.0.1:9001uri: lb://service-product # lb://表示根据微服务名称从注册中心拉去服务请求路径predicates:- Path=/product/**# 配置Eureka
eureka:client:service-url:defaultZone: http://localhost:9000/eureka/instance:prefer-ip-address: true #使用ip地址注册

1.2.4 开启微服务名称转发规则

server:port: 8080 #端口spring:application:name: api-gateway-server# 配置SpringCloud gateway的路由cloud:gateway:routes:# 配置路由:路由id,uri,断言(判断条件)- id: product-service
#        uri: http://127.0.0.1:9001uri: lb://service-product # lb://表示根据微服务名称从注册中心拉去服务请求路径predicates:- Path=/product-service/**# 配置自动的根据微服务名称进行转发discovery:locator:enabled: true #开启根据服务名称自动转发lower-case-service-id: true # 微服务名称以小写方式呈现#配置Eureka
eureka:client:service-url:defaultZone: http://localhost:9000/eureka/instance:prefer-ip-address: true #使用ip地址注册

1.3 过滤器

Spring Cloud Gateway除了具备请求路由功能之外,也支持对请求的过滤。通过Zuul网关类似,也是通过过滤器的形式来实现的。那么接下来我们一起来研究一下Gateway中的过滤器

1.3.4 过滤器基础

  1. 过滤器的生命周期
    Spring Cloud Gateway 的 Filter 的生命周期不像 Zuul 的那么丰富,它只有两个:“pre” 和 “post”。
  • PRE: 这种过滤器在请求被路由之前调用。我们可利用这种过滤器实现身份验证、在集群中选择请求的微服务、记录调试信息等。
  • POST:这种过滤器在路由到微服务以后执行。这种过滤器可用来为响应添加标准的 HTTP Header、收集统计信息和指标、将响应从微服务发送给客户端等。
  1. 过滤器类型

Spring Cloud Gateway 的 Filter 从作用范围可分为另外两种GatewayFilter 与 GlobalFilter。

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

1.3.5 局部过滤器

局部过滤器(GatewayFilter),是针对单个路由的过滤器。可以对访问的URL过滤,进行切面处理。在Spring Cloud Gateway中通过GatewayFilter的形式内置了很多不同类型的局部过滤器。这里简单将Spring Cloud Gateway内置的所有过滤器工厂整理成了一张表格,虽然不是很详细,但能作为速览使用。不重要,略过

1.3.6 全局过滤器

全局过滤器(GlobalFilter)作用于所有路由,Spring Cloud Gateway 定义了Global Filter接口,用户可以自定义实现自己的Global Filter。通过全局过滤器可以实现对权限的统一校验,安全性验证等功能,并且全局过滤器也是程序员使用比较多的过滤器

package com.runze.gateway.filter;import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;@Component
public class LoginFilter implements GlobalFilter,Ordered {/***@ClassName: 执行过滤器的业务逻辑*@Param*@Author 李润泽*@Return*@Time*/@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {System.out.println("执行自定义的全局过滤器");return chain.filter(exchange); //继续向下执行}/***@ClassName:过滤器的执行顺序,数字越小,优先级越高*@Param*@Author 李润泽*@Return*@Time*/@Overridepublic int getOrder() {return 0;}
}

1.4 统一鉴权

一个token案例(略)

1.5 网关限流

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

  2. 漏桶算法
    漏桶算法可以很好地限制容量池的大小,从而防止流量暴增。漏桶可以看作是一个带有常量服务时间的单服务器队列,如果漏桶(包缓存)溢出,那么数据包会被丢弃。 在网络中,漏桶算法可以控制端口的流量输出速率,平滑网络上的突发流量,实现流量整形,从而为网络提供一个稳定的流量。
    为了更好的控制流量,漏桶算法需要通过两个变量进行控制:一个是桶的大小,支持流量突发增多时可以存多少的水(burst),另一个是水桶漏洞的大小(rate)。

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

1.5.1 基于Filter的限流

  1. 引入依赖
        <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-actuator</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis-reactive</artifactId></dependency>
  1. 配置信息
server:port: 8080 #端口
spring:application:name: api-gateway-server #服务名称redis:host: localhostpool: 6379database: 0cloud: #配置SpringCloudGateway的路由gateway:routes:- id: product-serviceuri: lb://service-productpredicates:- Path=/product-service/**filters:- name: RequestRateLimiterargs:# 使用SpEL从容器中获取对象key-resolver: '#{@pathKeyResolver}'# 令牌桶每秒填充平均速率redis-rate-limiter.replenishRate: 1# 令牌桶的上限redis-rate-limiter.burstCapacity: 3- RewritePath=/product-service/(?<segment>.*), /$\{segment}
#eureka注册中心
eureka:client:service-url:defaultZone: http://localhost:9000/eureka/instance:prefer-ip-address: true #使用ip地址注册
  1. 编写代码
package com.runze.gateway;import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;@Configuration
public class KeyResolverConfigutration {/*** 编写基于路径的请求规则* 基于请求ip的*/@Beanpublic KeyResolver pathKeyResolver(){return new KeyResolver() {/*** serverWebExchange:上下文参数* @param exchange* @return*/@Overridepublic Mono<String> resolve(ServerWebExchange exchange) {return Mono.just(exchange.getRequest().getPath().toString());}};}}

1.5.2 基于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 分组维度进行限流。
  1. 导入依赖
<dependency><groupId>com.alibaba.csp</groupId><artifactId>sentinel-spring-cloud-gateway-adapter</artifactId><version>x.y.z</version>
</dependency>
  1. 编写配置类
package cn.itcast.gateway;import com.alibaba.csp.sentinel.adapter.gateway.common.SentinelGatewayConstants;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiDefinition;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiPathPredicateItem;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiPredicateItem;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.GatewayApiDefinitionManager;
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayFlowRule;
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayParamFlowItem;
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayRuleManager;
import com.alibaba.csp.sentinel.adapter.gateway.sc.SentinelGatewayFilter;
import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.BlockRequestHandler;
import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.GatewayCallbackManager;
import com.alibaba.csp.sentinel.adapter.gateway.sc.exception.SentinelGatewayBlockExceptionHandler;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.codec.ServerCodecConfigurer;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.server.ServerResponse;
import org.springframework.web.reactive.result.view.ViewResolver;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;import javax.annotation.PostConstruct;
import java.nio.charset.StandardCharsets;
import java.util.*;/*** sentinel限流的配置*/
//@Configuration
public class GatewayConfiguration {private final List<ViewResolver> viewResolvers;private final ServerCodecConfigurer serverCodecConfigurer;public GatewayConfiguration(ObjectProvider<List<ViewResolver>> viewResolversProvider,ServerCodecConfigurer serverCodecConfigurer) {this.viewResolvers = viewResolversProvider.getIfAvailable(Collections::emptyList);this.serverCodecConfigurer = serverCodecConfigurer;}/*** 配置限流的异常处理器:SentinelGatewayBlockExceptionHandler*/@Bean@Order(Ordered.HIGHEST_PRECEDENCE)public SentinelGatewayBlockExceptionHandler sentinelGatewayBlockExceptionHandler() {return new SentinelGatewayBlockExceptionHandler(viewResolvers, serverCodecConfigurer);}/*** 配置限流过滤器*/@Bean@Order(Ordered.HIGHEST_PRECEDENCE)public GlobalFilter sentinelGatewayFilter() {return new SentinelGatewayFilter();}/*** 配置初始化的限流参数*  用于指定资源的限流规则.*      1.资源名称 (路由id)*      2.配置统计时间*      3.配置限流阈值*/@PostConstructpublic void initGatewayRules() {Set<GatewayFlowRule> rules = new HashSet<>();
//      rules.add(new GatewayFlowRule("product-service")
//              .setCount(1)
//              .setIntervalSec(1)
//      );rules.add(new GatewayFlowRule("product_api").setCount(1).setIntervalSec(1));GatewayRuleManager.loadRules(rules);}/*** 自定义API限流分组*      1.定义分组*      2.对小组配置限流规则*/@PostConstructprivate void initCustomizedApis() {Set<ApiDefinition> definitions = new HashSet<>();ApiDefinition api1 = new ApiDefinition("product_api").setPredicateItems(new HashSet<ApiPredicateItem>() {{add(new ApiPathPredicateItem().setPattern("/product-service/product/**"). //已/product-service/product/开都的所有urlsetMatchStrategy(SentinelGatewayConstants.URL_MATCH_STRATEGY_PREFIX));}});ApiDefinition api2 = new ApiDefinition("order_api").setPredicateItems(new HashSet<ApiPredicateItem>() {{add(new ApiPathPredicateItem().setPattern("/order-service/order")); //完全匹配/order-service/order 的url}});definitions.add(api1);definitions.add(api2);GatewayApiDefinitionManager.loadApiDefinitions(definitions);}/*** 自定义限流处理器*/@PostConstructpublic void initBlockHandlers() {BlockRequestHandler blockHandler = new BlockRequestHandler() {public Mono<ServerResponse> handleRequest(ServerWebExchange serverWebExchange, Throwable throwable) {Map map = new HashMap();map.put("code",001);map.put("message","不好意思,限流啦");return ServerResponse.status(HttpStatus.OK).contentType(MediaType.APPLICATION_JSON_UTF8).body(BodyInserters.fromObject(map));}};GatewayCallbackManager.setBlockHandler(blockHandler);}}

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

分布式链路追踪

分布式链路追踪(Distributed Tracing),就是将一次分布式请求还原成调用链路,进行日志记录,性能监控并将 一次分布式请求的调用情况集中展示。比如各个服务节点上的耗时、请求具体到达哪台机器上、每个服务节点的请求状态等等。
目前业界比较流行的链路追踪系统如:Twitter的Zipkin,阿里的鹰眼,美团的Mtrace,大众点评的cat等,大部分都是基于google发表的Dapper。Dapper阐述了分布式系统,特别是微服务架构中链路追踪的概念、数据表示、埋点、传递、收集、存储与展示等技术细节。

1、Sleuth

1.1 Sleuth入门

  1. 导入依赖
        <dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-sleuth</artifactId></dependency>
  1. 配置文件
logging:level:root: INFOorg.springframework.web.servlet.DispatcherServlet: DEBUGorg.springframework.cloud.sleuth: DEBUG

2.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 客户端

SpringCloud03相关推荐

  1. SpringCloud03 Ribbon知识点、 Feign知识点、利用RestTemplate+Ribbon调用远程服务提供的资源、利用feign调用远程服务提供的资源、熔断...

    1 远程服务资源的调用 1.1 古老的套路 在微服务出现之前,所有的远程服务资源必须通过RestTemplate或者HttpClient进行:但是这两者仅仅实现了远程服务资源的调用,并未提供负载均衡实 ...

  2. SpringCloud-03

    第四天:Git版本控制+配置中心config+STS Git版本控制 版本控制 版本控制(Version Control System) 记录文件的所有历史变化 错误恢复到某个历史版本 多人协作开发编 ...

  3. SpringCloud—07—高级之SpringCloud Alibaba上

    文章目录 提前预知 17.SpringCloud Alibaba入门简介 17.1.为什么会出现SpringCloud alibaba 18.Spring Cloud Alibaba Nacos服务注 ...

  4. 狂神说笔记——SpringCloud快速入门23

    1.前言 学习前提 熟练使用SpringBoot 微服务快速开发框架 了解过Dubbo + Zookeeper 分布式基础 电脑配置内存不低于8G(个人是16G) SpringCloud五大组件 参考 ...

最新文章

  1. 一张图看懂React生命周期
  2. if not exist Oracle,oracle 兑现 if not exist 方法
  3. IOS应用开发版本控制工具之Versions使用,iosversions
  4. php 如何守护进程_PHP 如何实现守护进程
  5. websocket 压力测试_打造最强移动测试平台
  6. winform对话框控件、打印控件
  7. php中的数据库操作类、分页类,以及smarty扩展类
  8. C语言函数一章教学,c语言案例教程:函数教学讲义.ppt
  9. 惠普笔记本被政府盖章存隐藏键盘记录器,怎么回事?
  10. 10.8 SNK中国一面面经
  11. Excel如何将单元格中的多数字提取出来
  12. 关于新闻:西瓜3毛一斤仍滞销 被当垃圾扔掉 问题的一点看法
  13. 最全的蓝桥杯嵌入式备赛集合~
  14. 如何提高用户逃离成本
  15. SpringBoot:Whitelabel Error Page 404
  16. Matlab最小面积包围四边形
  17. SQL中的布尔型变量
  18. 汇编原理自我总结(三)
  19. xctf攻防世界 CRYPTO高手进阶区 告诉你个秘密
  20. shell 遍历文件循环每一行操作

热门文章

  1. AndroidStudio下载第三方插件慢,或者下载不下来
  2. 微软面试题:买啤酒问题
  3. 电脑的计算机文件打开格式,ai文件怎么打开,教您电脑打开ai文件的方法
  4. Vue下载本地pdf、word、excel文件
  5. 2016年最新增值税税目税率表
  6. 【BZOJ 2024】 2024: [SHOI2009] 舞会 (容斥原理+高精度)
  7. mllib调参 spark_Spark MLlib协同过滤算法
  8. 给Java程序猿们推荐一个个人觉得超级好的Java学习网站
  9. HHUOJ 1412 计算圆的周长
  10. 无需二次开发 搭建一套专属于自己的幼儿园智慧校园视频监控平台解决方案