Spring Cloud Zuul (五)

API网关服务 Spring Cloud Zuul

API网关是一个更为智能的应用服务器,它的存在就像是整个微服务架构系统的门面,所有的外部客户端访问都需要经过它来进行调度和过滤。除了需要实现请求路由,负载均衡及校验过滤等功能外还需要与服务治理框架的结合,请求转发时的熔断机制,服务的聚合等一系列高级功能。

在Spring Cloud中提供了基于Netflix Zuul实现的API网关组件Spring Cloud Zuul。对于路由规则与服务实例的维护,Spring Cloud Zuul通过与Spring Cloud Eureka进行整合,将自身注册为Eureka治理下的应用,同时从Eureka获得了所有其他微服务的实例信息,使得将维护服务实例的工作交给了服务治理框架自动完成。不在需要人工介入。对于路由规则的维护,Zuul默认将通过以<font color=#FF0000>服务名</font>作为ContextPath的方式创建路由映射。对于类似签名校验及登录校验,可以独立成一个单独的服务存在,只是它并不是给各个微服务调用,而是在API网关服务上进行统一调用来对微服务接口做前置过滤,实现对微服务接口的拦截和校验。Spring Cloud Zuul提供了一套过滤器机制,可以很好地支持这样的任务。

1. 快速集成 Spring Cloud Zuul

1.1 构建网关

  1. 创建基础Spring Boot工程

  2. 添加依赖: spring-cloud-starter-zuul

  3. 在主类上使用@EnableZuulProxy注解开启Zuul的API网关服务功能

  4. 配置Zuul应用的基础信息

    spring.application.name=api-gateway
    spring.port=8080
    

1.2 请求路由

启动Eureka服务注册中心和微服务应用

  • 传统路由方式

    对api-gateway服务增加路由配置规则

    zuul.routes.api-a-url.path=/api-a-url/**
    zuul.routes.api-a-url.url=http://localhost:8081/
    

    定义发往API网关的请求中,所有符合/api-a-url/**规则的访问都将被路由转发到http://localhost:8081/地址上。api-a-url部分为路由的名称,可任意定义,一组path与url映射关系的路由名要相同。

  • 面向服务的路由

    ​ Spring Cloud Zuul实现了与Spring Cloud Eureka的无缝整合,可以让路由的path不是映射具体的url,而是映射某个具体的服务,而具体的服务则交给Eureka的服务发现机制去自动维护,称之为面向服务的路由。

    1. 添加依赖:spring-cloud-starter-eureka

    2. 配置Zuul的Eureka注册中心的位置

    3. 配置服务路由

      zuul.routes.api-a.path=/api-a/**
      zuul.routes.api-a.serviceId=hello-service
      

      简洁配置方式

      zuul.routes.hello-service=/api-a/**
      

1.3 请求过滤

Zuul允许在API网关上通过定义过滤器实现对请求的拦截与过滤,只需要继承ZuulFilter抽象类并定义它的四个抽象函数。

public class AccessFilter extends ZuulFilter {
    /**
    * 过滤器类型,决定过滤器在请求的哪个生命周期执行
    */
    @Override
    public String filterType() {
        return "pre";
    }
    
    /**
    * 过滤器的执行顺序,当同一阶段存在多个过滤器时,需要根据该返回值指定执行顺序
    */
    @Override
    public int filterOrder() {
        return 0;
    }
    
    /**
    * 过滤器是否执行
    */
    @Override
    public boolean shouldFilter() {
        return true;
    }
    
    /**
    * 过滤器任务
    */
    @Override
    public Object run() {
        RequestContext ctx = RequestContext.getCurrentContext();
        HttpServletRequest request = ctx.getRequest();
        
        String token = request.getHeader("Authorization");
        
        if (token == null) {
            ctx.setSendZuulResponse(false);
            ctx.setResponseStatusCode(401);
            // ctx.setResponseBody(body);
        }
        
        return null;
    }
}

定义Zuul拦截器配置,并添加拦截器

@Configuration
public class ZuulFilterConfiguration {
    
    @Bean
    public AccessFilter accessFilter() {
        return new AccessFilter();
    }
}

2. 路由详解

2.1 服务路由配置

简洁配置方式

zuul.routes.hello-service=/api-a/**

2.2 服务路由默认规则

为Spring Cloud Zuul构建的API网关服务引入Spring Cloud Eureka之后,它为Eureka中的每个服务都自动创建一个默认路由规则,默认路由规则的path使用serviceId配置的服务名作为请求前缀,以hello-service为例:

zull.routes.hello-service.path=/hello-service/**
zull.routes.hello-service.serviceId=hello-service

2.3 路径匹配

在Spring Cloud Zuul中,路由匹配的路径表达式采用了Ant风格定义

通配符 说明
? 匹配任意单个字符
* 匹配任意数量字符
** 匹配任意数量字符,支持多级目录

2.4 忽略表达式

Spring Cloud Zuul忽略表达式参数zuul.ignored-patterns用来设置不希望被API网关进行路由的URL表达式。

zuul.ignored-patterns=/**/hello/**

2.5 Cookie与头信息

默认情况下,Spring Cloud Zuul请求路由时,会过滤掉HTTP请求头信息中的一些敏感信息,防止它们被传递到下游的外部服务器。默认的敏感头信息通过zuul.sensitiveheaders参数定义,包括Cookie, Set-Cookie, Authorization三个属性。

解决方案:

  • 设置全局参数为空覆盖默认值

    zuul.sensitiveheaders=
    
  • 设置指定路由的参数

    // 对指定路由开启自定义敏感头
    zuul.routes.<router>.customSensitiveHeaders=true
    // 将指定路由的敏感头设置为空
    zuul.routes.<router>.sensitiveheaders=
    

2.6 Hystrix与Ribbon支持

Zuul自身包含了对Hystrix和Ribbon的模块依赖,因此Zuul拥有线程隔离和断路器的自我保护功能,以及对服务调用的客户端负载均衡功能。当使用path和url的映射关系配置路由规则时没有上述功能,因此使用Zuul时尽量使用path和serviceId的组合进行配置。参数配置如下:

ribbon:
  ReadTimeout: 600000      // 设置路由转发请求的超时时间
  ConnectTimeout: 600000   // 设置路由转发请求,创建请求连接的超时时间
hystrix:
  command:
    default:
      execution:
        isolation:
          thread:
            timeoutInMilliseconds: 60000  // 设置API网关路由转发请求HystrixCommand执行超时时间

2.7 过滤器详解

在Zuul中,路由功能真正运行时,它的路由映射和请求转发由几个不同的过滤器完成。路由映射主要通过pre类过滤器完成,它将请求路径与配置的路由规则进行匹配,找到需要转发的路由地址。请求转发的部分由route类过滤器完成。

在Spring Cloud Zuul中实现的过滤器必须包含4个基本特征:过滤类型,执行顺序,执行条件,具体操作。实际上它们就是ZuulFilter接口中定义的4个抽象方法:

String filterType();
int filterOrder();
boolean shouldFilter();
Object run();
  • filterType:过滤器类型
    • pre:可以再请求被路由前调用
    • routing:在路由请求时被调用
    • post:在routing和error过滤器之后被调用
    • error:处理请求时发生错误时被调用
  • filterOrder:过滤器执行顺序,数值越小优先级越高
  • shouldFilter:是否执行过滤器
  • run:过滤逻辑

2.8 异常处理

在Spring Cloud Zuul中,在HTTP请求生命周期的各个阶段默认实现了一批核心过滤器,在API网关服务启动的时候被自动加载和启用,但核心过滤器中没有实现error阶段的过滤器,因此需要在自定义过滤器中处理异常内容:

  • try-catch处理

    error相关三个主要参数:

    1. error.status_code:错误码
    2. error.exception:Exception异常对象
    3. error.message:错误信息(优先级高于Exception对象中的message)
    public Object run() {
        RequestContext ctx = RequestContext.getCurrentContext();
        
        try {
            // TODO
        } catch (Exception exception) {
            ctx.set("error.status_code", HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
            ctx.set("error.exception", exception);
        }
        
        return null;
    }
    
  • ErrorFilter处理

    在请求生命周期的pre,route,post三个阶段有异常抛出的时候都会进入到error阶段处理,通过创建error类型过滤器捕获异常信息并处理。

    public Object run() {
        RequestContext ctx = RequestContext.getCurrentContext();
        Throwable throwable = ctx.getThrowable();
        
        ctx.set("error.status_code", HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
        ctx.set("error.exception", throwable.getCause());
        
        return null;
    }
    
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,271评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,275评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,151评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,550评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,553评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,559评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,924评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,580评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,826评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,578评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,661评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,363评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,940评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,926评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,156评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,872评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,391评论 2 342

推荐阅读更多精彩内容