本文已参与「新人创作礼」活动,一起开启掘金创作之路。
1、Gateway
1.1、概述简介
1. 是什么
Spring Cloud Gateway使用的Webflux中的reactor-netty响应式编程组件,底层使用了Netty通讯框架。
2. 能干嘛
- 反向代理
- 鉴权
- 流量控制
- 熔断
- 日志监控
3. 在微服务架构中的位置
- 网关处于微服务和负载均衡之间。
4. 选择Gateway的原因
neflix不太靠谱,zuul2.0一直跳票,迟迟不发布Spring Cloud Gateway是基于Spring Framework 5,Project Reactot和Spring Boot 2.0进行构建,可以实现动态路由,可以堆路由指定断言和过滤器;集成Hystrix的断路器功能;集成Spring Cloud服务发现功能;易于编写的断言和过滤器;同时支持请求限流功能和支持路径重写。Zuul 1.x是一个基于阻塞I/O的网关,Spring Cloud Gateway是基于Spring Framework 5,Project Reactot和Spring Boot 2.0进行构建;Spring Cloud Gateway还支持WebSocket,并且与Spring紧密集成拥有更好的开发体验。
1.2、三大核心概念
- Route(路由):路由是构建网关的基本模块,它由
ID,目标URI,一系列的断言和过滤器组成,如果断言为true则匹配该路由。 - Predicate(断言):参考的是
java8的java.util.function.Predicate开发人员可以匹配HTTP请求中的所有内容(例如请求头或请求参数),如果请求与断言相匹配则进行路由。 - Filter(过滤):指的是
Spring框架中GatewayFilter的实例,使用过滤器,可以在请求被路由前或者之后对请求进行修改。
web请求,通过一些匹配条件,定位到真正的服务节点。并在这个转发过程的前后,进行一些精细化控制。predicate就是我们的匹配条件;而filter,就可以理解为一个无所不能的拦截器。有了这两个元素,再加上目标uri,就可以实现一个具体的路由了。
1.3、Gateway工作流程
- 客户端向
Spring Cloud Gateway发出请求。然后在Gateway Handler Mapping中找到与请求相匹配的路由,将其发送到Gateway Web Handler。 handler再通过指定的过滤链来将请求发送到我们实际的服务执行业务逻辑,然后返回。过滤器之间用虚线分开是因为过滤器可能会在发送请求之前(pre)或之后(post)执行业务逻辑。- 过滤器在
pre类型的过滤器可以做参数检验、权限监控、日志输出、协议转换等,在post类型的过滤器可以做响应内容、响应头的修改,日志的输出,流量监控等有着非常重要的作用。
1.4、入门配置
1.4.1、使用yml文件配置路由
1. 建Module
Module的名称为cloud-gateway-gateway9527。
2. 改POM
<dependencies>
<!--新增gateway-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>com.xiao</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
3. 改YML
server:
port: 9527
spring:
application:
name: cloud-gateway
cloud:
gateway:
routes:
- id: payment_routh #路由的ID,没有固定规则但要求唯一,建议配合服务名
uri: http://localhost:8001 #匹配后提供服务的路由地址
predicates:
- Path=/payment/get/** #断言,路径相匹配的进行路由
- id: payment_routh2
uri: http://localhost:8001
predicates:
- Path=/payment/lb/** #断言,路径相匹配的进行路由
eureka:
instance:
hostname: cloud-gateway-service
client:
service-url:
register-with-eureka: true
fetch-registry: true
defaultZone: http://eureka7001.com:7001/eureka
4. 主启动
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@SpringBootApplication
@EnableEurekaClient
public class GateWayMain9527 {
public static void main(String[] args) {
SpringApplication.run(GateWayMain9527.class,args);
}
}
5. 测试结果
- 测试结果:我们不仅可以通过
8001端口进行访问,还可以通过9527端口进行访问。
1.4.2、使用配置类进行配置路由
1. 配置类
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class GateWayConfig {
@Bean
public RouteLocator getRoute(RouteLocatorBuilder builder){
RouteLocatorBuilder.Builder routes = builder.routes();
// 实现http://localhost:9527/guonei 跳转到 http://news.baidu.com/guonei
routes.route("path_route",r -> r.path("/guonei").uri("http://news.baidu.com/guonei")).build();
return routes.build();
}
}
2. 测试结果
- 实现
http://localhost:9527/guonei跳转到http://news.baidu.com/guonei
1.5、通过微服务名称实现动态路由
1. 修改相关的yml配置文件
server:
port: 9527
spring:
application:
name: cloud-gateway
cloud:
gateway:
discovery:
locator:
enabled: true #开启从注册中心动态创建路由的功能,利用微服务名进行路由
routes:
- id: payment_routh #路由的ID,没有固定规则但要求唯一,建议配合服务名
#uri: http://localhost:8001 #匹配后提供服务的路由地址
uri: lb://cloud-payment-service
predicates:
- Path=/payment/get/** #断言,路径相匹配的进行路由
- id: payment_routh2
#uri: http://localhost:8001
uri: lb://cloud-payment-service
predicates:
- Path=/payment/lb/** #断言,路径相匹配的进行路由
eureka:
instance:
hostname: cloud-gateway-service
client:
service-url:
register-with-eureka: true
fetch-registry: true
defaultZone: http://eureka7001.com:7001/eureka
2. 测试结果
- 测试结果:我们可以发现,当我们反复发送同一个请求的时候,它使用的不是同一个端口号进行访问,而是通过轮询的方式将请求发送到
8001和8002这两个服务器上进行请求相应的资源。
1.6、Predicate断言配置
1. Predicate是什么
- 说白了,
Predicate就是为了实现一组匹配规则,让请求过来找到对应的Route进行处理
2. 详细配置
spring:
cloud:
gateway:
routes:
predicates:
- Path=/payment/lb/** #断言,路径相匹配的进行路由
#- After=2020-03-08T10:59:34.102+08:00[Asia/Shanghai] 表示要在这个时间之后才能进行访问
#- Before=2020-03-08T10:59:34.102+08:00[Asia/Shanghai] 表示在这个时间之前
# - Between=2020-03-08T10:59:34.102+08:00[Asia/Shanghai] , 2020-03-08T10:59:34.102+08:00[Asia/Shanghai] 表示要在这两个时间之间
#- Cookie=username,zhangshuai #并且Cookie是username=zhangshuai才能访问
#- Header=X-Request-Id, \d+ #请求头中要有X-Request-Id属性并且值为整数的正则表达式
#- Host=**.xiao.com
#- Method=GET
#- Query=username, \d+ #要有参数名称并且是正整数才能路由
1.7、Filter的使用
1.7.1、过滤器的简介
1. 过滤器是什么
- 路由过滤器可用于修改进入
HTTP请求和返回的HTTP响应,路由过滤器只能指定路由进行使用。 - 就是对访问的请求进行再一次的条件筛选,如果满足相应的条件就放行通过;如果不满足相应的条件就进行拦截。
2. 过滤器的作用
-
全局日志记录
-
统一网关鉴权
3. 过滤器的分类
(1)、生命周期划分
-
pre:在业务逻辑之前 -
post:在业务逻辑之后
(2)、种类划分
-
GatewayFilter:单一 -
GlobalFilter:全局
1.7.2、自定义过滤器
1. 过滤器类的编写
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import java.util.Date;
@Component
@Slf4j
public class MyLogGateWayFilter implements GlobalFilter,Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
log.info("*********come in MyLogGateWayFilter: "+new Date());
String uname = exchange.getRequest().getQueryParams().getFirst("username");
if(StringUtils.isEmpty(uname)){
log.info("*****用户名为Null 非法用户,(┬_┬)");
exchange.getResponse().setStatusCode(HttpStatus.NOT_ACCEPTABLE);//给人家一个回应
return exchange.getResponse().setComplete();
}
return chain.filter(exchange);
}
@Override
public int getOrder() {
return 0;
}
}
2. 测试结果
(1)、携带正确参数
(2)、 未携带正确参数