SpringCloud:gateway和docke初了解

176 阅读10分钟

Gateway 服务网关

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第3天,点击查看活动详情

1. 为什么需要网关

1.1 网关功能

image-20220525222602496

  1. **网关能用于身份认证和权限校验。**请求来了,首先验证一下你是谁。
  2. **服务路由、负载均衡。**根据用户请求判断将请求发送到对应的微服务;如果有多个微服务负责同一个请求,网关还担任了一个负载均衡的功能。
  3. **请求限流。**当用户请求数超过微服务最大允许请求量,就会由网关负责请求限流。

常常会在网关前添加一个nginx,通过指定的请求路径传到网关,再由网关传至其他微服务。下面就让我们来实现一下网关的服务Gateway

2. gateway快速入门

2.1 搭建网关服务

  1. 创建新的模块

  2. 引入依赖

    <!--网关-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-gateway</artifactId>
    </dependency>
    <!--nacos服务发现依赖-->
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    </dependency>
    

    因为需要在获取用户请求之后,对其进行路由规则判断,之后需要根据服务路由的对应微服务的服务名称从Nacos注册中心获取服务列表

  3. 配置application.yml,编写请求路由和Nacos地址

    spring:
      cloud:
        gateway:
          routes: # 网关路由配置
            - id: user-service # 路由id,自定义,只要唯一即可,给 gateway 使用
              # uri: http://127.0.0.1:8081 # 路由的目标地址 http就是固定地址
              uri: lb://userservice # 路由的目标地址 lb就是负载均衡,后面跟对应微服务的服务名称
              predicates: # 路由断言,也就是判断请求是否符合路由规则的条件
                - Path=/user/** # 这个是按照路径匹配,只要以/user/开头就符合要求
            - id: order-service
              uri: lb://orderservice
              predicates:
                - Path=/order/**
    

image-20220525225107800

【总结】

  • 服务路由是包含一条路由规则,路由规则决定了服务消费者的调用目标,即规定了服务消费者可调用哪些服务提供者。
  • 路由配置包括
    1. 路由id:路由的唯一标识
    2. 路由目标uri:路由的目标地址,http代表固定地址,lb代表根据服务名称负载均衡。目前比较推荐lb
    3. 路由断言predicates:判断路由的规则
    4. 路由过滤器filter:对请求或响应做处理

3. 断言工厂

什么是断言呢?在程序设计中,断言是一种放在程序中的一阶逻辑,目的是为了标示与验证程序开发者预期的结果-当程序运行到断言的位置时,对应的断言应该为真。若断言不为真时,程序会中止运行,并给出错误消息。

我们通过配置文件中向路由配置传入的路由断言字符串,都是由断言工厂Predicate Factory读取并处理,转变为路由判断的条件

我们可以通过在配置文件里设置不同的路由断言来达到不同的效果

名称说明示例
After是某个时间点后的请求- After=2037-01-20T17:42:47.789-07:00[America/Denver]
Before是某个时间点之前的请求- Before=2031-04-13T15:14:47.433+08:00[Asia/Shanghai]
Between是某两个时间点之前的请求- Between=2037-01-20T17:42:47.789-07:00[America/Denver], 2037-01-21T17:42:47.789-07:00[America/Denver]
Cookie请求必须包含某些cookie- Cookie=chocolate, ch.p
Header请求必须包含某些header- Header=X-Request-Id, \d+
Host请求必须是访问某个host(域名)- Host=.somehost.org,.anotherhost.org
Method请求方式必须是指定方式- Method=GET,POST
Path请求路径必须符合指定规则- Path=/red/{segment},/blue/**
Query请求参数必须包含指定参数- Query=name, Jack或者- Query=name
RemoteAddr请求者的ip必须是指定范围- RemoteAddr=192.168.1.1/24
Weight权重处理

如果想要更多的了解,可以选择官网去查询Spring Cloud Gateway

4. 过滤器工厂

我们接受外部的请求,通过路由转到各个过滤器,可以对请求做各种的处理。例如给当前请求添加或移除一个请求头之类的。

image-20220526224043818

我们可以通过官网去查询Spring Cloud Gateway

image-20220526230846873

4.1 路由过滤器的种类

Spring提供了31种不同的路由过滤器工厂。例如:

名称说明
AddRequestHeader给当前请求添加一个请求头
RemoveRequestHeader移除请求中的一个请求头
AddResponseHeader给响应结果中添加一个响应头
RemoveResponseHeader从响应结果中移除有一个响应头
RequestRateLimiter限制请求的流量

4.2 路由过滤器

我们通过在配置文件中对应的服务路由设置该路由的过滤器

spring:
  cloud:
    gateway:
      routes:
        - id: user-service
          uri: lb://userservice
          predicates:
            - Path=/user/**
          # 过滤器
          filters:
          	# 添加过滤器,给当前请求添加一个请求头
            - AddRequestHeader=test, Test is test

重启服务就可以接受请求头内test的数据Test is test

  • 测试

    1. 网关设置配置

      spring:
        application:
          name: gateway
        cloud:
          nacos:
            server-addr: localhost:8848
          gateway:
            routes: # 网关路由配置
              - id: user-service # 路由id,自定义,只要唯一即可,给 gateway 使用
                # uri: http://127.0.0.1:8081 # 路由的目标地址 http就是固定地址
                uri: lb://userservice # 路由的目标地址 lb就是负载均衡,后面跟对应微服务的服务名称
                predicates: # 路由断言,也就是判断请求是否符合路由规则的条件
                  - Path=/user/** # 这个是按照路径匹配,只要以/user/开头就符合要求
                filters:
                  - AddRequestHeader=test, Test is test
      
    2. userservicecontroller层进行测试

      通过@RequestHeader获取请求头内容。如果请求头内容缺失会直接报错400

      public class UserController {
          /**
           * 路径: /user/110
           *
           * @param id 用户id
           * @return 用户
           */
          @GetMapping("/{id}")
          public User queryById(@PathVariable("id") Long id,@RequestHeader(value = "Test",required = true) String test) {
              System.out.println(test);
              return userService.queryById(id);
          }
          
          ...
      }
      
    3. 如果在控制台看见 Test is test成功打印说明路由过滤器生效。

4.3 默认过滤器

当我们有大量的服务路由,想要给它们同时实现添加过滤器,可以使用默认过滤器。默认过滤器是通过修改配置完成。

使用默认过滤器可以给所有的路由添加过滤器。对所有的路由都生效。通过default-filters来配置。

spring:
  cloud:
    gateway:
      default-filters:
        - AddRequestHeader=test, Test is test
  • 测试

    1. 网关设置配置

      spring:
        application:
          name: gateway
        cloud:
          nacos:
            server-addr: localhost:8848
          gateway:
            routes: # 网关路由配置
              - id: user-service # 路由id,自定义,只要唯一即可,给 gateway 使用
                # uri: http://127.0.0.1:8081 # 路由的目标地址 http就是固定地址
                uri: lb://userservice # 路由的目标地址 lb就是负载均衡,后面跟对应微服务的服务名称
                predicates: # 路由断言,也就是判断请求是否符合路由规则的条件
                  - Path=/user/** # 这个是按照路径匹配,只要以/user/开头就符合要求
            default-filters:
              - AddRequestHeader=test, Test is test
      
    2. userservicecontroller层进行测试

      public class UserController {
          /**
           * 路径: /user/110
           *
           * @param id 用户id
           * @return 用户
           */
          @GetMapping("/{id}")
          public User queryById(@PathVariable("id") Long id,@RequestHeader(value = "Test",required = true) String test) {
              System.out.println(test);
              return userService.queryById(id);
          }
          
          ...
      }
      
    3. 如果在控制台看见 Test is test成功打印说明默认过滤器生效。

5. 全局过滤器

我们通过配置文件能实现SpringCloud帮我们设置好的过滤器,但是面对一些复杂的业务需求的时候可能不足以应付。所以我们希望能够自己设置符合自己业务逻辑的过滤器。

5.1 全局过滤器的作用

全局过滤器的作用和Gateway的默认过滤器作用一样,是用来处理一切进入网关的请求和微服务响应的。

区别于GatewayFilter通过配置定义,处理逻辑是固定的。而GlobalFilter的逻辑需要自己写代码实现。下面我们就来写一下。

5.2 自定义全局过滤器

  1. 创建GlobalFilter接口实现类

    public class Login implements GlobalFilter {
        /**
         * 设置登录过滤器
         * @param exchange 请求上下文,里面可以获取Request、Response等信息
         * @param chain 用来把请求委托给下一个过滤器
         * @return 下一个过滤器
         */
        @Override
        public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
            return null;
        }
    }
    
  2. 编写逻辑

    需要将过滤器放入Spring容器当中才能调用

    @Component
    public class Login implements GlobalFilter {
        /**
         * 设置登录过滤器
         * @param exchange 请求上下文,里面可以获取Request、Response等信息
         * @param chain 用来把请求委托给下一个过滤器
         * @return 下一个过滤器
         */
        @Override
        public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
            // 1. 获取请求参数
            ServerHttpRequest request = exchange.getRequest();
            MultiValueMap<String, String> queryParams = request.getQueryParams();
            
            // 2. 获取参数中的username的参数
            String username = queryParams.getFirst("username");
            
            // 3. 判断参数值是否等于admin
            if ("admin".equals(username)) {
                // 4. 是,放行
                return chain.filter(exchange);
            }
            
            // 5. 否,拦截
            // 5.1 设置状态码
            exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
            // 5.2 拦截请求
            return exchange.getResponse().setComplete();
        }
    }
    
  3. 设置优先级

    过滤器先根据Order设置优先级的大小来选择执行的顺序,数字越小优先级越高。最小数字是-2147483647,最大数字是2147483647

    我们有两种方法设置过滤器优先级

    1. @Order注解

      我们可以通过在类上添加@Order注解来设置优先级

      @Component
      @Order(-1)
      public class Login implements GlobalFilter {
          
          ...
      }
      
    2. 实现Ordered接口

      我们通过重写Ordered接口的getOrder()方法根据其返回值大小来设置过滤器优先级大小

      @Component
      public class Login implements GlobalFilter , Ordered {
          
          ...
      
          @Override
          public int getOrder() {
              return -1;
          }
      }
      
  4. 测试

    当我们的请求后加上username=admin才能到达指定页面的时候,说明成功了。

5.3 过滤器顺序

请求进入网关会经过三个类型的过滤器:路由过滤器、Default-filterGlobalFilter。请求路由后,会将当前所有过滤器合并到一个过滤器链中,排序后依次执行每个过滤器。

GlobalFilter优先级是我们自己定义,而路由过滤器和Defult-filter过滤器则是由Spring指定,根据声明顺序由1开始递增。

当过滤器的优先级一样的时候,会根据Default-filter> 路由过滤器 > GlobalFilter

6. 跨域问题

6.1 什么是跨域问题

只要是域名或端口号发生改变便属于跨域。跨域问题就是浏览器禁止请求的发起者和服务端发生跨域ajax请求,请求被浏览器拦截的问题。我们通过CROS方案来解决。

6.2 什么是CROS

我们在日常开发中微服务之间的调用常常是跨域请求,出现跨域问题会非常不方便。所以CROS是一种解决浏览器禁止跨域ajax请求的方法。

  • 原理简述

    CROS是将请求分为简单请求和特殊请求。如果请求格式符合简单请求规则,就看服务端是否允许进行跨域ajax请求。如果是请求格式不符合见到那请求就定义为特殊请求,就会在特殊请求发送之前再发送一个预检请求,向服务端查询是否允许对应的请求发起者进行跨域ajax请求。

6.3 解决跨域问题

我们在网关的配置文件中修改

spring:
  cloud:
    gateway:
      # 。。。
      globalcors: # 全局的跨域处理
        add-to-simple-url-handler-mapping: true # 解决options请求被拦截问题
        corsConfigurations:
          '[/**]':
            allowedOrigins: # 允许哪些网站的跨域请求 
              - "http://localhost:8090"
            allowedMethods: # 允许的跨域ajax的请求方式
              - "GET"
              - "POST"
              - "DELETE"
              - "PUT"
              - "OPTIONS"
            allowedHeaders: "*" # 允许在请求中携带的头信息
            allowCredentials: true # 是否允许携带cookie
            maxAge: 360000 # 这次跨域检测的有效期

Docker

1. 初始Docker

  1. Docker和虚拟机的对比

    image-20220527170016844

    image-20220527170030545

    • docker是一个系统进程;虚拟机是在操作系统中的操作系统
    • docker体积小、启动速度快、性能好;虚拟机体积大、启动速度慢、性能一般
    • 在未来开发中,更加常用是用docker作为开发环境
  2. Docker如何解决大型项目依赖关系复杂,不同组件依赖的兼容性问题?

    • Docker允许开发中将应用、依赖、函数库、配置一起打包,形成可移植镜像
    • Docker应用运行在容器中,使用沙箱机制,相互隔离
  3. Docker如何解决开发、测试、生产环境有差异的问题

    • Docker镜像中包含完整运行环境,包括系统函数库,仅依赖系统的Linux内核,因此可以在任意Linux操作系统上运行