springcloud 学习笔记(四)微服务之springgateway

引言

前面章节中小猿都采用springcloud来搭建微服务,但是不够完美,消费者和服务提供者之间的调用往往是将地址暴露出来,这就造成安全隐患,为了解决这一隐患,springgateway就出世了。

什么是springgateway

This project provides a library for building an API Gateway on top of Spring WebFlux. Spring Cloud Gateway aims to provide a simple, yet effective way to route to APIs and provide cross cutting concerns to them such as: security, monitoring/metrics, and resiliency.

springgateway的特点

Built on Spring Framework 5, Project Reactor and Spring Boot 2.0.
Able to match routes on any request attribute.
Predicates and filters are specific to routes.
Circuit Breaker integration.
Spring Cloud DiscoveryClient integration.
Easy to write Predicates and Filters.
Request Rate Limiting.
Path Rewriting.
spring cloud gateway 的核心就是一系列的过滤器,可以将客户端的请求转到不同的服务器,即可简要的称为过滤和路由。

当加入springcloudgateway的后的架构

springcloud 学习笔记(四)微服务之springgateway_第1张图片

springcloudgateway核心功能

路由(route) 路由信息的组成:由一个ID、一个目的URL、一组断言工厂、一组Filter组成。如果路由断言为真,说明请求URL和配置路由匹配。
断言(Predicate) Spring Cloud Gateway中的断言函数输入类型是Spring 5.0框架中的ServerWebExchange。Spring Cloud Gateway的断言函数允许开发者去定义匹配来自于Http Request中的任何信息比如请求头和参数。
过滤器(Filter) 一个标准的Spring WebFilter。 Spring Cloud Gateway中的Filter分为两种类型的Filter,分别是Gateway Filter和Global Filter。过滤器Filter将会对请求和响应进行修改处理。

springcloudgateway工程案例

工程目录结构
springcloud 学习笔记(四)微服务之springgateway_第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>springcloudparents</artifactId>
        <groupId>org.example</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>springgateway</artifactId>

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

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

    </dependencies>

    <build>
        <plugins>
            <!--<plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>-->
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <!--<executions>
                    <execution>
                        <phase>none</phase>
                    </execution>
                </executions>-->
            </plugin>
        </plugins>
    </build>
    
</project>

启动类

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

配置文件

server:
  port: ${port:10010}
spring:
  application:
    name: apigateway
  cloud:
    gateway:
      routes:
        # 路由id,可以任意设置
        - id: userserviceroute
          uri: http://127.0.0.1:9090
        # 路由断言: 可以匹配映射路径,即当路径中包含userController时
          #就直接跳转到url: http://127.0.0.1:9090的服务器
          predicates:
            - Path=/userController/**
eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:10086/eureka
  instance:
    prefer-ip-address: true

其他的模块可以参考小猿的前几篇博文。

访问流解析

springcloud 学习笔记(四)微服务之springgateway_第3张图片

测试

两个服务已经成功注册到注册中心
springcloud 学习笔记(四)微服务之springgateway_第4张图片
springcloud 学习笔记(四)微服务之springgateway_第5张图片

实现动态路由

本案例中还是存在一个问题,路由的服务对象ip地址写死,我们需要修改uri

uri: lb://userservice

修改说明

springcloud 学习笔记(四)微服务之springgateway_第6张图片

路由前缀处理

添加前缀

添加前缀:对请求地址添加前缀路径之后再作为代理的服务地址;

spring:
  application:
    name: apigateway
  cloud:
    gateway:
      routes:
        # 路由id,可以任意设置
        - id: userserviceroute
          #此地址不可以写死
          #uri: http://127.0.0.1:9090
          uri: lb://userservice
        # 路由断言: 可以匹配映射路径,即当路径中包含userController时
          #就直接跳转到url: http://127.0.0.1:9090的服务器
          predicates:
            #- Path=/api/userController/**
            - Path=/**
          filters:
            - PrefixPath=/userController

springcloud 学习笔记(四)微服务之springgateway_第7张图片
系统自动帮我们添加userController对象。

删除前缀

配置删除前缀

spring:
  application:
    name: apigateway
  cloud:
    gateway:
      routes:
        # 路由id,可以任意设置
        - id: userserviceroute
          #此地址不可以写死
          #uri: http://127.0.0.1:9090
          uri: lb://userservice
        # 路由断言: 可以匹配映射路径,即当路径中包含userController时
          #就直接跳转到url: http://127.0.0.1:9090的服务器
          predicates:
            - Path=/api/userController/**
           # - Path=/**
          filters:
            #- PrefixPath=/userController
            # 1代表去除一个路径
            - StripPrefix=1

springcloud 学习笔记(四)微服务之springgateway_第8张图片

gateway过滤器

springcloud 学习笔记(四)微服务之springgateway_第9张图片

GatewayFilter Factories

Route filters allow the modification of the incoming HTTP request or outgoing HTTP response in some manner. Route filters are scoped to a particular route. Spring Cloud Gateway includes many built-in GatewayFilter Factories.

过滤器的类型

Gateway实现方式上,有两种过滤器;

  1. 局部过滤器:通过 spring.cloud.gateway.routes.filters 配置在具体路由下,只作用在当前路由上;自带的过滤器都可以配置或者自定义按照自带过滤器的方式。如果配置

  2. 全局过滤器:不需要在配置文件中配置,作用在所有的路由上;实现 GlobalFilter 接口即可。
    spring.cloud.gateway.default-filters 上会对所有路由生效也算是全局的过滤器;但是这些过滤器的实现上都是要实现GatewayFilterFactory接口。

smallcase

局部过滤器使用方法
The AddResponseHeader GatewayFilter Factory,The AddResponseHeader GatewayFilter Factory takes a name and value parameter. The following example configures an AddResponseHeader GatewayFilter:

spring:
  cloud:
    gateway:
      routes:
      - id: add_response_header_route
        uri: https://example.org
        filters:
        - AddResponseHeader=X-Response-Red, Blue

上述是spring官网给出添加响应的案例,若采用该类过滤器则仅限于该路由链路 。小猿在此就不做演示,小猿来演示一下全局过滤器。
Default Filters: To add a filter and apply it to all routes, you can use spring.cloud.gateway.default-filters. This property takes a list of filters. The following listing defines a set of default filters:

spring:
  application:
    name: apigateway
  cloud:
    gateway:
      routes:
        # 路由id,可以任意设置
        - id: userserviceroute
          #此地址不可以写死
          #uri: http://127.0.0.1:9090
          uri: lb://userservice
        # 路由断言: 可以匹配映射路径,即当路径中包含userController时
          #就直接跳转到url: http://127.0.0.1:9090的服务器
          predicates:
            #- Path=/api/userController/**
            - Path=/**
          filters:
            - PrefixPath=/userController
            # 1代表去除一个路径
            #- StripPrefix=1
      #默认过滤器,对所有路由都生效
      default-filters:
        - AddResponseHeader=X-Response-Default-MyName, xueshanfeitian
        - AddResponseHeader=X-Response-Default-MyName, feitian

springcloud 学习笔记(四)微服务之springgateway_第10张图片

过滤器执行周期

Spring Cloud Gateway 的 Filter 的生命周期也类似Spring MVC的拦截器有两个:“pre” 和 “post”。“pre”和 “post” 分别会在请求被执行前调用和被执行后调用。 pre 和 post 可以通过过滤器的 GatewayFilterChain 执行filter方法前后来实现。

自定义过滤器

通过以上对过滤器的分析,我们就可以实现自定义过滤器,自定义过滤器的一个重要应用是获取url中参数的值。

修改配置文件
server:
  #修改端口高可用
  port: ${port:10086}
spring:
  application:
    name: eureka-server
eureka:
  client:
    service-url:
      # eureka 服务地址,如果是集群的话;需要指定其他集群Eureka地址
      # 修改defaultZone高可用
      defaultZone: ${defaultZone:http://127.0.0.1:10086/euraka}
    #不注册自己 默认为ture
    register-with-eureka: false
    #不主动拉取服务 默认为ture
    fetch-registry: false
  server:
    #服务失效剔除时间间隔 默认60秒
    eviction-interval-timer-in-ms: 30000
    #关闭自我保护
    enable-self-preservation: false
编写自定义过滤器类
@Component
public class MyGatewayFilterFactory extends
        AbstractGatewayFilterFactory<MyGatewayFilterFactory.Config> {

    static final String PARAM_NAME = "param";
    public MyGatewayFilterFactory(){
        super(Config.class);
    }

    public List<String> shortcutFieldOrder(){
        //没有名称和数值
        //return Arrays.asList(GatewayFilter.NAME_KEY,GatewayFilter.VALUE_KEY);
        return Arrays.asList(PARAM_NAME);
    }

    @Override
    public GatewayFilter apply(Config config) {
        return (exchange, chain) -> {
            //获取请求参数中的param对应参数名 的参数值
            ServerHttpRequest request = exchange.getRequest();
            if(request.getQueryParams().containsKey(config.param)){
                request.getQueryParams().get(config.param).forEach(value-> System.out.printf(
                        "------局部过滤器------%s = %s ---------",config.param,value
                ));
            }
            return chain.filter(exchange);
        };
    }

    public static class Config{
        private  String param;

        /*public Config(String param) {
            this.param = param;
        }*/

        public String getParam() {
            return param;
        }

        public void setParam(String param) {
            this.param = param;
        }
    }
}
测试

springcloud 学习笔记(四)微服务之springgateway_第11张图片
springcloud 学习笔记(四)微服务之springgateway_第12张图片

notes

注意下面报错时的解决方法
springcloud 学习笔记(四)微服务之springgateway_第13张图片

自定义全局过滤器

模拟一个登录的校验。基本逻辑:如果请求中有token参数,则认为请求有效,放行。

@Component
public class MyGlobalFilter implements GlobalFilter , Ordered {


    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        System.out.println("---------执行全局过滤器-----------");
        String token = exchange.getRequest().getQueryParams().getFirst("token");
        if(StringUtils.isBlank(token)){
            exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
            return exchange.getResponse().setComplete();
        }
        return chain.filter(exchange);
    }


    //当有多个过滤器时值越小,越先执行
    @Override
    public int getOrder() {
        return 1;
    }
}

springcloud 学习笔记(四)微服务之springgateway_第14张图片
当请求参数含有token时,则正常访问。
springcloud 学习笔记(四)微服务之springgateway_第15张图片
在这里插入图片描述

gateway的负载均衡和熔断器

Gateway中默认就已经集成了Ribbon负载均衡和Hystrix熔断机制。但是所有的超时策略都是走的默认值,比如熔断超时时间只有1S,很容易就触发了。所以需要我们手动配置。

hystrix:
  command:
    default:
      execution:
        isolation:
          thread:
            timeoutInMilliseconds: 6000
ribbon:
  ConnectTimeout: 1000
  ReadTimeout: 2000
  MaxAutoRetries: 0
  MaxAutoRetriesNextServer: 0

gateway跨域请求

跨域是指在js请求访问中,如果访问的地址与当前服务器的域名、ip或者端口号不一致则称为跨域请求。若不解决则不能获取到对应地址的返回结果。
网管是所有微服务的请求入口,当前端发送ajax(axios)请求时则可能会产生跨域问题,下面是一个跨域请求配置:

spring:
  application:
    name: apigateway
  cloud:
    gateway:
      routes:
        # 路由id,可以任意设置
        - id: userserviceroute
          #此地址不可以写死
          #uri: http://127.0.0.1:9090
          uri: lb://userservice
        # 路由断言: 可以匹配映射路径,即当路径中包含userController时
          #就直接跳转到url: http://127.0.0.1:9090的服务器
          predicates:
            #- Path=/api/userController/**
            - Path=/**
          filters:
            - PrefixPath=/userController
            # 1代表去除一个路径
            #- StripPrefix=1
            - My=name
      #默认过滤器,对所有路由都生效
      default-filters:
        - AddResponseHeader=X-Response-Default-MyName, xueshanfeitian
        - AddResponseHeader=X-Response-Default-MyName, feitian
      globalcors:
        cors-configurations:
          #对所有访问到网关服务器的请求地址
          '[/**]':
            #allowedOrigins: *
            allowedOrigins:
              # 可以允许来自 http://docs.spring.io 的get请求方式获取服务数据。
              - "http://docs.spring.io"
            allowedMethod:
              - GET

具体的配置含义可以直接查看代码注释。

gateway和feign的区别

  1. Gateway 作为整个应用的流量入口,接收所有的请求,如PC、移动端等,并且将不同的请求转发至不同的处理微服务模块,其作用可视为nginx;大部分情况下用作权限鉴定、服务端流量控制。
  2. Feign 则是将当前微服务的部分服务接口暴露出来,并且主要用于各个微服务之间的服务调用。
    至此springgateway的内容就介绍到此,如有更高级的应用,小猿会持续更新。

你可能感兴趣的:(java,分布式)