使用API网关进行路由和过滤
本章将讨论基于微服务架构的下一个重要元素,即API网关(API Gateway)。这不是我们在实践中第一次遇到这个元素。本书在第4章“服务发现”中实现了一个简单的网关模式,目的是介绍分区机制如何在使用Eureka的情况下进行服务发现。我们还曾经使用过Netflix的Zuul库,它是一个基于Java虚拟机(Java Virtual Machine, JVM) 的路由器和服务器端负载均衡器。Netlix 设计了Zuul,以提供身份验证、压力测试和金丝雀测试(Canary Test) 、动态路由,以及主动流量管理/主动多区域流量管理( Active Multregional Traffic Management)等功能。虽然没有明确说明,但它也可以充当微服务架构中的网关,其主要任务是从外部客户端隐藏系统的复杂性。
事实上,到目前为止,Zuul 在Spring Cloud框架内的API网关模式实现方面没有任何竞争对手。但是,随着一个名为Spring Cloud Gateway的新项目的逐步发展,情况正在发生变化。这个新项目建立在Spring Framework 5. Project Reactor和Spring Boot 2.0的基础之上。该库的最近一个稳定版本是1.0.0,但目前正在开发的版本2.0.0中则有许多重要的变化,它仍然处于里程碑阶段。Spring Cloud Gateway旨在提供一种简单而有效的方法来路由API, 并提供与其相关的跨领域问题,如安全性、监控/指标和弹性。虽然该解决方案相对较新,但绝对值得关注。
本章将要讨论的主题包括:
口基于URL的静态路由和负载均衡。
口将Zuul和Spring Cloud Gateway与服务发现集成。
口使用Zuul创建自定义过滤器。
口使用Zuul自定义路由配置。
口在路由失败的情况下提供Hytrix后备。
口对Spring Cloud Gateway中包含的主要组件——谓词和网关过滤器的说明。
使用Spring Cloud Netflix Zul
SpringCloud实现了一个嵌入式Zuul代理,允许前端应用程序对后端服务的代理调用。此功能对外部客户端非常有用,因为它隐藏了系统复杂性,有助于避免为所有微服务独立管理跨源资源共享(Cross-Origin Resource Sharing,CORS) 和身份验证问题。要启用它,可以使用@EnableZulProxy注解Spring Boot 的main类,并将传入的请求转发到目标服务。当然,Zuul也可以与Ribbon负载均衡器、Hystrix 断路器和服务发现(如使用Eureka)集成在一起。
构建网关应用程序
现在不妨先回到第7章中的示例,将最后一个元素附加到基于微服务的架构: API网关中。我们尚未考虑的是外部客户端调用服务的方式。首先,我们不希望公开系统内运行的所有微服务的网络地址。我们还可以在一个地方执行某些操作,如请求身份验证或设置跟踪标头。对于上述问题的解决方案是仅共享单个边缘网络地址,该地址会将所有传入请求代理到适当的服务。当前示例的系统架构如图8.1所示。
对于当前示例的这些需求,不妨回过头来参考一下第7章已经讨论过的项目。它可以在GitHub的master分支(tps:/ithib com/piomin/sample-spring -cloud-comm.gt)中找到。现在,我们将为该项目添加-一个名为gateway-service 的新模块。第一步就是将Zuul包含在Maven依赖项中,这里必须使用spring- cloud-starter-zuul启动器。
org .spr ingf ramework .cloud
spring-cloud-starter – zuu1
在使用@EnableZuulProxy注解Spring Boot主类之后,可以继续进行路由配置,该配置在application.yml文件中提供。默认情况下,Zuul启动工件不包括服务发现客户端。路由是静态配置的,url属性将被设置为服务的网络地址。现在,如果启动所有微服务和网关应用程序,则可以尝试通过网关调用它们。每个服务在每个路由的配置属性path中设置的路径下可用,如htt:olho/t:8/0/account/地址将被转发到htp:/:o/ahost:.091/。
server:
port: ${PORT:8080)
zuu1:
routes:
account:
path: /account/**
url: http://1ocalhost:8091
customer:
path: /customer/**
url: http://localhost:8092
order:
path: /order/**
url: http://loca1host :8090
product:
path: /product/**
url: http://1ocalhost:8093
与服务发现集成
上一个示例中提供的静态路由配置对于基于微服务的系统来说是不够的。API 网关的主要需求是与服务发现的内置集成。要为Zuul启用在使用Eureka情况下的服务发现,开发人员必须在项目依赖项中包含spring- cloud starter-eureka启动器,并通过@EnableDiscoveryClient来注解应用程序的main类,然后再启用客户端。实际上,让网关在发现服务器中注册它自已是没有意义的,它必须仅获取注册服务的当前列表。因此,开发人员可以将eureka.client.registerWithEureka 属性设置为false, 以这种方式来禁用该注册。至于plicatin.yml 文件中的路由定义则非常简单。每个路由的名称都将映射到Eureka中的应用程序服务名称。
zuul :
routes:
account-service:
path: /account/**
customer-service:
path: /customer/**
order-service:
path: /order/**
product-service:
path: /product/**
自定义路由配置
有若千个配置设置均允许开发人员自定义Zuul代理的行为。其中一些与服务发现集成有非常密切的关联。
1.忽略已注册的服务
默认情况下,Spring Cloud Zuul会公开在Eureka服务器中注册的所有服务。如果要跳过自动添加每项服务,则必须使用与发现服务器中所有被忽略的服务名称相匹配的模式设置zulignored-services 属性。那么它在实践中是如何运作的呢?即使开发人员没有使用zuul.routes.*属性 提供任何配置,Zuul 也会从Eurcka获取服务列表并自动将它们绑定到具有服务名称的路径上。例如,account-service服务将在网关地址htp:/ocalhost:8080/0account-service/**下可用。现在,如果在application.yml文件中设置以下配置,那么它将忽略account-service服务并以HTTP 404状态响应。
zuul :
ignoredservices: ‘ account-service’
也可以通过将zuulignored-services 设置为*来忽略所有已注册的服务。如果某服务匹配了被忽略的模式,但它也包含在路由映射配置中,那么它将包含在Zuul中。例如, 在以下情况下,将仅处理customer-service服务。
zuul :
ignoredservices: ‘★’
routes:
customer-service: /customer/t★★
2.明确设置服务名称
开发人员还可以使用serviceld 属性在配置中设置发现服务器的服务名称。这种方式可以提供对路径的细粒度控制,因为这样意味着可以单独指定路径和serviceld.以下是路由的等效配置。
zuul :
routes :
accounts :
path: /account/★*
serviceId: account-service
Cus tomers :
path: /customer/**
serviceId: customer-service
orders:
path: /order/★★
serviceId: order-service
products :
path: /product/**
serviceId: product-service
3.使用Ribbon客户端进行路由定义
还有另一种配置路由的方法。开发人员可能会禁用Eureka发现,以便仅依赖Ribbon客户端的listOfServers属性提供的网络地址列表。默认情况下,可以通过Ribbon客户端在所有服务实例之间对网关的所有传入请求进行负载均衡。即使启用或禁用服务发现,此规则也适用,示例代码如下。
zuul :
routes:
accounts:
path: /account/**
serviceId: account- service
ribbon:
eureka:
enabled: false
account- service :
ribbon:
listofservers: http://localhost:8091,http://1ocalhost:9091
4.在路径中添加前缀
有时需要为通过网关调用的服务设置不同的路径,而不是允许它们直接可用。在这种情况下,Zuul提供了为所有定义的映射添加前缀的功能。这可以使用zulprefx 属性轻松配置。默认情况下,Zuul 会在将请求转发给服务之前去掉该前缀。
但是,也可以通过将zul.stripPrefix属性设置为false来禁用该行为。stripPrefx 属性不仅可以为所有已定义的路由全局配置,还可以为每个路由配置。
以下就是一个为所有转发的请求添加/api 前缀的示例。举例来说,现在开发人员如果想调用account-service 服务的GET /{id} 端点,则应该使用的地址是htp:/localhostr8080/api/account/1。
zuul:
prefix: /api
routes:
accounts:
path: /account/**
serviceId: account-service
customers:
path: /customer/**
serviceId: customer-service
如果此时提供了将stripPrefix属性设置为fale的配置,会出现什么问题呢?在这种情况下,Zuul 会尝试在上下文路径lapi/account和/apicustomer下查找目标服务中的端点。
zuul:
prefix: /api
stripPrefix: false
5.连接设置和超时
Spring Cloud Netflix Zuul的主要任务是将传入请求路由到下游服务。因此,它必须使用HTTP客户端实现来与这些服务进行通信.Zuul使用的默认HTTP客户端现在由Apache HTTP Client支持,而不是已经不推荐使用的Ribbon RestClient.如果要使用Ribbon,则应该将rbbon.restclient. enabled的属性设置为true.或者也可以通过将ribbon, okhttp enabled属性设置为true来尝试OkHttpClient.
开发人员可以配置HTTP客户端的基本设置,如连接或读取超时,以及最大连接数。根据是否使用了服务发现,此类配置有两个可用选项。如果已通过url属性定义了具有指定网络地址的Zuul 路由,则应设置zuul host.ononect-timeut-millis和zul.host .socket-timeut-illis.为了控制最大连接数,开发人员应该覆盖zuul.host.maxTotalConections 属性的默认值,该属性的默认设置为200。还可以通过设置zul.host maxerRouteConnections属性定义每个路由的最大连接数,该属性的默认值为20。
如果Zuul已经被配置为从发现服务器获取服务列表,则需要使用Ribbon客户端属性ribbon. ReadTimeout和ribbon SocketTimeout配置与以前相同的超时值。还可以使用ribbon.MaxTotalConnections和ribbon.MaxConnectionsPerHost属性自定义最大连接数。
6.保护标头的安全
如果在请求中已经设置了诸如Authorization之类的HTTP标头,但是它未被转发到下游服务,你是否会感到有些疑惑?其实这是因为Zuul定义了敏感标头的默认列表,这些标头在路由过程中将被删除。这个默认列表中的标头包括Cookie. Set-Cookie 和Authorization。此功能是站在与外部服务器进行通信的角度进行设计的。虽然不反对在同一系统中的服务之间共享标头,但出于安全原因,不建议与外部服务器共享标头。如果有必要,可以通过覆盖sensitiveHeaders属性的默认值来自定义此方法。它可以为所有路由全局设置,也可以仅针对单个路由设置。sensitiveHeaders 不是一个空的黑名单,所以,要让Zuul转发所有标头,则应该明确地将它设置为空列表。
zuul :
routes:
accounts :
path: /account/
sensitiveHeaders:
serviceId: account- service
管理端点
Spring Cloud Nettlix Zuul公开了两个额外的管理端点用于监控。
口Routes (路由) :打印已经定义的路由的列表。
口Filters (过滤器) :打印已经实现的过滤器的列表(可以从Spring Cloud Netlix的1.4.0版本获得)。
要启用管理端点功能,必须在项目依赖项中包含spring-boot starter-actuator,这和前文介绍的方法是一样的。另外,考虑到测试需要,最好能禁用端点安全性设置,方法是将management security .enabled属性设置为false.现在可以调用GET /routes 方法,它将为我们的示例系统打印以下JSON响应。
“/api/account/++.: “account-service”,
“/ap1/customer/t: “customer-service” 。
“/api/order/**”: “order-service”,
“/api/product/***: “product-service”,
}
要获得更多详细信息,必须将format-details查询字符串添加到/routes路径。该选项在Spring Cloud 1.4.0版( Edgware版本列车)中也可以使用。还有一种POST /route方法会强制刷新当前存在的路由。此外,也可以通过将endpoints.routes.enabled 设置为false来禁用整个端点。
“/api/account/*:
“id”: “account-service”,
“ful1Path”: “/api/account/**m,
“location”: “account-service”,
“path”: “/*由”,
“prefix”: “/api/account”,
“retryable”: false,
“customSensitiveHeaders”: false,
“prefixstripped”: true
}
/filters端点的响应结果非常有趣。开发人员可以在Zuul网关上查看默认情况下可用的过滤器数量和类型。以下就是使用一个选定过滤器的响应片段。它包含完整的类名、调用顺序和状态。有关过滤器的更多信息,请参阅本章第8.1.6节“Zuul过滤器”。
“route”: [(
“class”:
“org . springframework. cloud。netflix. zuul. filters. route .RibbonRoutingFilter”,
“order”: 10,
“disabled”: false,
“static”: true
}, {
…
]
本文给大家讲解的内容是使用API网关进行路由和过滤
- 下篇文章给大家讲解的是提供Hystrix回退bean;
- 觉得文章不错的朋友可以转发此文关注小编;
- 感谢大家的支持!