微服务网关

~本人的案例是承接上面的需要朋友可以 访问这里~

微服务网关概述

在学习完前面的知识后,微服务架构已经初具雏形。但还有一些问题:

  • 不同的微服务一般会有不同的网络地址
  • 客户端在访问这些微服务时必须记住几十甚至几百个地址
  • 这对于客户端方来说太复杂也难以维护。
    《微服务网关》
  • 微服务项目开发, 每一个功能都对应一个模块~
    而每个模块又都是一个独立的小型项目工程:具有独立的 ip 端口...
  • 这种情况就会导致:
    1.在某些场景下存在跨域请求的问题
    2.加大身份认证的难度,每个微服务需要独立认证
    3.客户端会请求多个不同的服务,需要维护不同的请求地址,增加开发难度

因此,我们需要一个微服务网关

  • 介于客户端与服务器之间的中间层:所有的外部请求都会先经过微服务网关。
  • 客户端只需要与网关交互,只知道一个网关地址即可

这样简化了开发还有以下优点:

  1. 易于监控
  2. 易于认证
  3. 减少了客户端与各个微服务之间的交互次数
    《微服务网关》

什么是微服务网关

  • API网关是一个服务器, 是系统对外的 唯一入口
      客户端不需要在记录 大量的微服接口只需要记住一个 网关服务就行了, 通过它就可以找到需要的服务接口;
  • API网关方式的核心要点是: 所有的 客户端 和 消费端 都通过统一的网关接入微服务. 在网关层处理所有的非业务功能。
  • 通常,网关也是提供REST/HTTP的访问API。服务端通过API-GW注册和管理服务。

作用和应用场景

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

常见的API网关实现方式

Kong

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

Zuul

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

Traefifik

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

Gateway

  • SpringCloud提供的网关服务!!

微服务网关Zuul

Zuul简介

  • ZUUL是Netflflix开源的微服务网关 它可以和Eureka、Ribbon、Hystrix等组件配合使用 (都是一家的怎么见外?)

Zuul组件的核心是一系列的过滤器,这些过滤器可以完成以下功能:

  • 动态路由:动态将请求路由到不同后端集群
  • 压力测试:逐渐增加指向集群的流量,以了解性能
  • 负载分配:为每一种负载类型分配对应容量,并弃用超出限定值的请求 对于不符要求请求直接丢弃
  • 静态响应处理:为每一种负载类型分配对应容量,并弃用超出限定值的请求 页面的响应在 网关进行展示不会影响到 服务模块~
  • 身份认证和安全:识别每一个资源的验证要求,并拒绝那些不符的请求。

搭建Zuul网关服务器

《微服务网关》

添加一个模块 zuul_server 网关模块

继续 三板斧: 依赖-注解-配置

导入需要依赖:

pom.xml

   <dependencies>
        <!-- Eureka客户端 -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <!-- starter-netflix-zuul:所有zuul的依赖 -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
            <version>2.1.0.RELEASE</version>
        </dependency>
        <!-- 网关的限流配置依赖: -->
        <dependency>
            <groupId>com.marcosbarbero.cloud</groupId>
            <artifactId>spring-cloud-zuul-ratelimit</artifactId>
            <version>1.3.4.RELEASE</version>
        </dependency>
    </dependencies>
  • zuul 网卡, 属于服务
  • 总的来说也还是一个 SpringBoot 微服务项目
  • 而且 它要对 服务模块接口 进行获取管理, 就要去注册中心中获取管理的模块接口;
  • 所有它也是一个 Eureka-client

编写启动类Myzuul

Myzuul.Java

@SpringBootApplication
@EnableZuulProxy        //通过 @EnableZuulProxy 注解开启Zuul网管功能
public class Myzuul { 
    public static void main(String[] args) { 
        SpringApplication.run(Myzuul.class, args);
    }
}
  • @EnableZuulProxy 注解开启Zuul网管功能
  • 因为它是依赖注册中心的:
    底层应该已经开启了支持所有注册中心的客户端注解了…@EnableDiscoveryClient
    个人猜测, 因为并不需要声明 Eureka的客户端注解:@EnableEurekaClient

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

application.yml

server:
  port: 7004 #zuul网关端口
spring:
  application:
    name: zuul-server      #zuul网关微服名~
eureka:
  client:
    service-url:
      defaultZone: http://localhost:7001/eureka/  #指定注册的注册中心~
  instance:
    prefer-ip-address: true #显示浏览器中的状态栏显示ip
#zuul网关配置
zuul:
  routes:
    #定义路由,因为是自定义的所有下面并没有提示~
    myuser-consumer:            #这里是路由id, myuser-consumer是随意写,后面根据 路由做:限流操作!
      path: /userserver/**      #这里是映射路径,指在网关请求时候,需要在原请求下加上前缀进行访问!
      serviceId: user-server    #指定当前路由的——微服务名(到注册中心中找对应的名获取对应的 ip 端口~)
  • path:
    这里是映射路径, 指在网关请求时候,需要在原请求下加上前缀 userserver进行访问!
    当然也可以不设置前缀:直接 /**
  • 设置前缀是为了软件开发中,区分当前是那给模块下的 接口
  • zuul 这里设置的前缀并不会对调用方模块没有任何影响, 只是网关调用需要 +前缀!

测试:

ok,这样就大致完成了!
《微服务网关》
可以看到, 通过网关完成了 服务的调用! 这样只需要给前端网关的 ip 端口即可~

ZUUL 搭建总结:

  • 说实话实操一遍下来, 并不是很难, 关键是理论…有点像负载均衡~负责管理分配:
  • 负责均衡是: 通过算法机制 对多个提供者的均衡调用...减轻服务器压力..
  • 网关:是统一多个微服务 IP 端口 方便前端的访问调用...减轻前端的压力
  • 实现的话
    三板斧原则: 依赖-注解-配置
    导入依赖——主程序启动网关注解:@EnableZuulProxy——-.yml的网关配置:

Zuul 管理的架构

《微服务网关》

  • pc / 移动 通过网关进行请求:
  • Zuul网关去注册中心去拉取 服务 并做出 对应的请求服务
  • (当然, 事先这些服务都注册到注册中心去...)

Zuul中的过滤器

通过之前的学习,我们得知Zuul它包含了两个核心功能:对请求的 路由 和 过滤

动态路由:负责将外部请求转发到具体的微服务实例上 是实现外部访问统一入口的基础;

过滤器:

  • 负责对请求的处理过程进行干预: 是实现请求校验、服务聚合等功能的基础。
    例如:验证用户是否登录校验 未登录直接过滤!

  • 路由功能在真正运行时
    它的路由映射和请求转发同样也由几个不同的过滤器完成的。
    所以,过滤器可以说是Zuul实现API网关功能最为核心的部件

  • 每一个进入Zuul的HTTP请求都会经过一系列的过滤器处理链得到请求响应并返回给客户端。

ZuulFilter 拦截简介:

Zuul 中的过滤器总共有 4 种类型,且每种类型都有对应的使用场景。 (类似于AOP的环绕增强!)

  • PRE
    这种过滤器在请求被路由之前调用。
    我们可利用这种过滤器实现身份验证、在集群中选择请求的微服务、记录调试信息等。
  • ROUTING
    这种过滤器将请求路由到微服务。用于构建发送给微服务的请求(就是在PRE过滤器之后执行)
    使用Apache HttpClient或Netfifilx Ribbon请求微服务。
  • POST
    这种过滤器在路由到微服务以后执行。
    这种过滤器可用来为响应添加标准的HTTP
    Header、收集统计信息和指标、将响应从微服务发送给客户端等。
  • ERROR
    在其他阶段发生错误时执行该过滤器。
  • 不同过滤器的场景:
    请求鉴权: 一般放在pre类型,如果发现没有访问权限,直接就拦截了
    异常处理: 一般会在error类型和post类型过滤器中结合来处理。
    服务调用时长统计: pre和post结合使用。 分布获取当前时间 相差即可!

《微服务网关》

执行流程:

正常流程:

  • 请求到达首先会经过pre类型过滤器
  • 而后到达routing类型,进行路由请求就到达 真正的服务提供者;
  • 执行请求,返回结果后,会到达post过滤器。而后返回响应。

异常流程:

  • 整个过程中,pre或者routing过滤器出现异常,都会直接进入error过滤器
    再error 处理完毕后,会将请求交给POST过滤器,最后返回给用户。
  • 如果是error过滤器自己出现异常, 最终也会进入POST过滤器,而后返回。
  • 如果是POST过滤器出现异常, 会跳转到error过滤器,但是与pre和routing不同的时, 请求不会再到达POST过滤器了。

Zuul 实现拦截器: 验证当前是否Token登录!

《微服务网关》

创建过滤器包 fitter

实现用户登录: 是否存在Token 如果没有则过滤掉请求!
PowerFilter.Java

import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;

//使当前类被Spring容器管理!
@Component
public class PowerFilter extends ZuulFilter {    //继承ZuulFilter抽象类,并实现拦截器方法;

    @Override
    public String filterType() {                //当前拦截器类型;
        return FilterConstants.PRE_TYPE;       //根据常量来确定当前过滤器类型 pre 路由前执行~
    }                                          // 常量值就是pre直接写字符pre也可以

    @Override
    public int filterOrder() { 
        return 0;                              //设置过滤器级别 值越大执行顺序越慢
    }

    @Override
    public boolean shouldFilter() { 
        return true;                           //有可能存在多个拦截器,设置false 则忽略之后的过滤器!直接执行代码!
    }

    @Override
    public Object run() throws ZuulException {  //主要过滤的操作run()

        System.out.println("zuul 前过滤!");
        //Java原生的Servlet类: RequestContext 获得请求上下文对象
        RequestContext currentContext = RequestContext.getCurrentContext();
        //获取去一个request 对象,根据对象获取页面参数是否存在 Tonken如果没有Tonken则直接报错不给登录!
        HttpServletRequest request = currentContext.getRequest();
        String token = request.getParameter("token");
        //判断
        if (token == null || token.equals("")) { 
            //发送zuul 响应终止. 过滤器~
            currentContext.setSendZuulResponse(false);
            //给浏览器发送当前状态码!
            currentContext.setResponseStatusCode(401);
            //给浏览器返回响应数据!
            currentContext.setResponseBody("{'code:':401,'data':'token not found!'}");
        }
        return "next";      //通过当前 过滤器
    }
}

测试

《微服务网关》

ZUUL 过滤总结:

  • 实现 ZuulFilter 抽象类重写方法!
    根据需求编写对应的方法:
  • 在 run() 方法中编写过滤的操作…

ZUUL 网关限流

通俗易懂: 为了防止请求流量过大, 程序服务器扛不住压力! 对请求流量做出限制的操作!

常见的限流算法

计数器 可以直接通过配置来设置的 ip 限流

  • 计数器限流算法是最简单的一种限流实现方式。限制一定时间内访问的数量...
  • ZUUL可以直接通过配置 .yml 来设置的 ip 技术限流

.yml

#zuul网关配置
zuul:
  routes:
    #定义路由,因为是自定义的所有下面并没有提示~
    myuser-consumer:            #这里是路由id, myuser-consumer是随意写,后面根据 路由做:限流操作!
      path: /userserver/**      #这里是映射路径
      serviceId: user-server    #指定当前路由的——微服务名(到注册中心中找对应的名获取对应的 ip 端口~)
  ratelimit:
    enabled: true #开启限流
    policies:
      #指定 路由 进行的限流设置!
      myuser-consumer:
        limit: 10 #60s 内请求超过10次,服务端就抛出异常,60s后可以恢复正常请求,抛出异常就会被全局的异常处理接受导!
        refresh-interval: 60
        type: origin          #针对IP进行限流,不影响其他IP

#还可以通过如下配置来开启全局的服务限流! default-policy:
#但不建议! 正常情况下只是对某一个高访问的模块进行限流..没必要所有模块都做限流!
# ratelimit:
# enabled: true #开启限流
# default-policy: #所有的路由都限流...
# limit: 3 
# refresh-interval: 60
# type: origin 

测试:

《微服务网关》

Spring全局异常处理!

《微服务网关》
MyErrorController.Java

@RestController
public class MyErrorController implements ErrorController { 
    @Override
    public String getErrorPath() { 
        return "error";
    }

    @RequestMapping("/error")
    public String error(HttpServletResponse response)
    { 
        int code = response.getStatus();
        if (404 == code) { 
            System.out.println(404+"未找到资源");
        } else if (403 == code) { 
            System.out.println(403+"没有访问权限");
        } else if (401 == code) { 
            System.out.println(401+"登录过期");
        } else { 
            System.out.println(500+"服务器错误");
        }
        //可以根据返回的 状态码来返回指定的页面!
        return "{\"result\":\"访问太多频繁,请稍后再访问!!!\"}";
    }
}
  • SpringBoot 类实现ErrorController 接口来完成 全局异常处理
    如果避免出现404,405,500这种报错信息时, 可以通过全局异常处理
  • 实现接口 重写getErrorPath() 方法…指定异常跳转的URL

总结:

  • 技术限流 ZUUL可以直接通过 yml配置直接修改! 实现对应用的限流
  • 超出的请求, 会抛出异常! 可以通过 SpringBoot全局异常来捕获处理!

漏桶算法

《微服务网关》

  • 漏桶算法可以很好地限制容量池的大小,从而防止流量暴增。
  • 漏桶可以看作是一个带有常量服务时间的单服务器队列,如果漏桶(包缓存)溢出,那么数据包会被丢弃。
  • 在网络中,漏桶算法可以控制端口的流量输出速率,
    平滑网络上的突发流量,实现流量整形,从而为网络提供一个稳定的流量。
  • 为了更好的控制流量,漏桶算法需要通过两个变量进行控制:
    一个是桶的大小,支持流量突发增多时可以存多少的水(burst),另一个是水桶漏洞的大小(rate)。
  • 总的来说就是:
    水龙头比作请求 不管你多大的请求我都, 先经过的的 桶;
    桶底有一个孔 ,决定了可以通过的请求 平稳的流速 完成请求…
    如果有溢出的请求服务, 则就是直接抛弃…

令牌桶算法

在漏桶算法基础上的更改:

  • 桶算法能够限制请求调用的速率,无论请求如何都不会超出某一个请求值
  • 而令牌桶算法能够在限制调用 的平均速率的同时还允许一定程度的突发调用。

《微服务网关》

在令牌桶算法中,存在一个桶,用来存放固定数量的令牌

  • 算法中存在一种机制,以一定的速率往桶中放令牌。
    每次请求调用需要先获取令牌,只有拿到令牌,才有机会继续执行 否则选择等待可用的令牌、或者直接拒绝。
  • 放令牌这个动作是持续不断的进行,如果桶中令牌数达到上限,就丢弃令牌
  • 所以就存在这种情况,桶中一直有大量的可用令牌,
  • 这时进来的请求就可以直接拿到令牌执行,比如设置qps并发请求为100,
  • 那么限流器初始化完成一秒后,桶中就已经有100个令牌了,该限流器可以抵挡瞬时 的100个请求。
  • 所以,只有桶中没有令牌时,请求才会进行等待,最后相当于以一定的速率执行。(一次创建多少令牌 允许多少请求的匀速请求)

实现令牌桶算法:

首先注释 .yml中的 计数限流避免冲突~
《微服务网关》
RouteFilter.Java

import com.google.common.util.concurrent.RateLimiter;       //谷歌的组件..
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
import org.springframework.stereotype.Component;

//令牌限流:
@Component
public class RouteFilter extends ZuulFilter { 


    //设置常量令牌每秒产生的次数;
    //定义一个令牌桶,每秒产生2个令牌,即每秒最多处理2个请求
    private static final RateLimiter rateLimiter = RateLimiter.create(2);
    //过滤器类型 pre 路由前执行
    @Override
    public String filterType() { 
        return FilterConstants.PRE_TYPE;
    }
    //级别 越小越先执行
    @Override
    public int filterOrder() { 
        return -10;
    }
    //方法返回 ture/false ture继续执行,false跳出不在执行后面~
    @Override
    public boolean shouldFilter() { 
        System.out.println("令牌生效");

        RequestContext currentContext = RequestContext.getCurrentContext();
        //判断当前是否存在令牌:没有令牌则不同行!
        if (!rateLimiter.tryAcquire()) {     //RateLimiter的方法底层会自动获取当前是否存在令牌...
            currentContext.setSendZuulResponse(false);
            currentContext.setResponseStatusCode(401);
            currentContext.setResponseBody("{'code:':401,'data':'limit'}");
            return false;
        }
        //如果成立则同行~
        return true;
    }
    //run().....拦截器的代码...
    @Override
    public Object run() throws ZuulException { 
        return null;
    }
}

令牌桶对象: RateLimiter

  • tryAcquire()
    只要能够马上获致到1个令牌,则返回true,不阻塞
  • tryAcquire(5, 3, TimeUnit.SECONDS)
    在3秒钟内可以获取到5个令牌,则返回true,不阻塞
  • acquire(5)
    获取到5个令牌,否则一直等待,会阻塞,返回值为阻塞的时长
  • acquire()
    获取到1个令牌,否则一直等待,会阻塞,返回值为阻塞的时长

测试:

《微服务网关》

总结:

  • 貌似没啥总结的, 代码不难… 需要掌握理论!

ZUUL 替代技术 GateWay

Zuul网关存在的问题

  • Zuul1x版本本质上就是一个同步Servlet,采用多线程阻塞模型进行请求转发。
    Servelt 会问每一个请求,专门分配一个线程来执行… 直到客户端响应线程才返回 线程池
    而 Servelt 是Java编写的… 对于多线程的操作并不完善…线程池大小存在限制.
    如果, 后台频繁调用 比较耗时的业务 那么 , 执行的线程就会堵塞来完成该功能~ 线程资源会被占用

  • 很容易耗尽容器线程池内的线程,造成容器无法接受新的请求。不支持任何长连接

  • Zuul 2.x(基于Netty,也是非阻塞的,支持长连接) 但Spring Cloud 暂时还没有整合计划。
    Spring Cloud Gateway 比 Zuul 1.x 系列的性能和功能整体要好。

GateWay

  • Spring Cloud Gateway 是 Spring 官方基于 Spring 5.0
    难怪不计划对 Zuul 2.x整合 有亲儿子了…

  • SpringCloud Gateway 作为 Spring Cloud 生态系中的网关,目标是替代 Netflflix ZUUL
    其不仅提供统一的路由方式,并且基于 Filter 链的方式提供了网关基本的功能 例如:
    安全,监控/埋点,和限流等。它是基于Nttey的响应式开发模式。

  • RPS: 即Requests Per Second的缩写,每秒能处理的请求数目。
    Spring Cloud Gateway 的RPS 是Zuul的1.6倍

GateWay核心概念

  • 路由(route)
    路由是构建网关的基本模块,它由ID,目标URI,一系列的断言和过滤器组成,如果断言为true则匹配该路由

  • 断言(predicates)
    开发人员可以匹配HTTP请求中的所有内容(例如请求头或请求参数),如果请求与断言相匹配则进行路由

  • 过滤器(filter)
    指的是Spring框架中GatewayFilter的实例,使用过滤器,可以在请求被路由前或者之后对请求进行修改

GateWay入门案例

《微服务网关》

依赖

pom.xml

<!-- SpringCloud提供的 gateway依赖 -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>

注意

  • spring-cloud-starter-gateway依赖 和 spring-boot-startet-web 有冲突
  • 建议Maven开发的话: 父工程不要在放 web依赖.. 不然会发生冲突... 移动到需要的子模块配置 web依赖...

主程序

Mygateway.Java

@SpringBootApplication		//SpringBoot主程序
@EnableEurekaClient			//开启注册中心客户端注解
public class Mygateway { 
    public static void main(String[] args) { 
        SpringApplication.run(Mygateway.class, args);
    }
}

.yml 配置

application.yml

 server:
  port: 7005
spring:
  application:
    name: gateway-server
  #gateway 的配置...
  cloud:
    gateway:
      routes:
      - id: user-server             #自定义的路由 ID,保持唯一
        uri: http://127.0.0.1:6002 #通过uri 指定目标服务地址,可以通过 lb://指定注册中心的服务,实现动态调用。。。请求
        predicates:                 #路由规则,返回一个布尔值结果。符合条件才能请求~
        - Path=/user/**             #请求前缀需要 /user 才能请求! (调用的服务也要有 /user前缀!)
#Eureka 注册服务;
eureka:
  client:
    service-url:
      defaultZone: http://localhost:7001/eureka/
  instance:
    prefer-ip-address: true #显示浏览器中的状态栏显示ip

测试:

网关 和 微服的请求前缀必须加 /user 才能进行请求!
《微服务网关》
微服 controller层加上:@RequestMapping("/user") , 请求前缀加上 user 便于区分模块!

常用配置:

动态路由 url

上面的url 写四了请求, 当然也可以同注册中心来完成动态调用

  • 首先确保存在 注册中心!

application.yml
lb://指定注册中心的服务,实现动态调用。。。请求

 cloud:
    gateway:
      routes:
      - id: user-server
# uri: http://127.0.0.1:6002
        uri: lb://user-server		#动态请求 user-server的 ip 端口...
        predicates:
        - Path=/user/**

《微服务网关》

重写转发路径

  • 我们通过网关请求时候, 有时需要在请求前加上前缀,
    但并不像要修改 微服务的请求方式, 只是像网关的请求 +前缀;
  • 可以通过RewritePath配置重写转发的url
    例如:将/user-server/(?.*) 重写为${segment},然后转发到订单微服务。
    网关上请求http://localhost:8080/user-service/user/1
    此时会将请求转发到http://localhost:8080/user/1 ( 值得注意的是在yml文档中 $ 要写成 $\ )

测试

首先把 user-server模块的@RequestMapping("/user") 移除掉~
《微服务网关》
ok, 发现网关请求 需要前缀, 而服务单独请求不在需要了…! 完成!

路由规则

上面 .yml配置可以查看, 创建一个路由需要:id url 规则

常见的网关规则:

《微服务网关》
建议浏览一遍, 需要时候copy即可

#路由断言之后匹配 
spring: 
cloud: 
gateway: 
routes: 
- id: after_route 
uri: https://xxxx.com 
#路由断言之前匹配 
predicates: 
- After=2017-01-20T17:42:47.789-07:00[America/Denver]
#路由断言之前匹配 
- id: before_route 
uri: https://xxxxxx.com 
predicates: 
- Before=2017-01-20T17:42:47.789-07:00[America/Denver]
#路由断言之间 
- id: between_route 
uri: https://xxxx.com 
predicates: 
- Between=xxxx,xxxx
#路由断言Cookie匹配,此predicate匹配给定名称(chocolate)和正则表达式(ch.p) 
curl -H 'Cookie:name=forezp' localhost:8081
- id: cookie_route 
uri: https://xxxx.com 
predicates: 
- Cookie=name, forezp
#路由断言Header匹配,header名称匹配X-Request-Id,且正则表达式匹配\d+
- id: header_route 
uri: https://xxxx.com 
predicates: 
- Header=X-Request-Id, \d+ 
#路由断言匹配Host匹配,匹配下面Host主机列表,**代表可变参数 
- id: host_route 
uri: https://xxxx.com 
predicates: 
- Host=**.somehost.org,**.anotherhost.org
#路由断言Method匹配,匹配的是请求的HTTP方法
- id: method_route 
uri: https://xxxx.com 
predicates: 
- Method=GET 
#路由断言匹配,{segment}为可变参数 
- id: host_route 
uri: https://xxxx.com 
predicates: 
- Path=/foo/{ segment},/bar/{ segment} 
#路由断言Query匹配,将请求的参数param(baz)进行匹配,也可以进行regexp正则表达式匹配 (参数包含 foo,并且foo的值匹配ba.) 
- 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 
- id: remoteaddr_route 
uri: https://example.org 
predicates: 
- RemoteAddr=192.168.1.1/24

Gate way 过滤器:

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

Gate way 过滤器的生命周期

Spring Cloud Gateway 的 Filter 的生命周期不像 Zuul 的那么丰富,它只有两个:prepost

PRE

这种过滤器在请求被路由之前调用。

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

POST

这种过滤器在路由到微服务以后执行。

  • 这种过滤器可用来为响应添加标准的:
    HTTP Header、收集统计信息和指标、将响应从微服务发送给客户端等

Gate way 过滤器类型

Spring Cloud Gateway 分为另外两种 GatewayFilter(内置过滤器)GlobalFilter(自定义过滤器)

GatewayFilter 内置过滤器:局部过滤器。

  • GatewayFilter 又称 局部过滤器 是针对单个路由的过滤器。
    声明在单个, 路由id下,只有改路由存在改过滤器!

  • 在Spring Cloud Gateway中通过GatewayFilter的形式内置了很多不同类型的局部过滤器。
    《微服务网关》

  • 每个过滤器工厂都对应一个实现类,并且这些类的名称必须以 GatewayFilterFactory 结尾 1. Spring Cloud Gateway的一个约定

  • RewritePath 对应的实现类就是 RewritePathGatewayFilterFactory idea 查找类 Ctrl+N 输入:RewritePath
    可以在类中方一个断点,运行类。发现程序暂停!!

  • ctrl+N 输入GatewayFilterFactory 它是一个接口 内置过滤器集合!

GlobalFilter 自定义过滤器:全局过滤器

全局过滤器(GlobalFilter)和 局部相反就是作用于所有路由

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

Spring Cloud Gateway内部也是通过一系列的内置全局过滤器对整个路由转发进行处理如下:
《微服务网关》

Gate way 全局过滤器开发:

  • 内置的过滤器已经可以完成大部分的功能
  • 但是对于企业开发的一些业务功能处理,
    还是需要我们自己编写过滤器来实现的,通过代码的形式自定义一个过滤器,完成统一的权限校验。

功能场景:统一鉴权

开发中的鉴权逻辑:

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

代码实现

  • 自定义一个GlobalFilter
  • 去校验所有请求的请求参数中是否包含“token”,
  • 如何不包含请求参数“token”则不转发路由,否则执行正常的逻辑。
    《微服务网关》
    TokenFitter.Java
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

//Gate way实现过滤器:
@Component
@Slf4j
public class TokenFitter implements GlobalFilter, Ordered {      //自定义过滤器类 实现GlobalFilter Ordered接口并实现两个方法;
    //执行过滤
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { 
        //根据参数 exchange 获得request对象 并从url中获取参数...
        String token = exchange.getRequest().getQueryParams().getFirst("token");
        //判断是否存在Token
        if (token == null || "".equals(token)) { 
            log.info("用户没有登录, 权限被拦截!");
            //返回页面状态码!
            exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
			//终止请求!
            return exchange.getResponse().setComplete();        //退出..,拦截拦截退出...
        }
        //存在Token 则同行;
        return chain.filter(exchange);
    }

    //过滤顺序级别
    @Override
    public int getOrder() { 
        return 0;
    }
}

总结:

  • 自定义全局过滤器需要实现GlobalFilter和Ordered接口。
    在filter方法中完成过滤器的逻辑判断处理
    在getOrder方法指定此过滤器的优先级,返回值越大级别越低

  • ServerWebExchange 就相当于当前请求和响应的上下文
    存放着重要的请求-响应属性、请求实 例和响应实例等等。
    一个请求中的request,response都可以通过 ServerWebExchange 获取

  • 调用 chain.filter 继续向下游执行(放行)
    exchange.getResponse().setComplete(); 终止请求;

测试:

这时候,你所有的 网关请求就必须要带着 Token才可以执行请求。 不然都会被 全局拦截!
《微服务网关》
这里这是一个展示,更多功能请自己学习… 学会了教我~

Gateway 基于Filter(过滤器) 的限流

  • SpringCloudGateway官方就提供了基于令牌桶的限流支持。 默认就提供了 令牌桶的限流支持!

  • 基于其内置的过滤器工厂 RequestRateLimiterGatewayFilterFactory 实现

  • 在过滤器工厂中是通过Redislua脚本结合的方式进行流量控制。使用Redis 进行监听并对 相同数量进行限制!

环境搭建

启动redis服务

redis 目录下:redis-server.exe

开启Redis 的监听

首先启动 Redis 服务
《微服务网关》

导入 pom 依赖:

pom.xml

<!--监控依赖-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!--redis的依赖-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>

修改 .yml 配置:

spring:
  application:
    name: gateway-server
  #gateway 的配置...
  cloud:
    gateway:
      routes:
      - id: user-server             #自定义的路由 ID,保持唯一
# uri: http://127.0.0.1:6002 #通过uri 指定目标服务地址,可以通过 lb://指定注册中心的服务,实现动态调用。。。请求
        uri: lb://user-server
        predicates:                 #路由条件,返回一个布尔值结果。符合条件才能请求~
        - Path=/user/**             #请求前缀需要 /user 才能请求! (调用的服务也要有 /user前缀!)
        filters:
        - RewritePath=/user/(?<segment>.*), /$\{ segment}  #网关请求的 /user/** 请求会重写为 /** 请求~

        #Gateway 限流过滤操作! 默认令牌桶操作!
        - name: RequestRateLimiter
          args:
            key-resolver: '#{@tokenKeyResolver}' # 使用SpEL从容器中获取对象,用户自定义的类!
            redis-rate-limiter.replenishRate: 1 # 令牌桶每秒填充平均速率
            redis-rate-limiter.burstCapacity: 3 # 令牌桶的总容量
  #Spring 节点下配置redis依赖!(本人没有密码!)
  redis:
    host: 127.0.0.1
    port: 6379
  • 在 application.yml 中添加了redis的信息,并配置了RequestRateLimiter的限流过滤器

  • - name: RequestRateLimiter: 设置Gate way的限流配置:
    key-resolver 使用SpEL从容器中获取对象  {@beanName} 从Spring 容器中获取 Bean 对象。开发者自定义的类!
    burstCapacity 令牌桶总容量。
    replenishRate 令牌桶每秒填充平均速率。

开发者根据需求定义 限流类:LimitConfig

  • 配置KeyResolver 请求限制的条件!
  • 为了达到不同的限流效果和规则,可以通过实现 KeyResolver 接口,定义不同请求类型的限流键。
    本次简单实现两种: 根据ip 限流 根据token 登录用户限流

LimitConfig.Java

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;

//gatewat 完成限流: 采用令牌桶限流法....
@Configuration
public class LimitConfig { 
    
    //根据ip地址进行 限流,
   /* @Bean public KeyResolver pathKeyResolver() { return new KeyResolver() { @Override public Mono<String> resolve(ServerWebExchange exchange) { System.out.println("ip 限流"); String addr = exchange.getRequest().getPath().toString(); System.out.println("===================:" + addr); return Mono.just(addr); } }; }*/

    //根据token 登录用户限流...
    @Bean
    public KeyResolver tokenKeyResolver() { 
        return new KeyResolver() { 
            @Override
            public Mono<String> resolve(ServerWebExchange exchange) { 
            
                System.out.println("token 限流");
                String addr = exchange.getRequest().getQueryParams().getFirst("token");     //获取请求中的Token 打印;
                System.out.println("===================:" + addr); 
                //返回,Redis会记录每次的返回结果. 对一样的解决进行计数.令牌桶限流!
                //同一个Token 一端时间请求多次超过令牌桶则不在进行响应直接限制访问!!等待新的令牌!
                return Mono.just(addr);  	
            }
        };
    }
    
}
  • 类中有两个方法:tokenKeyResolver pathKeyResolver 分别是两种不同的限流方式;
  • 使用时直接修改 yml: key-resolver: '#{@方法名}' 配置限流的操作!

测试:

《微服务网关》

Gate way 网关 .yml 配置:

Gate way网关统一实现: 跨域请求

.yml 配置:使网关统一完成 跨域的操作!

#Spring 配置下
  cloud:
    gateway:
      globalcors:
        corsConfigurations:
          '[/**]':            	# 匹配所有请求
            allowedOrigins: "*" #跨域处理 允许所有的域
            allowedMethods:   	# 支持的方法
            - GET
            - POST
            - PUT
            - DELETE

微服模块区分请求 Path ,简化模块请求

网关管理者很多的微服务暴漏的 接口

为了方便区别通常会设置前缀… 而一个微服务可能会有很多的 controller 又作不同的事情(进行区分)…

  • 当用户访问/api/user/的时候我们再根据用户请求调用用户微服务的指定方法。
  • 当然,除了/api/user/还有/api/address/、/api/areas/、/api/cities/、/api/provinces/都需要由user微服务处理
    《微服务网关》
  • - Path=/api/...
    用户模块下的多个功能 Contrller的区分可以, 使用 , 逗号 进行分隔区分…
  • filters: 过滤器
    - StripPrefix=1 移除 微服模块 /api/user... 请求前缀的前一个… 这里就是移除 /api/
    Gateway 的请求前缀是要在 微服模块和网关都要有前缀才可以的请求. 通常都会在微服务模块加入 @RequestMapping("/api/user")
    而 StripPrefix=1 就可以省去 微服务模块 @RequestMapping("/user") 的 /api/ 前缀! 很大程度上简化了开发!优化格式
    原文作者:Java.慈祥
    原文地址: https://blog.csdn.net/qq_45542380/article/details/113572549
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞