跟着老侯玩编程 跟着老侯玩编程
首页
  • 基础语法
  • 网络编程
  • 设计模式
  • 基础篇
  • 进阶篇
  • 框架篇
  • Redis
  • Alibaba
  • 课程目录
  • 码农宝典
留言
首页
  • 基础语法
  • 网络编程
  • 设计模式
  • 基础篇
  • 进阶篇
  • 框架篇
  • Redis
  • Alibaba
  • 课程目录
  • 码农宝典
留言
  • Redis

    • 基础环境
    • Redis详解
    • 快速列表
    • 链表
    • 跳跃表
    • 压缩列表
    • Redis配置文件详解
    • Ruby环境
  • SpringCloudAlibaba

    • 微服务-注册中心
    • 微服务-负载均衡
    • 微服务-微服务调用
    • 微服务-Sentinel
    • 微服务-配置中心
    • 微服务-网关
      • 简述
        • Spring Cloud Gateway
        • 核心概念
        • 工作原理
      • 快速开始
      • 集成Nacos
      • 路由断言工厂
        • 基于Datetime类型的断言工厂
        • 基于远程地址的断言工厂
        • 基于Cookie的断言工厂
        • 基于Header的断言工厂
        • 基于Host的断言工厂
        • 基于Method请求方法的断言工厂
        • 基于Path请求路径的断言工厂
        • 基于Query请求参数的断言工厂
        • 基于路由权重的断言工厂
      • 自定义路由断言工厂
      • 过滤器工厂
        • 为匹配的路由统一添加前缀
        • 自定义过滤器工厂
      • 全局过滤器
        • 自定义全局过滤器
      • gateway整合sentinel
        • 控制台实现方式
  • 分布式
  • SpringCloudAlibaba
舞动的代码
2022-08-30
目录

微服务-网关

# 简述

img.png

这样的架构,会存在着诸多的问题

每个业务都会需要鉴权、限流、权限校验、跨域等逻辑,如果每个业务都各自为战,自己造轮子实现一遍,会很蛋疼,完全可以抽出来,放到一个统一的地方去做。

如果业务量比较简单的话,这种方式前期不会有什么问题,但随着业务越来越复杂,比如淘宝、亚马逊打开一个页面可能会涉及到数百个微服务协同工作,如果每一个微服务都分配一个域名的话,一方面客户端代码会很难维护,涉及到数百个域名,另一方面是连接数的瓶颈,想象一下你打开一个APP,通过抓包发现涉及到了数百个远程调用,这在移动端下会显得非常低效。

后期如果需要对微服务进行重构的话,也会变的非常麻烦,需要客户端配合你一起进行改造,比如商品服务,随着业务变的越来越复杂,后期需要进行拆分成多个微服务,这个时候对外提供的服务也需要拆分成多个,同时需要客户端配合你进行改造

API网关,就是指系统的统一入口,它封装了应用程序的内部结构,为客户端提供统一服务,一些与业务本身功能无关的公共逻辑可以在这里实现,诸如认证、鉴权、监控、路由转发等等

img.png

# Spring Cloud Gateway

Spring Cloud Gateway 是Spring Cloud官方推出的第二代网关框架,定位于取代 Netflix Zuul1.0。相比 Zuul 来说,Spring Cloud Gateway 提供更优秀的性能,更强大的有功能。

Spring Cloud Gateway 是由 WebFlux + Netty + Reactor 实现的响应式的 API 网关。它不能在传统的 servlet 容器中工作,也不能构建成 war 包。

Spring Cloud Gateway 旨在为微服务架构提供一种简单且有效的 API 路由的管理方式,并基于 Filter 的方式提供网关的基本功能,例如说安全认证、监控、限流等等。

在SpringCloud微服务体系中,有个很重要的组件就是网关,在1.x版本中都是采用的Zuul网关;但在2.x版本中,zuul的升级一直跳票,SpringCloud最后自己研发了一个网关替代Zuul,那就是SpringCloud Gateway

官方文档:https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#gateway-request-predicates-factories

# 核心概念

  • 路由(route)

路由是网关中最基础的部分,路由信息包括一个ID、一个目的URI、一组断言工厂、一组Filter组成。如果断言为真,则说明请求的URL和配置的路由匹配。

  • 断言(predicates)

Java8中的断言函数,SpringCloud Gateway中的断言函数类型是Spring5.0框架中的ServerWebExchange。断言函数允许开发者去定义匹配Http request中的任何信息,比如请求头和参数等。

  • 过滤器(Filter)

SpringCloud Gateway中的filter分为Gateway FilIer和Global Filter。Filter可以对请求和响应进行处理。

# 工作原理

img.png

执行流程大体如下:

  • Gateway Client向Gateway Server发送请求

  • 请求首先会被HttpWebHandlerAdapter进行提取组装成网关上下文

  • 网关的上下文会传递到DispatcherHandler,它负责将请求分发给RoutePredicateHandlerMapping

  • RoutePredicateHandlerMapping负责路由查找,并根据路由断言判断路由是否可用

  • 如果过断言成功,由FilteringWebHandler创建过滤器链并调用

  • 请求会一次经过PreFilter--微服务--PostFilter的方法,最终返回响应

# 快速开始

引入依赖

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

注意:会和spring-webmvc的依赖冲突,需要排除spring-webmvc

编写yml配置文件

server:
  port: 8888
spring:
  application:
    name: api-gateway
  cloud:
    gateway:
      routes: # 路由数组[路由 就是指定当请求满足什么条件的时候转到哪个微服务]
        - id: product_route # 当前路由的标识, 要求唯一 
          uri: http://localhost:8081 # 请求要转发到的地址
          predicates: # 断言(就是路由转发要满足的条件)
            - Path=/product-serv/** # 当请求路径满足Path指定的规则时,才进行路由转发
          filters: # 过滤器,请求在传递过程中可以通过过滤器对其进行一定的修改
            - StripPrefix=1    # 转发之前去掉1层路径.
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 集成Nacos

现在在配置文件中写死了转发路径的地址, 前面我们已经分析过地址写死带来的问题, 接下来我们从注册中心获取此地址。

<dependencys>
    <!-- nacos服务注册与发现 -->
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    </dependency>
    <!--负载均衡器-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-loadbalancer</artifactId>
    </dependency>
</dependencys>

1
2
3
4
5
6
7
8
9
10
11
12
13

配置yaml

server:
  port: 8888

spring:
  application:
    name: api-gateway
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
    gateway:
      routes:
        - id: product_route
          uri: lb://service-product # lb指的是从nacos中按照名称获取微服务,并遵循负载均衡策略
          predicates:
            - Path=/product-serve/**
          filters:
            - StripPrefix=1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

简写: 去掉关于路由的配置,自动寻找服务

server:
  port: 8888

spring:
  application:
    name: api-gateway
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
    gateway:
      discovery:
        locator:
          enabled: true
1
2
3
4
5
6
7
8
9
10
11
12
13
14

img.png

# 路由断言工厂

文档地址:https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#gateway-request-predicates-factories

作用: 当请求gateway的时候,使用断言对请求进行匹配.如果匹配成功就路由转发,如果匹配失败就返回404

SpringCloud Gateway包括许多内置的断言工厂,所有这些断言都与HTTP请求的不同属性匹配

# 基于Datetime类型的断言工厂

此类型的断言根据时间做判断,主要有三个:

  • AfterRoutePredicateFactory: 接收一个日期参数,判断请求日期是否晚于指定日期

  • BeforeRoutePredicateFactory: 接收一个日期参数,判断请求日期是否早于指定日期

  • BetweenRoutePredicateFactory: 接收两个日期参数,判断请求日期是否在指定时间段内

- After=2019-12-31T23:59:59.789+08:00[Asia/Shanghai]
1

# 基于远程地址的断言工厂

RemoteAddrRoutePredicateFactory:接收一个IP地址段,判断请求主机地址是否在地址段中

- RemoteAddr=192.168.1.1/24
1

# 基于Cookie的断言工厂

CookieRoutePredicateFactory:接收两个参数,cookie 名字和一个正则表达式。 判断请求cookie是否具有给定名称且值与正则表达式匹配。


-Cookie=chocolate, ch.

1
2
3

# 基于Header的断言工厂

HeaderRoutePredicateFactory:接收两个参数,标题名称和正则表达式。判断请求Header是否具有给定名称且值与正则表达式匹配。

-Header=X-Request-Id, \d+
1

# 基于Host的断言工厂

HostRoutePredicateFactory:接收一个参数,主机名模式。判断请求的Host是否满足匹配规则。

-Host=**.testhost.org
1

# 基于Method请求方法的断言工厂

MethodRoutePredicateFactory:接收一个参数,判断请求类型是否跟指定的类型匹配。

-Method=GET
1

# 基于Path请求路径的断言工厂

PathRoutePredicateFactory:接收一个参数,判断请求的URI部分是否满足路径规则。

-Path=/foo/{segment}
1

# 基于Query请求参数的断言工厂

QueryRoutePredicateFactory :接收两个参数,请求param和正则表达式, 判断请求参数是否具有给定名称且值与正则表达式匹配。

-Query=baz, ba.
1

# 基于路由权重的断言工厂

WeightRoutePredicateFactory:接收一个[组名,权重], 然后对于同一个组内的路由按照权重转发

routes:
    -id: weight_route1 
     uri: host1 
     predicates:
        -Path=/product/**
        -Weight=group3, 1
    -id: weight_route2 
    uri: host2 
    predicates:
        -Path=/product/**
        -Weight= group3, 9
1
2
3
4
5
6
7
8
9
10
11

# 自定义路由断言工厂

自定义路由断言工厂需要继承 AbstractRoutePredicateFactory 类,重写 apply 方法的逻辑。在 apply 方法中可以通过 exchange.getRequest() 拿到 ServerHttpRequest 对象,从而可以获取到请求的参数、请求方式、请求头等信息。

  1. 必须spring组件 bean
  2. 类必须加上RoutePredicateFactory作为结尾
  3. 必须继承AbstractRoutePredicateFactory
  4. 必须声明静态内部类 声明属性来接收 配置文件中对应的断言的信息
  5. 需要结合shortcutFieldOrder进行绑定
  6. 通过apply进行逻辑判断 true就是匹配成功 false匹配失败

注意: 命名需要以 RoutePredicateFactory 结尾

package com.example.factory;

import org.springframework.cloud.gateway.handler.predicate.AbstractRoutePredicateFactory;
import org.springframework.cloud.gateway.handler.predicate.GatewayPredicate;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;

import java.util.Collections;
import java.util.List;
import java.util.function.Predicate;
@Component
public class CheckAuthRoutePredicateFactory extends AbstractRoutePredicateFactory<CheckAuthRoutePredicateFactory.Config> {
    public CheckAuthRoutePredicateFactory() {
        super(Config.class);
    }

    @Override
    public Predicate<ServerWebExchange> apply(Config config) {
        return new GatewayPredicate() {

            @Override
            public boolean test(ServerWebExchange serverWebExchange) {
                System.out.println("调用CheckAuthRoutePredicateFactory" + config.getName());
                if(config.getName().equals("xiaohei")){
                    return true;
                }
                return false;
            }
        };
    }

    /**
     * 快捷配置
     * @return
     */
    @Override
    public List<String> shortcutFieldOrder() {
        return Collections.singletonList("name");
    }
    public static class Config {

        private String name;

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }
    }
}

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

编写配置

spring:
  cloud:
    gateway:
      #设置路由:路由id、路由到微服务的uri、断言
      routes:
      - id: order_route  #路由ID,全局唯一
        uri: http://localhost:8020  #目标微服务的请求地址和端口
        predicates:
         # 测试:http://localhost:8888/order/findOrderByUserId/1
        - Path=/order/**   #Path路径匹配
        #自定义CheckAuth断言工厂 
        - CheckAuth=xiaohei
1
2
3
4
5
6
7
8
9
10
11
12

# 过滤器工厂

gateway 内置了很多的过滤器工厂,我们通过一些过滤器工厂可以进行一些业务逻辑处理器,比如添加剔除响应头,添加去除参数等

文档地址:https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#gatewayfilter-factories

过滤器工厂 作用 参数
AddRequestHeader 为原始请求添加Header Header的名称及值
AddRequestParameter 为原始请求添加请求参数 参数名称及值
AddResponseHeader 为原始响应添加Header Header的名称及值
DedupeResponseHeader 剔除响应头中重复的值 需要去重的Header名称及去重策略
Hystrix 为路由引入Hystrix的断路器保护 HystrixCommand 的名称
FallbackHeaders 为fallbackUri的请求头中添加具体的异常信息 Header的名称
PrefixPath 为原始请求路径添加前缀 前缀路径
PreserveHostHeader 为请求添加一个preserveHostHeader=true 的 属 性,路由过滤器会检查该属性以决定是否要发送原始的Host 无
RequestRateLimiter 用于对请求限流,限流算法为令牌桶 keyResolver、rateLimiter、statusCode、denyEmptyKey、emptyKeyStatus
RedirectTo 将原始请求重定向到指定的URL http状态码及重定向的url
RemoveHopByHopHeadersFilter 为原始请求删除IETF组织规定的一系列Header 默认就会启用,可以通过配置指定仅删除哪些Header
RemoveRequestHeader 为原始请求删除某个Header Header名称
RemoveResponseHeader 为原始响应删除某个Header Header名称
RewritePath 重写原始的请求路径 原始路径正则表达式以及重写后路径的正则表达式
RewriteResponseHeader 重写原始响应中的某个Header Header名称,值的正 则表达式,重写后的值
SaveSession 在转发请求之前,强制执行WebSession::save 操作 无
secureHeaders 为原始响应添加一系列起安全作用的响应头 无,支持修改这些安全响应头的值
SetPath 修改原始的请求路径 修改后的路径
SetResponseHeader 修改原始响应中某个Header的值 Header名称,修改后的值
SetStatus 修改原始响应的状态码 HTTP 状态码,可以是数字,也可以是字符串
StripPrefix 用于截断原始请求的路径 使用数字表示要截断的路径的数量
Retry 针对不同的响应进行重试 retries、statuses、methods、series
RequestSize 设置允许接收最大请求包的大 小。如果请求包大小超过设置的值,则返回 413 Payload TooLarge 请求包大小,单位为字节,默认值为5M
ModifyRequestBody 在转发请求之前修改原始请求体内容 修改后的请求体内容
ModifyResponseBody 修改原始响应体的内容 修改后的响应体内容

# 为匹配的路由统一添加前缀

spring:
  cloud:
    gateway:
      #设置路由:路由id、路由到微服务的uri、断言
      routes:
      - id: order_route  #路由ID,全局唯一
        uri: http://localhost:8020  #目标微服务的请求地址和端口
        #配置过滤器工厂
        filters:
        - PrefixPath=/mall-order  # 添加前缀 对应微服务需要配置context-path
1
2
3
4
5
6
7
8
9
10

在order服务中配置

server:
  servlet:
    context-path: /mall-order
1
2
3

# 自定义过滤器工厂

继承AbstractNameValueGatewayFilterFactory且我们的自定义名称必须要以GatewayFilterFactory结尾并交给spring管理。

@Component
@Slf4j
public class CheckAuthGatewayFilterFactory extends AbstractNameValueGatewayFilterFactory{

    @Override
    public GatewayFilter apply(NameValueConfig config) {
        return (exchange, chain) -> {
            log.info("调用CheckAuthGatewayFilterFactory==="
                    + config.getName() + ":" + config.getValue());
            return chain.filter(exchange);
        };
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13

配置自定义的过滤器工厂

spring:
  cloud:
    gateway:
      #设置路由:路由id、路由到微服务的uri、断言
      routes:
      - id: order_route  #路由ID,全局唯一
        uri: http://localhost:8020  #目标微服务的请求地址和端口
        #配置过滤器工厂
        filters:
        - CheckAuth=fox,男
1
2
3
4
5
6
7
8
9
10

# 全局过滤器

img.png

局部:局部针对某个路由, 需要在路由中进行配置

全局:针对所有路由请求, 一旦定义就会投入使用

# 自定义全局过滤器

@Component
public class LogFilter implements GlobalFilter {
    Logger log= LoggerFactory.getLogger(this.getClass());

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {

        log.info(exchange.getRequest().getPath().value());
        return chain.filter(exchange);
    }
}
1
2
3
4
5
6
7
8
9
10
11

# gateway整合sentinel

网关作为内部系统外的一层屏障, 对内起到一定的保护作用, 限流便是其中之一. 网关层的限流可以简单地针对不同路由进行限流, 也可针对业务的接口进行限流,或者根据接口的特征分组限流。

文档地址:https://github.com/alibaba/Sentinel/wiki/%E7%BD%91%E5%85%B3%E9%99%90%E6%B5%81

<dependencys>
    <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> 
</dependencys>

1
2
3
4
5
6
7
8
9
10
11
12

添加配置

 sentinel:
      transport:
        # 添加sentinel的控制台地址
        dashboard: 127.0.0.1:8080
1
2
3
4

# 控制台实现方式

Sentinel 1.6.3 引入了网关流控控制台的支持,用户可以直接在 Sentinel 控制台上查看 API Gateway 实时的 route 和自定义 API 分组监控,管理网关规则和 API 分组配置

从 1.6.0 版本开始,Sentinel 提供了 Spring Cloud Gateway 的适配模块,可以提供两种资源维度的限流

  • route 维度:即在 Spring 配置文件中配置的路由条目,资源名为对应的 routeId

  • 自定义 API 维度:用户可以利用 Sentinel 提供的 API 来自定义一些 API 分组

自定义异常方式

通过yml

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

通过GatewayCallbackManager

@Configuration
public class GatewayConfig {

    @PostConstruct
    public void init(){
        BlockRequestHandler blockRequestHandler = new BlockRequestHandler() {
            @Override
            public Mono<ServerResponse> handleRequest(ServerWebExchange exchange, Throwable throwable) {
                return ServerResponse.status(HttpStatus.OK)
                        .contentType(MediaType.APPLICATION_JSON)
                        .body(BodyInserters.fromValue("降级了!"));
            }
        };

        GatewayCallbackManager.setBlockHandler(blockRequestHandler);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
微服务-配置中心

← 微服务-配置中心

Theme by Vdoing | Copyright © 2013-2023 冀ICP备16006233号-1
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式
×