服务网关 Spring Cloud Zuul

(git上的源码:https://gitee.com/rain7564/spring_microservices_study/tree/master/fifth-spring-cloud-zuul)
对于服务网关是什么、有什么用?使用API Gateway这篇文章已经讲得很清楚了,这里就不再赘述。当然这只是翻译版,原版在这里:Building Microservices: Using an API Gateway
Spring Cloud和Netflix Zuul
Using an API Gateway一文中提到Netflix API Gateway其实就是Netflix Zuul。Zuul是一个服务网关,Spring Cloud融入Zuul后,使用Spring Cloud提供的注解很容易就能搭建一个API Gateway。Zuul提供了几个功能,包括:

只用一个URL就能映射应用中所有服务的路由。但Zuul并不局限于单个URL,也可以定义多个路由入口,做到细粒度的路由映射。
自定义过滤器对经过网关的所有请求进行过滤。服务网关的过滤器,可以实现对所有请求进行过滤,而不用在各个服务实现过滤器。

下面进入正题,服务网关的实现。
创建gateway-zuul服务
创建一个Zuul服务端,首先要创建一个Spring Boot项目,然后引入Zuul相关的启动依赖。
创建Spring boot项目并修改pom文件
创建一个空Spring Boot项目后,pom文件修改如下:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>cn.study.microservice</groupId>
    <artifactId>gateway-zuul</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>gateway-zuul</name>
    <description>API Gateway</description>

    <parent>
        <groupId>cn.study.microservice</groupId>
        <artifactId>fifth-spring-cloud-zuul</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-zuul</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka</artifactId>
        </dependency>
    </dependencies>

</project>

上面的xml文件,只引入了两个启动依赖,第一个是Zuul的启动依赖,该启动依赖除了包含Zuul的核心jar包外,还包括Hystrix、Ribbon、actuator等。第二个是后文介绍将Zuul服务托管在Eureka时会用到。
修改启动类
pom文件修改好之后,需要修改gateway-zuul服务的启动类,如下:
@SpringBootApplication
@EnableZuulProxy
public class GatewayZuulApplication {

    public static void main(String[] args) {
        SpringApplication.run(GatewayZuulApplication.class, args);
    }
}

上面的代码实际上只加了一个注解—@EnableZuulProxy。

若在添加注解@EnableZuulProxy时是手动输入,且IDE开启自动补全功能,那么应该会看到另一个注解——@EnableZuulServer。如果使用该注解,虽然也会创建一个Zuul服务端,但不加载任何反向代理过滤器,不使用Eureka的服务发现来发现其他服务。@EnableZuulServer只在搭建自己的路由服务并不使用Zuul的预构建功能时使用。比如需要使用Zuul来配合其它不是Eureka的服务发现引擎,如Consul。

与Eureka结合使用
Zuul proxy server本来就是被设计用在Spring Cloud项目中。正因为如此,Zuul自动使用Eureka来作为服务发现的依赖,可以通过服务发现其它服务,然后在Zuul内部使用Ribbon实现客户端负载均衡。
添加application.yml文件,然后在该文件中加入如下配置:
server:
  port: 5555

eureka:
  instance:
    preferIpAddress: true
  client:
    serviceUrl:
      defaultZone: http://localhost:8761/eureka/

配置路由规则
Zuul的核心是反向代理。反向代理实际上是一个中间服务器,处于想要访问某个资源的客户端与资源中间。反向代理会捕捉客户端的请求并代表客户端想远程资源发起请求。
在微服务架构中,Zuul(反向代理)从客户端“得到”一个调用,然后转发给下游服务。所以,从客户端服务角度来看,与客户端交互的实际上是Zuul。而Zuul需要与下游的服务进行交互,所以必须知道客户端的请求想要路由到哪个服务。Zuul可以通过几种途径来达到这一目标,包括:

通过服务发现自动映射路由
使用服务发现手动映射路由
使用静态URLs手动映射路由

通过服务发现自动映射路由
Zuul的所有路由映射都可以在zuul服务的application.yml文件中定义。然而,Zuul还可以在零配置的情况下,根据请求url携带的serviceId将请求正确路由到目标服务的某个实例。如果没有指定特定的路由(手动配置,下文会介绍),Zuul默认会使用被调用服务的Eureka service ID并将其映射到其中一个目标服务实例。举个简单的例子,如果你想访问organization-service服务的一个接口,该接口是根据orgId获取对应的organization详细信息,而且希望使用Zuul的自动路由,那么你可以让客户端直接访问Zuul服务的实例,然后使用如下URL:
http://localhost:5555/organizationservice/v1/organizations/e254f8c-c442-4ebe-a82a-e2fc1d1ff78a

其中http://localhost:5555,是Zuul服务实例的访问地址;而organizationservice是organization服务的service ID;剩下的部分则是希望调用的接口。看下面的图可能更容易理解,如下:

image.png

Eureka配合Zuul使用的优美之处在于,不仅可以通过单个端点来访问应用的所有服务,而且,在添加或移除服务实例的时候不用修改Zuul的路由配置。另外,也可以添加一个新的服务到Eureka,而Zuul会对访问新添加的服务自动路由,因为Zuul是通过与Eureka通信然后从Eureka获取微服务实例真正物理地址,只要服务托管在Eureka中。
如果想要查看Zuul服务器管理的路由,可以访问Zuul暴露的/routes端点。该端点会返回所有服务的映射列表。启动所有服务,然后访问http://localhost:5555/routes,若跟着本教程走,应该可以看到类似如下图的返回:

image.png

如果出现下面的情况:

image.png

则需要在application.yml或bootstrap.yml文件中加入如下配置:

management:
  security:
    enabled: false

观察正常访问http://localhost:5555/routes后的返回json对象,类似"/config-server/**"的key,是Zuul基于Eureka service ID自动为服务创建的服务路由,请求匹配到的服务路由就可以映射得到Eureka service ID,即json对象的value,最后就可以根据这个ID定位具体的服务实例。
使用服务发现手动映射路由
Zuul允许配置更细粒度的路由映射规则,可以明确定义路由映射而不是单纯依赖使用服务的Eureka service ID自动创建。假设想要缩短organizationservice来简化路由,而不是使用Zuul默认提供的/organizationservice/**,那么可以通过在Zuul服务的配置文件中手动定义路由映射关系,例如:
zuul:
  routes:
    organizationservice: /organization/**

在Zuul服务的application.yml加上上面的配置后,就可以使用类似/organization/v1/organizations/{organization-id}的路由来访问organization服务了。此时,若重启Zuul然后再次访问http://localhost:5555/routes,可以出现如下返回结果:

image.png

观察上图,可以看到Zuul为organization服务提供了“入口”,第一个是刚刚手动配置上去的,而第二个则是Zuul默认提供的。

注意:如果使用Zuul基于Eureka service ID自动创建的路由映射,那么当某个服务没有任何一个实例处于运行状态,那么Zuul将不会为该服务创建路由映射。然而,如果手动将路由映射到Eureka service ID,那么,即使没有实例注册到Eureka,Zuul依旧会暴露出手动配置的。当然,若尝试使用不存在任何服务实例的路由,Zuul将直接返回500错误。

忽略某些服务
如果想要将Zuul自动创建的路由映射从路由列表中移除,只留下手动配置的,那么可以在application.yml文件中在加一个额外的Zuul参数——ignored-services即可。示例如下:
zuul:
  ignored-services: 'organizationservice'
  routes:
    organizationservice: /organization/**

这样,就能将Zuul根据Eureka Service ID自动创建的"/organizationservice/**": "organizationservice"从路由映射列表中移除。添加上面的配置然后重启,访问/routes端点,可以看到如下返回:

image.png

上图中,Zuul默认给organization服务创建的路由映射已经被忽略了,只留下手动配置的。如果希望Zuul忽略所有自动配置的路由映射,可以使用:

zuul:
  ignored-services: '*'
  routes:
    organizationservice: /organization/**

加上如上配置后重启,然后访问端点/routes,返回如下:

image.png

手动配置多个服务
云应用肯定会包含许多微服务,所以一般都有为多个服务手动配置路由映射的需求,这样的需求实现起来也比较简单,如下是对organization服务和license服务的路由映射做手动配置:
zuul:
  ignored-services: '*'
  routes:
    organizationservice: /organization/**
    licenseservice: /license/**

访问/routes结果是:

image.png

不同API路由共用一样的模型
在不同服务路由的开头附加一个的前缀是很常见的。比如希望在不同服务的路由的开头都加上一个/api的前缀,Zuul也是支持的。可以使用如下配置来实现这一功能:
zuul:
  ignored-services: '*'
  prefix: /api
  routes:
    organizationservice: /organization/**
    licenseservice: /license/**

可见,zuul.prefix属性可以用来定制所有服务路由的统一前缀。再次访问/routes端点,返回如下:

image.png

现在若需要访问organization服务的/v1/organizations/{organization-id}端口,则需要使用:
http://localhost:5555/api/organization/v1/organizations/{organization-id}

使用静态URLs手动映射路由
Zuul也可以用来转发没有注册到Eureka的服务的请求。在某些情况下,需要设置Zuul将部分请求直接路由到定义的静态URL。比如,假设organization服务是使用Python编写的,然后也想让Zuul来做反向代理,那么可以使用如下的配置实现:
zuul:
  prefix: /api
  routes:
    organizationstatic:
      path: /organizationstatic/**
      url: http://localhost:11000

关闭其他服务,只启动zuul服务和organization服务,启动过程中会报错,这是因为eureka没启动,服务没办法注册到eureka,可以不管它,只要成功启动就行。然后访问zuul的/routes端点,可以看到只有刚刚配置的静态URL,如图:

image.png

然后你会发现,访问

http://localhost:5555/api/organizationstatic/v1/organizations/e254f8c-c442-4ebe-a82a-e2fc1d1ff78a/时,可以成功访问,结果如下:

image.png

下面来分析一下,之前的配置是什么含义,如下图:

image.png

但是,问题又来了,因为这样的配置会绕过eureka,这就导致请求都会指向单个路由,那么当organization服务有多个怎么办?怎么利用Ribbon来实现服务均衡?有两种做法:

第一种:
zuul:
  prefix: /api
  routes:
    organizationstatic:
      path: /organizationstatic/**
      serviceId: organizationstatic
ribbon:
  eureka:
    enabled: false
organizationstatic:
  ribbon:
    listOfServers: http://localhost:11000,http://localhost:11001

第二种:
zuul:
  prefix: /api
  routes:
    organizationstatic:
      path: /organizationstatic/**
      serviceId: organizationstatic
organizationstatic:
  ribbon:
    NIWSServerListClassName: com.netflix.loadbalancer.ConfigurationBasedServerList
    listOfServers: http://localhost:11000,http://localhost:11001

第一种实现方法需要Ribbon禁用eureka,感觉不太友好。官方文档是这样说的:

image.png

我的理解是:因为如果不禁用的话,zuul会以为organizationstatic这个service ID是来自Eureka的,所以就去eureka那边查找对应的服务实例,然而,organizationstatic根本就没注册到eureka,所以就直接报500了。
而第二种实现方法,是告诉zuul在使用Ribbon做负载均衡时,直接在提供的server列表中获取服务实例。观察第二种方法的配置,可以看到添加了.ribbon.NIWSServerListClassName属性。官方文档是这样说的:

image.png

官方文档地址:https://github.com/spring-cloud/spring-cloud-netflix/blob/master/docs/src/main/asciidoc/spring-cloud-netflix.adoc
使用上面的配置后,只启动gateway-zuul服务和两个organization服务实例,两个organization服务的端口分别是11000和11001。然后访问http://localhost:5555/api/organizationstatic/v1/organizations/e254f8c-c442-4ebe-a82a-e2fc1d1ff78a/,会发现负载均衡器起作用了。至于如何启动多个实例,请参考另一篇教程服务注册与发现——Netflix Eureka,这里面有提及。

非java服务的处理

之前说到静态映射路由负载均衡的第一种实现,需要zuul服务器的Ribbon禁用eureka,禁用后会有一个问题,就是其他注册到eureka的服务无法通过网关访问,需要手动自己配置.listOfServers属性。

所以对于其它语言编写的服务也想要通过zuul统一管理,有两个方案:第一,使用一个独立的zuul服务专门转发那些其它语言的服务;第二,创建Spring Cloud Sidecar实例(推荐使用这种),Spring Cloud sidecar允许注册其它语言实现的服务到eureka,然后就可以通过zuul代理了。Spring Cloud sidecar这里就不细讲了,实现也比较简单,可参考Polyglot support with Sidecar。

zuul的超时时间
zuul使用Hystrix和Ribbon库来帮助避免一个耗时较长的调用影响到整个网关的性能。默认情况下,当一个调用经过1s后还为处理完成并返回,zuul会终止该调用并统一返回500错误(这个1s的超时时间,其实是Hystrix的默认超时时间)。但是,我们可以在zuul的配置文件中对这个超时时间进行修改。比如,将其修改成7s,可以这样设置:
...
hystrix:
  command:
    default:
      execution:
        isolation:
          thread:
            timeoutInMilliseconds: 7000

假如需要修改特定的服务的超时时间,比如organization服务,则需要将上面的hystrix.command.defualt.换成hystrix.command.organizationservice,即将default换成对应的服务名。
最后,需要修改另一个超时时间属性。当我们覆盖hystrix默认超时时间且新超时时间大于5s,那么当一个调用超过5s时,Ribbon也会认为该调用超时。也就是说,当配置hystrix的超时时间大于5s,那么还需要配置Ribbon的超时时间,配置如下:
ribbon:
  ReadTimeout: 7000

如果需要指定某个服务的Ribbon超时时间,则要用(clientname为服务名):
clientname:
  ribbon:
    ReadTimeout: 7000

动态重新加载路由配置
zuul动态加载路由配置,需要具备Spring Cloud Config基础,若不了解Spring Cloud Config,可参考另一篇教程分布式配置——Spring Cloud Configuration。
动态加载路由在实际应用中是极其有用的,因为可以在变更路由配置后,不用重新编译、重新部署zuul服务。在分布式配置——Spring Cloud Configuration中已经讲过如何将配置文件迁移到config server中,我们也可以将zuul的配置交由config server管理。(注意:本教程不使用git作为config server的配置文件存储库,而是直接使用config server的classpath,这样比较简单,只是每次改完配置文件需要重新启动config server)。
在config server的classpath:config目录下,创建目录gateway-zuul,然后创建gateway-zuul.yml文件,内容如下:
server:
  port: 5555

eureka:
  instance:
    preferIpAddress: true
  client:
    serviceUrl:
      defaultZone: http://localhost:8761/eureka/

接着修改config server服务的application.yml,添加config server扫描路径,如下:
...
spring:
  profiles:
    active: native
  cloud:
    config:
      server:
        native:
          search-locations: classpath:config/,classpath:config/licenseservice, classpath:config/gateway-zuul

然后修改zuul服务的pom.xml文件和bootstrap.yml文件,分别为:
...
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-config-client</artifactId>
</dependency>
...

spring:
  application:
    name: gateway-zuul
  profiles:
    active: default
  cloud:
    config:
      uri: http://localhost:8888
management:
  security:
    enabled: false

然后重启config-server服务和gateway-zuul服务,当看到zuul服务的控制台有类似如下输出,才证明成功从config server加载配置文件(不然就要找找是哪里出错了):

image.png

访问zuul服务的/routes端点,返回结果如下:

image.png

修改config-server服务管理的zuul服务的配置文件,添加如下配置:
zuul:
  ignored-services: '*'
  prefix: /api
  routes:
    organizationservice: /organization/**
    licenseservice: /license/**

然后使用POST方式访问http://localhost:555/refresh(记得要先重启config-server服务),可以看到返回如下:

image.png

证明zuul服务的配置信息中的上面4个配置的值已变更。再次访问zuul服务的/routes,返回结果如下:

image.png

证明zuul已经动态加载配置成功了。
有关Spring Cloud Zuul的入门教程就介绍到这里,后续会在进阶教程中继续介绍zuul真正强大的功能——filter。
完!

我的官网

我的官网http://guan2ye.com
我的CSDN地址http://blog.csdn.net/chenjianandiyi
我的简书地址http://www.jianshu.com/u/9b5d1921ce34
我的githubhttps://github.com/javanan
我的码云地址https://gitee.com/jamen/
阿里云优惠券https://promotion.aliyun.com/ntms/act/ambassador/sharetouser.html?userCode=vf2b5zld&utm_source=vf2b5zld

阿里云教程系列网站http://aliyun.guan2ye.com

我的开源项目spring boot 搭建的一个企业级快速开发脚手架

时间: 2024-09-18 02:13:13

服务网关 Spring Cloud Zuul的相关文章

Spring Cloud -- Zuul

Zuul可以通过加载动态过滤机制,从而实现以下各项功能: 验证与安全保障: 识别面向各类资源的验证要求并拒绝那些与要求不符的请求. 审查与监控: 在边缘位置追踪有意义数据及统计结果,从而为我们带来准确的生产状态结论. 动态路由: 以动态方式根据需要将请求路由至不同后端集群处. 压力测试: 逐渐增加指向集群的负载流量,从而计算性能水平. 负载分配: 为每一种负载类型分配对应容量,并弃用超出限定值的请求. 静态响应处理: 在边缘位置直接建立部分响应,从而避免其流入内部集群. 多区域弹性: 跨越AWS

《Spring Cloud与Docker微服务架构实战》配套代码

不才写了本使用Spring Cloud玩转微服务架构的书,书名是<Spring Cloud与Docker微服务架构实战> - 周立,已于2017-01-12交稿.不少朋友想先看看源码,现将代码放出. 本次放出的代码: 共计70+个DEMO 覆盖Eureka.Ribbon.Feign.Hystrix.Zuul.Spring Cloud Config.Spring Cloud Bus.Spring Cloud Sleuth.Docker.Docker Compose等. 1-11章代码地址: ht

Spring Cloud构建微服务架构:服务网关(过滤器)【Dalston版】

2017年架构师最重要的48个小时 | 8折倒计时 在前两篇文章:服务网关(基础).服务网关(路由配置)中,我们了解了Spring Cloud Zuul作为网关所具备的最基本功能:路由.本文我们将具体介绍一下Spring Cloud Zuul的另一项核心功能:过滤器. 过滤器的作用 通过上面所述的两篇我们,我们已经能够实现请求的路由功能,所以我们的微服务应用提供的接口就可以通过统一的API网关入口被客户端访问到了.但是,每个客户端用户请求微服务应用提供的接口时,它们的访问权限往往都需要有一定的限

Spring Cloud构建微服务架构:服务网关(路由配置)【Dalston版】

在上一篇<Spring Cloud构建微服务架构:服务网关(基础)>一文中,我们通过使用Spring Cloud Zuul构建了一个基础的API网关服务,同时也演示了Spring Cloud Zuul基于服务的自动路由功能.在本文中,我们将进一步详细地介绍关于Spring Cloud Zuul的路由功能,以帮助读者可以更好的理解和使用它,以完成更复杂的路由配置. 传统路由配置 所谓的传统路由配置方式就是在不依赖于服务发现机制的情况下,通过在配置文件中具体指定每个路由表达式与服务实例的映射关系来

springcloud(十):服务网关zuul

前面的文章我们介绍了,Eureka用于服务的注册于发现,Feign支持服务的调用以及均衡负载,Hystrix处理服务的熔断防止故障扩散,Spring Cloud Config服务集群配置中心,似乎一个微服务框架已经完成了. 我们还是少考虑了一个问题,外部的应用如何来访问内部各种各样的微服务呢?在微服务架构中,后端服务往往不直接开放给调用端,而是通过一个API网关根据请求的url,路由到相应的服务.当添加API网关后,在第三方调用端和服务提供方之间就创建了一面墙,这面墙直接与调用方通信进行权限控制

Spring Cloud实战小贴士:Zuul的饥饿加载(eager-load)使用

上一篇我们介绍了如何使用Ribbon的earger-load配置加速Spring Cloud中对服务接口的第一次调用.可是这样只是解决了内部服务间的调用,另外一个问题依然经常困扰我们,那就是网关到内部服务的访问.由于Spring Cloud Zuul的路由转发也是通过Ribbon实现负载均衡的,所以它也会存在第一次调时比较慢的情况.那么这个时候我们要如何设置呢? Zuul中的Eager Load配置 在Spring Cloud Zuul中也提供了一个配置参数来实现earger-load,具体如下

在阿里云容器服务上开发基于Docker的Spring Cloud微服务应用(五)

服务智能路由 本文为阿里云容器服务Spring Cloud应用开发系列文章的第五篇,讨论如何利用Spring Cloud 对 Netflix Zuul支持,完成服务的职能路由功能. 一.在阿里云容器服务上开发Spring Cloud微服务应用 二.部署Spring Cloud应用示例 三.服务发现 四.服务间通信与集成 五.服务智能路由(本文) 六.集中配置管理 七.高可用和容错 八.监控和日志 九.服务的部署和发布策略 使用Zuul构建简单API Gateway 在手机端完成一个功能有可能需要

使用Spring Cloud和Docker构建微服务

本文讲的是使用Spring Cloud和Docker构建微服务,[编者的话]这是系列博文中的第一篇,本文作者使用Spring Cloud和Docker构建微服务平台,文章的例子浅显易懂. 本系列博文主要向大家介绍如何使用Spring Cloud和Docker构建微服务平台. 什么是Spring Cloud? Spring Cloud 是Pivotal提供的用于简化分布式系统构建的工具集.Spring Cloud引入了云平台连接器(Cloud Connector)和服务连接器(Service Co

微服务网关解决方案调研和使用总结

一.什么是网关 1.1 什么是网关 API Gateway(APIGW / API 网关),顾名思义,是出现在系统边界上的一个面向API的.串行集中式的强管控服务,这里的边界是企业IT系统的边界,可以理解为企业级应用防火墙,主要起到隔离外部访问与内部系统的作用.在微服务概念的流行之前,API网关就已经诞生了,例如银行.证券等领域常见的前置机系统,它也是解决访问认证.报文转换.访问统计等问题的. API网关的流行,源于近几年来,移动应用与企业间互联需求的兴起.移动应用.企业互联,使得后台服务支持的