第七章:服务网关Gateway
1 网关介绍
在微服务架构中,顾名思义,一个大的系统会被拆分成多个小的服务,如果没有网关的存在,发请求的时候,客户端只能记录每个服务的地址和端口,然后分别去调用
很显然你在访问淘宝的时候,他所有的前缀都是淘宝自己的域名,不可能说去购物车是一个ip,去订单是一个ip。这就是用网关实现的,那网关具体能做什么呢
- 客户端发请求只会发送给网关,网关统一转发,对外暴露的ip只会有一个
- 网关可以实现认证,只需要一次认证,不然的话,进入每个服务都需要认证一次
- 解决跨域,服务间调用时候可能存在跨域问题,网关能实现
整体架构就会变成如下
2.简介
spring公司基于5.0版本,Spring Boot2.0等技术开发的网关,目的就是为了为微服务架构中提供一种简单,有效,统一的API路由管理方式,目标是为了取代netflfiix Zuul,基于Filter链的方式提供网关的基本功能,例如:安全,监控,限流
优点:
- 性能强:相比zuul快很多
- 功能强:内置了很多功能,限流,转发,监控等
- 设计强:容易拓展
缺点:
- 实现依赖于Netty和Webful,学习成本较高
- 不支持部署在常规容器中,比如tomcat,只能打包成jar
- Spring Boot2.0以上版本才支持
3 创建一个网关模块,并且导入依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>server-parent</artifactId>
<groupId>com.threesum</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>server-gateway</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
</dependencies>
</project>
3.1 实现网关转发(基础版本)
3.1.1编写配置文件
server:
port: 7000
spring:
application:
name: server-gateway #服务名
devtools:
restart:
enabled: true #是否支持热部署
cloud:
gateway:
routes: #路由数组
- id: product_route #路由的ID(类似mysql主键ID),有固定规则但要求唯一,建议配合服务名
uri: http://localhost:8002 #这里写死,请求转发到的地址
#uri: lb://server-product #lb指的是从nacos中获取服务的名字,并且遵守负载均衡策略
order: 1 #路由的优先级,数字越小优先级越高
predicates: # 断言(转发的时候需要满足哪些条件才转发到这个路由)
- Path=/service/**
filters: # 过滤器,请求在传递过程中可以通过过滤器对其进行一定的修改
- StripPrefix=1 # 转发之前去掉1层路径,数字表示要截断的路径的数量,因为上面配置的是满足前缀是/service,所以转发要直接把这个service去除
3.1.2调用测试
原本商品服务直接访问接口:http://localhost:8002/product/1
使用网关转发:http://localhost:7000/service/product/1
3.2 使用nacos实现(增强版本)
3.2.1加入nacos依赖
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
3.2.2在主启动类上加上启动nacos客户端
@SpringBootApplication
@EnableDiscoveryClient
public class GateWayApplication {
public static void main(String[] args) {
SpringApplication.run(GateWayApplication.class, args);
}
}
3.2.3修改配置文件
server:
port: 7000
spring:
application:
name: server-gateway #服务名
devtools:
restart:
enabled: true #是否支持热部署
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
gateway:
discovery:
locator:
enabled: true #让网关可以发现nacos中的服务
routes: #路由数组
- id: product_route #路由的ID(类似mysql主键ID),有固定规则但要求唯一,建议配合服务名
#uri: http://localhost:8002 #这里写死,请求转发到的地址
uri: lb://server-product #lb指的是从nacos中获取服务的名字,并且遵守负载均衡策略
order: 1 #路由的优先级,数字越小优先级越高
predicates: # 断言(转发的时候需要满足哪些条件才转发到这个路由)
- Path=/service/**
filters: # 过滤器,请求在传递过程中可以通过过滤器对其进行一定的修改
- StripPrefix=1 # 转发之前去掉1层路径 数字表示要截断的路径的数量,因为上面配置的是满足前缀是/service,所以转发要直接把这个service去除
3.2.4测试
4 断言
Predicate(断言),用于进行条件判断的,当断言结果返回真,才会执行路由,直白的说就是,在什么条件下才进行路由转发
4.1内置路由断言工厂
断言工厂 | 作用 | 示例 |
---|---|---|
AfterRoutePredicateFactory | 接收一个日期参数,判断请求的日期是否晚于指定日期 | -After=2019-12-31T23:59:59,789+08:00[Asia/Shanghai] |
BeforeRoutePredicateFactory | 接收一个日期参数,判断请求的日期是否早于指定日期 | -After=2019-12-31T23:59:59,789+08:00[Asia/Shanghai] |
BetweenRoutePredicateFactory | 接收两个日期参数,判断请求的日期是否在指定日期内 | -Betweenr=2019-12-31T23:59:59,789+08:00[Asia/Shanghai],2021-12-31T23:59:59,789+08:00[Asia/Shanghai] |
RemoteAddrRoutePredicateFactory | 接收一个ip的地址段,判断参数是否在指定的地址段内部 | -RemoteAddr=192.168.1.1/24 |
CookieRoutePredicateFactory | 接收两个参数 | -Cookie=chocolate,ch. |
HeaderRoutePredicateFactony | 接收两个参数,标题名称和正则表达式。判断请求Header是否具有给定名称且值与正则表达式匹配 | -Header=X-Request-ld, \d+ |
HostRoutePredicateFactory | 接收一个参数,主机名模式。判断请求的Host是否满足匹配规则 | -Host=**.testhost.org |
MethodRoutePredicateFactory | 接收一个参数,判断请求类型是否跟指定的类型匹配 | -Method=GET |
PathRoutePredicateFactory | 接收一个参数,判断请求的URI部分是否满足路径规则 | -Path=/foo/{segment} |
QueryRoutePredicateFactory | 接收两个参数,请求param和正则表达式,判断请求参数是否具有给定名称且值与正则表达式匹配 | -Query-baz, ba. |
WeightRoutePredicateFactory | 收一个[组名,权重],然后对于同一个组内的路由按照权重转发 |
4.2自定义路由断言工厂
设置一个场景,我们的接口只能让当前的登录用户年纪为18-20岁之间的人访问
4.2.1配置文件中,添加一个Age的断言
4.2.2自定义一个断言工厂,实现断言方法
package com.threesum.predicates;
import org.springframework.cloud.gateway.handler.predicate.AbstractRoutePredicateFactory;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;
@Component
public class AgeRoutePredicatesFactory extends AbstractRoutePredicateFactory<AgeRoutePredicatesFactory.Config> {
public AgeRoutePredicatesFactory() {
super(AgeRoutePredicatesFactory.Config.class);
}
public List<String> shortcutFieldOrder(){
//这里的顺序要跟配置文件中的参数顺序一样
return Arrays.asList("minAge","maxAge");
}
@Override
public Predicate<ServerWebExchange> apply(Config config) {
return new Predicate<ServerWebExchange>() {
@Override
public boolean test(ServerWebExchange serverWebExchange) {
//获取参数
String ageStr=serverWebExchange.getRequest().getQueryParams().getFirst("age");
if(!StringUtils.isEmpty(ageStr)){
int age = Integer.parseInt(ageStr);
return age>config.getMaxAge() && age<config.getMinAge();
}
return true;
}
};
}
class Config {
private int minAge;
private int maxAge;
public int getMinAge() {
return minAge;
}
public void setMinAge(int minAge) {
this.minAge = minAge;
}
public int getMaxAge() {
return maxAge;
}
public void setMaxAge(int maxAge) {
this.maxAge = maxAge;
}
}
}
http://localhost:7000/product-serv/product/1?age=30 不可以访问
http://1ocalhost:7000/product-serv/product/1?age=19 可以访问
4.3过滤器
- 作用:在请求传递过程中,对请求和响应做一些修改
- 分类:局部(作用域某一个路由),全局(作用在所有的路由)
- 生命周期:Pre和Post
在Gateway中,过滤器的生命周期只有两个,Pre和Post
- Pre:这种过滤器在请求被路由之前调用。我们可利用这种过滤器实现身份验证、在集群中选择请求的微服务,记录调试信息等
- Post:这种过滤器在路由到微服务以后执行。这种过滤器可用来为响应添加标准的HTTP,Header、收集统计信息和指标、将响应从微服务发送给客户端等。