本文主要介绍微服务中网关的作用,并比较了几种主流网关的区别,重点介绍了网关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/开头就符合要求
配置文件中主要在 {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 详解,你用的到:juejin.cn/post/710791…
- Spring Cloud Gateway:docs.spring.io/spring-clou… (官方文档)
- 14.服务网关Zuul和Gateway:www.cnblogs.com/wmd-l/p/163…
- Spring Cloud Gateway过滤器配置:juejin.cn/post/713874…