目录
一、Gateway网关创建
核心功能:反向代理、鉴权、流量控制、熔断、监控日志
三大核心:路由(route)、断言(predicate)、过滤(filter)
工作流程核心逻辑:路由转发-》断言判断-》执行过滤链
1.1、创建微服务子工程9527及配置和依赖
1.1.1、pom依赖
XML
<dependencies>
<!--gateway-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!--服务注册发现consul discovery,网关也要注册进服务注册中心统一管控-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-consul-discovery</artifactId>
</dependency>
<!-- 指标监控健康检查的actuator,网关是响应式编程删除掉spring-boot-starter-web dependency-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
1.1.2、yml配置
XML
server:
port: 9527
spring:
application:
name: cloud-gateway #以微服务注册进consul或nacos服务列表内
cloud:
consul: #配置consul地址
host: localhost
port: 8500
discovery:
prefer-ip-address: true
service-name: ${spring.application.name}
1.1.3、主启动类并测试入驻consul
java
@SpringBootApplication
@EnableDiscoveryClient
public class Main9527 {
public static void main(String[] args) {
SpringApplication.run(Main9527.class,args);
}
}
二、实现路由映射
2.1、服务8001新增测试代码
java
@RestController
public class PayGateWayController {
@Resource
private PayService payService;
@GetMapping(value = "/pay/gateway/get/{id}")
public ResultData<Pay> getById(@PathVariable("id") Integer id)
{
Pay pay = payService.getById(id);
return ResultData.success(pay);
}
@GetMapping(value = "/pay/gateway/info")
public ResultData<String> getGatewayInfo()
{
return ResultData.success("gateway info test:"+ IdUtil.simpleUUID());
}
}
2.2、修改9527服务yml配置文件
java
server:
port: 9527
spring:
application:
name: cloud-gateway #以微服务注册进consul或nacos服务列表内
cloud:
consul: #配置consul地址
host: localhost
port: 8500
discovery:
prefer-ip-address: true
service-name: ${spring.application.name}
gateway:
routes:
- id: pay_routh1 #pay_routh1 #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
uri: http://localhost:8001 #匹配后提供服务的路由地址
predicates:
- Path=/pay/gateway/get/** # 断言,路径相匹配的进行路由
- id: pay_routh2 #pay_routh2 #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
uri: http://localhost:8001 #匹配后提供服务的路由地址
predicates:
- Path=/pay/gateway/info/** # 断言,路径相匹配的进行路由
结果:
2.3、远程调用接口加gateway
java
/**
* GateWay进行网关测试案例01
* @param id
* @return
*/
@GetMapping(value = "/pay/gateway/get/{id}")
public ResultData getById(@PathVariable("id") Integer id);
/**
* GateWay进行网关测试案例02
* @return
*/
@GetMapping(value = "/pay/gateway/info")
public ResultData<String> getGatewayInfo();
2.3.1、新增80服务测试代码
java
@RestController
public class OrderGateWayController {
@Resource
private PayFeignApi payFeignApi;
@GetMapping(value = "/feign/pay/gateway/get/{id}")
public ResultData getById(@PathVariable("id") Integer id)
{
return payFeignApi.getById(id);
}
@GetMapping(value = "/feign/pay/gateway/info")
public ResultData<String> getGatewayInfo()
{
return payFeignApi.getGatewayInfo();
}
}
三、路由以微服务名动态获取服务URL
3.1、修改9527服务yml文件
java
gateway:
routes:
- id: pay_routh1 #pay_routh1 #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
# uri: http://localhost:8001 #匹配后提供服务的路由地址
uri: lb://cloud-payment-service
predicates:
- Path=/pay/gateway/get/** # 断言,路径相匹配的进行路由
- id: pay_routh2 #pay_routh2 #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
# uri: http://localhost:8001 #匹配后提供服务的路由地址
uri: lb://cloud-payment-service
predicates:
- Path=/pay/gateway/info/** # 断言,路径相匹配的进行路由
四、断言
4.1、常用断言api的yml配置
4.1.1、时间范围访问设置
gateway:
routes:
- id: pay_routh1 #pay_routh1 #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
# uri: http://localhost:8001 #匹配后提供服务的路由地址
uri: lb://cloud-payment-service
predicates:
- Path=/pay/gateway/get/** # 断言,路径相匹配的进行路由
# - After=2024-05-04T23:53:30.106114600+08:00[Asia/Shanghai] #在此时间之后访问
# - Before=2024-05-04T23:56:30.106114600+08:00[Asia/Shanghai] #在此时间之前访问
# - Between=2024-05-04T23:57:30.106114600+08:00[Asia/Shanghai],2024-05-05T00:59:30.106114600+08:00[Asia/Shanghai]
4.1.2、cookie访问
gateway:
routes:
- id: pay_routh1 #pay_routh1 #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
# uri: http://localhost:8001 #匹配后提供服务的路由地址
uri: lb://cloud-payment-service
predicates:
- Path=/pay/gateway/get/** # 断言,路径相匹配的进行路由
# - After=2024-05-04T23:53:30.106114600+08:00[Asia/Shanghai] #在此时间之后访问
# - Before=2024-05-04T23:56:30.106114600+08:00[Asia/Shanghai] #在此时间之前访问
# - Between=2024-05-04T23:57:30.106114600+08:00[Asia/Shanghai],2024-05-05T00:59:30.106114600+08:00[Asia/Shanghai]
# - Cookie=username,cjc #必须要携带cookie才能访问
测试1:原生命令访问
测试2:使用postman访问
测试3:使用浏览器访问
4.1.3、X-Request-Id访问
gateway:
routes:
- id: pay_routh1 #pay_routh1 #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
# uri: http://localhost:8001 #匹配后提供服务的路由地址
uri: lb://cloud-payment-service
predicates:
- Path=/pay/gateway/get/** # 断言,路径相匹配的进行路由
# - After=2024-05-04T23:53:30.106114600+08:00[Asia/Shanghai] #在此时间之后访问
# - Before=2024-05-04T23:56:30.106114600+08:00[Asia/Shanghai] #在此时间之前访问
# - Between=2024-05-04T23:57:30.106114600+08:00[Asia/Shanghai],2024-05-05T00:59:30.106114600+08:00[Asia/Shanghai]
# - Cookie=username,cjc #必须要携带cookie才能访问
- Header=X-Request-Id, \d+ # 请求头要有X-Request-Id属性并且值为整数
4.1.4、Host访问
gateway:
routes:
- id: pay_routh1 #pay_routh1 #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
# uri: http://localhost:8001 #匹配后提供服务的路由地址
uri: lb://cloud-payment-service
predicates:
- Path=/pay/gateway/get/** # 断言,路径相匹配的进行路由
# - After=2024-05-04T23:53:30.106114600+08:00[Asia/Shanghai] #在此时间之后访问
# - Before=2024-05-04T23:56:30.106114600+08:00[Asia/Shanghai] #在此时间之前访问
# - Between=2024-05-04T23:57:30.106114600+08:00[Asia/Shanghai],2024-05-05T00:59:30.106114600+08:00[Asia/Shanghai]
# - Cookie=username,cjc #必须要携带cookie才能访问
# - Header=X-Request-Id, \d+ # 请求头要有X-Request-Id属性并且值为整数
- Host=**.cjc.com
4.1.5、Query访问
gateway:
routes:
- id: pay_routh1 #pay_routh1 #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
# uri: http://localhost:8001 #匹配后提供服务的路由地址
uri: lb://cloud-payment-service
predicates:
- Path=/pay/gateway/get/** # 断言,路径相匹配的进行路由
# - After=2024-05-04T23:53:30.106114600+08:00[Asia/Shanghai] #在此时间之后访问
# - Before=2024-05-04T23:56:30.106114600+08:00[Asia/Shanghai] #在此时间之前访问
# - Between=2024-05-04T23:57:30.106114600+08:00[Asia/Shanghai],2024-05-05T00:59:30.106114600+08:00[Asia/Shanghai]
# - Cookie=username,cjc #必须要携带cookie才能访问
# - Header=X-Request-Id, \d+ # 请求头要有X-Request-Id属性并且值为整数
# - Host=**.cjc.com
- Query=username, \d+ # 要有参数名username并且值还要是整数才能路由
4.1.6、RemoteAddr访问
gateway:
routes:
- id: pay_routh1 #pay_routh1 #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
# uri: http://localhost:8001 #匹配后提供服务的路由地址
uri: lb://cloud-payment-service
predicates:
- Path=/pay/gateway/get/** # 断言,路径相匹配的进行路由
# - After=2024-05-04T23:53:30.106114600+08:00[Asia/Shanghai] #在此时间之后访问
# - Before=2024-05-04T23:56:30.106114600+08:00[Asia/Shanghai] #在此时间之前访问
# - Between=2024-05-04T23:57:30.106114600+08:00[Asia/Shanghai],2024-05-05T00:59:30.106114600+08:00[Asia/Shanghai]
# - Cookie=username,cjc #必须要携带cookie才能访问
# - Header=X-Request-Id, \d+ # 请求头要有X-Request-Id属性并且值为整数
# - Host=**.cjc.com
# - Query=username, \d+ # 要有参数名username并且值还要是整数才能路由
- RemoteAddr=192.168.200.1/24 # 外部访问我的IP限制,最大跨度不超过32,目前是1~24它们是 CIDR 表示法。
4.1.7、Method访问
gateway:
routes:
- id: pay_routh1 #pay_routh1 #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
# uri: http://localhost:8001 #匹配后提供服务的路由地址
uri: lb://cloud-payment-service
predicates:
- Path=/pay/gateway/get/** # 断言,路径相匹配的进行路由
# - After=2024-05-04T23:53:30.106114600+08:00[Asia/Shanghai] #在此时间之后访问
# - Before=2024-05-04T23:56:30.106114600+08:00[Asia/Shanghai] #在此时间之前访问
# - Between=2024-05-04T23:57:30.106114600+08:00[Asia/Shanghai],2024-05-05T00:59:30.106114600+08:00[Asia/Shanghai]
# - Cookie=username,cjc #必须要携带cookie才能访问
# - Header=X-Request-Id, \d+ # 请求头要有X-Request-Id属性并且值为整数
# - Host=**.cjc.com
# - Query=username, \d+ # 要有参数名username并且值还要是整数才能路由
- RemoteAddr=192.168.200.1/24 # 外部访问我的IP限制,最大跨度不超过32,目前是1~24它们是 CIDR 表示法。
- Method=Put
4.2、自定义断言
测试代码
java
@Component
public class MyRoutePredicateFactory extends AbstractRoutePredicateFactory<MyRoutePredicateFactory.Config> {
public MyRoutePredicateFactory(){
super(MyRoutePredicateFactory.Config.class);
}
//断言规则
@Validated
public static class Config{
@Getter@Setter@NotEmpty
private String userType;
}
//重写支持短格式
@Override
public List<String> shortcutFieldOrder() {
return Collections.singletonList("userType");
}
@Override
public Predicate<ServerWebExchange> apply(MyRoutePredicateFactory.Config config) {
return new Predicate<ServerWebExchange>() {
@Override
public boolean test(ServerWebExchange serverWebExchange) {
//检查request的参数里面,userType是否为指定的值,符合配置就通过
String userType = serverWebExchange.getRequest().getQueryParams().getFirst("userType");
//如果说参数存在,就和config的数据进行比较
if (userType.equalsIgnoreCase(config.getUserType())) {
return true;
} else if (userType == null) {
return false;
}
return false;
}
};
}
}
配置yml文件
java
gateway:
routes:
- id: pay_routh1 #pay_routh1 #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
# uri: http://localhost:8001 #匹配后提供服务的路由地址
uri: lb://cloud-payment-service
predicates:
- Path=/pay/gateway/get/** # 断言,路径相匹配的进行路由
# - After=2024-05-04T23:53:30.106114600+08:00[Asia/Shanghai] #在此时间之后访问
# - Before=2024-05-04T23:56:30.106114600+08:00[Asia/Shanghai] #在此时间之前访问
# - Between=2024-05-04T23:57:30.106114600+08:00[Asia/Shanghai],2024-05-05T00:59:30.106114600+08:00[Asia/Shanghai]
# - Cookie=username,cjc #必须要携带cookie才能访问
# - Header=X-Request-Id, \d+ # 请求头要有X-Request-Id属性并且值为整数
# - Host=**.cjc.com
# - Query=username, \d+ # 要有参数名username并且值还要是整数才能路由
# - RemoteAddr=192.168.200.1/24 # 外部访问我的IP限制,最大跨度不超过32,目前是1~24它们是 CIDR 表示法。
# - Method=Put
- My=diamond
# - name: My #全面支持
# args:
# userType: diamond
五、过滤器
5.1、常用内置过滤器
5.1.1、请求头添加、修改、删除
修改9527yml配置文件
java
gateway:
routes:
- id: pay_routh1 #pay_routh1 #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
# uri: http://localhost:8001 #匹配后提供服务的路由地址
uri: lb://cloud-payment-service
predicates:
- Path=/pay/gateway/get/** # 断言,路径相匹配的进行路由
# - After=2024-05-04T23:53:30.106114600+08:00[Asia/Shanghai] #在此时间之后访问
# - Before=2024-05-04T23:56:30.106114600+08:00[Asia/Shanghai] #在此时间之前访问
# - Between=2024-05-04T23:57:30.106114600+08:00[Asia/Shanghai],2024-05-05T00:59:30.106114600+08:00[Asia/Shanghai]
# - Cookie=username,cjc #必须要携带cookie才能访问
# - Header=X-Request-Id, \d+ # 请求头要有X-Request-Id属性并且值为整数
# - Host=**.cjc.com
# - Query=username, \d+ # 要有参数名username并且值还要是整数才能路由
# - RemoteAddr=192.168.200.1/24 # 外部访问我的IP限制,最大跨度不超过32,目前是1~24它们是 CIDR 表示法。
# - Method=Put
- My=diamond
# - name: My #全面支持
# args:
# userType: diamond
- id: pay_routh2 #pay_routh2 #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
# uri: http://localhost:8001 #匹配后提供服务的路由地址
uri: lb://cloud-payment-service
predicates:
- Path=/pay/gateway/info/** # 断言,路径相匹配的进行路由
- id: pay_routh3 #pay_routh3
uri: lb://cloud-payment-service #匹配后提供服务的路由地址
predicates:
- Path=/pay/gateway/filter/** # 断言,路径相匹配的进行路由
filters:
- AddRequestHeader=X-Request-cjc1,cjcValue1 # 请求头kv,若一头含有多参则重写一行设置
- AddRequestHeader=X-Request-cjc2,cjcValue2
- RemoveRequestHeader=cache-control # 删除请求头cache-control
- SetRequestHeader=x-forwarded-host, yyyy # 将请求头x-forwarded-host对应的值修改为yyyy
8001测试代码
java
@GetMapping(value = "/pay/gateway/filter")
public ResultData<String> getGatewayFilter(HttpServletRequest request)
{
String result = "";
Enumeration<String> headers = request.getHeaderNames();
while(headers.hasMoreElements())
{
String headName = headers.nextElement();
String headValue = request.getHeader(headName);
System.out.println("请求头名: " + headName +"\t\t\t"+"请求头值: " + headValue);
if(headName.equalsIgnoreCase("X-Request-cjc1")
|| headName.equalsIgnoreCase("X-Request-cjc2")) {
result = result+headName + "\t " + headValue +" ";
}
}
return ResultData.success("getGatewayFilter 过滤器 test: "+result+" \t "+ DateUtil.now());
}
5.1.2、请求参数删除和添加
java
- id: pay_routh3 #pay_routh3
uri: lb://cloud-payment-service #匹配后提供服务的路由地址
predicates:
- Path=/pay/gateway/filter/** # 断言,路径相匹配的进行路由
filters:
- AddRequestHeader=X-Request-cjc1,cjcValue1 # 请求头kv,若一头含有多参则重写一行设置
- AddRequestHeader=X-Request-cjc2,cjcValue2
- RemoveRequestHeader=cache-control # 删除请求头cache-control
- SetRequestHeader=x-forwarded-host, yyyy # 将请求头x-forwarded-host对应的值修改为yyyy
- AddRequestParameter=customerId,9527001 # 新增请求参数Parameter:k ,v
- RemoveRequestParameter=customerName # 删除url请求参数customerName,你传递过来也是null
5.1.3、回应头添加、修改、删除
java
- id: pay_routh3 #pay_routh3
uri: lb://cloud-payment-service #匹配后提供服务的路由地址
predicates:
- Path=/pay/gateway/filter/** # 断言,路径相匹配的进行路由
filters:
- AddRequestHeader=X-Request-cjc1,cjcValue1 # 请求头kv,若一头含有多参则重写一行设置
- AddRequestHeader=X-Request-cjc2,cjcValue2
- RemoveRequestHeader=cache-control # 删除请求头cache-control
- SetRequestHeader=x-forwarded-host, yyyy # 将请求头x-forwarded-host对应的值修改为yyyy
- AddRequestParameter=customerId,9527001 # 新增请求参数Parameter:k ,v
- RemoveRequestParameter=customerName # 删除url请求参数customerName,你传递过来也是null
- AddResponseHeader=X-Response-cjc, BlueResponse # 新增请求参数X-Response-cjc并设值为BlueResponse
- SetResponseHeader=Date,2099-11-11 # 设置回应头Date值为2099-11-11
- RemoveResponseHeader=Content-Type # 将默认自带Content-Type回应属性删除
5.1.4、前缀和路径相关
java
- id: pay_routh3 #pay_routh3
uri: lb://cloud-payment-service #匹配后提供服务的路由地址
predicates:
- Path=/pay/gateway/filter/** # 断言,路径相匹配的进行路由
# - Path=/gateway/filter/** # 断言,为配合PrefixPath测试过滤,暂时注释掉/pay
# - Path=/XYZ/abc/{segment} # 断言,为配合SetPath测试,{segment}的内容最后被SetPath取代
filters:
- RedirectTo=302, http://www.baidu.com/ # 访问http://localhost:9527/pay/gateway/filter跳转到http://www.baidu.com/
# - SetPath=/pay/gateway/{segment} # {segment}表示占位符,你写abc也行但要上下一致
# - PrefixPath=/pay # http://localhost:9527/pay/gateway/filter PrefixPath + Path
5.2、自定义过滤器
5.2.1、自定义全局过滤器
9527服务测试接口耗时代码
java
@Component
@Slf4j
public class MyGlobalFilter implements GlobalFilter, Ordered {
private static final String BEGIN_VISIT_TIME = "begin_visit_time";
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
exchange.getAttributes().put(BEGIN_VISIT_TIME, System.currentTimeMillis());
return chain.filter(exchange).then(Mono.fromRunnable(()-> {
Long bvtime = exchange.getAttribute(BEGIN_VISIT_TIME);
if (bvtime != null){
log.info("访问接口主机: " + exchange.getRequest().getURI().getHost());
log.info("访问接口端口: " + exchange.getRequest().getURI().getPort());
log.info("访问接口URL: " + exchange.getRequest().getURI().getPath());
log.info("访问接口URL参数: " + exchange.getRequest().getURI().getRawQuery());
log.info("访问接口时长: " + (System.currentTimeMillis() - bvtime) + "ms");
log.info("我是美丽分割线: ###################################################");
System.out.println();
}
}));
}
//数字越小,优先级越高
@Override
public int getOrder() {
return 0;
}
}
5.2.2、自定义条件过滤器
9527服务测试代码
java
@Component
public class MyGatewayFilterFactory extends AbstractGatewayFilterFactory<MyGatewayFilterFactory.Config> {
public MyGatewayFilterFactory() {
super(MyGatewayFilterFactory.Config.class);
}
@Override
public List<String> shortcutFieldOrder() {
return Arrays.asList("status");
}
@Override
public GatewayFilter apply(MyGatewayFilterFactory.Config config) {
return new GatewayFilter() {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
if (request.getQueryParams().containsKey("cjc")){
return chain.filter(exchange);
}else {
exchange.getResponse().setStatusCode(HttpStatus.BAD_REQUEST);
return exchange.getResponse().setComplete();
}
}
};
}
public static class Config{
@Getter@Setter
private String status;//设置一个状态标志
}
}
java
- id: pay_routh3 #pay_routh3
uri: lb://cloud-payment-service #匹配后提供服务的路由地址
predicates:
- Path=/pay/gateway/filter/** # 断言,路径相匹配的进行路由
# - Path=/gateway/filter/** # 断言,为配合PrefixPath测试过滤,暂时注释掉/pay
# - Path=/XYZ/abc/{segment} # 断言,为配合SetPath测试,{segment}的内容最后被SetPath取代
filters:
- My=cjc