3.spring cloud gateway
1.gateway的引入
为什么使用网关?
2.gateway快速入门
2.1 专业术语 Glossary
Route: The basic building block of the gateway. It is defined by an ID, a destination URI, a collection of predicates, and a collection of filters. A route is matched if the aggregate predicate is true.
路由 是gateway网关的基本组件,它是由一个 id,一个目标服务的地址,一组 断言,一组过滤器构成,如果断言匹配,则向目标服务的地址请求
Predicate: This is a Java 8 Function Predicate. The input type is a Spring Framework
ServerWebExchange
. This lets you match on anything from the HTTP request, such as headers or parameters.断言 这是 java8 的 函数式断言, 输入的是 spring框架中的 ServerWebExchange 组件,通过它可以得到代表请求的 request
Filter: These are instances of
GatewayFilter
that have been constructed with a specific factory. Here, you can modify requests and responses before or after sending the downstream request.
过滤器 这些实例都是GatewayFilter的实例,被一个特殊的工厂构建,他可以对请求和响应进行拦截处理。
2.2 工作流程
3.搭建网关服务
3.1 创建sb工程,添加依赖
<dependencies>
<dependency>
<groupId>com.glls.java2110</groupId>
<artifactId>01-cloud-common</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.6.0</version>
</dependency>
</dependencies>
注意:不要添加spring-boot-starter-web 这个依赖,因为gateway 是基于 spring5的新特性,webflux,它不是基于servlet的,避免冲突。
3.2 配置文件
spring:
application:
name: cloud-gateway # 网关的服务名
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848 #注册中心的地址
namespace: java2110
gateway:
routes[0]: # 配置的第一个路由
id: test_route1 # 路由id
uri: http://www.baidu.com # 目标 uri
predicates[0]: # 第一个断言
Query=url,bd. # The Query Route Predicate Factory
routes[1]:
id: customer_route
uri: lb://cloud-customer
predicates[0]:
Path=/customer/**
predicates[1]:
After=2021-10-29T22:24:40.626+08:00[Asia/Shanghai]
predicates[2]:
MyHeader=name,zs # 自定义断言
routes[2]:
id: search_route
uri: lb://cloud-search
predicates[0]:
Path=/search/**
filters[0]:
AddRequestHeader=Foo,Bar
filters[1]:
CalServiceTime=x,y # 自定义过滤器
routes[3]:
id: search_route
uri: lb://cloud-search
predicates[0]:
Path=/api/search/**
filters[0]:
RewritePath=/api(?<segment>/?.*), $\{segment} # 实际开发当中 前端请求过来的路径 可能跟接口的路径不太匹配,所以可以 使用这个过滤器 进行 路径重写
server:
port: 88
3.3 自定义断言
自定义谓词工厂的类名规范:后缀必须是RoutePredicateFactory
//自定义断言
package com.glls.gateway.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;
/**
* @ClassName : MyHeaderRoutePredicateFactory
* @Author : glls
* @Date: 2021/12/22 10:56
* @Description :
*/
@Component
public class MyHeaderRoutePredicateFactory extends AbstractRoutePredicateFactory<MyConfig> {
public MyHeaderRoutePredicateFactory() {
super(MyConfig.class);
}
@Override
public Predicate<ServerWebExchange> apply(MyConfig config) {
return new Predicate<ServerWebExchange>() {
@Override
public boolean test(ServerWebExchange exchange) {
String value = exchange.getRequest().getHeaders().getFirst(config.getKey());
System.out.println(value);
if(StringUtils.isEmpty(value)){
return false;
}else{
if(value.equals(config.getValue())){
return true;
}else{
return false;
}
}
}
};
}
@Override
public List<String> shortcutFieldOrder() {
//- MyHeader=bbb,cccc
//[bbb,cccc]
//bbb赋值给MyConfig#key
//cccc赋值给MyConfig#value
return Arrays.asList("key","value");
}
}
3.4 自定义过滤器
命名规范:过滤器工厂的类名必须以GatewayFilterFactory为后缀
//自定义过滤器
package com.glls.gateway.filters;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import java.util.Arrays;
import java.util.List;
/**
* @ClassName : CalServiceTimeGatewayFilterFactory
* @Author : glls
* @Date: 2021/12/22 11:40
* @Description :
*/
@Component
public class CalServiceTimeGatewayFilterFactory extends AbstractGatewayFilterFactory<MyConfig> {
public CalServiceTimeGatewayFilterFactory() {
super(MyConfig.class);
}
@Override
public GatewayFilter apply(MyConfig config) {
return new GatewayFilter() {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
//前处理
long startTime = System.currentTimeMillis();
System.out.println("name:"+config.getKey());
System.out.println("value:"+config.getValue());
// return chain.filter(exchange);//放⾏
return chain.filter(exchange).then(
//后置处理
Mono.fromRunnable(()->{
System.out.println("post come in");
//获取系统当前时间戳为endTime
long entTime = System.currentTimeMillis();
System.out.println("接口响应时间time="+(entTime-startTime));
}));
}
};
};
@Override
public List<String> shortcutFieldOrder() {
return Arrays.asList("key","value");
}
}
package com.glls.gateway.filters;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @ClassName : MyConfig
* @Author : glls
* @Date: 2021/12/22 11:40
* @Description : 建议 断言 和 过滤器 各建一个 这个类
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class MyConfig {
private String key;
private String value;
}
4.全局过滤器的使用
package com.glls.gateway.filters;
import cn.hutool.json.JSONUtil;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.util.HashMap;
import java.util.Map;
/**
* @ClassName : AuthFilter
* @Author : glls
* @Date: 2021/12/22 14:59
* @Description :
*/
@Component
public class AuthFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
//过滤器的前处理
ServerHttpRequest request = exchange.getRequest();
ServerHttpResponse response = exchange.getResponse();
String token = request.getHeaders().getFirst("token");
if(StringUtils.isEmpty(token)){
Map res = new HashMap(){{
put("msg", "没有登录!!");
}};
return response(response,res);
}else{
if(!"123".equals(token)){
Map res = new HashMap(){{
put("msg", "令牌⽆效!!");
}};
return response(response,res);
}else{
return chain.filter(exchange); //放⾏
}
}
}
private Mono<Void> response(ServerHttpResponse response,Object
msg){
response.getHeaders().add("Content-Type",
"application/json;charset=UTF-8");
String resJson = JSONUtil.toJsonPrettyStr(msg);
DataBuffer dataBuffer =
response.bufferFactory().wrap(resJson.getBytes());
return response.writeWith(Flux.just(dataBuffer));//响应json数据
}
@Override
public int getOrder() {
return 0;
}
}