当前位置:   article > 正文

Spring Cloud Alibaba微服务架构实战教程—15最详细的Gateway统一网关_springcloud gateway+springcloudalibab微服务网关统一鉴权

springcloud gateway+springcloudalibab微服务网关统一鉴权

前言

在分布式微服务架构中,会将服务进行拆分,不同的服务负责各自的业务功能,拆分后也有一个问题?每一个服务都有自己的服务名、ip、端口等,服务越多数量越多时,这样怎么记忆这么多的URL呢? 此外,一些公共性的功能(如认证、鉴权、服务流控等)需要重复在各子模块中自身实现,造成的代码冗余怎么办??
网关图
如图,诞生了一个统一网关,它将所有子服务封装起来,外部请求服务时,由网关统一调配URL和转发各个请求到不同的微服务去,并且可以在网关层针对所有公共性的功能作统一的处理,避免冗余。

一、网关简介

网关介绍:

  • 网关:流量请求的入口
  • 功能:服务网关 = 路由转发+过滤器。
  • 路由转发:接受一切外界的请求,转发到后端的微服务上去。
  • 过滤器:在网关中可以完成一系列的横切功能,如权限校验,限流,及监控等。(其实路由转发也是个过滤器)

本案例项目中,网关的作用:

  • Smartcat-project 项目中,后台请求会先访问到API网关,然后网关通过注册中心实时感知各个微服务的状态及路由地址,最后准确地将请求路由到具体的服务中去处理。
    如图:
    API网关工作图

既然网关作用这么大,我们该怎么实现它呢?就可以借用一下开源组件了。

  • 第一代网关Zuul,阻塞式,不支持websockets长连接,是netflix公司项目。(底层是servlet,Zuul处理的是http请求)
  • 第二代网关Gateway 是基于Netty,由Cloud官方提供,使用的是异步IO,非阻塞式,性能较Zuul提升1.6倍不止。

二、Gateway网关组件

注意:GateWay跟 Servlet 不兼容,不能打成war包,工程中不能出现Servlet的组件。

根据 官方的原理图
在这里插入图片描述
可知,网关原理大致如下:

  • 请求到达网关后,先经过断言Predicate(主要用来判断一个参数是否符合要求);

  • 判断是否符合某个路由的规则?如果符合,则按规则路由到指定地址,如不符合,则退回请求;

  • 请求和响应可以通过过滤器Filter进行配置,过滤I一些不正常的请求和异常返回。

使用gateway之后,客户端只需要记住一个gateway的地址即可,类似于java变量的作用,用来标识内存地址。不用记录地址,只需要记录变量名称,也好比学校的传达室,从此由他来传达请求。

三、项目集成gateway网关

由于geateway是一个相对独立的个体,而且作用和其安全性不言而喻,因此在我们当前的项目中,集成gateway网关,我们采用再建立一个单独的子模块,实现网关功能。

3.1.创建Gateway子模块

创建Gateway 微服务,取名smartcar-gateway,如下所示:
创建网关微服务

3.2.引入Gateway依赖

在其pom.xml中,引入Gateway依赖,与父类关联。

<parent>
    <groupId>com.smart.car.root</groupId>
    <artifactId>smartcar-project</artifactId>
    <version>0.0.1-SNAPSHOT</version>
</parent>
<!--引入gateway-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

注意:记得如果自动生成的项目中,存在web依赖,请删除掉。因为Gateway跟 Servlet 不兼容。(你知道为什么吗?)

3.3.在父类中聚合Gateway模块

在父类的pom.xml中,增加聚会服务的标识。
聚合网关服务
这样打包的时候,会起到聚合作用。

3.4.添加配置文件

Gateway网关的路由过滤器有两种配置方式:

  • 1.在配置文件yml中配置
  • 2.代码中注入RouteLocator的Bean(用编码的方式实现路由映射)

这种用编码方式实现gateway进行路由映射的配置方法,自行百度即可,具体不表述。 但是有一点需要记住,这两种方式都是不支持动态配置的。(在本案例中,采用第一种配置方案)

将application.properties改为application.yml。

server:
  port: 8008
spring:
  application:
    name: samrtcar-geteway
  cloud:
    #gateway config
    gateway:
      discovery:
        locator:
          enabled: true  #开启网关映射
          lower-case-service-id: true #将请求路径上的服务名配置为小写
          filters:  #去掉path的前缀
            - StripPrefix=1
      #配置路由数组
      routes:
        #member子服务
        - id: member_service      #自定义唯一名称-限流时用到
          uri: http://localhost:8003  #映射提供服务的uri
          predicates:
            - Path= /member/**   #断言,映射路径
#          filters:             #去掉前缀,视自身URL而定
#            - StripPrefix=1
        #路由实例
        - id: route_baidu
          uri: http://www.baidu.com
          predicates:
            - Query= url,baidu

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

注意,写的是URI不是URL,你知道两者区别么?我们在配置中,指定当Path中含有member/路径时候,就会路由到localhost:8003的微服务去,也就是member项目的接口去。这样就实现了路由的转发。

参数说明:

  • StripPrefix=1,是去掉URL的前缀,如果你发现你访问地址老是404,请检查是否需要开启去前缀。

  • Predicates(断言):一个Java 8的定义。 作用:符合 Predicate 条件的,就使用该路由配置,否则就不管。(用来判断是否符合规范的)。

3.5.启动项目测试

启动member项目和gateway项目,访问URL。

目前有两种方式可以访问接口,一种是直接访问原服务的URL,一种是访问网关,网关再路由到对应服务URL去。

访问效果:
网关访问
其次,只要当请求中包含 url=baidu的就会进行匹配和路由到baidu网站了。

测试效果:
CURL测试

上面这种做法是直接在配置文件中配置了路由地址, 但是有个问题?到此,依然可以直接访问其目标服务的接口,那以后别人绕开网关直接访问URL怎么办? 所以就不让直接请求具体的服务,请求都走网关,就是只开放网关的端口,其它微服务的端口都不对外开放-需要直接隐藏起来。

微服务开发,各种服务都是用nacos注册中心来统一管理的,nacos中通过服务名也有映射,那现在我们应该让 gateway 直接去 nacos 中发现服务 ,然后再自动转发到对应的服务去。

四、Gateway结合Nacos

4.1.引入Nacos组件

因common模块引入了nacos注册中心组件,所以我们可以直接引用common模块。

<!--引入公共工具微服务-->
<dependency>
    <groupId>com.smart.car.common</groupId>
    <artifactId>smartcar-common</artifactId>
    <version>0.0.1-SNAPSHOT</version>
   <exclusions>
       <!--排出servlet容器-->
       <exclusion>
           <groupId>org.apache.tomcat.embed</groupId>
           <artifactId>tomcat-embed-core</artifactId>
       </exclusion>
   </exclusions>
</dependency>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

注意:Gateway跟 Servlet 不兼容,所以网关服务中不能出现spring-web类的依赖。(你知道为什么吗?)

4.2.添加开启nacos注解

启动类上添加开启nacos的@EnableDiscoveryClient注解

@RefreshScope
@EnableDiscoveryClient
@SpringBootApplication
public class SmartGatewayApplication {
    public static void main(String[] args) {
        SpringApplication.run(SmartGatewayApplication.class, args);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

4.3.配置路由方式

在yml中配置nacos地址,还需要配置新的路由方式。

在gateway中配置uri配置有三种方式,包括

  • 第一种:websocket方式: uri: ws://localhost:9000
  • 第二种:http方式: uri: http://localhost:8130/
  • 第三种:lb(注册中心中服务名字)方式: uri: lb://brilliance-consumer

其中ws和http方式不容易出错,因为http格式比较固定,但是lb方式比较灵活自由,且使用 uri: lb:// 方式配置时,服务名有特殊要求(见文末)。

  • lb: //后面的是其他服务注册在nacos上的名称,也就是spring.applicaiton.name属性。

我们采用lb的方式配置,如下:

server:
  port: 8008
spring:
  application:
    name: samrtcar-geteway
  cloud:
    #nacos config
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
        register-enabled: true
    #gateway config
    gateway:
      discovery:
        locator:
          enabled: true  #开启网关映射
          lower-case-service-id: true #将请求路径上的服务名配置为小写
          filters:            #去掉path的前缀
            - StripPrefix=1
      #配置路由数组
      routes:
        #member子服务
        - id: member_route      #自定义唯一名称
          uri: lb://smartcar-member  # 映射提供服务的uri
          predicates:
            - Path= /member/**   #断言,映射路径
#          filters:             #去掉前缀,视URL而定
#            - StripPrefix=1
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

当选择了nacos做服务的映射,则不需要考虑前缀问题,会自动处理。

4.4.测试lb路由方式

启动member8003和80013两个服务和gateway8008一个服务,如图:
启动项目测试
然后查看naocs注册列表:
nacos注册列表
通过网关访问接口:http://localhost:8008/member//list

你会发现在,接口在 轮询 请求8003和8004端口的member服务,不光证明了可以通过网关调用服务,还证明了相同服务的负载均衡成功。
网关调用
这样以后,前端直接访问网关,不必再关心子服务的服务名、服务端口,路由地址等情况,即使是我们修改了其他服务的端口号,也不影响前端的调用。

五、网关服务熔断

网关虽然仅仅是转发路由到对应服务,但是也是请求的入口处,如果对应的服务宕机了,请求就会一致堆积在网关处,为了不引起网关的阻塞,我们需要在网关处,配置熔断机制。

本实例降级方案,就采用hystrix来快速熔断吧。在前文中我说过,真正的业务开发上,很多都是多组件相互协助,并不是仅仅靠谁就可以独立完成。

5.1.采用hystrix快速熔断

因此,网关的熔断,我们用hystrix来快速完成工作。

1.引入hystrix依赖。

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
  • 1
  • 2
  • 3
  • 4

2.设置熔断超时时间设置

#hystrix超时时间,默认时间为1000ms
hystrix:
  command:
    default:
      execution:
        isolation:
          thread:
            timeoutInMilliseconds: 2000
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

3.新增降级兜底方法

熔断后,会由gateway层提供一个快速失败的方法返回给请求的调用方,我们也可称为降级过滤器

@Slf4j
@RestController
@RequestMapping("error")
public class FallbackController {

    @RequestMapping("/fallback")
    public ResponseResult<String> fallback() {
        ResponseResult<String> result = new ResponseResult<>();
        log.error("Invoke service failed...");
        result.setCode(429);
        result.setMsg("对应的服务调用失败,快速熔断,进入fallback降级方法。");
        return result;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

4.配置指定的过滤器

并在yml配置文件中指向该降级过滤器

#member子服务
- id: member-service      #自定义唯一名称-限流时用到
  uri: lb://smartcar-member  # 映射提供服务的uri
  predicates:
    - Path= /member/**   #断言,映射路径
  filters:
    #结合Hystrix对服务宕机做fallback机制(降级过滤器)
    - name: Hystrix
      args:
        name: fallback
        fallbackUri: forward:/error/fallback
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

还有一种情况,假如服务是暂时的不可用,发起重试之后又能调用正常返回结果,则可以设置重试次数,来确保服务的可用性,我们称为重试过滤器

配置如下:

fallbackUri: forward:/error/fallback
 #重试次数(重试过滤器)(若两者都配置了,降级过滤器需要配在重试过滤器之前)
- name: Retry
  args:
    #重试3次,加上初次访问,正确执行应当是4次访问
    retries: 3
    statuses:
      - OK
    methods:
      - GET
      - POST
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

这就需要你在对应的调用方法上,人为增加一个异常,才会触发。本案例不详说。

最终给出完整的yml内容:

server:
  port: 8008
  servlet:
    context-path: /gateway
#logback配置
logging:
  level:
    root: info
    #关闭nacos心跳日志
    com.alibaba.nacos.client.*: WARN
    #org.springframework:cloud.gateway: debug
  file:
    name: ./tmp/gateway.log

spring:
  application:
    name: samrtcar-geteway
  cloud:
    #nacos config
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
        register-enabled: true
    #gateway config
    gateway:
      #开启网关映射
      discovery:
        locator:
          enabled: true
          lower-case-service-id: true  #将请求路径上的服务名配置为小写
          filters:                  #去掉path的前缀,视自身URL而定
            - StripPrefix=1
      #配置路由数组
      routes:
        #member子服务
        - id: member-service      #自定义唯一名称-限流时用到
          uri: lb://smartcar-member  # 映射提供服务的uri
          predicates:
            - Path= /member/**   #断言,映射路径
          filters:
            #结合Hystrix对服务宕机做fallback机制(降级过滤器)
            - name: Hystrix
              args:
                name: fallback
                fallbackUri: forward:/error/fallback
            #重试次数(重试过滤器)(若两者都配置了,位置不可变)
            - name: Retry
              args:
                #重试3次,加上初次访问,正确执行应当是4次访问
                retries: 3
                statuses:
                  - OK
                methods:
                  - GET
                  - POST
        #points子服务
        - id: points-service      #自定义唯一名称-限流时用到
          uri: lb://smartcar-points  # 映射提供服务的uri
          predicates:
            - Path= /points/**   #断言,映射路径

#hystrix超时时间,默认时间为1000ms
hystrix:
  command:
    default:
      execution:
        isolation:
          thread:
            timeoutInMilliseconds: 2000
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69

5.启动gateway项目,停掉member项目,让其宕机不可用,如果调用返回错误,或者超时的话,进入降级处理,如果调用成功,则返回结果。
fallback回退

这样,如果遇到服务宕机或者超时情况,gateway网关就不会再去请求该服务,会直接调用对应的fallback方法,实现快速响应。

六、网关服务限流

sentinel可以作为各微服务的限流,也可以作为gateway网关的限流组件。
虽然spring cloud gateway自带了限流功能,但此处用sentinel来作为替待,可以更好的管控。

SpringCloud Gateway原生限流,主要基于过滤器实现,我们可以直接使用内置的过滤器RequestRateLimiterGatewayFilterFactory。这里就不说了,因为大部分情况下,是不采用它的。

采用Sentinel服务限流

本段落介绍Spring Cloud Gateway集成Sentinel和Nacos实现网关的动态限流。

Sentinel从 1.6.0 版本之后,提供两种资源维度的限流:

  • Route 维度:即在 Spring 配置文件中配置的路由条目,资源名为对应的 routeId自定义
  • API 维度:用户可以利用 Sentinel 提供的 API 来自定义一些 API 分组。

1.引入依赖如下:

<!--集成sentinel持久化到nacos-->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<!--集成sentinel网关层限流start-->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId>
</dependency>
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<!--sentinel限流规则持久化 -->
<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
<!--集成sentinel网关层限流end-->
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

由于需要使用 nacos作为sentinel的配置中心,所以也引入了sentinel-datasource-nacos 引入nacos分布式配置后,必须在bootstrap.yml文件中声明,否则无法获取相应的数据。

2.新建bootstrap.yml:

#nacos配置中心
spring:
  cloud:
    nacos:
      config:
        server-addr: 127.0.0.1:8848
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

3.在application.yml文件中添加对应的sentinel与集成的功能

spring:
  cloud:
    sentinel:
      transport:
        dashboard: 10.0.10.48:8858
      eager: true
      datasource:
        ds:
          nacos:
            server-addr: 10.0.10.48:8848
            data-id: gateway-sentinel-flow
            group-id: DEFAULT_GROUP
            rule-type: gw-flow
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

这里主要是sentinel的相关配置,从nacos配置中心获取 gateway-sentinel-flow 配置文件,限流类型是网关类型gw-flow或者是flow。

参数说明如下:

  • data-id:可以使用了${spring.application.name}变量,方便区分不同应用的配置。
  • data-type:指配置项的内容格式,提供了 JSON 和 XML 两种格式,
  • rule-type:表示数据源中规则属于哪种类型,如 flow、degrade、param-flow、gw-flow 等。

4.编写简单的测试类

@Slf4j
@RestController
@RequestMapping("sentinel")
public class SentinelTestController {
    @PostMapping("flowTest")
    public String test() {
        String dString = UUID.randomUUID().toString();
        log.info("产生的结果是:{}",dString);
        return dString;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

5.限流配置

前一篇,写了可以在nacos中配置流控规则,然后会自动同步到sentinel中去,因此我们也在试试效果。在nacos控制台中建立对应的配置文件smartcar-gateway-sentinel-nacos,格式为json格式,如下内容:

[
  {
    "resource": "member-service", //服务路由的名称
    "limitApp": "default",
    "grade": 1,
    "count": 2,
    "strategy": 0,
    "controlBehavior": 0,
    "clusterMode": false
  },
  {
    "resource": "flowTest", //网关本地接口的名称
    "limitApp": "default",
    "grade": 1,
    "count": 3,
    "strategy": 0,
    "controlBehavior": 0,
    "clusterMode": false
  }
]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

网关限流规则,如图:
网关配置
即使服务进行重启,nacos中的限流规则也不会再丢失。

6.配置完成以后启动网关项目,登录sentinel控制台,查看限流规则:
网关限流规则
对应关系:
限流的对应id

7.运行项目,测试限流结果,发起get请求:http://localhost:8008/member/list
jmemter并发请求
当触发限流后页面显示的是Blocked by Sentinel: FlowException。
(这个原理是DefaultBlockRequestHandler,实现了BlockRequesthandler接口)

100个并发中,只要前两个成功,其它都被拦截,也可以直接在sentinel控制台中看到直观的监测结果:
限流效果图

关于在 Gateway 用 Sentinel还是 Hystrix 的问题?

  1. 在 Sentinel 控制台对 网关模块 进行具体的限流可视化配置,方式比较友好。
  2. 直接用 Gateway 内置的 RequestRateLimiter 跟 Hystrix 进行熔断配置比较方便快速。

其实两种可以搭配使用的,一个做限流的,一个做快速失败的,没有什么工具是全面的,懂得配合就好。

关于网关的限流或熔断更多的方式配置,可参考 sentinel下网关规则配置

小总结:

  • 网关的熔断降级:在分布式系统中,网关作为流量的入口,大量请求进入网关,向后端远程系统或服务发起调用,后端服务不可避免的会产生调用失败(超时或异常),失败时不能让请求堆积在网关上,需要快速失败并返回回去,这就需要在网关上做熔断、降级操作。

  • 网关的限流:网关上有大量请求,对指定服务进行限流,可以很大程度上提高服务的可用性与稳定性,限流的目的是通过对并发访问/请求进行限速,或对一个时间窗口内的请求进行限速来保护系统。一旦达到限制速率则可以拒绝服务、排队或等待、降级操作。

七、自定义网关的响应异常

当触发限流后,接口显示的是 Blocked by Sentinel: FlowException。 为了展示更加友好的限流提示, Sentinel支持自定义异常处理。同样,也有两种方案可以使用。

7.1.方案一:配置文件

properties、yml配置文件类

spring.cloud.sentinel.scg.fallback.mode = response
spring.cloud.sentinel.scg.fallback.response-body = '{"code":429,"mes":"限流了"}'
  • 1
  • 2

或:

spring:
  cloud:
    sentinel:
      scg:
        fallback:
          mode: response
          response-body: '{"code":403,"msg":"不好意思,请稍后再试(您被限流了)"}'
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

7.2.方案二:配置类注入Bean

import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.BlockRequestHandler;
import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.GatewayCallbackManager;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.server.ServerResponse;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import javax.annotation.PostConstruct;
import java.util.HashMap;

@Configuration
public class GatewayConfiguration {

    //自定义限流异常页面
    @PostConstruct
    public void initBlockHandlers(){
        BlockRequestHandler blockRequestHandler = new BlockRequestHandler() {
            @Override
            public Mono<ServerResponse> handleRequest(ServerWebExchange serverWebExchange, Throwable throwable) {
                Map map = new HashMap();
                map.put("code",0);
                map.put("msg","被限流了");
                return ServerResponse.status(HttpStatus.OK)
                        .contentType(MediaType.APPLICATION_JSON)
                        .body(BodyInserters.fromObject(map));
            }
        };
        GatewayCallbackManager.setBlockHandler(blockRequestHandler);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

图示:
自定义网关异常
当触发流控规则后,就会达到自定义异常的效果了。(图片是后补的,没有user)

其实,有两种方式来实现网关这层的限流。

  • 第一种:基于路由的限流 ,就是上述说写的。
  • 第二种:基于 API 分组限流(基于路由配置)。这个留给读者去探索把

八、网关Cors跨域支持

系统架构基本是前、后端独立部署,到时候会直接引发一个问题:跨域请求

前端通过网关入口,去调用其它微服务时,通常会出现如下错误:

Access to XMLHttpRequest at 'xxx' from origin 'xxx' has been been blocked by CORS policy
  • 1

所以,必须要在网关层配置支持跨域,不然无法将请求路由到正确的处理节点,常见的有两种解决方式。

8.1第一种,代码方式

新增一个配置类配置

@Configuration
public class CORSConfiguration {

    @Bean
    public CorsWebFilter corsWebFilter() {
        CorsConfiguration config = new CorsConfiguration();
        config.setAllowCredentials(Boolean.TRUE);
        //config.addAllowedMethod("*");
        config.addAllowedOrigin("*");
        config.addAllowedHeader("*");
        config.addExposedHeader("setToken");

        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(new PathPatternParser());
        source.registerCorsConfiguration("/**", config);

        return new CorsWebFilter(source);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

8.2.第二种,注解方式

只需要在 application.yml 配置文件中添加:

spring:
  cloud:
    gateway:
      globalcors:
        cors-configurations:
          '[/**]':
            allowedHeaders: "*"
            allowedOrigins: "*"
            allowedMethods:
              - GET
              - POST
              - PUT
              - DELETE
              - OPTION
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

(最简单的方式。 本文也是采用的这种。)

就注意几点:

  1. 既在网关里边来解决跨域问题,就不能在后面的其他服务里边,再重复引入解决跨域的配置,否则会导致跨域失效,出现错误。

  2. 若要求跨域请求必须携带cookie时,则请求头中需要设置"Access-Control-Allow-Credentials:true" 这个属性。

  3. allowedOrigins(“*”) 中的 *号,不能和allowCredentials(true) 同时存在。


好了,到这里,本章节就讲完了,关于Gateway网关,还是有很多值得单独去学习的地方,希望读者朋友们,可以自行多学习和大胆尝试。


小课堂

1.启动网关报错:Failed to configure a DataSource: ‘url’ attribute is not specified and no embedded datasource could be configured.

报错信息
那是因为我们的common包中包含了mysql依赖,但是你又没在yml指定mysql的连接信息。 因为我们网关不需要配置,只需要在启动类上声明一下即可:

@SpringBootApplication(exclude= {DataSourceAutoConfiguration.class})
  • 1

2.uri为什么前面要加lb:呢?

spring:
  cloud:
    gateway:
      routes:       #路由配置
          uri: lb://provider #目标路径
  • 1
  • 2
  • 3
  • 4
  • 5

因为在org.springframework.cloud.gateway.discovery.DiscoveryLocatorProperties类中限制了前缀。
原理图
注意,它有2个特殊要求:

  • 1.能被gateway的lb方式识别到的命名规则为:

    "[a-zA-Z]([a-zA-Z]|\\d|\\+|\\.|-)*:.*"
    
    • 1

    这也意味着,java命名规范中可以使用的英文下划线(“_”),在这里会不能识别。
    你会见到这样的报错异常信息:

    java.lang.IllegalStateException: Invalid host: lb://brilliance_consumer
    
    • 1
  • 2.lb方式用的是注册中心,只要你的服务能注册到同一个注册中心,就没问题,不在同一个注册中心,无法调通。 要求gateway和其他微服务在一个注册中心中

3.在nacos + gateway的基础上,如何实现负载平衡?关掉其中一个provider,立即通过gateway进行轮询,发现gateway仍然会call已经down掉的provider,导致查询失败。但在大约5秒后,轮询恢复正常,不再call已经挂掉的prodiver,这个现象是为什么?

  • 因为gateway call provider流程是:请求会先经过网关,网关根据nacos注册中心获取服务地址,但nacos心跳机制默认每5秒钟检查一次provider是否正常?当服务挂掉时,nacos还未更新,导致gateway仍然去轮询挂掉的服务。因此就会出现上面现象。

4.什么是Api分组?作用是什么?

  • API 分组:比如我们可以定义一个 API 叫 my_api,请求 path 模式为 /A/ 和 /B/ 的都归到 my_api 这个 API 分组下面,对这个api进行限流,就对A和B接口路径都限流了。 (如果想让两个路由共用一个限流规则,就可以用这个方式)

ps: 最后说两点小常识:

1.网关的核心概念就是路由配置和路由规则,且作为所有请求流量的入口,在实际生产环境中要保证高可靠和高可用,尽量要避免重启,所以实现动态路由是非常有必要的;

2.Api网关结合ribbon完成负载均衡和动态路由,还是比较复杂的,我不建议刚入门的朋友学习,所以不写在本教程中了。将会单独写成博客,感兴趣的朋友单独搜索即可。

(内容看来有点多,后期我将考虑拆分多章节来讲吧,如果效果不好的话,有什么意见请评论区踊跃提出~)

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/很楠不爱3/article/detail/301978
推荐阅读
相关标签
  

闽ICP备14008679号