服务网关 Spring Cloud Zuul

本文涉及的产品
传统型负载均衡 CLB,每月750个小时 15LCU
应用型负载均衡 ALB,每月750个小时 15LCU
EMR Serverless StarRocks,5000CU*H 48000GB*H
简介: (git上的源码:https://gitee.com/rain7564/spring_microservices_study/tree/master/fifth-spring-cloud-zuul)对于服务网关是什么、有什么用?使用API Gateway这篇文章已经讲得很清楚了,这里就不再赘述。
(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

1.png

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

1.jpg

相关实践学习
SLB负载均衡实践
本场景通过使用阿里云负载均衡 SLB 以及对负载均衡 SLB 后端服务器 ECS 的权重进行修改,快速解决服务器响应速度慢的问题
负载均衡入门与产品使用指南
负载均衡(Server Load Balancer)是对多台云服务器进行流量分发的负载均衡服务,可以通过流量分发扩展应用系统对外的服务能力,通过消除单点故障提升应用系统的可用性。 本课程主要介绍负载均衡的相关技术以及阿里云负载均衡产品的使用方法。
相关文章
|
24天前
|
安全 5G 网络性能优化
|
2天前
|
负载均衡 Java 应用服务中间件
|
1月前
|
前端开发 Java API
vertx学习总结5之回调函数及其限制,如网关/边缘服务示例所示未来和承诺——链接异步操作的简单模型响应式扩展——一个更强大的模型,特别适合组合异步事件流Kotlin协程
本文是Vert.x学习系列的第五部分,讨论了回调函数的限制、Future和Promise在异步操作中的应用、响应式扩展以及Kotlin协程,并通过示例代码展示了如何在Vert.x中使用这些异步编程模式。
45 5
vertx学习总结5之回调函数及其限制,如网关/边缘服务示例所示未来和承诺——链接异步操作的简单模型响应式扩展——一个更强大的模型,特别适合组合异步事件流Kotlin协程
|
30天前
|
XML Java 数据格式
如何使用 Spring Cloud 实现网关
如何使用 Spring Cloud 实现网关
28 3
|
2月前
|
负载均衡 Java 网络架构
实现微服务网关:Zuul与Spring Cloud Gateway的比较分析
实现微服务网关:Zuul与Spring Cloud Gateway的比较分析
79 5
|
2月前
|
测试技术 微服务
微服务(八)-服务网关zuul(四)
微服务(八)-服务网关zuul(四)
|
2月前
|
SpringCloudAlibaba API 开发者
新版-SpringCloud+SpringCloud Alibaba
新版-SpringCloud+SpringCloud Alibaba
|
3月前
|
资源调度 Java 调度
Spring Cloud Alibaba 集成分布式定时任务调度功能
定时任务在企业应用中至关重要,常用于异步数据处理、自动化运维等场景。在单体应用中,利用Java的`java.util.Timer`或Spring的`@Scheduled`即可轻松实现。然而,进入微服务架构后,任务可能因多节点并发执行而重复。Spring Cloud Alibaba为此发布了Scheduling模块,提供轻量级、高可用的分布式定时任务解决方案,支持防重复执行、分片运行等功能,并可通过`spring-cloud-starter-alibaba-schedulerx`快速集成。用户可选择基于阿里云SchedulerX托管服务或采用本地开源方案(如ShedLock)
111 1
|
26天前
|
JSON SpringCloudAlibaba Java
Springcloud Alibaba + jdk17+nacos 项目实践
本文基于 `Springcloud Alibaba + JDK17 + Nacos2.x` 介绍了一个微服务项目的搭建过程,包括项目依赖、配置文件、开发实践中的新特性(如文本块、NPE增强、模式匹配)以及常见的问题和解决方案。通过本文,读者可以了解如何高效地搭建和开发微服务项目,并解决一些常见的开发难题。项目代码已上传至 Gitee,欢迎交流学习。
100 1
Springcloud Alibaba + jdk17+nacos 项目实践
|
14天前
|
消息中间件 自然语言处理 Java
知识科普:Spring Cloud Alibaba基本介绍
知识科普:Spring Cloud Alibaba基本介绍
45 2