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的搭建

引入依赖

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文件

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的服务列表查看是否注册成功

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时,请求会自动转发到百度的页面

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

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负载均衡

引入依赖

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

修改application配置文件

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的依赖

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

actuator配置文件的配置

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

对actuator路由的操作

GateWay原理

谓词工厂-构造模式

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

谓词 -> 判断

result: true/false

输入数据: 一个/多个

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

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

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