It has been 535 days since the last update, the content of the article may be outdated.

GateWay

什么是GateWay

基于Spring 5.0 、SpringBoot 2.0和Project Reactor开发的高性能API网关组件

API网关

API网关是一个中间层,集中管理、保护和优化API请求与响的通信。

API 网关提供了安全、流控、过滤、缓存、计费以及监控等 API 管理功能,使得系统更易于管理、更安全、更高效,并提供了更好的可视化和分析能力。

GateWay的主要概念

Route(路由) : 由一个 ID、一个目标 URI、一组断言(Predicate)和一组过滤器(Filter)组成

1.地址映射转发:

http://网关ip:网关端口/服务名映射/xxxx?xxx=x

2.服务名映射/xxxx?xxx=x 转换成对应的微服务的请求地址,再把请求发出

Predicate(断言): 对 HTTP 请求进行匹配 -> 判读用哪一条路由

Filter(过滤器): 对请求进行拦截和修改 -> 插件机制 ,责任链模式组装,aop增强机制

GateWay开始

GateWay的搭建

引入依赖

xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!-- 利用actuator查看gateway 配置的理由路径(api形式)-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- 将gateway注册到nacos上-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>


配置yml文件

yml
1
2
3
4
5
6
7
8
9
10
11
12
13
server:
port: 20001

spring:
application:
name: res-gateway
# 将服务注册到nacos上
cloud:
nacos:
discovery:
server-addr: localhost:8848
username: nacos
password: nacos

写启动类

启动服务,将服务注册到nacos上,并在nacos的服务列表查看是否注册成功

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

路由配置

当我们访问http://localhost:20001时,请求会自动转发到百度的页面

yml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Spring:
cloud:
gateway: #基础的路由转发配置
routes: #查看配置源码 routes是一个有序的list集合 可以配置多个 读取顺序从上往下
- id: res-good #唯 一区别的名字
uri: http://localhost:9200
#- Path符合localhost:20001/resfood 被替换 http://localhost:9200/resfood/findById/1
predicates:
- Path=/resfood/** #将resfood后面的内容替换 #http://localhost:20001/resfood/findById/1


- id: baidu #唯 一区别的名字
uri: http://www.baidu.com #都转发到baidu 替换请求路径http://localhost:20001/ xx
predicates: #比较条件
- Path=/** #所有请求(通用配置) 路径断言

日志文件配置

启动类配置虚拟机参数,启用Reactor Netty HTTP服务器的访问日志

-Dreactor.netty.http.server.accessLogEnabled=true

xml
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
<?xml version="1.0" encoding="UTF-8"?>

<!-- 配置文件每隔1分钟,就检查更新 -->
<configuration scan="true" scanPeriod="60 seconds" debug="false">

<!-- ch.qos.logback.core.ConsoleAppender 控制台输出 -->
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<!-- 过去使用layout,现在都推荐使用encoder
作用一:将日志信息转换(格式化)为字符串
作用二:然后写入到文件中-->
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] [%-5level] %logger{50} - %msg%n</pattern>
</encoder>
</appender>
<!-- 日志以文件形式保存-->
<appender name="accessLog" class="ch.qos.logback.core.FileAppender">
<!-- 日志文件名称-->
<file>gateway_access_log.log</file>
<!-- 日志输出的格式-->
<encoder>
<pattern>%msg%n</pattern>
</encoder>
</appender>
<!-- 日志同步-->
<appender name="async" class="ch.qos.logback.classic.AsyncAppender">
<appender-ref ref="accessLog" />
</appender>
<!-- 记录到文件中-->
<logger name="reactor.netty.http.server.AccessLog" level="INFO" additivity="false">
<appender-ref ref="async"/>
</logger>
<!-- 控制台打印-->
<logger name="org.springframework" level="info">
<appender-ref ref="console" />
</logger>

<logger name="com.yc" level="info">
<appender-ref ref="console" />
</logger>

<!-- 也是一种<logger>,是所有<logger>的父级 -->
<!-- 不设置additivity属性,或者设置additivity = true的<logger>,会继承root的子标签<appender-ref ref="console" />的设置,
将它自己设置的level属性级别及以上的日志打印到控制台 -->
<!-- <root level="info">-->
<!-- &lt;!&ndash; 表示按照console的设置去输出日志到控制台 &ndash;&gt;-->
<!-- <appender-ref ref="console"/>-->
<!-- </root>-->
</configuration>

测试结果

GateWay负载均衡

引入依赖

xml
1
2
3
4
5
<!-- 引入riboon,客户端保存服务列表信息 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>

修改application配置文件

yml
1
2
3
4
5
6
7
8
9
10
Spring:
cloud:
gateway: #基础的路由转发配置
routes: #查看配置源码 routes是一个有序的list集合 可以配置多个 读取顺序从上往下
- id: res-good #唯 一区别的名字
uri: lb://resfood #resfood服务名 -> 利用loadBalancer保存的服务列表选一台服务器 (gateway与resfood服务在同一个命名空间下)
predicates:
- Path=/resfood/** #将resfood后面的内容替换 #http://localhost:20001/resfood/findById/1


测试

开启resfood服务,并启用多个端口,这里给该服务启动了9200 、9300两个端口用于负载均衡,重复访问两次localhost:20001/resfood/findById/1,查看负载均衡结果

actuator的路由操作

导入actuator的依赖

xml
1
2
3
4
5
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

actuator配置文件的配置

yml
1
2
3
4
5
management:                                #通过 actuator暴露此服务的管理端口
endpoints:
web: #通过web来暴露管理API,
exposure:
include: "*" #显示所有的信息

对actuator路由的操作

GateWay原理

谓词工厂-构造模式

根据输入(条件)生成一个匹配规则对象,创建型的设计模式(例: 单例,构造器builder-流式方案创建,简单工厂,抽象工厂)

谓词 -> 判断

result: true/false

输入数据: 一个/多个

谓词接口: ->将if-else的判断转换成一个对象

java
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
><!--1-->
>public static void main(String[] args) {
>// 使用Predicate过滤集合
List<String> ns = Arrays.asList("Alice", "Bob", "Charlie", "David"); //创建 不可变集合
List<String> names=new ArrayList<>( ns ); //转为可变集合,以便后面使用 removeIf删除元素

>// 定义一个Predicate,判断字符串长度是否大于3
Predicate<String> lengthPredicate = s -> s.length() > 3;
>names.removeIf(lengthPredicate);
>System.out.println(names);
}

>//removeIf底层
>//names("Alice", "Bob", "Charlie", "David")
>//filter: s.length() > 3
>default boolean removeIf(Predicate<? super E> filter) {
Objects.requireNonNull(filter);
boolean removed = false;
//names数组的迭代
final Iterator<E> each = iterator();、
//迭代names数组里面的元素,看看是否满足移除的条件
while (each.hasNext()) {
//判断 names是否满足filter过滤器的条件
if (filter.test(each.next())) {
//移除满足条件names元素
each.remove();
//判断是否删除成功
removed = true;
}
}
return removed;
>}
>//所以这里输出的结果为Bob

><!--2-->
public static void main(String[] args) {
//TODO: Predicate<T> and 方法使用
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
Predicate<Integer> predicate1 = x -> x > 3;
Predicate<Integer> predicate2 = x -> x < 9;
//底层实现 -> 多个条件(断言)用and进行连接
//流式调用builder的设计模式
List<Integer> collect = list.stream().filter(predicate1.and(predicate2)).collect(Collectors.toList());
System.out.println(collect);
>}
>//result 4,5,6,7,8

><!--3-->
//多个参数
>// 使用BiPredicate判断两个字符串是否相等
BiPredicate<String, String> equalPredicate = (s1, s2) -> s1.equals(s2);
System.out.println(equalPredicate.test("Hello", "Hello")); // true
System.out.println(equalPredicate.test("Hello", "World")); // false
}

示例

resgateway

yml
1
2
3
4
5
6
7
8
>spring:
cloud:
gateway:
routes:
- id: resIndex
uri: lb://resIndex
predicates:
- Path=/**

html的路径中

resfood/findByPage?pageno=1&pagesize=5是相对路径

访问浏览器index.html http://localhost:2001/index.html

相对于浏览器的地址

gateway /resfood/**

http://localhost:2001/ resfood/findByPage?pageno=1&pagesize=5