6.2 Spring Cloud和Netflix Zuul简介

Spring Cloud集成了Netflix开源项目Zuul。Zuul是一个服务网关,它非常容易通过Spring Cloud注解进行创建和使用。Zuul提供了许多功能,具体包括以下几个。
    将应用程序中的所有服务的路由映射到一个URL——Zuul不局限于一个URL。在Zuul中,开发人员可以定义多个路由条目,使路由映射非常细粒度(每个服务端点都有自己的路由映射)。然而,Zuul最常见的用例是构建一个单一的入口点,所有服务客户端调用都将经过这个入口点。
    构建可以对通过网关的请求进行检查和操作的过滤器——这些过滤器允许开发人员在代码中注入策略执行点,以一致的方式对所有服务调用执行大量操作。
    要开始使用Zuul,需要完成下面3件事。
    (1)建立一个Zuul Spring Boot项目,并配置适当的Maven依赖项。
    (2)使用Spring Cloud注解修改这个Spring Boot项目,将其声明为Zuul服务。
    (3)配置Zuul以便Eureka进行通信(可选)。
    
6.2.1 建立一个Zuul Spring Boot项目
    如果读者在本书中按顺序读了前几章,应该会对接下来要做的工作很熟悉。要构建一个Zuul服务器,需要建立一个新的Spring Boot服务并定义相应的Maven依赖项。读者可以在本书的GitHub存储库中找到本章的项目源代码。幸运的是,在Maven中建立Zuul只需要很少的步骤,只需要在zuulsvr/pom.xml文件中定义一个依赖项:
    <dependency>   
<groupId>org.springframework.cloud</groupId>   
<artifactId>spring-cloud-starter-zuul</artifactId> 
</dependency>
    这个依赖项告诉Spring Cloud框架,该服务将运行Zuul,并适当地初始化Zuul。
   
 6.2.2 为Zuul服务使用Spring Cloud注解
    在定义完Maven依赖项后,需要为Zuul服务的引导类添加注解。Zuul服务实现的引导类可以在zuulsvr/src/main/java/com/thoughtmechanix/zuulsvr/Application.java中找到。
代码清单6-1展示了如何为Zuul服务的引导类添加注解。
    
代码清单6-1 创建Zuul服务器引导类
    
package com.thoughtmechanix.zuulsvr; 
import org.springframework.boot.SpringApplication; 
import org.springframework.boot.autoconfigure.SpringBootApplication; 
import org.springframework.context.
 
@SpringBootApplication 
@EnableZuulProxy  ⇽--- 使服务成为一个Zuul服务器 
public class ZuulServerApplication {     
public static void main(String[] args) {         
SpringApplication.run(ZuulServerApplication.class, args);     } }
    
就这样,这里只需要一个注解:@EnableZuulProxy。
    
注意
    
如果读者浏览过文档或启用了自动补全,那么可能会注意到一个名为@EnableZuulServer的注解。使用此注解将创建一个Zuul服务器,它不会加载任何Zuul反向代理过滤器,也不会使用Netflix Eureka进行服务发现(我们将很快进入Zuul和Eureka集成的主题)。开发人员想要构建自己的路由服务,而不使用任何Zuul预置的功能时会使用@EnableZuulServer,
举例来讲,当开发人员需要使用Zuul与Eureka之外的其他服务发现引擎(如Consul)进行集成的时候。
本书只会使用@EnableZuulProxy注解。
    
6.2.3 配置Zuul与Eureka进行通信
    Zuul代理服务器默认设计为在Spring产品上工作。因此,Zuul将自动使用Eureka来通过服务ID查找服务,然后使用Netflix Ribbon对来自Zuul的请求进行客户端负载均衡。
    
注意
 
我经常不按顺序阅读书中的章节,而是会跳到我最感兴趣的主题上。如果读者也这么做,并且不知道Netflix Eureka和Ribbon是什么,那么,我建议读者先阅读第4章,然后再进行下一步。Zuul大量采用这些技术进行工作,因此了解Eureka和Ribbon带来的服务发现功能会更容易理解Zuul。
    
配置过程的最后一步是修改Zuul服务器的zuulsvr/src/main/resources/application.yml文件,以指向Eureka服务器。代码清单6-2展示了Zuul与Eureka通信所需的Zuul配置。
 
代码清单6-2中的配置应该看起来很熟悉,因为它与第4章中介绍的配置相同。
    
代码清单6-2 配置Zuul服务器与Eureka通信
eureka:
  instance:
    preferIpAddress: true
  client:
    registerWithEureka: true
    fetchRegistry: true
    serviceUrl:
        defaultZone: http://localhost:8761/eureka/
 
6.3 在Zuul中配置路由
    Zuul的核心是一个反向代理。反向代理是一个中间服务器,它位于尝试访问资源的客户端和资源本身之间。客户端甚至不知道它正与代理之外的服务器进行通信。反向代理负责捕获客户端的请求,然后代表客户端调用远程资源。
    在微服务架构的情况下,Zuul(反向代理)从客户端接收微服务调用并将其转发给下游服务。服务客户端认为它只与Zuul通信。Zuul要与下游服务进行沟通,Zuul必须知道如何将进来的调用映射到下游路由。Zuul有几种机制来做到这一点,包括:
    通过服务发现自动映射路由;
    使用服务发现手动映射路由;
    使用静态URL手动映射路由。
    6.3.1 通过服务发现自动映射路由
    Zuul的所有路由映射都是通过在zuulsvr/src/main/resources/application.yml文件中定义路由来完成的。但是,Zuul可以根据其服务ID自动路由请求,而不需要配置。如果没有指定任何路由,Zuul将自动使用正在调用的服务的Eureka服务ID,并将其映射到下游服务实例。例如,如果要调用organizationservice并通过Zuul使用自动路由,则可以使用以下URL作为端点,让客户端调用Zuul服务实例:
    Zuul服务器可通过 http://localhost:5555进行访问。该服务中的端点路径的第一部分表示正在尝试调用的服务(organizationservice)。
    
图6-3阐明了该映射的实际操作。
 
图6-3 Zuul将使用organizationservice应用程序名称来将请求映射到组织服务实例
    使用带有Eureka的Zuul的优点在于,开发人员不仅可以拥有一个可以发出调用的单个端点,有了Eureka,开发人员还可以添加和删除服务的实例,而无须修改Zuul。例如,可以向Eureka添加新服务,Zuul将自动路由到该服务,因为Zuul会与Eureka进行通信,了解实际服务端点的位置。
    如果要查看由Zuul服务器管理的路由,可以通过Zuul服务器上的/routes端点来访问这些路由,这将返回服务中所有映射的列表。图6-4展示了访问 http://localhost:5555/routes的输出结果。
图6-4 在Eureka中映射的每个服务现在都将被映射为Zuul路由
    
在图6-4中,通过zuul注册的服务的映射展示在从/route调用返回的JSON体的左边,路由映射到的实际Eureka服务ID展示在其右边。
    
6.3.2 使用服务发现手动映射路由
    
Zuul允许开发人员更细粒度地明确定义路由映射,而不是单纯依赖服务的Eureka服务ID创建的自动路由。假设开发人员希望通过缩短组织名称来简化路由,而不是通过默认路由/organizationservice/v1/organizations/{organization-id}在Zuul中访问组织服务。开发人员可以通过在zuulsvr/src/main/resources/application.yml中手动定义路由映射来做到这一点。
    zuul:   
       routes:     
          organizationservice: /organization/­**
    
通过添加上述配置,现在我们就可以通过访问/organization/v1/organizations/ {organization-id}路由来访问组织服务了。如果再次检查Zuul服务器的端点,读者应该会看到图6-5所示的结果。
 
图6-5 将组织服务进行手动映射后Zuul /routes的调用结果
    如果仔细查看图6-5,读者会注意到有两个条目代表组织服务。第一个服务条目是在application.yml文件中定义的映射"organization/**": "organizationservice"。第二个服务条目是由Zuul根据组织服务的Eureka ID创建的自动映射"/organizationservice/**":"organizationservice"。
    
注意
    
    在使用自动路由映射时,Zuul只基于Eureka服务ID来公开服务,如果服务的实例没有在运行,Zuul将不会公开该服务的路由。然而,如果在没有使用Eureka注册服务实例的情况下,手动将路由映射到服务发现ID,那么Zuul仍然会显示这条路由。如果尝试为不存在的服务调用路由,Zuul将返回500错误。
 
如果想要排除Eureka服务ID路由的自动映射,只提供自定义的组织服务路由,可以向application.yml文件添加一个额外的Zuul参数ignored-services。
    以下代码片段展示了如何使用ignored-services属性从Zuul完成的自动映射中排除Eureka服务ID organizationservice。
    zuul:   ignored-services: 'organizationservice'   routes:     organizationservice: /organization/­**
    ignored-services属性允许开发人员定义想要从注册中排除的Eureka服务ID的列表,该列表以逗号进行分隔。现在,在调用/routes端点时,应该只能看到自定义的组织服务映射。图6-6展示了此映射的结果。
 
图6-6 Zuul中现在只定义了一个组织服务
    
如果要排除所有基于Eureka的路由,可以将ignored-services属性设置为“*”。
    服务网关的一种常见模式是通过使用/api之类的标记来为所有的服务调用添加前缀,从而区分API路由与内容路由。Zuul通过在Zuul配置中使用prefix属性来支持这项功能。图6-7在概念上勾画了这种映射前缀的样子。
 
 
图6-7 通过使用前缀,Zuul会将/api前缀映射到它管理的每个服务
    
在代码清单6-3中,我们将看到如何分别为组织服务和许可证服务建立特定的路由,排除所有Eureka生成的服务,并使用/api前缀为服务添加前缀。
    
代码清单6-3 使用前缀建立自定义路由
    
zuul:   i
gnored-services: '*'  
 
prefix: /api  
⇽--- 所有已定义的服务都将添加前缀/api  
 routes:     
⇽--- organizationservice和licensingservice分别映射到organization和licensing
organizationservice: /organization/­**  
licensingservice: /licensing/­**
    
完成此配置并重新加载Zuul服务后,访问/routes端点时应该会看到以下两个条目:/api/organization和/api/licensing。图6-8展示了这些路由条目。
图6-8 Zuul中的路由现在添加了/api前缀
    
现在让我们来看看如何使用Zuul来映射到静态URL。静态URL是指向未通过Eureka服务发现引擎注册的服务的URL。
    
6.3.3 使用静态URL手动映射路由
    Zuul可以用来路由那些不受Eureka管理的服务。在这种情况下,可以建立Zuul直接路由到一个静态定义的URL。
例如,假设许可证服务是用Python编写的,并且仍然希望通过Zuul进行代理,那么可以使用代码清单6-4中的Zuul配置来达到此目的。
    
代码清单6-4 将许可证服务映射到静态路由
    zuul:  
       routes:    
          licensestatic:  ⇽--- Zuul用于在内部识别服务的关键字      
             path: 
                 /licensestatic/­**  ⇽--- 许可证服务的静态路由
                 url: http://licenseservice-static:8081 ⇽-已建立许可证服务的静态实例,它将被直接调用,而不是由Zuul通过Eureka调用
    
完成这一配置更改后,就可以访问/routes端点来看添加到Zuul的静态路由。图6-9展示了/routes端点的结果。
图6-9 现在已经将静态路由映射到许可证服务
    
现在,licensestatic端点不再使用Eureka,而是直接将请求路由到http://licenseservice-static:8081端点。这里存在一个问题,那就是通过绕过Eureka,只有一条路径可以用来指向请求。幸运的是,开发人员可以手动配置Zuul来禁用Ribbon与Eureka集成,然后列出Ribbon将进行负载均衡的各个服务实例。代码清单6-5展示了这一点。
 
代码清单6-5 将许可证服务静态映射到多个路由
    zuul:   
      routes:     
         licensestatic:       
             path: /licensestatic/­**       
             serviceId: licensestatic  ⇽--- 定义一个服务ID,该服务ID将用于在Ribbon中查找服务 
      ribbon:   
        eureka:     
           enabled: false  ⇽--Ribbon中禁用Eureka支持 
       licensestatic:   
          ribbon:     
             listOfServers: http://licenseservice-static1:8081,       
                                    http://licenseservice-static2:8082  ⇽--- 指定请求会路由到的服务器列表
    
配置完成后,调用/routes端点现在将显示/api/licensestatic路由已被映射到名为licensestatic的服务ID。图6-10展示了这一点。
图6-10 /api/licensestatic现在映射到名为licensestatic的服务ID
    
处理非JVM服务
    静态映射路由并在Ribbon中禁用Eureka支持会造成一个问题,那就是禁用了对通过Zuul服务网关运行的所有服务的Ribbon支持。这意味着Eureka服务器将承受更多的负载,因为Zuul无法使用Ribbon来缓存服务的查找。记住,Ribbon不会在每次发出调用的时候都调用Eureka。相反,它将在本地缓存服务实例的位置,然后定期检查Eureka是否有变化。缺少了Ribbon,Zuul每次需要解析服务的位置时都会调用Eureka。
    在本章早些时候,我们讨论了如何使用多个服务网关,根据所调用的服务类型来执行不同的路由规则和策略。对于非JVM应用程序,可以建立单独的Zuul服务器来处理这些路由。然而,我发现对于非基于JVM的语言,最好是建立一个Spring Cloud “Sidecar”实例。Spring Cloud Sidecar允许开发人员使用Eureka实例注册非JVM服务,然后通过Zuul进行代理。我没有在本书中介绍Spring Sidecar,因为本书不会让读者编写任何非JVM服务,但是建立一个Sidecar实例是非常容易的。读者可以在Spring Cloud网站上找到相关指导。
    6.3.4 动态重新加载路由配置
    接下来我们要在Zuul中配置路由来看看如何动态重新加载路由。动态重新加载路由的功能非常有用,因为它允许在不回收Zuul服务器的情况下更改路由的映射。现有的路由可以被快速修改,以及添加新的路由,都无需在环境中回收每个Zuul服务器。在第3章中,我们介绍了如何使用Spring Cloud配置服务来外部化微服务配置数据。读者可以使用Spring Cloud Config来外部化Zuul路由。在EagleEye示例中,我们可以在configrepo( http://github.com/carnellj/config-repo)中创建一个名为zuulservice的新应用程序文件夹。就像组织服务和许可证服务一样,我们将创建3个文件(即zuulservice.yml、zuulservice-dev.yml和zuulservice-prod.yml),它们将保存路由配置。
 
为了与第3章配置中的示例保持一致,我已经将路由格式从层次化格式更改为“.”格式。初始的路由配置将包含一个条目:
    zuul.prefix=/api
    如果访问/routes端点,应该会看到在Zuul中显示的所有基于Eureka的服务,并带有/api的前缀。现在,如果想要动态地添加新的路由映射,只需对配置文件进行更改,然后将配置文件提交回Spring Cloud Config从中提取配置数据的Git存储库。例如,如果想要禁用所有基于Eureka的服务注册,并且只公开两个路由(一个用于组织服务,另一个用于许可证服务),则可以修改zuulservice-*.yml文件,如下所示:
   
zuul.ignored-services: '*' 
zuul.prefix: /api 
zuul.routes.organizationservice: /organization/­** 
    
接下来,将更改提交给GitHub。Zuul公开了基于POST的端点路由/refresh,其作用是让Zuul重新加载路由配置。在访问完refresh端点之后,如果访问/routes端点,就会看到两条新的路由,所有基于Eureka的路由都不见了。
    
6.3.5 Zuul和服务超时
    Zuul使用Netflix的Hystrix和Ribbon库,来帮助防止长时间运行的服务调用影响服务网关的性能。在默认情况下,对于任何需要用超过1 s的时间(这是Hystrix默认值)来处理请求的调用,Zuul将终止并返回一个HTTP 500错误。
幸运的是,开发人员可以通过在Zuul服务器的配置中设置Hystrix超时属性来配置此行为。
    
开发人员可以使用 hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds属性来为所有通过Zuul运行的服务设置Hystrix超时。例如,如果要将默认的Hystrix超时设置为2.5 s,就可以在Zuul的Spring Cloud配置文件中使用以下配置:
    
zuul.prefix:  /api 
zuul.routes.organizationservice: /organization/**
zuul.routes.licensingservice: /licensing/­** 
zuul.debug.request: true 
    
如果需要为特定服务设置Hystrix超时,可以使用需要覆盖超时的服务的Eureka服务ID名称来替换属性的default部分。例如,如果想要将licensingservice的超时更改为3 s,并让其他服务使用默认的Hystrix超时,可以在配置中添加与下面类似的内容:
   
 最后,读者需要知晓另外一个超时属性。虽然已经覆盖了Hystrix的超时,Netflix Ribbon同样会超时任何超过5 s的调用。尽管我强烈建议读者重新审视调用时间超过5 s的调用的设计,但读者可以通过设置属性servicename.ribbon.ReadTimeout来覆盖Ribbon超时。例如,如果想要覆盖licensingservice超时时间为7 s,可以使用以下配置:
hystrix.command.licensingservice.execution. isolation.thread.timeoutInMilliseconds: 7000 
licensingservice.ribbon.ReadTimeout: 7000
    
注意
    
    对于超过5 s的配置,必须同时设置Hystrix和Ribbon超时。
 
 
 
 
 
 

猜你喜欢

转载自www.cnblogs.com/mongotea/p/11974144.html