Gateway服务网关

时间:2020-10-21
本文章向大家介绍Gateway服务网关,主要包括Gateway服务网关使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

  Zuul1.X是netflix公司开发的网关组件。在升级2.X,并且在2.X中引入了许多新的思想,更新比较慢。

  gateway是spring公司自己开发的网关组件。

  官网:https://spring.io/projects/spring-cloud-gateway

1.简介

1.什么是gateway  

  SpringCloud Gateway 是 Spring Cloud 的一个全新项目,该项目是基于 Spring 5.0,Spring Boot 2.0 和 Project Reactor 等技术开发的网关,它旨在为微服务架构提供一种简单有效的统一的 API 路由管理方式。其核心逻辑是路由转发+执行过滤器链。

  SpringCloud Gateway 作为 Spring Cloud 生态系统中的网关,目标是替代 Zuul,在Spring Cloud 2.0以上版本中,没有对新版本的Zuul 2.0以上最新高性能版本进行集成,仍然还是使用的Zuul 2.0之前的非Reactor模式的老版本。而为了提升网关的性能,SpringCloud Gateway是基于WebFlux框架实现的,而WebFlux框架底层则使用了高性能的Reactor模式通信框架Netty。

  Spring Cloud Gateway 的目标,不仅提供统一的路由方式,并且基于 Filter 链的方式提供了网关基本的功能,例如:安全,监控/指标,和限流。

注意:Spring Cloud Gateway 底层使用了高性能的通信框架Netty。

2.特性

SpringCloud官方,对SpringCloud Gateway 特征介绍如下:

(1)基于 Spring Framework 5,Project Reactor 和 Spring Boot 2.0

(2)集成 Hystrix 断路器

(3)集成 Spring Cloud DiscoveryClient

(4)Predicates 和 Filters 作用于特定路由,易于编写的 Predicates 和 Filters

(5)具备一些网关的高级功能:动态路由、限流、路径重写

简单说明一下上文中的三个术语:

(1)Filter(过滤器):

和Zuul的过滤器在概念上类似,可以使用它拦截和修改请求,并且对上游的响应,进行二次处理。过滤器为org.springframework.cloud.gateway.filter.GatewayFilter类的实例。

(2)Route(路由):

网关配置的基本组成模块,和Zuul的路由配置模块类似。一个Route模块由一个 ID,一个目标 URI,一组断言和一组过滤器定义。如果断言为真,则路由匹配,目标URI会被访问。

(3)Predicate(断言):(面向函数编程,实际是返回bool类型的一个方法)

这是一个 Java 8 的 Predicate,可以使用它来匹配来自 HTTP 请求的任何内容,例如 headers 或参数。断言的输入类型是一个 ServerWebExchange。

 3.和zuul区别

  Zuul1已经进入维护阶段,而且Gateway是Springcloud团队研发的。

  SpringCloud Gateway和Zuul主要的区别,还是在底层的通信框架上。

  Zuul1是一个基于阻塞IO的API网关,基于Servlet2.5使用阻塞架构,不支持长连接(如websocket)等。Zuul的设计模式和Nginx较像,每次IO都是从工作线程中选择一个执行,请求线程被阻塞到工作线程完成。Spring中集成的Zuul版本,采用的是Tomcat容器,使用的是Servlet IO处理模型。

  Gateway基于非阻塞模型进行开发,性能方面不需要担心。Servlet3.1之后有了异步非阻塞的支持。WebFlux是一个典型的异步非阻塞框架。SpringWebflux是Spring5.0引入的新的响应式框架,区别于SpringMVC,它不需要ServletAPI,它是完全异步非阻塞的,并且基于Reactor来实现响应式流规范。

4.Springcloud工作流程

(1) 客户端向gateway发出请求。然后在Gateway Handler Mapping中找到与请求匹配的路由,将其发送到Gateway Web Handler

(2)Handler再通过指定的过滤器链来将请求发送到我们实际的服务执行业务逻辑,然后返回。(过滤器链之间虚线分开是因为可能会在发送代理请求之前"pre"或者之后"post"来执行业务逻辑)。可以在pre类型的过滤器链做参数校验、权限校验、流量监控、日志输出、协议转换等等。在"post"类型的过滤器链可以做响应内容、响应头的修改、日志输出、流量监控等等。

2.使用

1.新建子模块cloud-gateway-gateway9527

 2.修改pom

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>cloud</artifactId>
        <groupId>cn.qz.cloud</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>cloud-gateway-gateway9527</artifactId>

    <dependencies>
        <!--gateway-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>
        <!--eureka-client-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <!--引入自己抽取的工具包-->
        <dependency>
            <groupId>cn.qz.cloud</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>${project.version}</version>
        </dependency>
        <!--一般基础配置类-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

</project>

3.修改yml

server:
  port: 9527

spring:
  application:
    name: cloud-gateway
  cloud:
    gateway:
      routes:
        - id: payment_routh #payment_route    #路由的ID,没有固定规则但要求唯一,建议配合服务名
          uri: http://localhost:8081          #匹配后提供服务的路由地址
          predicates:
            - Path=/pay/listAll/**         # 断言,路径相匹配的进行路由

        - id: payment_routh2 #payment_route    #路由的ID,没有固定规则但要求唯一,建议配合服务名
          uri: http://localhost:8081          #匹配后提供服务的路由地址
          predicates:
            - Path=/pay/getServerPort/**         # 断言,路径相匹配的进行路由

eureka:
  instance:
    hostname: cloud-gateway-service
  client: #服务提供者provider注册进eureka服务列表内
    service-url:
      register-with-eureka: true
      fetch-registry: true
      defaultZone: http://localhost:7001/eureka

  上面相当于配置了两条路由。实际上相当于从9527端口转发到8081端口。

4.启动类:

package cn.qz.cloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;

/**
 * @Author: qlq
 * @Description
 * @Date: 22:29 2020/10/20
 */
@SpringBootApplication
@EnableEurekaClient
public class GateWayMain9527 {
    public static void main(String[] args) {
        SpringApplication.run(GateWayMain9527.class, args);
    }
}

5.启动后测试:

liqiang@root MINGW64 ~/Desktop
$ curl http://localhost:9527/pay/getServerPort
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100    52    0    52    0     0   1106      0 --:--:-- --:--:-- --:--:--  3250{"success":true,"code":"200","msg":"","data":"8081"}

liqiang@root MINGW64 ~/Desktop
$ curl http://localhost:9527/pay/listAll
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   406    0   406    0     0   6444      0 --:--:-- --:--:-- --:--:--  8638{"success":true,"code":"200","msg":"","data":[{"createtime":"2020-09-25T06:58:21.000+0000","serial":"测试","id":1},{"createtime":"2020-09-25T07:06:10.000+0000","serial":"测试","id":2},{"createtime":"2020-09-25T14:34:43.000+0000","serial":"测试1测试测试序列好23456","id":3},{"createtime":"2020-09-25T15:25:35.000+0000","serial":"测试1测试测试序列好23456555","id":1309514424784064514}]}

补充:uri 按服务名称负载均衡调用(实现动态路由)

server:
  port: 9527

spring:
  application:
    name: cloud-gateway
  cloud:
    gateway:
      routes:
        - id: payment_routh #payment_route    #路由的ID,没有固定规则但要求唯一,建议配合服务名
#          uri: http://localhost:8081          #匹配后提供服务的路由地址
          uri: lb://cloud-payment-service          #根据服务名称进行负载均衡替换
          predicates:
            - Path=/pay/listAll/**         # 断言,路径相匹配的进行路由

        - id: payment_routh2 #payment_route    #路由的ID,没有固定规则但要求唯一,建议配合服务名
#          uri: http://localhost:8081          #匹配后提供服务的路由地址
          uri: lb://cloud-payment-service          #根据服务名称进行负载均衡替换
          predicates:
            - Path=/pay/getServerPort/**         # 断言,路径相匹配的进行路由

eureka:
  instance:
    hostname: cloud-gateway-service
  client: #服务提供者provider注册进eureka服务列表内
    service-url:
      register-with-eureka: true
      fetch-registry: true
      defaultZone: http://localhost:7001/eureka

测试:

hyyd@HYYD-M905AEEE MINGW64 /f/eclipseworkspace/inner-platform (dev)
$ curl http://localhost:9527/pay/getServerPort
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100    52    0    52    0     0   3466      0 --:--:-- --:--:-- --:--:-- 52000{"success":true,"code":"200","msg":"","data":"8082"}

hyyd@HYYD-M905AEEE MINGW64 /f/eclipseworkspace/inner-platform (dev)
$ curl http://localhost:9527/pay/getServerPort
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100    52    0    52    0     0     52      0 --:--:-- --:--:-- --:--:-- 52000{"success":true,"code":"200","msg":"","data":"8081"}

2.路由的两种配置方式

一种是上面的xml配置,还有一种是基于代码的配置方式,如下:

package cn.qz.cloud.config;

import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class GatewayConfig {
    /**
     * 配置了一个id为route-name的路由规则
     * 当访问地址 http://localhost:9527/guonei时会自动转发到地址: http://news.baidu.com/guonei
     *
     * @param builder
     * @return
     */
    @Bean
    public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
        RouteLocatorBuilder.Builder routes = builder.routes();
        routes.route("path_route_eiletxie",
                r -> r.path("/guonei")
                        .uri("http://news.baidu.com/guonei")).build();
        return routes.build();
    }

    @Bean
    public RouteLocator customRouteLocator2(RouteLocatorBuilder builder) {
        RouteLocatorBuilder.Builder routes = builder.routes();
        routes.route("path_route_eiletxie2",
                r -> r.path("/guoji")
                        .uri("http://news.baidu.com/guoji")).build();
        return routes.build();
    }
}

3. 常用的断言

  常用的类图如下:

 主要介绍几个常用的:

(1)Path 匹配路径,用法如上面

(2)After 匹配日期,指定日期之后,如下:

 - After=2020-03-12T15:44:15.064+08:00[Asia/Shanghai] #日期后面(用于判断日期)

(3) Before 用于判断指定日期之前,如下:

Before=2020-03-12T15:44:15.064+08:00[Asia/Shanghai] #日期后面(用于判断日期)

(4) Between用于判断指定日期之间:

- Between=2020-03-12T15:44:15.064+08:00[Asia/Shanghai], 2021-03-12T15:44:15.064+08:00[Asia/Shanghai] #日期之间(用于判断日期)

(5) Cookie 匹配带有指定Cookie

- Cookie=uname,zs   #带Cookie,并且uname的值为zs

(6)Header 带有指定的header

- Header=X-Request-Id,\d+ #请求头要有 X-Request-Id属性并且值为整数的正则表达式

  测试方法可以用curl进行测试,也可以用postman进行测试。

补充:日期判断,不知道日期格式可以用如下方法获取

import java.time.ZonedDateTime;

public class PlainTest {
    public static void main(String[] args) {
        ZonedDateTime zbj = ZonedDateTime.now(); // 默认时区
        System.out.println(zbj);
    }
}

结果:

2020-10-21T22:01:13.089+08:00[Asia/Shanghai]

4.过滤器的使用

  类似于servlet的filter,可以实现前后进行特殊处理。比如过滤器开始校验权限等,过滤器处理之后处理响应头等操作。如下模拟校验权限:

package cn.qz.cloud.filter;

import cn.hutool.core.util.StrUtil;
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.HttpCookie;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.Date;

@Component
@Slf4j
public class MyLogGatewayFilter implements GlobalFilter, Ordered {


    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        log.info("****** come in MyLogGateWayFilter: " + new Date());
        ServerHttpRequest request = exchange.getRequest();
        String unameHeader = request.getHeaders().getFirst("uname");
        String unameParam = request.getQueryParams().getFirst("uname");
        HttpCookie unameCookie = request.getCookies().getFirst("uname");
        if (StrUtil.isAllBlank(unameHeader, unameParam) && unameCookie == null) {
            log.info("*****用户名为null,非法用户");
            exchange.getResponse().setStatusCode(HttpStatus.NOT_ACCEPTABLE);
            return exchange.getResponse().setComplete();
        }

        return chain.filter(exchange);
    }

    /**
     * 加载过滤器的顺序,数字越小,优先级越高
     *
     * @return
     */
    @Override
    public int getOrder() {
        return 0;
    }
}

测试:带uname参数、cookie、header的可以正常访问(curl 可以携带指定的cookie和header)

hyyd@HYYD-M905AEEE MINGW64 /f/eclipseworkspace/inner-platform (dev)
$ curl http://localhost:9527/pay/getServerPort
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0

hyyd@HYYD-M905AEEE MINGW64 /f/eclipseworkspace/inner-platform (dev)
$ curl http://localhost:9527/pay/getServerPort?uname=123
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100    52    0    52    0     0     52      0 --:--:-- --:--:-- --:--:-- 52000{"success":true,"code":"200","msg":"","data":"8081"}

hyyd@HYYD-M905AEEE MINGW64 /f/eclipseworkspace/inner-platform (dev)
$ curl http://localhost:9527/pay/getServerPort --Cookie 'uname=12'
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100    52    0    52    0     0   3250      0 --:--:-- --:--:-- --:--:--  3250{"success":true,"code":"200","msg":"","data":"8082"}

hyyd@HYYD-M905AEEE MINGW64 /f/eclipseworkspace/inner-platform (dev)
$ curl http://localhost:9527/pay/getServerPort --Header 'uname:12'
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100    52    0    52    0     0   3466      0 --:--:-- --:--:-- --:--:--  3466{"success":true,"code":"200","msg":"","data":"8081"}

原文地址:https://www.cnblogs.com/qlqwjy/p/13843822.html