什么是微服务网关
在微服务众多的服务的治理过程中,服务网关的作用在微服务框架中可以提供统一入口
、鉴权校验
、动态路由
、降低耦合度
的功能,关于springcloud的网关有三个,分别是zuul、zuul2和gateway,其中zuul/zuul2是Netflix公司开发的,但是因为zuul的性能不够好,zuul2的开发内部有歧义,所以springcloud便自研了一套网关——gateway。
组件名称 | 所属公司 | 组件简介 |
---|---|---|
zuul | Netflix | 停更进维 |
zuul2 | Netflix | 由于zuul核心开发人员离职了3人,神仙打架,出路不明 |
gateway | springcloud | springcloud自研的一套非阻塞的网络异步网关 |
关于Gateway
Gateway
是在Spring
生态系统之上构建的API网关服务,基于Spring 5
,,Spring Boot 2
和Project Reactor
等技术。Gateway旨在提供-种简单而有效的方式来对API进行路由, 以及提供一些强大的过滤器功能, 例如: 熔断、限流、重试等。
SpringCloud Gateway作为Spring Cloud生态系统中的网关,目标是替代Zuul,在Spring Cloud 2.0以上版本中,没有对新版本的Zuul 2.0以上最新高性能版本进行集成,仍然还是使用的Zuul 1.x
非Reactor模式的老版本。而为了提升网关的性能,SpringCloud Gateway是基于WebFlux
框架实现的,而WebFlux
框架底层则使用了高性能的Reactor
模式通信框架Netty
。
Spring Cloud Gateway的目标提供统-的路由方式且基于 Filter链的方式提供了网关基本的功能,例如:安全,监控/指标,和限流。Spring Cloud Gateway的目标提供统-的路由方式且基于 Filter链的方式提供了网关基本的功能,例如:安全,监控/指标,和限流。
zuul 和 gateway
基本对比
zuul | zuul2 | Gateway | |
---|---|---|---|
所属公司 | Netflix | Netflix | springcloud |
springboot版本 | springcloud 1.x | Springcloud 2.x | Springcloud 2.x |
架构 | 基于Servlet 2.5,使用阻塞架构,不支持任何长连接 | 基于Netty非阻塞 | 基于webFlux非阻塞 |
长连接 | 不支持任何长连接 | 支持 | 支持 |
性能 | 已过时 | 较好 | 较好 |
webflux
webflux
是一个非阻塞的web框架,类似springmvc
。传统的Web框架,比如说: struts2
, springmvc
等都是基于Servlet API
与Servlet
容器基础之上运行的。但是在Senv/let3.1之后有了导步非阻塞的支持。而WebFlux是-个典型非阻塞异步的框架,它的核心是基于Reactor
的相关API实现的。相对于传统的web框架来说,它可以运行在诸如Netty
,Undertow
及支持Servlet3.1
的容器 上。非阻塞式+函数式编程(Spring5必须让你使用java8)Spring WebFlux是Spring 5.0引入的新的响应式框架,区别于Spring MVC
, 它不需要依赖Servlet API, 它是完全异步非阻塞
的,并且基于Reactor
来实现响应式流规范。
gateway原理
三要素
-
Route
(路由)路由是构建网关的基本模块,它由ID,目标URI,一系列的断言和过滤器组成,如果断言为true则匹配该路由
-
Predicate
(断言)判断请求的转发条件
-
Filter
(过滤)指的是Spring框架中GatewayFilter的实例,使用过滤器,可以在请求被路由前或者之后对请求进行修改。
工作原理
客户端向Spring Cloud Gateway发出请求,然后在
Gateway Handler Mapping
中找到与请求相匹配的路由,将其发送到Gateway web hander
Handler再通过指定的过滤器链来将请求发送到我们实际的服务执行业务逻辑,然后返回。过滤器之间用虚线分开是因为过滤器可能会在发送代理请求之前( "pre" )或之后( "post" )执行业务逻辑。
Filter在"pre” 类型的过滤器可以做参数校验、权限校验、流量监控、日志输出、协议转换等,在"post"类型的过滤器中可以做响应内容、响应头的修改,日志的输出,流量监控等有着非常重要的作用。
搭建gateway
注意事项
- gateway 需要注册到注册中心,这里我使用的是
Eureka
,其他注册中心也可。 - gateway项目不能同时依赖
spring-boot-starter-web
依赖
org.springframework.cloud
spring-cloud-starter-gateway
配置路由
gateway项目需要先把需要转发的路由先配置到项目中去,他支持两种方式配置路由:yml文件配置和注入
RouteLocator
的方式。每个路由中都包含了predicate
、filter
、实际的转发主机uri
以及设定的一个id(不能重复)。
其中:
-
predicate
包含多种类型,可以一起使用,具体类型查看 官网包含有
after
、before
、between
、cookies
、header
、host
、method
、path
、query
、remoteaddr
、weigth
等Predicate就是为了实现一组匹配规则,让请求过来找到对应的Route进行处理。
-
Filter
也包含许多种可选类型,可以一起使用,具体类型查看 官网包含有
AddRequestHeader
、AddRequestParameter
、AddResponseHeader
、DedupeResponseHeader
、Hystrix
、CircuitBreaker
、FallbackHeaders
、MapRequestHeader
、PrefixPath
、PreserveHostHeader
、RequestRateLimiter
、RedirectTo
、RemoveHopByHopHeadersFilter
、RemoveRequestHeader
、RemoveResponseHeader
、RemoveRequestParameter
、RewritePath
、RewriteLocationResponseHeader
、RewriteResponseHeader
、SaveSession
、SecureHeaders
、SetPath
、SetRequestHeader
、SetResponseHeader
、SetStatus
、StripPrefix
、Retry
、RequestSize
等
yml 方式示例
spring:
cloud:
gateway:
routes:
- id: host_route
uri: https://example.org
predicates:
- Path=/red/{segment},/blue/{segment}
上面这个配置的意思是,访问gateway-host/red/xxx
或者gateway-host/blue/xxx
的话,这个请求会转发到https://example.org
,相当于https://example.org/red/xxx
或https://example.org/blue/xxx
,实现了一个转发
的功能。我们还可以用硬编码的方式来实现上面的配置。
注入RouteLocator的方式示例
@Configuration
public class GatewayConfiguration {
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder routeLocatorBuilder){
RouteLocatorBuilder.Builder routes = routeLocatorBuilder.routes();
routes.route("host_route",r->r.path("/red/{segment},/blue/{segment}").uri("https://example.org")).build();
return routes.build();
}
}
这样的方式就需要注入routeLocator来实现网关服务的配置。两种方式的效果都一样的,个人建议使用yml配置的方式。
通过微服务名动态路由
上面两种方式我们都是在uri上绑定了服务的主机地址来实现转发的,但是如果服务地址变了,网关就得修改其配置,这样就显得很麻烦。那我们能不能通过微服务名来调用服务呢?答案是肯定的。
修改配置
需要注意的是uri的协议为lb,表示启用Gateway的负载均衡功能。
spring:
application:
name: cloud-springcloud-gateway
cloud:
gateway:
discovery:
locator:
enabled: true # 开启从注册中心动态创建路由的功能
routes:
- id: userinfo_user
uri: lb://cloud-eureka-provide-userinfo-service # lb协议表示启用负载均衡功能,然后后面跟着微服务名称
predicates:
- Path=/user/**
上面只是演示了
Predicates
中的Path
的用法,还有许多其他非常高级的用法,可以自己查询官网文档去学习下哈~
过滤器
路由过滤器可以用于修改进入的http请求和返回的http响应,路由过滤器只能指定路由使用。官网提供了两类过滤器
GatewayFilter
和GlobalFilter
过滤器,具体可以参考官网。
自定义过滤器
@Component
public class CustomFilter implements GlobalFilter, Ordered {
@Override
public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) {
/**
* 从请求中获取是否有token参数
*/
String token = exchange.getRequest().getQueryParams().getFirst("token");
if (token==null){
/**
* 直接拒绝
*/
exchange.getResponse().setStatusCode(HttpStatus.NOT_ACCEPTABLE);
return exchange.getResponse().setComplete();
}
/**
* 通过这个过滤器,进入过滤链中的下一个过滤器
*/
return chain.filter(exchange);
}
@Override
public int getOrder() {
return 0;
}
}
发现需要添加token参数的请求才能访问成功,否则会报406错误。
总结
springcloud的gateway还是非常强大的,这边笔记只能说做了简单的了解,要发掘其强大的功能依然建议查阅 官网
在我学习过程中觉得美中不足的地方就是这些服务只能通过配置的方式来加入,而不能像添加到数据库中这么方便,是否每次有服务变动了就需要更改网关(配置)呢?后面会学习到springcloud的配置中心,应该可以解决这个问题。不过用起来和我之前用到的kong
网关还是各有优劣吧。