Gateway 服务网关
持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第3天,点击查看活动详情
1. 为什么需要网关
1.1 网关功能
- **网关能用于身份认证和权限校验。**请求来了,首先验证一下你是谁。
- **服务路由、负载均衡。**根据用户请求判断将请求发送到对应的微服务;如果有多个微服务负责同一个请求,网关还担任了一个负载均衡的功能。
- **请求限流。**当用户请求数超过微服务最大允许请求量,就会由网关负责请求限流。
常常会在网关前添加一个nginx,通过指定的请求路径传到网关,再由网关传至其他微服务。下面就让我们来实现一下网关的服务Gateway
2. gateway快速入门
2.1 搭建网关服务
-
创建新的模块
-
引入依赖
<!--网关--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-gateway</artifactId> </dependency> <!--nacos服务发现依赖--> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency>因为需要在获取用户请求之后,对其进行路由规则判断,之后需要根据服务路由的对应微服务的服务名称从
Nacos注册中心获取服务列表 -
配置
application.yml,编写请求路由和Nacos地址spring: cloud: gateway: routes: # 网关路由配置 - id: user-service # 路由id,自定义,只要唯一即可,给 gateway 使用 # uri: http://127.0.0.1:8081 # 路由的目标地址 http就是固定地址 uri: lb://userservice # 路由的目标地址 lb就是负载均衡,后面跟对应微服务的服务名称 predicates: # 路由断言,也就是判断请求是否符合路由规则的条件 - Path=/user/** # 这个是按照路径匹配,只要以/user/开头就符合要求 - id: order-service uri: lb://orderservice predicates: - Path=/order/**
【总结】
- 服务路由是包含一条路由规则,路由规则决定了服务消费者的调用目标,即规定了服务消费者可调用哪些服务提供者。
- 路由配置包括
- 路由
id:路由的唯一标识 - 路由目标
uri:路由的目标地址,http代表固定地址,lb代表根据服务名称负载均衡。目前比较推荐lb - 路由断言
predicates:判断路由的规则 - 路由过滤器
filter:对请求或响应做处理
- 路由
3. 断言工厂
什么是断言呢?在程序设计中,断言是一种放在程序中的一阶逻辑,目的是为了标示与验证程序开发者预期的结果-当程序运行到断言的位置时,对应的断言应该为真。若断言不为真时,程序会中止运行,并给出错误消息。
我们通过配置文件中向路由配置传入的路由断言字符串,都是由断言工厂Predicate Factory读取并处理,转变为路由判断的条件
我们可以通过在配置文件里设置不同的路由断言来达到不同的效果
| 名称 | 说明 | 示例 |
|---|---|---|
| After | 是某个时间点后的请求 | - After=2037-01-20T17:42:47.789-07:00[America/Denver] |
| Before | 是某个时间点之前的请求 | - Before=2031-04-13T15:14:47.433+08:00[Asia/Shanghai] |
| Between | 是某两个时间点之前的请求 | - Between=2037-01-20T17:42:47.789-07:00[America/Denver], 2037-01-21T17:42:47.789-07:00[America/Denver] |
| Cookie | 请求必须包含某些cookie | - Cookie=chocolate, ch.p |
| Header | 请求必须包含某些header | - Header=X-Request-Id, \d+ |
| Host | 请求必须是访问某个host(域名) | - Host=.somehost.org,.anotherhost.org |
| Method | 请求方式必须是指定方式 | - Method=GET,POST |
| Path | 请求路径必须符合指定规则 | - Path=/red/{segment},/blue/** |
| Query | 请求参数必须包含指定参数 | - Query=name, Jack或者- Query=name |
| RemoteAddr | 请求者的ip必须是指定范围 | - RemoteAddr=192.168.1.1/24 |
| Weight | 权重处理 |
如果想要更多的了解,可以选择官网去查询Spring Cloud Gateway
4. 过滤器工厂
我们接受外部的请求,通过路由转到各个过滤器,可以对请求做各种的处理。例如给当前请求添加或移除一个请求头之类的。
我们可以通过官网去查询Spring Cloud Gateway
4.1 路由过滤器的种类
Spring提供了31种不同的路由过滤器工厂。例如:
| 名称 | 说明 |
|---|---|
| AddRequestHeader | 给当前请求添加一个请求头 |
| RemoveRequestHeader | 移除请求中的一个请求头 |
| AddResponseHeader | 给响应结果中添加一个响应头 |
| RemoveResponseHeader | 从响应结果中移除有一个响应头 |
| RequestRateLimiter | 限制请求的流量 |
4.2 路由过滤器
我们通过在配置文件中对应的服务路由设置该路由的过滤器
spring:
cloud:
gateway:
routes:
- id: user-service
uri: lb://userservice
predicates:
- Path=/user/**
# 过滤器
filters:
# 添加过滤器,给当前请求添加一个请求头
- AddRequestHeader=test, Test is test
重启服务就可以接受请求头内test的数据Test is test
-
测试
-
网关设置配置
spring: application: name: gateway cloud: nacos: server-addr: localhost:8848 gateway: routes: # 网关路由配置 - id: user-service # 路由id,自定义,只要唯一即可,给 gateway 使用 # uri: http://127.0.0.1:8081 # 路由的目标地址 http就是固定地址 uri: lb://userservice # 路由的目标地址 lb就是负载均衡,后面跟对应微服务的服务名称 predicates: # 路由断言,也就是判断请求是否符合路由规则的条件 - Path=/user/** # 这个是按照路径匹配,只要以/user/开头就符合要求 filters: - AddRequestHeader=test, Test is test -
userservice的controller层进行测试通过
@RequestHeader获取请求头内容。如果请求头内容缺失会直接报错400。public class UserController { /** * 路径: /user/110 * * @param id 用户id * @return 用户 */ @GetMapping("/{id}") public User queryById(@PathVariable("id") Long id,@RequestHeader(value = "Test",required = true) String test) { System.out.println(test); return userService.queryById(id); } ... } -
如果在控制台看见
Test is test成功打印说明路由过滤器生效。
-
4.3 默认过滤器
当我们有大量的服务路由,想要给它们同时实现添加过滤器,可以使用默认过滤器。默认过滤器是通过修改配置完成。
使用默认过滤器可以给所有的路由添加过滤器。对所有的路由都生效。通过default-filters来配置。
spring:
cloud:
gateway:
default-filters:
- AddRequestHeader=test, Test is test
-
测试
-
网关设置配置
spring: application: name: gateway cloud: nacos: server-addr: localhost:8848 gateway: routes: # 网关路由配置 - id: user-service # 路由id,自定义,只要唯一即可,给 gateway 使用 # uri: http://127.0.0.1:8081 # 路由的目标地址 http就是固定地址 uri: lb://userservice # 路由的目标地址 lb就是负载均衡,后面跟对应微服务的服务名称 predicates: # 路由断言,也就是判断请求是否符合路由规则的条件 - Path=/user/** # 这个是按照路径匹配,只要以/user/开头就符合要求 default-filters: - AddRequestHeader=test, Test is test -
userservice的controller层进行测试public class UserController { /** * 路径: /user/110 * * @param id 用户id * @return 用户 */ @GetMapping("/{id}") public User queryById(@PathVariable("id") Long id,@RequestHeader(value = "Test",required = true) String test) { System.out.println(test); return userService.queryById(id); } ... } -
如果在控制台看见
Test is test成功打印说明默认过滤器生效。
-
5. 全局过滤器
我们通过配置文件能实现SpringCloud帮我们设置好的过滤器,但是面对一些复杂的业务需求的时候可能不足以应付。所以我们希望能够自己设置符合自己业务逻辑的过滤器。
5.1 全局过滤器的作用
全局过滤器的作用和Gateway的默认过滤器作用一样,是用来处理一切进入网关的请求和微服务响应的。
区别于GatewayFilter通过配置定义,处理逻辑是固定的。而GlobalFilter的逻辑需要自己写代码实现。下面我们就来写一下。
5.2 自定义全局过滤器
-
创建
GlobalFilter接口实现类public class Login implements GlobalFilter { /** * 设置登录过滤器 * @param exchange 请求上下文,里面可以获取Request、Response等信息 * @param chain 用来把请求委托给下一个过滤器 * @return 下一个过滤器 */ @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { return null; } } -
编写逻辑
需要将过滤器放入
Spring容器当中才能调用@Component public class Login implements GlobalFilter { /** * 设置登录过滤器 * @param exchange 请求上下文,里面可以获取Request、Response等信息 * @param chain 用来把请求委托给下一个过滤器 * @return 下一个过滤器 */ @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { // 1. 获取请求参数 ServerHttpRequest request = exchange.getRequest(); MultiValueMap<String, String> queryParams = request.getQueryParams(); // 2. 获取参数中的username的参数 String username = queryParams.getFirst("username"); // 3. 判断参数值是否等于admin if ("admin".equals(username)) { // 4. 是,放行 return chain.filter(exchange); } // 5. 否,拦截 // 5.1 设置状态码 exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED); // 5.2 拦截请求 return exchange.getResponse().setComplete(); } } -
设置优先级
过滤器先根据
Order设置优先级的大小来选择执行的顺序,数字越小优先级越高。最小数字是-2147483647,最大数字是2147483647。我们有两种方法设置过滤器优先级
-
@Order注解我们可以通过在类上添加
@Order注解来设置优先级@Component @Order(-1) public class Login implements GlobalFilter { ... } -
实现
Ordered接口我们通过重写
Ordered接口的getOrder()方法根据其返回值大小来设置过滤器优先级大小@Component public class Login implements GlobalFilter , Ordered { ... @Override public int getOrder() { return -1; } }
-
-
测试
当我们的请求后加上
username=admin才能到达指定页面的时候,说明成功了。
5.3 过滤器顺序
请求进入网关会经过三个类型的过滤器:路由过滤器、Default-filter、GlobalFilter。请求路由后,会将当前所有过滤器合并到一个过滤器链中,排序后依次执行每个过滤器。
GlobalFilter优先级是我们自己定义,而路由过滤器和Defult-filter过滤器则是由Spring指定,根据声明顺序由1开始递增。
当过滤器的优先级一样的时候,会根据Default-filter> 路由过滤器 > GlobalFilter
6. 跨域问题
6.1 什么是跨域问题
只要是域名或端口号发生改变便属于跨域。跨域问题就是浏览器禁止请求的发起者和服务端发生跨域ajax请求,请求被浏览器拦截的问题。我们通过CROS方案来解决。
6.2 什么是CROS
我们在日常开发中微服务之间的调用常常是跨域请求,出现跨域问题会非常不方便。所以CROS是一种解决浏览器禁止跨域ajax请求的方法。
-
原理简述
CROS是将请求分为简单请求和特殊请求。如果请求格式符合简单请求规则,就看服务端是否允许进行跨域ajax请求。如果是请求格式不符合见到那请求就定义为特殊请求,就会在特殊请求发送之前再发送一个预检请求,向服务端查询是否允许对应的请求发起者进行跨域ajax请求。
6.3 解决跨域问题
我们在网关的配置文件中修改
spring:
cloud:
gateway:
# 。。。
globalcors: # 全局的跨域处理
add-to-simple-url-handler-mapping: true # 解决options请求被拦截问题
corsConfigurations:
'[/**]':
allowedOrigins: # 允许哪些网站的跨域请求
- "http://localhost:8090"
allowedMethods: # 允许的跨域ajax的请求方式
- "GET"
- "POST"
- "DELETE"
- "PUT"
- "OPTIONS"
allowedHeaders: "*" # 允许在请求中携带的头信息
allowCredentials: true # 是否允许携带cookie
maxAge: 360000 # 这次跨域检测的有效期
Docker
1. 初始Docker
-
Docker和虚拟机的对比docker是一个系统进程;虚拟机是在操作系统中的操作系统docker体积小、启动速度快、性能好;虚拟机体积大、启动速度慢、性能一般- 在未来开发中,更加常用是用
docker作为开发环境
-
Docker如何解决大型项目依赖关系复杂,不同组件依赖的兼容性问题?Docker允许开发中将应用、依赖、函数库、配置一起打包,形成可移植镜像Docker应用运行在容器中,使用沙箱机制,相互隔离
-
Docker如何解决开发、测试、生产环境有差异的问题Docker镜像中包含完整运行环境,包括系统函数库,仅依赖系统的Linux内核,因此可以在任意Linux操作系统上运行