本文主要介绍微服务中网关的作用,并比较了几种主流网关的区别,重点介绍了网关gateway。
一、背景介绍
在微服务架构中,一个系统会被拆封成多个微服务,每个微服务可以单独对外提供服务,如果需要统一的对外服务入口则需要网关来实现,网关记录业务微服务的服务名和地址,每次调用可以正确的映射到正确的真实地址上。总的来说网关的作用有:
- 路由转发:外部接口对微服务的调用转发到对应的真实地址上。
- 鉴权认证:网关作为系统的入口,可以在进入系统前进行鉴权和认证。
- 流控:对于进入的流量进行控制,如果流量过大可以进行限流。
- 监控:针对进入的流量进行监控和告警,也可以设置黑白名单等。
常见的网关对比:
- Zuul 1.0 : Netflix开源的网关,使用Java开发,基于Servlet架构构建,便于二次开发。因为基于Servlet内部延迟严重,并发场景不友好,一个线程只能处理一次连接请求。
- Zuul 2.0 : 采用Netty实现异步非阻塞编程模型,一个CPU一个线程,能够处理所有的请求和响应,请求响应的生命周期通过事件和回调进行处理,减少线程数量,开销较小。相比于zuul 1.0,zuul 2.0实现的异步非阻塞的特性,在性能上有较大提升。
- Gateway:是springcloud的全新API网关项目,旨在替换zuul的网关服务,基于spring framework5.0+springboot 2.0+webFlux开发,其也实现了异步非阻塞的特性,有较高的性能,其有丰富的过滤器类型,可以根据自身需求来自定义过滤器。
- Nginx : 使用Nginx的反向代理和负载均衡实现对API服务器的负载均衡以及高可用,一般放在整个系统的前端,进行静态资源的负载均衡。
- Kong : 基于OpenResty(Nginx + Lua模块)编写的高可用、易扩展的,性能高效且稳定,支持多个可用插件(限流、鉴权)等,开箱即可用,只支持HTTP协议,且二次开发扩展难,缺乏更易用的管理和配置方式
二、Gateway介绍
1.基本概念
- Route(路由):路由是构建网关的基本模块,它是由ID,目标URI,一系列的断言和过滤器组成,如果断言为true则匹配该路由。
- Predicate(断言):开发人员可以匹配HTTP请求中的所有内容(例如请求头或者请求参数),如果请求和断言相匹配则进行路由。
- Filter(过滤器):指的是spring框架中GatewayFiletr实例,使用过滤器,可以在请求被路由前或者后对请求进行修改。
2.主要流程
流程说明:
Gateway Client
向Spring Cloud Gateway
发送请求。- 请求首先会被
HttpWebHandlerAdapter
进行提取组装成网关上下文。 - 然后网关的上下文会传递到
DispatcherHandler
,它负责将请求分发给RoutePredicateHandlerMapping
。 RoutePredicateHandlerMapping
负责路由查找,并根据路由断言判断路由是否可用。- 如果过断言成功,由
FilteringWebHandler
创建过滤器链并调用。 - 通过特定于请求的
Fliter
链运行请求,Filter
被虚线分隔的原因是Filter可以在发送代理请求之前(pre)和之后(post)运行逻辑。 - 执行所有pre过滤器逻辑。然后进行代理请求。发出代理请求后,将运行“post”过滤器逻辑。
- 处理完毕之后将
Response
返回到Gateway
客户端。
3.过滤器
todo
4.核心思想
用户发送的请求到达Gateway后,根据请求的匹配条件来匹配真正地址,这个匹配条件可以是URL也可以是服务名,这个匹配条件即是断言(Predicate),匹配后的请求会经过一系列的过滤,这个就是过滤器起作用的时候。
三、代码实践
利用Gateway作为微服务网关,需要注册中心进行服务注册,在本次实践中,我们选用nacos作为注册中心。
1.配置pom文件
<!--Gateway网关--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <!-- Maven整个生命周期内排除内置容器,排除内置容器导出成war包可以让外部容器运行spring-boot项目--> <exclusions> <exclusion> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-gateway</artifactId> </dependency> <!--nacos服务发现依赖--> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> <exclusions> <exclusion> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-ribbon</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-loadbalancer</artifactId> </dependency>
pom文件主要引入spring-cloud-starter-gateway
网关和spring-cloud-starter-loadbalancer
负债均衡,其中可能存在以下问题:
(1)由于spring-cloud-starter-gateway采用的是响应式的WebFlux web容器,这和spring-boot-starter-web默认的web容器会冲突,所以需要排除。一般的报错信息如下:
Parameter 0 of method modifyRequestBodyGatewayFilterFactory in org.springframework.cloud.gateway.config.GatewayAutoConfiguration required a bean of type 'org.springframework.http.codec.ServerCodecConfigurer' that could not be found.
(2)引入负债均衡spring-cloud-starter-loadbalancer需要配排除nacos中的spring-cloud-starter-netflix-ribbon,这两者会包冲突,到时无法使用服务名来进行路由转发。
2.配置文件
server: port: 9010 spring: application: name: test-gateway cloud: nacos: discovery: server-addr: x.x.x.x:8848 enabled: true gateway: routes: # 网关路由配置 - id: my-test # 路由id,自定义,只要唯一即可 # uri: http://127.0.0.1:8081 # 路由的目标地址 http就是固定地址 uri: lb://my-test # 路由的目标地址 lb就是负载均衡,后面跟服务名称 predicates: # 路由断言,也就是判断请求是否符合路由规则的条件 - Path=/my-test/** # 这个是按照路径匹配,只要以/user/开头就符合要求
配置文件中主要在s p r i n g . g a t e w a y . g a t e w a y 中,其中 {spring.gateway.gateway}中,其中spring.gateway.gateway中,其中{routes.uri}表示需要转发到的URL或者服务名。
3.过滤器
@Slf4j @Component public class MyFilter implements Ordered, GlobalFilter { /** * @param exchange 可以拿到对应的request和response * @param chain 过滤器链 * @return 是否放行 */ @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { String name = exchange.getRequest().getQueryParams().get("name").get(0); log.info("用户:{} 开始进入系统", name); if (!StringUtils.isEmpty(name)) { if (name.equals("noName")) { log.info("用户没有访问权限"); exchange.getResponse().setStatusCode(HttpStatus.PROXY_AUTHENTICATION_REQUIRED); return exchange.getResponse().setComplete(); } } return chain.filter(exchange); } /** * 设定过滤器的优先级,值越小则优先级越高 * @return */ @Override public int getOrder() { return 0; } }
如果需要在Gateway中实现过滤器,可以实现GlobalFilter接口,以上Filter是用来进行鉴权的,如果进入网关的请求没有权限就会被拦截。
TODO
- 把过滤器的类别和作用写的更具体。
参考资料
- 这篇SpringCloud GateWay 详解,你用的到:https://juejin.cn/post/7107911617601339423#heading-1
- Spring Cloud Gateway:https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#gateway-starter (官方文档)
- 14.服务网关Zuul和Gateway:https://www.cnblogs.com/wmd-l/p/16324387.html
- Spring Cloud Gateway过滤器配置:https://juejin.cn/post/7138740852720926733#heading-26