第六章 6.4-6.8 API服务网关----Zuul

书籍地址: Spring Cloud 微服务架构开发实战

6.4 路由配置规则

Zuul 提供了多种机制对请求路由进行配置,如下:

  • 与 Eureka 服务器整合自动根据微服务的 ID 进行映射,这是默认机制。
  • 结合微服务 ID 通过自定义方式进行路由映射。
  • 添加全局路由映射。
  • 通过自定义路由转换器,实现更灵活的路由映射。

6.4.1 服务路由默认规则

当构建 API服务网关时,如果有 Eureka 服务器时,Zuul 会自动为注册到 Eureka服务器上的每个服务都创建一个默认路由规则:访问路径的前缀为 serviceId 配置的服务名称。

就会行程如下格式的访问路径:
http://[zuul路由服务器地址]/[serviceId]/[具体服务的端点]

当微服务部署架构中包含了 Eureka 服务时,在增加或移除一个服务时无需对 Zuul 进行任何修改,Zuul 可以自动根据 Eureka 服务器中所注册的服务自动完成路由映射、负载均衡等处理

6.4.2 自定义微服务访问路径

通过在配置文件中配置一下内容

zuul:
 routes:
  userservice: /user/**

我们就可以通过/user/来访问userserivice所提供的服务。也就是说我们之前的访问路径可以更改为
http://localhost:8280/user/users/2

注意此时我们依然是通过zuul服务器做的访问

那么此时,我们再通过userservice 即:http://localhost:8280/userservice/users/2来进行访问,是否依然可以得到正确信息呢?

其实是完全OK的。

我们访问 http://localhost:8280/routes可以看到所有的可用的路由映射

可以发现,user的微服务存在两个 路由映射,user 和 userservice,userservice作为默认的配置,不会因为我们的自定义配置而失效,如果想让默认配置失效,需要做下面的配置才可以。

6.4.3 忽略指定微服务

zuul.ignored-services: 该属性可以指定在默认映射中所要忽略的微服务,指定后 Zuul 的路由服务将不再代理该路径下的访问。参数的值可以设置多个服务的 ID,如果需要忽略多个服务,那么服务 ID之间用逗号隔开即可。

例:

zuul:
 ignored-services: userservice

此时我们通过上面配置的/user/ 进行访问

zuul:
 routes:
  userservice: /user/**

如果想让 Zuul 忽略所有服务的路由映射,并全部采用自定义方式,那么只需要将 zuul.ignored-services 的值设置为 *即可。

6.4.4 设置路由前缀

Zuul 提供了 zuul.prefix属性可为所有的路由映射增加统一前缀,如:
zuul.prefix=/api
此时我们再通过postMan访问routes端点,如下图:

默认情况下,Zuul代理会再转发到具体服务实例时自动剥离这个前缀。

如果需要在转发时带上该前缀,可以将 zuul.stripPrefix属性的值设置为 false 来关闭这个默认行为。

zuul.stripPrefix只会对zuul.prefix的前缀起作用,而对于path指定的前缀不会起作用。

6.4.5 自定义路由规则

如果路由规则比较复杂,那么我们可以定义一个转换器,让 serviceId 和路由之间使用自定义的规则进行转换。
比如下面代码中通过一个正则表达式来自动匹配,将形如servicename-vx的服务名称映射为/vx/servicename的访问路径

@Bean
public PatternServiceRouteMapper serviceRouteMapper() {
	return new PatternServiceRouteMapper(
   		"(?<name>^.+)-(?<version>v.+$)","${version}/${name}";
    );
}

以上服务 ID 为 users-v1 的服务,就会被映射到路由为/v1/users/的路径上。但是对于所定义的命名组必须包括 servicePattern 和 routePattern 两部分。
如果 servicePattern没有匹配一个 serviceId, 那么就会使用默认的。

上例中,一个服务实例的 ID 为 users 的服务,将会被映射到路由 /users/ 中,这个特性默认是关闭的,而且只适用于已经发现的服务。

6.5 Zuul 路由其他配置

Zuul在微服务架构中,扮演的是代理服务器的角色

对Zuul服务器来说,其核心就是如何处理与下游各个微服务之间的关系

6.5.1 HttpClient 设置

Zuul 的 HTTP 客户端支持 Apache Http、Ribbon 的 RestClient 和 OKHttpClient,默认使用 Apache Http客户端。可通过如下方式启用:

#启用Ribbon的RestClient
ribbon:
 restclient:
  enabled: true

#启用OKHttpClient
ribbon:
 okhttp:
  enabled: true

如果使用 OKHttpClient,需要确保项目中已经包含 com.squareup.okhttp3 包。

6.6 Zuul 容错与回退

6.6.1 实现 Zuul 的回退

Zuul 提供了一个 ZuulFallbackProvider 接口,通过实现该接口就可以为 Zuul 实现回退功能。
我们就在我们的例子中实现响应的服务降级处理功能。

/**userservice的回退实现
 * @author:turnsole
 * @date:2019/11/4,15:54
 * @what I say:just look,do not be be
 */
public class UserServiceFallbackProvider implements FallbackProvider {
    @Override
    public String getRoute() {
        //这里是route的名称,而不是服务的名称
        //如果这里写成大写USERSERVICE,则无法起到回退的作用
        return "userservice";
    }

    @Override
    public ClientHttpResponse fallbackResponse(String route, Throwable cause) {
        //创建一个Fallback响应
        return new ClientHttpResponse() {
            @Override
            public HttpStatus getStatusCode() throws IOException {
                return HttpStatus.OK;
            }

            @Override
            public int getRawStatusCode() throws IOException {
                return 200;
            }

            @Override
            public String getStatusText() throws IOException {
                return "OK";
            }

            @Override
            public void close() {}

            //针对回退,构建了一个Fake用户信息
            @Override
            public InputStream getBody() throws IOException {
                JSONObject mockUserJson = new JSONObject();
                mockUserJson.put("id","-3");
                mockUserJson.put("nickname","fakeUser");
                mockUserJson.put("avatar","haha.jpg");
                return new ByteArrayInputStream(mockUserJson.toJSONString().getBytes());
            }

            @Override
            public HttpHeaders getHeaders() {
        //需要将返回的格式设置为JSON
                HttpHeaders headers = new HttpHeaders();
                headers.setContentType(MediaType.APPLICATION_JSON_UTF8);
                return headers;
            }
        };
    }
}

说明:

  • getRoutes()方法返回的是要为那个微服务提供回退功能。这里需要注意其返回的值是 route的名称,而不是微服务的名称,所以不能写为 USERSERVICE,否则该回退将不起作用。

  • fallbackResponse()方法返回 ClientHttpResponse对象,作为我们的回退响应。这里实现简单,仅仅是返回一个假的用户对象。

6.6.2 服务超时

当 Zuul 路由服务将客户端请求转发到具体服务时,Zuul会使用 HystrixCommand来包装这些执行过程,所以Hystrix的配置及服务容错机制,对于 Zuul 的请求都适用,也会影响到 API 服务网关的行为。

就是说,默认情况下,当 Zuul 请求执行执行一个服务时间超过 1秒时,则会中断执行并返回一个 500 的错误。

我们可以通过hystrix.command.default.execution.isolation.thread.timeoutInMillseconds属性重新设置这个值。

 #超时设置 
 hystrix:
   command:
     default:
       execution:
         isolation:
           thread:
             timeoutInMilliseconds: 3000

也可针对某一个服务独立设置,比如将用户设置为 5秒,而其它服务执行超时时间不变,可如下配置:

 hystrix:
   command:
     userservice:
       execution:
         isolation:
           thread:
             timeoutInMilliseconds: 5000

另,Ribbon 在服务执行时也有一个超时时间配置,默认 5秒,就是说当一个服务执行时间超过了5秒,那么也会被中断并返回500的错误,我们需要对Ribbon的超时时间也做一个配置,如下:

超时时间应该:zuul > hystrix > ribbon (否则重试完成之前会被熔断)

hystrix:
   command:
     userservice:
       execution:
         isolation:
           thread:
             timeoutInMilliseconds: 3000

userservice:
    ribbon:
      ReadTimeout: 5000

这里关于cloud 的超时配置,放一个链接
springcloud中hystrix和ribbon的一些超时设置

6.7 Zuul过滤器

我们之前提起过, Zuul 其实包含了两个功能: 路由过滤器

路由的功能是负责对请求的处理过程进行干预,是实现请求校验。

实际上,Zuul 路由映射和请求转发这些功能都是由几个不同的过滤器组合完成的,所以说 Zuul 的过滤器才是核心所在。

对于 API 网关,开发者希望可以为服务请求的执行统一提供安全管控、日志、监控等处理,所以概念上类似于面向切面编程。

6.7.1 过滤器特性

当我们要实现一个 过滤器时,需要继承 ZuulFilter,我们先看一下其源码(只选我们需要的看):

public abstract class ZuulFilter implements IZuulFilter, Comparable<ZuulFilter> {
  	//返回过滤器类型: pre|route|post等 
    public abstract String filterType();
	//返回过滤器执行顺序号,不需要是连续的
    public abstract int filterOrder();
}

/**
* IZuulFilter 过滤器接口定义
*/
public interface IZuulFilter {
	/**
	* 如果返回 true, 那么 run() 将被执行,如果返回false 则不执行 run() 方法 
	* return true/false
	*/
    boolean shouldFilter();
	/**
	* 假如 shouldFilter() 返回 true, 那么该方法将被执行
	* @return 可返回任意对象,当前实现中忽略该返回值
	*/
    Object run() throws ZuulException;
}

从源码中可以看到 Zuul 过滤器的关键特性有以下几点:

  • Type: 定义在请求执行过程中何时被执行。
  • Execution Order: 当存在多个过滤器时,用来指示执行的顺序,值越小就越早执行;
  • Criteria:: 执行的条件,即该过滤器何时被触发。
  • Action: 该过滤器具体要执行的动作。

对应上面的 Zuul 过滤器特性和源码,在实现一个自定义过滤器需要实现的方法有以下几点:

  • filterType() 方法返回过滤器的类型;
  • filterOrder() 方法返回过滤器的执行顺序;
  • shouldFilter() 方法判断是否需要执行该过滤器;
  • run() 方法是该过滤器所要执行的具体过滤动作;

6.7.2 过滤器类型及生命周期

在 Zuul 中定义了 4 种标准的过滤器类型,这些过滤器类型对应于一个服务请求的典型生命周期。

  • PRE 过滤器: **在请求被路由之前调用;**可用来实现身份验证、在集群中选择请求的微服务、记录调试信息等;
  • ROUTING 过滤器: **在调用目标服务之前被调用;**通常可以用来处理一些动态路由。比如:A/B测试,在这里可以随机让部分用户访问指定版本的服务,然后通过用户体验数据的采集和分析来决定那个版本更好。另外,还可以结合 PRE 过滤器实现不同版本服务之间的处理。
  • POST 过滤器: 在目标微服务执行以后,所返回的结果在送回给客户端时被调用; 我们可以利用该过滤器实现为响应添加标准的 **HTTP Header、**数据采集、统计信息和指标、审计日志处理等。
  • ERROR 过滤器: 该过滤器在处理请求过程中发生错误时被调用; 可以使用该过滤器实现对异常、错误的统一处理,从而为客户端调用显示更加友好的界面。
  • 当然我们还可以自定义过滤器类型

6.7.3 自定义 Zuul 过滤器

6.7.4 禁用 Zuul 过滤器

在配置文件中只需要按照如下格式就可配置所需要禁用的过滤器:
zuul.[filter-name].[filter-type].disable=true

比如,禁用 testFilter 中的 PRE过滤器:
zuul.testFilter.pre.disable=true

6.8 @EnableZuulServer@EnableZuulProxy比较

简单来说,@EnableZuulProxy注解包含@EnableZuulServer的所有功能,并且还加入了@EnableCircuitBreaker@EnableDiscoveryClient

当我们需要运行一个没有代理功能的 Zuul 服务,或者需要有选择地 开/关部分代理功能时,需要使用@EnableZuulServer代替@EnableZuulProxy。这时开发者添加的任何 ZuulFilter 类型实体类都会被自动加载,这和使用@EnableZuulProxy一样的。
但是@EnableZuulProxy不会自动加载任何处理过滤器。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值