目录
Spring Cloud GateWay是Spring Cloud的⼀个全新项⽬,⽬标是取代Netflix Zuul,它基于Spring5.0+SpringBoot2.0+WebFlux(基于⾼性能的Reactor模式响应式通信框架Netty,异步⾮阻塞模型)等技术开发,性能⾼于Zuul,官⽅测试,GateWay是Zuul的1.6倍,旨在为微服务架构提供⼀种简单有效的统⼀的API路由管理⽅式。
Spring Cloud GateWay不仅提供统⼀的路由⽅式(反向代理)并且基于 Filter(定义过滤器对请求过滤,完成⼀些功能) 链的⽅式提供了⽹关基本的功能,例如:鉴权、流量控制、熔断、路径重写、⽇志监控等。
网关在架构中的位置,可以看到是请求进来由网关路由分配找到需要请求的服务,其中Nginx是用来做网关高可用的。
Spring Cloud GateWay天⽣就是异步⾮阻塞的,基于Reactor模型;
⼀个请求—>⽹关根据⼀定的条件匹配—匹配成功之后可以将请求转发到指定的服务地址;⽽在这个过程中,我们可以进⾏⼀些⽐较具体的控制(限流、⽇志、⿊⽩名单)
Predicates断⾔就是我们的匹配条件,⽽Filter就可以理解为⼀个⽆所不能的拦截器,有了这两个元素,结合⽬标URL,就可以实现⼀个具体的路由转发。
Spring Cloud GateWay 帮我们内置了很多 Predicates功能,实现了各种路由匹配规则(通过 Header、请求参数等作为条件)匹配到对应的路由。
一般都会使用请求路径正则匹配
spring:
cloud:
gateway:
routes: # 路由可以有多个
- id: service-xxx-router # 我们⾃定义的路由 ID,保持唯⼀
uri: lb://server-name
predicates: #路由条件
- Path=/xx/xxxx/**
客户端向Spring Cloud GateWay发出请求,然后在GateWay Handler Mapping中找到与请求相匹配的路由,将其发送到GateWay Web Handler;Handler再通过指定的过滤器链来将请求发送到我们实际的服务执⾏业务逻辑,然后返回。过滤器之间⽤虚线分开是因为过滤器可能会在发送代理请求之前(pre)或者之后(post)执⾏业务逻辑。
Filter在“pre”类型过滤器中可以做参数校验、权限校验、流量监控、⽇志输出、协议转换等,在“post”类型的过滤器中可以做响应内容、响应头的修改、⽇志的输出、流量监控等。
从过滤器⽣命周期(影响时机点)的⻆度来说,主要有两个pre和post:
从过滤器类型的⻆度 ,Spring Cloud GateWay的过滤器分为GateWayFilter和GlobalFilter两种。
一般情况下GlobalFilter全局过滤器是程序员使⽤⽐较多的过滤器;
可以用来自定义一些黑名单校验等
⾃定义GateWay全局过滤器时,我们实现Global Filter接⼝即可,通过全局过滤器可以实现⿊⽩名单、限流等功能。
@Slf4j
@Component
public class BlackListFilter implements GlobalFilter, Ordered {
private static List<String> blackList = new ArrayList<>();
static {
blackList.add("0:0:0:0:0:0:0:1"); // 模拟本机地址
}
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// 思路:获取客户端ip,判断是否在⿊名单中,在的话就拒绝访问,不在的话就放⾏
// 从上下⽂中取出request和response对象
ServerHttpRequest request = exchange.getRequest();
ServerHttpResponse response = exchange.getResponse();
// 从request对象中获取客户端ip
String clientIp = request.getRemoteAddress().getHostString();
// 拿着clientIp去⿊名单中查询,存在的话就决绝访问
if (blackList.contains(clientIp)) {
// 决绝访问,返回
response.setStatusCode(HttpStatus.UNAUTHORIZED); // 状态码
log.debug("=====>IP:" + clientIp + " 在⿊名单中,将被拒绝访 问!");
String data = "请求被禁止!";
DataBuffer wrap = response.bufferFactory().wrap(data.getBytes());
return response.writeWith(Mono.just(wrap));
}
// 合法请求,放⾏,执⾏后续的过滤器
return chain.filter(exchange);
}
@Override
public int getOrder() {
return 0; //越小优先级越高
}
}
官方全局过滤器使用规范例子
@RestController
@SpringBootApplication
public class DemogatewayApplication {
@RequestMapping("/hystrixfallback")
public String hystrixfallback() {
return "This is a fallback";
}
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
//@formatter:off
return builder.routes()
.route("path_route", r -> r.path("/get")
.uri("http://httpbin.org"))
.route("host_route", r -> r.host("*.myhost.org")
.uri("http://httpbin.org"))
.route("rewrite_route", r -> r.host("*.rewrite.org")
.filters(f -> f.rewritePath("/foo/(?<segment>.*)",
"/${segment}"))
.uri("http://httpbin.org"))
.route("hystrix_route", r -> r.host("*.hystrix.org")
.filters(f -> f.hystrix(c -> c.setName("slowcmd")))
.uri("http://httpbin.org"))
.route("hystrix_fallback_route", r -> r.host("*.hystrixfallback.org")
.filters(f -> f.hystrix(c -> c.setName("slowcmd").setFallbackUri("forward:/hystrixfallback")))
.uri("http://httpbin.org"))
.route("limit_route", r -> r
.host("*.limited.org").and().path("/anything/**")
.filters(f -> f.requestRateLimiter(c -> c.setRateLimiter(redisRateLimiter())))
.uri("http://httpbin.org"))
.route("websocket_route", r -> r.path("/echo")
.uri("ws://localhost:9000"))
.build();
//@formatter:on
}
@Bean
RedisRateLimiter redisRateLimiter() {
return new RedisRateLimiter(1, 2);
}
@Bean
SecurityWebFilterChain springWebFilterChain(ServerHttpSecurity http) throws Exception {
return http.httpBasic().and()
.csrf().disable()
.authorizeExchange()
.pathMatchers("/anything/**").authenticated()
.anyExchange().permitAll()
.and()
.build();
}
@Bean
public MapReactiveUserDetailsService reactiveUserDetailsService() {
UserDetails user = User.withDefaultPasswordEncoder().username("user").password("password").roles("USER").build();
return new MapReactiveUserDetailsService(user);
}
public static void main(String[] args) {
SpringApplication.run(DemogatewayApplication.class, args);
}
}
当然也可以单独做过滤器,比如,黑白名单过滤器,限流,认证,以及token校验等过滤器
import org.apache.commons.chain.Command;
import org.apache.commons.chain.impl.ChainBase;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* 责任链模式
* 接口限流->黑名单->白名单->数字签名验证,解码body数据->检查api信息及api参数->远程调用对应的接口->调用结果加密->返回
*/
@Configuration
public class ChainConfig {
@Bean
Command interfaceFlowLimitHandler() {
return new InterfaceFlowLimitHandler();
}
// @Bean
// Command blackIpListCheckHandler() {
// return new BlackIpListCheckHandler();
// }
@Bean
Command whiteIpListCheckHandler() {
return new WhiteIpListCheckHandler();
}
@Bean
Command signatureCheckHandler() {
return new SignatureCheckHandler();
}
@Bean
Command apiInfoCheckHandler() {
return new ApiInfoCheckHandler();
}
@Bean
Command invokeInterfaceHandler() {
return new InvokeInterfaceHandler();
}
@Bean
Command encodeResponseHandler() {
return new EncodeResponseHandler();
}
@Bean
ChainBase chains() {
ChainBase chainBase = new ChainBase();
chainBase.addCommand(interfaceFlowLimitHandler());
chainBase.addCommand(blackIpListCheckHandler());
chainBase.addCommand(whiteIpListCheckHandler());
chainBase.addCommand(signatureCheckHandler());
chainBase.addCommand(apiInfoCheckHandler());
chainBase.addCommand(invokeInterfaceHandler());
chainBase.addCommand(encodeResponseHandler());
return chainBase;
}
}
每个bean类实现Command接口。命令封装要执行的处理工作单元,其目的是检查或修改由上下文表示的事务的状态。单个命令可以被组装成一个链,这允许它们完成所需的处理,或者将进一步的处理委托给链中的下一个命令。命令实现应该以线程安全的方式设计,适合包含在可能由不同线程同时处理的多个链中。 一般来说,这意味着命令类不应该维护实例变量中的状态信息。相反,应该通过对传递给execute()命令的上下文属性的适当修改来维护状态信息。 命令实现通常在上下文实例中检索和存储状态信息,这些信息作为参数传递给execute()方法.
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package org.apache.commons.chain;
public interface Command {
boolean CONTINUE_PROCESSING = false;
boolean PROCESSING_COMPLETE = true;
boolean execute(Context var1) throws Exception;
}
路由
spring:
application:
name: gateway
cloud:
gateway:
routes:
- id: demo
uri: lb://demo
predicates:
- Path=/rest-web/**
filters:
- StripPrefix=1
Java配置:
代表将 ip地址为10.1.1.1的访问转发到 down。。。。
断言 predicates: 下面加个 -可以添加多种断言
路由过滤器允许以某种方式修改传入的HTTP请求或传出的HTTP响应。路由过滤器适用于特定路由。Spring Cloud Gateway包括许多内置的xGatewayFilter工厂。
配置:
spring:
application:
name: gateway
cloud:
gateway:
routes:
- id: demo
uri: lb://demo
predicates:
- Path=/rest-web/**
filters:
- StripPrefix=1 # 将url前缀去掉比如ip,port,http等
- AddRequestHeader=X-Request-red, blue
此清单将X-Request-red:blue标头添加到所有匹配请求的下游请求的标头中。
AddRequestHeader了解用于匹配路径或主机的URI变量。URI变量可以在值中使用,并在运行时扩展。
java config
形式使用ModifyRequestBody过滤器过滤器在网关向下游发送请求主体之前对其进行修改。
尝试通过代码的形式将转发的请求添加头信息:
@Bean
public RouteLocator routes(RouteLocatorBuilder builder) {
return builder.routes()
.route("route1", r -> r.path("/payment/nacos/header/**")
.filters(f->f.addRequestHeader("json", "123"))
.uri("lb://nacos-payment-provider")).build();
}
常见的有Zuul、Gateway、Nginx
首先创建父类工程和两个子工程:demo和gateway
父类pom文件定义版本
<modules>
<module>gateway</module>
<module>dubbo-api</module>
<module>rest-web</module>
</modules>
<properties>
<java.version>1.8</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<spring-boot.version>2.3.7.RELEASE</spring-boot.version>
<spring-cloud-alibaba.version>2.2.2.RELEASE</spring-cloud-alibaba.version>
<spring-cloud.version>Hoxton.SR9</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>${spring-cloud-alibaba.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
pom.xml
<dependencies>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--nacos 配置中心-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<!-- commons-lang3 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.9</version>
</dependency>
</dependencies>
配置文件application.yml
server:
port: 8000
spring:
application:
name: demo
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
config:
server-addr: 127.0.0.1:8848
file-extension: yaml
简单的controller路由
package com.demo.restweb;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@SpringBootApplication
@RestController
@EnableDiscoveryClient
public class RestWebApplication {
public static void main(String[] args) {
SpringApplication.run(RestWebApplication.class, args);
}
@GetMapping("/demo")
public String demo(String name) {
System.out.println("ssss " + name);
return "hello, " + name + "i am demo";
}
}
pom.xml
<dependencies>
<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-sentinel</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
</dependencys>
配置文件application.yml
spring:
application:
name: gateway
cloud:
gateway:
routes:
- id: demo
uri: lb://demo
predicates:
- Path=/rest-web/**
filters:
- StripPrefix=1
nacos:
discovery:
server-addr: 127.0.0.1:8848
enabled: true