天天看点

微服务网关的总结和实践

作者:搬山道猿
微服务网关的总结和实践

一、背景介绍

在微服务架构中,一个系统会被拆封成多个微服务,每个微服务可以单独对外提供服务,如果需要统一的对外服务入口则需要网关来实现,网关记录业务微服务的服务名和地址,每次调用可以正确的映射到正确的真实地址上。总的来说网关的作用有:

  1. 路由转发:外部接口对微服务的调用转发到对应的真实地址上。
  2. 鉴权认证:网关作为系统的入口,可以在进入系统前进行鉴权和认证。
  3. 流控:对于进入的流量进行控制,如果流量过大可以进行限流。
  4. 监控:针对进入的流量进行监控和告警,也可以设置黑白名单等。

常见的网关对比:

  1. Zuul 1.0 : Netflix开源的网关,使用Java开发,基于Servlet架构构建,便于二次开发。因为基于Servlet内部延迟严重,并发场景不友好,一个线程只能处理一次连接请求。
  2. Zuul 2.0 : 采用Netty实现异步非阻塞编程模型,一个CPU一个线程,能够处理所有的请求和响应,请求响应的生命周期通过事件和回调进行处理,减少线程数量,开销较小。相比于zuul 1.0,zuul 2.0实现的异步非阻塞的特性,在性能上有较大提升。
  3. Gateway:是springcloud的全新API网关项目,旨在替换zuul的网关服务,基于spring framework5.0+springboot 2.0+webFlux开发,其也实现了异步非阻塞的特性,有较高的性能,其有丰富的过滤器类型,可以根据自身需求来自定义过滤器。
  4. Nginx : 使用Nginx的反向代理和负载均衡实现对API服务器的负载均衡以及高可用,一般放在整个系统的前端,进行静态资源的负载均衡。
  5. Kong : 基于OpenResty(Nginx + Lua模块)编写的高可用、易扩展的,性能高效且稳定,支持多个可用插件(限流、鉴权)等,开箱即可用,只支持HTTP协议,且二次开发扩展难,缺乏更易用的管理和配置方式

二、Gateway介绍

1.基本概念

  1. Route(路由) :路由是构建网关的基本模块,它是由ID,目标URI,一系列的断言和过滤器组成,如果断言为true则匹配该路由。
  2. Predicate(断言) :开发人员可以匹配HTTP请求中的所有内容(例如请求头或者请求参数),如果请求和断言相匹配则进行路由。
  3. Filter(过滤器) :指的是spring框架中GatewayFiletr实例,使用过滤器,可以在请求被路由前或者后对请求进行修改。

2.主要流程

微服务网关的总结和实践

流程说明:

  1. Gateway Client 向 Spring Cloud Gateway 发送请求。
  2. 请求首先会被 HttpWebHandlerAdapter 进行提取组装成网关上下文。
  3. 然后网关的上下文会传递到 DispatcherHandler ,它负责将请求分发给 RoutePredicateHandlerMapping。
  4. RoutePredicateHandlerMapping 负责路由查找,并根据路由断言判断路由是否可用。
  5. 如果过断言成功,由FilteringWebHandler 创建过滤器链并调用。
  6. 通过特定于请求的 Fliter 链运行请求,Filter 被虚线分隔的原因是Filter可以在发送代理请求之前(pre)和之后(post)运行逻辑。
  7. 执行所有pre过滤器逻辑。然后进行代理请求。发出代理请求后,将运行“post”过滤器逻辑。
  8. 处理完毕之后将 Response 返回到 Gateway 客户端。

3.过滤器

todo

4.核心思想

用户发送的请求到达Gateway后,根据请求的匹配条件来匹配真正地址,这个匹配条件可以是URL也可以是服务名,这个匹配条件即是断言(Predicate),匹配后的请求会经过一系列的过滤,这个就是过滤器起作用的时候。

三、代码实践

利用Gateway作为微服务网关,需要注册中心进行服务注册,在本次实践中,我们选用nacos作为注册中心。

微服务网关的总结和实践

1.配置pom文件

xml复制代码<!--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容器会冲突,所以需要排除。一般的报错信息如下:

lua复制代码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.配置文件

yaml复制代码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/开头就符合要求
           

配置文件中主要在 spring.gateway.gateway中,其中{spring.gateway.gateway} 中,其中 spring.gateway.gateway中,其中{routes.uri} 表示需要转发到的URL或者服务名。

3.过滤器

kotlin复制代码@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

  • 把过滤器的类别和作用写的更具体。

继续阅读