SpringCloud——Gateway(服务网关的学习和使用)

Posted *^O^*—*^O^*

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SpringCloud——Gateway(服务网关的学习和使用)相关的知识,希望对你有一定的参考价值。

什么是Spring Cloud Gateway

Spring Cloud Gateway不仅提供了统一的路由方式,并且还基于Filter链的方式提供了网关基本的功能,提供一种简单而有效的方法来路由到API,并为它们提供了跨领域的关注点,例如:安全性,监控/指标,限流等等。

什么是服务网关

API Gateway 是出现在系统边界上的一个面向API的,串行集中式的强管服务,边界可以理解为企业级的防火墙,主要起到 隔离外部访问与内部系统的作用,API网关是一个服务器,是系统对外的唯一接口。API网关封装了系统内部架构,为每个客户端定制的API,所有的客户端和消费端都通过统一的网关接入微服务,在网关层处理所有的业务功能。API网关并不是微服务场景中必须的组件。

为什么要使用网关

微服务的应用可能部署在不同的机房,不同的地区,不同的域名下,此时客户端(浏览器、手机,软件)想要访问对应的服务,都需要知道机器的具体的IP或者域名URL,当微服务实例众多时,对于客户端来说太复杂难以维护了,此时就有了网关,客户端相应的请求直接发送到网关,由网关根据请求标识解析判断出具体的微服务地址,再把请求转发到微服务实例,这其中的记忆功能就全部交由网关来操作了。

网关解决了什么问题

统一接入:为各种无线应用提供了统一接入服务,高性能,高并发,高可靠性,负载均衡,容灾切换(异地灵活)
协议适配:前端系统(http,http2)后端业务系统(RPC),长、短连接支持,根据前端请求路由至相应的SOA服务并执行,返回结果给前端
安全防护:和安全部合作,IP黑名单,URL黑名单,风控防刷,防恶意攻击等
流量监控:服务降级,熔断,路由-(异地多活中的应用)
网关应该具有以下功能
性能:API高可用,负载均衡,容错机制
安全:权限身份认证,脱敏,流量清洗,后端签名(保证全链路可行调用),黑名单(非法调用的限制)
日志:日志记录,一旦涉及分布式,全链路跟踪必不可少
缓存:数据缓存。
监控:记录请求响应数据,API耗时分析,性能监控。
限流:流量控制,错峰流控,可以定义多种限流规则。
灰度:线上灰度部署,可以减少风险。
路由:动态路由规则

Gateway实现API网关

路由(Route):路由是网关最基础的部分,路由信息由ID,目标URL,一组断言和一组过滤器组成,如果断言路由为真,则说明请求的URL的配置匹配
断言(Predicate):Spring Cloud Gateway中的断言函数允许开发者去定义匹配来自于HTTP Request中的任何信息,比如请求头和参数等;
过滤器(Filter):一个标准的Spring Web Filter。Spring Cloud Gateway中的Filter分为两种类型,分别是Gateway Filter 和Global Filter。过滤器将会对请求和响应进行处理

工作原理

客户端向Spring Cloud Gateway发出请求,再由网关处理程序Gateway Handler Mapping 映射确定与请求相匹配的路由,将其发送到网关Web处理程序 Gateway Web handler,该处理程序通过指定的过滤器链将请求发送到我们实际的服务执行业务逻辑,然后返回。过滤器由虚线分割的原因是,过滤器可以在发送代理请求之前和之后进行运行逻辑。所有pre过滤器逻辑均被执行。然后发出代理请求,发出代理请求之后,将运行post过滤器逻辑

环境搭建

kts文件

import org.jetbrains.kotlin.gradle.tasks.KotlinCompile

plugins 
    id("org.springframework.boot") version "2.3.7.RELEASE"
    id("io.spring.dependency-management") version "1.0.10.RELEASE"
    kotlin("jvm") version "1.6.0"
    kotlin("plugin.spring") version "1.6.0"


group = "com"
version = "0.0.1-SNAPSHOT"
java.sourceCompatibility = JavaVersion.VERSION_1_8

repositories 
    mavenCentral()


dependencies 
    implementation("org.springframework.boot:spring-boot-starter")
    implementation("org.jetbrains.kotlin:kotlin-reflect")
    implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
    testImplementation("org.springframework.boot:spring-boot-starter-test") 
        exclude(group = "org.junit.vintage", module = "junit-vintage-engine")
    
    // https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-dependencies
    implementation("org.springframework.cloud:spring-cloud-dependencies:Hoxton.SR12")
    // https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-gateway
    implementation("org.springframework.cloud:spring-cloud-starter-gateway:2.2.10.RELEASE")


tasks.withType<Test> 
    useJUnitPlatform()


tasks.withType<KotlinCompile> 
    kotlinOptions 
        freeCompilerArgs = listOf("-Xjsr305=strict")
        jvmTarget = "1.8"
    


这里Gateway不支持低版本的kotlin,我这里试了,1.6.0是可以的,需要注意的是,在使用Gateway时,不能导入web的依赖

application.yml

server:
  port: 9001

spring:
  application:
    name: gateway-server

  cloud:
    gateway:
      routes:  #路由规则
        - id: product-service       #路由ID
          uri: http://localhost:7070/    #目标URI,路由到微服务地址
          predicates:            # 断言
            - Path=/product/**     #匹配对应URL的请求,将匹配到的请求追加在目标URI之后

启动类无需加注解

路由规则

  cloud:
    gateway:
      routes:  #路由规则
        - id: product-service       #路由ID
          uri: http://localhost:7070/    #目标URI,路由到微服务地址
          predicates:            # 断言
            - Path=/product/**     #匹配对应URL的请求,将匹配到的请求追加在目标URI之后

Path

  cloud:
    gateway:
      routes:  #路由规则
        - id: product-service       #路由ID
          uri: http://localhost:7070/    #目标URI,路由到微服务地址
          predicates:            # 断言
            - Path=/product/**     #匹配对应URL的请求,将匹配到的请求追加在目标URI之后

Query

  cloud:
    gateway:
      routes:  #路由规则
        - id: product-service       #路由ID
          uri: http://localhost:7070/    #目标URI,路由到微服务地址
          predicates:            # 断言
            - Query=token        #匹配请求参数包含token的请求
#            - Query=token,abc.        #匹配请求参数包含token 并且其参数值满足正则表达式abc.的请求

Method

  cloud:
    gateway:
      routes:  #路由规则
        - id: product-service       #路由ID
          uri: http://localhost:7070/    #目标URI,路由到微服务地址
          predicates:            # 断言
            - Method=GET        #匹配任意的GET请求

Datetime

  cloud:
    gateway:
      routes:  #路由规则
        - id: product-service       #路由ID
          uri: http://localhost:7070/    #目标URI,路由到微服务地址
          predicates:            # 断言
            - After=2022-08-02T20:20:20.000+08:00[Asia/Shanghai] #匹配中国伤害事件2022-08-02 20:20:20之后的请求

RemoteAddr

  cloud:
    gateway:
      routes:  #路由规则
        - id: product-service       #路由ID
          uri: http://localhost:7070/    #目标URI,路由到微服务地址
          predicates:            # 断言
            - RemoteAddr=192.168.10.13/0  #匹配远程地址请求是RemoteAddr的请求,0表示子网掩码

Header

  cloud:
    gateway:
      routes:  #路由规则
        - id: product-service       #路由ID
          uri: http://localhost:7070/    #目标URI,路由到微服务地址
          predicates:            # 断言
            - Header=X-Request-Id, \\d+  #匹配请求头包含X-Request-Id 并且其值匹配正则表达式 \\d+ 的请求

动态路由(服务发现的路由规则)

面向服务的路由,根据serviceId自动从注册中心获取服务地址并转发请求

添加依赖

    // https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-consul-discovery
    implementation("org.springframework.cloud:spring-cloud-starter-consul-discovery:2.2.8.RELEASE")
// https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-actuator
    implementation("org.springframework.boot:spring-boot-starter-actuator:2.6.6")

动态获取URI

server:
  port: 9001

spring:
  application:
    name: gateway-server

  cloud:
    consul:
      # 注册中心地址
      host: localhost
      port: 8500
      # 服务提供者信息
      discovery:
        register: true      #是否注册
        instance-id: $spring.application.name-01   #注册实例id(必须唯一)
        service-name: $spring.application.name     #服务名称
        port: $server.port            #服务端口
        prefer-ip-address: true         #是否使用ip 地址注册
        ip-address: $spring.cloud.client.ip-address      # 服务请求IP

    gateway:
      routes:  #路由规则
        - id: service-provider       #路由ID
          uri: lb://service-provider   #lb:// 根据服务名称从注册中心获取服务请求地址
          predicates:            # 断言
            - Path=/product/**     #匹配对应URL的请求,将匹配到的请求追加在目标URI之后

服务名称转发
当有多个服务的时候,不可能一个一个的去写,使用SpringBoot约定大于配置

server:
  port: 9001

spring:
  application:
    name: gateway-server

  cloud:
    consul:
      # 注册中心地址
      host: localhost
      port: 8500
      # 服务提供者信息
      discovery:
        register: true      #是否注册
        instance-id: $spring.application.name-01   #注册实例id(必须唯一)
        service-name: $spring.application.name     #服务名称
        port: $server.port            #服务端口
        prefer-ip-address: true         #是否使用ip 地址注册
        ip-address: $spring.cloud.client.ip-address      # 服务请求IP

    gateway:
      discovery:
        locator:
          # 是否与服务发现组件结合,通过 serviceId 转发到具体的实例
          enabled: true        # 是否开启基于服务发现的路由规则
          lower-case-service-id: true   #是否将服务名称转为小写


这里本来的访问地址是localhost:9001/product/one/lalal,然后在其中加上服务名localhost:9001/service-provider/product/one/lalal

过滤器

网关过滤器GatewayFilter

Path路径过滤器:

可以实现URL的重写,通过重写URL可以实现隐藏实际路径提高安全性,易于用户记忆和键入,易于被搜索引擎收录等优点

RewritePathGatewayFilterFactory (支持正则) 将不能进去的换一下,就进去了

    gateway:
      routes:  #路由规则
        - id: service-provider       #路由ID
          uri: lb://service-provider   #lb:// 根据服务名称从注册中心获取服务请求地址
          predicates:            # 断言
            - Path=/product/**  , /api-gateway/**  #匹配对应URL的请求,将匹配到的请求追加在目标URI之后
          filters:          #网关过滤器
            # 将 /api-gateway/product/1 重写为 /product/1
            - RewritePath=/api-gateway(?<segment>/?.*),$\\segment

PrefixPathGatewayFilterFactory 将不够的加一下,就可以继续访问了

    gateway:
      routes:  #路由规则
        - id: service-provider       #路由ID
          uri: lb://service-provider   #lb:// 根据服务名称从注册中心获取服务请求地址
          predicates:            # 断言
            - Path=/**   #匹配对应URL的请求,将匹配到的请求追加在目标URI之后
          filters:          #网关过滤器
            # 将 /1 重写为 /product/1
            - PrefixPath=/product

StripPrefixGatewayFilterFactory 删除多余的进行访问

    gateway:
      routes:  #路由规则
        - id: service-provider       #路由ID
          uri: lb://service-provider   #lb:// 根据服务名称从注册中心获取服务请求地址
          predicates:            # 断言
            - Path=/**   #匹配对应URL的请求,将匹配到的请求追加在目标URI之后
          filters:          #网关过滤器
            # 将 /api/123/product/1 重写为 /product/1
            - StripPrefix=2

SetPathGatewayFilterFactory 对访问的路径进行匹配修改

    gateway:
      routes:  #路由规则
        - id: service-provider       #路由ID
          uri: lb://service-provider   #lb:// 根据服务名称从注册中心获取服务请求地址
          predicates:            # 断言
            - Path=/api/product/segment   #匹配对应URL的请求,将匹配到的请求追加在目标URI之后
          filters:          #网关过滤器
            # 将 /api/product/1 重写为 /product/1
            - SetPath=/product/segment

Parameter参数过滤器

    gateway:
      routes:  #路由规则
        - id: service-provider       #路由ID
          uri: lb://service-provider   #lb:// 根据服务名称从注册中心获取服务请求地址
          predicates:            # 断言
            - Path= /api-gateway/**  #匹配对应URL的请求,将匹配到的请求追加在目标URI之后
          filters:          #网关过滤器
            # 将 /api-gateway/product/1 重写为 /product/1
            - RewritePath=/api-gateway(?<segment>/?.*),$\\segment
            # 在下游请求中添加 flag=1
            - AddRequestParameter=flag, 1

Status状态过滤器

    gateway:
      routes:  #路由规则
        - id: service-provider       #路由ID
          uri: lb://service-provider   #lb:// 根据服务名称从注册中心获取服务请求地址
          predicates:            # 断言
            - Path=/api-gateway/**  #匹配对应URL的请求,将匹配到的请求追加在目标URI之后
          filters:          #网关过滤器
            # 将 /api-gateway/product/1 重写为 /product/1
            - RewritePath=/api-gateway(?<segment>/?.*),$\\segment
            # 任何情况下,响应的HTTP请求状态都将设置为404
            - SetStatus=404

全局过滤器GlobalFilter

全局过滤器不需要再配置文件中配置,作用在所有的路由上

自定义过滤器

自定义网关过滤器

package com.gateway.filter

import org.springframework.cloud.gateway.filter.GatewayFilter
import org.springframework.cloud.gateway.filter.GatewayFilterChain
import org.springframework.core.Ordered
import org.springframework.web.server.ServerWebExchange
import reactor.core.publisher.Mono

class CustomGatewayFilter : GatewayFilter,Ordered 

    /**
     * 过滤器业务逻辑
     */
    override fun filter(exchange: ServerWebExchange?, chain: GatewayFilterChain?): Mono<Void> 
        println("自定义网关过滤器被执行")
        return chain!!.filter(exchange)
    

    /**
     * 过滤器执行顺序,数值越小,优先级越高
     */
    override fun getOrder(): Int 
        return 0
    

在上述文件中写过滤规则就可以了


import com.gateway.filter.CustomGatewayFilter
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
class GatewayRoutesConfiguration 

    @Bean
    fun routeLocator(builder:RouteLocatorBuilder):RouteLocator
        return builder.routes().route  r ->
            r
                    .path("/product/**")
                    .uri("lb://product-service")
                    .filters(CustomGatewayFilter())
                    .id("product-service")
        
                .build()

    

自定义全局过滤器

import org.springframework.cloud.gateway.filter.GatewayFilterChain
import org.springframework.cloud.gateway.filter.GlobalFilter
import org.springframework.core.Ordered
import org.springframework.stereotype.Component
import org.springframework.web.server.ServerWebExchange
import reactor.core.publisher.Mono

@Component
class CustomGlobalFilter : GlobalFilter , Ordered 


    /**
     * 过滤器业务逻辑
     */
    override fun filter(exchange: ServerWebExchange?, chain: GatewayFilterChain?): Mono<Void> 
        println("自定义网关过滤器被执行")
        return chain!!.filter(exchange)
    

    /**
     * 过滤器执行顺序,数值越小,优先级越高
     */
    override fun getOrder(): Int 
        return 0
    

在全局网关过滤器中通过token判断用户是否登录,完成一个统一鉴权案例

网关限流

为什么要限流:
用户增长过快;因为某个热点事件,竞争对象爬虫,恶意的请求

限流算法

计数器算法
漏桶算法
令牌桶算法(gateway使用)


Gateway限流
SpringCloudGateway 官方提供了 RequestRateLimiterGatewayFilterFactory 过滤器工厂,使用Redis加Lua脚本实现了令牌桶算法

高可用网关

实现高可用,数据的冗余备份,服务的失效转移
需要使用nginx+网关进行集群配置,Nginx进行轮询或者别的方式,进行网关的选择

一个请求过来,首先经过Nginx的一层负载,到达网关,然后由网关负载到真实后端,若后端有问题,网关会进行重试访问,多次访问后返回失败,可以通过熔断或服务降级立即返回结果。由于是负载均衡,网关重试时不一定会访问到出错的后端。

以上是关于SpringCloud——Gateway(服务网关的学习和使用)的主要内容,如果未能解决你的问题,请参考以下文章

SpringCloud --- 服务网关 (Gateway)

SpringCloud --- 服务网关 (Gateway)

重学SpringCloud系列九微服务网关-GateWay

十一SpringCloud实用篇_Gateway服务网关

十一SpringCloud实用篇_Gateway服务网关

SpringCloud学习笔记-p4(Gateway服务网关)