这篇随笔接着学习微服务中一个比较重要的组件API网关服务。当我们微服务架构完成后最终是要提供给外部访问的,于是我们需要一个统一的访问入口,能隐藏我们内部服务URL细节,这就有点像局域网里那个网关的概念了,这是API网关服务就应运而生了。API网关作用有能为实现请求路由、负载均衡、校验过滤等基础功能,还能实现请求转发的熔断机制、服务集合等高级功能。补充下通常我们对外服务统一入口可以采用F5、Nginx等方式也能实现前面的请求路由与负载均衡,但是要实现后面功能了F5、Nginx就无能为力了吧,这就是API网关服务强大之处。在Spring Cloud体系中我们可以采用Spring Cloud Zuul搭建我们的API网关服务,Zuul能与Spring Cloud全家桶这些组件整合,通过这样的设计也就能更好的实现无论是请求路由、负载均衡还是熔断、服务机制了。
接着以API网关的请求路由、过滤器、服务容错三个方面学习,API网关的搭建就跳过了,因为它和Eureka一样都是非常简单即可搭建好的,可自行了解下。
一、请求路由
服务路由配置
由于Zuul与Eureka整合便可知道每个服务的请求地址,因此可通过zuul.routes.<service-name>=<path>配置路由,当访问地址如http://127.0.0.1:7001/api-a/user/1,将会配置该路由股则并向对应服务转发。
#凡是以/api-a/开头的URL都转发到服务名为service-a的应用 zuul.routes.service-a=/api-a/**
默认路由规则
Zuul在不配置的情况下是有默认路由规则的,可以免去我们逐一配置的麻烦。默认情况都会以服务名作为path配置对应的服务。比方说我们有个服务叫service-a,那Zuul默认就会创建如下的路由规则。
zuul.routes.service-a=/service-a/** #如果不希望使用默认路由规则,我们可以使用zuul.ignored-servies=*取消zuul的默认路由
自定义路由规则
如果觉得默认路由规则不符合自己业务需求还可以通过自定义路由规则定义自己路由规则,还是举个例子:服务名为service-a,默认路由规则是/service-a/**,但如果希望把默认路由规则变成/service/a/**可以在启动类添加PatternServiceRouteMapper类,并且采用正则表达式命名分组去匹配服务名称和定制自己的路由规则,如下例子,当服务名称符合PatternServiceRouteMapper构造函数的第一个参数(?<part1>^.+)-(?<part2>.+$)这个正则表达式,并且获取了命名分组part1和part2,然后就可以通过第二个参数去定义路由。当然如果不符合第一个参数的正则表达式则走默认路由规则。
package com.pumpkin; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.cloud.client.SpringCloudApplication; import org.springframework.cloud.netflix.zuul.EnableZuulProxy; import org.springframework.cloud.netflix.zuul.filters.discovery.PatternServiceRouteMapper; import org.springframework.context.annotation.Bean; @EnableZuulProxy @SpringCloudApplication public class Application { public static void main(String[] args){ new SpringApplicationBuilder(Application.class).web(true).run(args); } @Bean public PatternServiceRouteMapper serviceRouteMapper() { return new PatternServiceRouteMapper( "(?<part1>^.+)-(?<part2>.+$)", "${part1}/${part2}"); } }
路径匹配
在定义路由是通常需要使用通配符的,Zuul的通配符采用了Ant风格定义,具体如下。需要注意的是Zuul的路由配置是根据顺序配置而不是最长配置路径匹配。也就是说一个URL区配置路由的时候,并且有多个路由可以匹配到,一旦找到第一个可以匹配上的路由就会结束下面的匹配了。
除此之外zuul还提供了忽略表达式用于忽略不希望被API网关进行路由的URL表达式,比方说service-a服务的/hello.do接口不希望被路由到,可以添加如下配置。
zuul.ignored-patterns=/**/hello.do/**
本地跳转
在Zuul实现的API网关路由功能中,还支持forward形式的服务器端跳转配置,就是说可以设置一个路由规则,符合该路由规则的讲会转发到zuul网关自身去处理,当然使用本地跳转时,Zuul网关也有实现相应的接口。
#本地跳转#当我访问http://127.0.0.1:7001/api-b-url/hello,将会转发给给Zuul自身去处理 zuul.routes.api-b-url.path=/api-b-url/** zuul.routes.api-b-url.url=forward:/local
Cookie与头信息
默认情况下Zuul在处理请求时会过滤掉一些HTTP请求头信息,默认的敏感头信息通过zuul.sensitiveHeaders参数定义,包括Cookie、Set-Cookie、Authoriztion三个属性,所以Web项目常用的Cookie默认情况下是不会传递的,为了解决这个问题,有如下几个配置方法。
#通过设置全局参数为空覆盖默认值 #zuul.sensitiveHeaders= #对指定路由开启自定义敏感头 #zuul.routers.<router>.customSensitiveHeaders=true #讲指定路由的敏感头设置为空 #zuul.routers.<router>.sensitiveHeaders=
除了Cookie问题外,还有一个重定向问题需要了解的。由于Zuul没有正确的处理HTTP请求头信息中的Host,在某些需要重定向场景下会出现如下问题:比方说向zuul网关发送一个登录请求,登录成功后后端服务返回一个302重新定向响应,该响应的host头信息可能是具体实例的IP和端口,此时浏览器可能看到的就是实例的IP而不是网关的IP了,如下图。需要解决这个问题在Camden版本之后可以通过属性zuul.add-host-header=true得到处理,不过基于目前我学习的这个版本的话是没有提供直接的解决办法的,需要自行参考Camden版本的PreDecorationFilter过滤器去处理。
二、过滤器
Zuul过滤器应该可以说是Zuul最精华的一部分了,它主要提供了四种类型的过滤器,这四种过滤器基础的的抽象类(ZuulFilter)是一样的,但不同的过滤器在Zuul中作用不一样,生命周期也是不一样的,下面先看看ZuulFilter接抽象类几个重要抽象方法。
#这个属性有四种类型对应不同的过滤器分别是pre、routing、posting、error public abstract java.lang.String filterType(); #决定过滤器执行顺序,数值越小优先级越高 public abstract int filterOrder(); #判断过滤器是否要执行 boolean shouldFilter(); #过滤器执行逻辑 java.lang.Object run();
介绍四种过滤器
通过ZuulFilter介绍基本就了解过滤器是怎么个使用了吧,直接编写一个类继承ZuulFilter,指明该过滤器的类型、优先级、逻辑等然后被Spring去加载便可,那么不同类型的过滤器生命周期和作用是什么?下面一张官方大图展示的比较清楚了。
pre过滤器:在请求路由前被调用,大家都熟知的这个阶段适合做一些校验逻辑
routing过滤器:在路由请求时被调用
post过滤器:在routing和error过滤器之后被调用,这个类型的过期就一般用于对请求响应结果做一些加工
error:处理请求时发生错误被调用
在Spring Cloud Zuul中已经是有一批默认的实现了的核心过滤器了,它们会在Zuul启动的时候自动加载和启用,从图中可以看到一个有趣的路径是error过滤器在任一类型的过滤器发生异常时都会进入,但它最终也还是会进入到post过滤器返回请求给客户端。
禁用过滤器
有时我们需要禁用zuul某些默认过滤器或自定义过滤器的需求,那么可以如下
#其中<SimpleClassName>为简单类名,<filterType>为过滤器#类型 zuul.<SimpleClassName>.<filterType>.disable=true eg: zuul.AccessFilter.pre.disable=true
处理异常信息
这是开发中常见需求,在<<Spring Cloud微服务实战>>一书以及永超大神博客中有比较详细的介绍,跳过~~
三、服务容错
既然是Spring Cloud的一员,那么在服务容错方面当然有Hystrix和Ribbon良好的支持啦。也就是Zuul天生就拥有线程隔离、断路器、客户端负载均衡的能力了,不过在设置路由的时候需要以zuul.routes.service-a=/api-a/**这种服务路由的方式设置,否则将不具备服务容错能力。而Hystrix和Ribbon的配置还是和原本一样,简单介绍下。
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds - 设置路由转发请求执行总时间,超时会返回TIMEOUT错误 ribbon.ConnectTimeout - 设置请求创建连接时间 ribbon.ReadTimeout - 设置请求超时时间 ribbon.ConnectTimeout和ribbon.ReadTimeout 设置时间小于hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds 时出现请求创建连接超时或者请求超时,ribbon都会进行重试路由请求的,如果我们不希望有这个重试机制可以通过 zuul.retryable=false - 全局关闭 或者 zuul.routers.<route>.retryable=false - 指定路由关闭
四、总结
Api网关可以说是微服务闭环中不可或缺的一部分,今天对Spring Cloud Zuul相关内容进行了学习最大感受是Zuul其实是具有“微服务”特色的一个服务代理工具,正是这个特点使得它与Nginx和F5这些服务代理工具不一样,要做的事情更多,例如检验、服务容错、对请求结果进行加工等等,才能保证微服务可以得到稳定运行。
六、参考资料
Spring Cloud微服务实战-翟永超。本系列的学习都是参考该书籍学习的,同时源码使用的Spring Boot和Spring Cloud的版本也与该书保持一致。
七、源码
码云地址:[email protected]:pumpkingg/Spring-Cloud-Study.git 该篇随笔对应的代码是master分支下命名为blog4的Tag
原文地址:https://www.cnblogs.com/yipaihushuo/p/9281935.html