在微服务架构里,服务的粒度被进一步细分,各个业务服务可以被独立的设计、开发、测试、部署和管理。这时,各个独立部署单元可以用不同的开发测试团队维护,可以使用不同的编程语言和技术平台进行设计,这就要求必须使用一种语言和平 台无关的服务协议作为各个单元间的通讯方式。
网关的角色是作为一个 API 架构,用来保护、增强和控制对于 API 服务的访问。
API 网关是一个处于应用程序或服务(提供 REST API 接口服务)之前的系统,用来管理授权、访问控制和流量限制等,这样 REST API 接口服务就被 API 网关保护起来,对所有的调用者透明。因此,隐藏在 API 网关后面的业务系统就可以专注于创建和管理服务,而不用去处理这些策略性的基础设施。
Spring Cloud Gateway是Spring官方基于Spring 5.0,Spring Boot 2.0和Project Reactor等技术开发的网关,Spring Cloud Gateway旨在为微服务架构提供一种简单而有效的统一的API路由管理方式。Spring Cloud Gateway作为Spring Cloud生态系中的网关,目标是替代ZUUL,其不仅提供统一的路由方式,并且基于Filter链的方式提供了网关基本的功能,例如:安全,监控/埋点,和限流等
- Spring Cloud Gateway 可以看做是一个 Zuul 1.x 的升级版和代替品,比 Zuul 2 更早的使用 Netty 实现异步 IO,从而实现了一个简单、比 Zuul 1.x 更高效的、与 Spring Cloud 紧密配合的 API 网关。
- Spring Cloud Gateway 里明确的区分了 Router 和 Filter,并且一个很大的特点是内置了非常多的开箱即用功能,并且都可以通过 SpringBoot 配置或者手工编码链式调用来使用。
- 比如内置了 10 种 Router,使得我们可以直接配置一下就可以随心所欲的根据 Header、或者 Path、或者 Host、或者 Query 来做路由。
- 比如区分了一般的 Filter 和全局 Filter,内置了 20 种 Filter 和 9 种全局 Filter,也都可以直接用。当然自定义 Filter 也非常方便。
SpringCloud中所集成的Zuul版本,采用的是Tomcat容器,使用的是传统的Servlet IO处理模型。
- Servlet生命周期?
- servlet 由 servlet container 进行生命周期管理
- container 启动时构造 servlet 对象并调用 servlet init() 进行初始化;
- container 运行时接受请求,并为每个请求分配一个线程(一般从线程池中获取空闲线程)然后调用service();
- container 关闭时调用 servlet destory() 销毁servlet;
- servlet是一个简单的网络IO模型,当请求进入servlet container时,servlet container就会为其绑定一个线程,在并发不高的场景下这种模型是适用的。但是一旦高并发(比如用jmeter压测),线程数量就会涨,而线程资源代价是昂贵的(上下文切换,内存消耗大)严重影响请求的处理时间。在一些简单业务场景下,不希望为每个request分配一个线程,只需要1个或几个线程就能应对极大并发的请求,这种业务场景下servlet模型没有优势
- 所以Zuul 1.x是基于servlet之上的一个阻塞式处理模型,即spring实现了处理所有request请求的一个servlet(DispatcherServlet)并由该servlet阻塞式处理。所以SpringCloud Zuul无法摆脱servlet模型的弊端。
- Gateway支持 Reactor 和 WebFlux
- 传统的Web框架,比如说:struts2,springmvc等都是基于Servlet API与servlet容器基础之上运行的。
但是Servlet3.1之后有了异步非阻塞的支持,而WebFlux是一个典型非阻塞异步的框架,它的核心是基于Reactor的相关API实现的。相对与传统的Web框架来说,它可以运行在诸如Netty,Undertow及支持Servlet3.1的容器上。- Spring WebFlux 是 Spring 5.0引入的新的响应式框架,区别于Spring MVC,它不需要依赖Servlet API,它是完全异步非阻塞的,并且基于 Reactor 来实现响应式流规范
客户端向 Spring Cloud Gateway 发出请求。然后在 Gateway Handler Mapping 中找到与请求相匹配的路由,将其发送到 Gateway Web Handler。Handler 再通过指 定的过滤器链来将请求发送到我们实际的服务执行业务逻辑,然后返回。过滤器之间用虚线分开是因为过滤器可能会在发送代理请求之前(“pre”)或之后(“post”)执行业务逻辑。
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-gatewayartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
dependency>
<dependency>
<groupId>com.shugroupId>
<artifactId>cloud-aplartifactId>
<version>1.0-SNAPSHOTversion>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-devtoolsartifactId>
<scope>runtimescope>
<optional>trueoptional>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<optional>trueoptional>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
dependencies>
server:
port: 9527
spring:
application:
name: cloud-gateway
cloud:
gateway:
routes:
- id: payment_routh #payment_routh #路由的ID,没有固定规则但要求唯一,简易配合服务名
uri: http://localhost:8007 #匹配后提供服务的路由地址
predicates:
- Path=/payment/hystrix/ok/** #断言,路径相匹配的进行路由
- id: payment_routh2 #payment_routh #路由的ID,没有固定规则但要求唯一,简易配合服务名
uri: http://localhost:8007 #匹配后提供服务的路由地址
predicates:
- Path=/payment/hystrix/timeout/** #断言,路径相匹配的进行路由
eureka:
instance:
hostname: cloud-gateway-service
client:
register-with-eureka: true
fetch-registry: true
service-url:
defaultZone: http://eureka7001.com:7001/eureka
package com.shu.config;
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 MyConfig {
/**
* 配置一个id为route-name的路由规则,
* 当访问地址http://localhost:9527/guonei时会自动转发到地址:http://news.baidu.com/guonei
* @param routeLocatorBuilder
* @return
*/
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder routeLocatorBuilder){
RouteLocatorBuilder.Builder routes = routeLocatorBuilder.routes();
routes.route("path_route_01",
r -> r.path("/guonei")
.uri("http://news.baidu.com/guonei")).build();
return routes.build();
}
}
package com.shu;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@SpringBootApplication
@EnableEurekaClient
public class GetWay9527 {
public static void main(String[] args) {
SpringApplication.run(GetWay9527.class);
}
}
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
dependency>
server:
port: 9527
spring:
application:
name: cloud-gateway
cloud:
gateway:
#开启服务
discovery:
locator:
enabled: true
routes:
- id: payment_routh #payment_routh #路由的ID,没有固定规则但要求唯一,简易配合服务名
uri: lb://CLOUD-PROVIDER-HYSTRIX #匹配后提供服务的路由地址,通过服务名调用
predicates:
- Path=/payment/hystrix/ok/** #断言,路径相匹配的进行路由
- id: payment_routh2 #payment_routh #路由的ID,没有固定规则但要求唯一,简易配合服务名
uri: lb://CLOUD-PROVIDER-HYSTRIX #匹配后提供服务的路由地址
predicates:
- Path=/payment/hystrix/timeout/** #断言,路径相匹配的进行路由
eureka:
instance:
hostname: cloud-gateway-service
client:
register-with-eureka: true
fetch-registry: true
service-url:
defaultZone: http://eureka7001.com:7001/eureka
@SpringBootTest
public class MyTest {
public static void main(String[] args) {
ZonedDateTime now = ZonedDateTime.now();
System.out.println(now);
}
}
routes:
- id: payment_routh #payment_routh #路由的ID,没有固定规则但要求唯一,简易配合服务名
uri: lb://CLOUD-PROVIDER-HYSTRIX #匹配后提供服务的路由地址,通过服务名调用
predicates:
- Path=/payment/hystrix/ok/** #断言,路径相匹配的进行路由
- After=2021-03-07T16:28:34.711+08:00[Asia/Shanghai] #在这个时间之后,可以访问
- Before=2021-03-07T16:28:34.711+08:00[Asia/Shanghai] #在这个时间之后前,可以访问
- Between= 2019-01-20T06:06:06+08:00[Asia/Shanghai],2021-03-07T16:28:34.711+08:00[Asia/Shanghai] #在这两个时间之间可以访问
routes:
- id: payment_routh #payment_routh #路由的ID,没有固定规则但要求唯一,简易配合服务名
uri: lb://CLOUD-PROVIDER-HYSTRIX #匹配后提供服务的路由地址,通过服务名调用
predicates:
- Path=/payment/hystrix/ok/** #断言,路径相匹配的进行路由
- Cookie=ityouknow, kee.e #通过cookie进行匹配
Spring Cloud Gateway同zuul类似,有“pre”和“post”两种方式的filter。客户端的请求先经过“pre”类型的filter,然后将请求转发到具体的业务服务,比如上图中的user-service,收到业务服务的响应之后,再经过“post”类型的filter处理,最后返回响应到客户端。
与zuul不同的是,filter除了分为“pre”和“post”两种方式的filter外,在Spring Cloud Gateway中,filter从作用范围可分为另外两种,一种是针对于单个路由的gateway filter,它在配置文件中的写法同predict类似;另外一种是针对于所有路由的global gateway filer。现在从作用范围划分的维度来讲解这两种filter。
package com.shu.config;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.http.HttpStatus;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import java.util.Date;
@Configuration
@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");//每次进来后判断带不带uname这个key
if(uname == null){
log.info("*********用户名为null ,非法用户,o(╥﹏╥)o");
exchange.getResponse().setStatusCode(HttpStatus.NOT_ACCEPTABLE); //uname为null非法用户
return exchange.getResponse().setComplete();
}
return chain.filter(exchange);
}
@Override
public int getOrder() {
return 0;
}
}
更多参考博客:https://blog.csdn.net/qq_41211642/article/details/104881451