代码地址:https://gitee.com/interface_xiongtete/spring-cloud2022 去年我发过两篇网关的文章:Spring Cloud Gateway(微服务网关),gateway网关的断言(predicate)和过滤(filter) 笔记为本人根据周阳老师的SpringCloud视频手敲总结出来的。
官网文档:https://cloud.spring.io/spring-cloud-static/spring-cloud-gateway/2.2.1.RELEASE/reference/html/
该项目提供了一个库,用于在 Spring WebFlux 之上构建 API 网关。Spring Cloud Gateway 旨在提供一种简单而有效的方式来路由到 API,并为它们提供横切关注点,例如:安全性、监控/指标和弹性。
SpringCloud Gateway 使用的Webflux中的reactor-netty响应式编程组件,底层使用了Netty通讯框架。
传统的Web框架,比如说:struts2,springmvc等都是基于Servlet API与Servlet容器基础之上运行的。 但是在Servlet3.1之后有了异步非阻塞的支持。而WebFlux是一个典型异步非阻塞的框架,它的核心是基于Reactor的相关API实现的。相对于传统的web框架来说,它可以运行在诸如Netty,Undertow及支持Servlet3.1的容器上。非阻塞式+函数式编程(Spring5必须让你使用java8)
Spring WebFlux 是 Spring 5.0 引入的新的响应式框架,区别于 Spring MVC,它不需要依赖Servlet API,它是完全异步非阻塞的,并且基于 Reactor 来实现响应式流规范。
ServerWebExchange
。这使您可以匹配来自 HTTP 请求的任何内容,例如请求头或请求参数。如果请求与断言相匹配,就进行路由。总体来看:
web请求,通过一些匹配条件,定位到真正的服务节点。并在这个转发过程的前后,进行一些精细化控制。 predicate就是我们的匹配条件;而filter,就可以理解为一个无所不能的拦截器。有了这两个元素,再加上目标uri,就可以实现一个具体的路由了
SpringCloud Gateway的工作机制大概如下:
<dependencies>
<!--gateway-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!--eureka-client-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!-- 引入自己定义的api通用包,可以使用Payment支付Entity -->
<dependency>
<groupId>com.atguigu.springcloud</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>${project.version}</version>
</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>
server:
port: 9527
spring:
application:
name: cloud-gateway
eureka:
instance:
hostname: cloud-gateway-service
client: #服务提供者provider注册进eureka服务列表内
service-url:
register-with-eureka: true
fetch-registry: true
defaultZone: http://eureka7001.com:7001/eureka
我们去cloud-provider-payment8001看看controller的访问地址
我们现在的需求是:不想暴露8001端口,希望在8001外面套一层9527,只让外界访问网关,让网关统一进行路由的转发。
server:
port: 9527
spring:
application:
name: cloud-gateway
cloud:
gateway:
routes:
- id: payment_routh #payment_route #路由的ID,没有固定规则但要求唯一,建议配合服务名
uri: http://localhost:8001 #匹配后提供服务的路由地址
predicates:
- Path=/payment/get/** # 断言,路径相匹配的进行路由
- id: payment_routh2 #payment_route #路由的ID,没有固定规则但要求唯一,建议配合服务名
uri: http://localhost:8001 #匹配后提供服务的路由地址
predicates:
- Path=/payment/lb/** # 断言,路径相匹配的进行路由
eureka:
instance:
hostname: cloud-gateway-service
client: #服务提供者provider注册进eureka服务列表内
service-url:
register-with-eureka: true
fetch-registry: true
defaultZone: http://eureka7001.com:7001/eureka
启动eureka7001注册中心模块
启动cloud-provider-payment8001微服务
启动cloud-gateway-gateway9527网关微服务
访问测试:
添加网关之前的访问方式:http://localhost:8001/payment/get/31
添加网关之后的访问方式:http://localhost:9527/payment/get/31
Gateway其实有两种配置方式:yml配置和代码注入方式配置。由于代码注入方式看起来有点不美观所以这里暂且写,详细的去查官网,这种方式其实用的不多。
默认情况下Gateway会根据注册中心注册的服务列表,以注册中心上微服务名为路径创建动态路由进行转发,从而实现动态路由的功能。
我们启动一个eureka7001,和两个服务提供者payment8001和payment8002,顺便测试下负载均衡(前提是引入了Ribbon)。
新的网关配置文件如下:
server:
port: 9527
spring:
application:
name: cloud-gateway
cloud:
gateway:
discovery:
locator:
enabled: true #开启从注册中心动态创建路由的功能,利用微服务名进行路由
routes:
- id: payment_routh #payment_route #路由的ID,没有固定规则但要求唯一,建议配合服务名
# uri: http://localhost:8001 #匹配后提供服务的路由地址
uri: lb://cloud-payment-service #匹配后提供服务的路由地址
predicates:
- Path=/payment/get/** # 断言,路径相匹配的进行路由
- id: payment_routh2 #payment_route #路由的ID,没有固定规则但要求唯一,建议配合服务名
# uri: http://localhost:8001 #匹配后提供服务的路由地址
uri: lb://cloud-payment-service #匹配后提供服务的路由地址
predicates:
- Path=/payment/lb/** # 断言,路径相匹配的进行路由
eureka:
instance:
hostname: cloud-gateway-service
client: #服务提供者provider注册进eureka服务列表内
service-url:
register-with-eureka: true
fetch-registry: true
defaultZone: http://eureka7001.com:7001/eureka
需要注意的是uri的协议为lb
,表示启用Gateway的负载均衡功能。
lb://serviceName
是spring cloud gateway在微服务中自动为我们创建的负载均衡uri
确保服务都正确启动
查看服务注册中心:localhost:7001
可以看到网关服务和两个服务提供者都已经注册成功。
访问:http://localhost:9527/payment/lb
第一次:
第二次:
即使不断地刷新,输出结果一直在8001和8002之间切换,因为Ribbon默认就是轮询算法,说明负载均衡是成功的。
Spring Cloud Gateway将路由匹配作为Spring WebFlux HandlerMapping基础架构的一部分。Spring Cloud Gateway包括许多内置的Route Predicate工厂。所有这些Predicate都与HTTP请求的不同属性匹配。多个Route Predicate工厂可以进行组合。
Spring Cloud Gateway 创建 Route 对象时, 使用 RoutePredicateFactory 创建 Predicate 对象,Predicate 对象可以赋值给 Route。 Spring Cloud Gateway 包含许多内置的Route Predicate Factories。
所有这些谓词都匹配HTTP请求的不同属性。多种谓词工厂可以组合,并通过逻辑and。
After Route Predicate
after 路由谓词工厂采用一个参数,即日期时间。此谓词匹配在指定日期时间之后发生的请求
Before Route Predicate:before 路由谓词工厂采用一个参数 a datetime
。此谓词匹配在指定的 之前发生的请求datetime
。
Between Route Predicate:路由谓词工厂之间有两个参数,datetime1
和datetime2
。此谓词匹配发生在 afterdatetime1
和 before的请求datetime2
。datetime2
参数必须在之后datetime1
。
Cookie Route Predicate:cookie 路由谓词工厂有两个参数,cookie 名称和正则表达式。此谓词匹配具有给定名称且其值与正则表达式匹配的 cookie。
Header Route Predicate:标头路由谓词工厂有两个参数,标头名称和正则表达式。此谓词与具有给定名称且值与正则表达式匹配的标头匹配。
Host Route Predicate:主机路由谓词工厂采用一个参数:主机名模式列表。该模式是一种 Ant 风格的模式,.
以分隔符为分隔符。此谓词匹配Host
与模式匹配的标头。
Method Route Predicate:Method Route Predicate Factory 采用一个或多个参数:要匹配的 HTTP 方法。
Path Route Predicate:Path Route Predicate Factory 有两个参数:一个 SpringPathMatcher
模式列表和一个名为 的可选标志matchOptionalTrailingSeparator
Query Route Predicate:查询路由谓词工厂有两个参数:一个 requiredparam
和一个 optional regexp
。
Predicate就是为了实现一组匹配规则,让请求过来找到对应的Route进行处理。
路由过滤器可用于修改进入的HTTP请求和返回的HTTP响应,路由过滤器只能指定路由进行使用。
Spring Cloud Gateway 内置了多种路由过滤器,他们都由GatewayFilter的工厂类来产生
/**
* 自定义全局GlobalFilter
*/
@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("uname");
if(uname==null){
log.info("*******用户名为null,非法用户!!!");
exchange.getResponse().setStatusCode(HttpStatus.NOT_ACCEPTABLE);
return exchange.getResponse().setComplete();
}
return chain.filter(exchange);
}
@Override
public int getOrder() {
return 0;
}
}
上面配置的意思是,请求种必须带有uname参数,不带不进行正常的路由转发并返回自定义状态码。
启动
访问带uname参数的:http://localhost:9527/payment/lb?uname=z3
访问不带uname参数的:http://localhost:9527/payment/lb
此时,无法正常使用转发功能。
到此,微服务网关的基本功能就介绍完了,高级配置后面碰到再另写一篇。