当前位置 博文首页 > RtxTitanV的博客:SpringCloud 服务网关Zuul Hoxton版本
Spring Cloud Zuul简介:Spring Cloud Zuul是Spring Cloud Netflix子项目的核心组件之一,作为微服务API网关,具有动态路由、过滤、压力测试、监控、弹性伸缩和安全等功能,并且能够与Eureka、Ribbon、Hystrix等组件配合使用。
本文主要对Spring Cloud Zuul的基本使用进行简单总结,其中SpringBoot使用的2.2.2.RELEASE
版本,SpringCloud使用的Hoxton.SR1
版本。这里将沿用SpringCloud 服务注册与发现Eureka Hoxton版本的eureka-server
作为注册中心,eureka-client
作为服务生产者,还有ribbon
、hystrix
和openfeign
这3个服务也都是前几篇文章中创建的。
通过Maven新建一个名为spring-cloud-netflix-zuul
的项目。
SpringBoot和SpringCloud依赖这里就不列出来了,还需引入以下依赖,其中spring-retry
在配置重试的时候用,这里提前引入了。
<!-- Spring Cloud Zuul 起步依赖 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
<!-- Spring Cloud Eureka Client 起步依赖 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!-- Actuator 起步依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- SpringRetry 重试框架依赖 -->
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
</dependency>
package com.rtxtitanv;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
/**
* @author rtxtitanv
* @version 1.0.0
* @name com.rtxtitanv.ZuulApplication
* @description 主启动类
* @date 2020/3/2 12:06
*/
@EnableZuulProxy
@SpringBootApplication
public class ZuulApplication {
public static void main(String[] args) {
SpringApplication.run(ZuulApplication.class, args);
}
}
@EnableZuulProxy
:启用Zuul的API网关功能。
在application.yml
中进行如下配置:
server:
port: ${PORT:1500}
spring:
application:
name: zuul
eureka:
client:
# 服务注册,是否将服务注册到Eureka注册中心,true:注册,false:不注册
register-with-eureka: true
# 服务发现,是否从Eureka注册中心获取注册信息,true:获取,false:不获取
fetch-registry: true
# 配置Eureka注册中心即Eureka服务端的地址,集群地址以,隔开
service-url:
defaultZone: http://rtxtitanv:rtxtitanv@eureka-server-01:8001/eureka/,http://rtxtitanv:rtxtitanv@eureka-server-02:8002/eureka/,http://rtxtitanv:rtxtitanv@eureka-server-03:8003/eureka/
instance:
# 将ip地址注册到Eureka注册中心
prefer-ip-address: true
# 该服务实例在注册中心的唯一实例ID,${spring.cloud.client.ip-address}获取该服务实例ip
instance-id: ${spring.application.name}:${spring.cloud.client.ip-address}:${server.port}
# 该服务实例向注册中心发送心跳间隔,单位秒,默认30秒
lease-renewal-interval-in-seconds: 20
# Eureka注册中心在删除此实例之前收到最后一次心跳后的等待时间,单位秒,默认90秒
lease-expiration-duration-in-seconds: 60
management:
endpoints:
web:
exposure:
# 暴露指定端点,routes为路由端点
include: 'routes'
这时候一个基本的服务网关就搭建好了,由于通过SpringBoot Actuator可以查看Zuul的路由信息,所以引入了spring-boot-starter-actuator
依赖并配置了management.endpoints.web.exposure.include='routes'
暴露路由端点。IDEA启动eureka-server
集群,eureka-client
集群和ribbon
,hystrix
,openfeign
和zuul
,访问注册中心,下图为服务注册信息:
访问http://localhost:1500/actuator/routes查看路由信息:
访问http://localhost:1500/actuator/routes/details查看详细路由信息:
由于Zuul里面集成了Ribbon,所以可以进行Ribbon的配置和通过Ribbon实现负载均衡。不断访问http://localhost:1500/eureka-client/home,根据下面动图中的测试过程和结果,说明成功路由到了eureka-client
服务的/home
接口并实现了负载均衡。
在没有显式配置路由规则时依然能查看到路由信息并实现路由功能,这是因为Zuul结合Eureka使用时,它为Eureka中的每一个服务都自动创建一个默认路由规则,这些默认规则的path
会使用serviceId
配置的服务名作为请求前缀,相当于以下配置:
zuul:
# 路由配置
routes:
# serviceName为路由名
serviceName:
path: /serviceName/**
serviceId: serviceName
默认情况Zuul会为所有Eureka服务自动创建映射关系来进行路由,这会使不希望对外开放的服务也可以被外部访问到,这时候需要在自动创建默认路由规则时忽略那些不希望对外开放的服务。可以通过zuul.ignored-services
设置一个服务名匹配表达式来指定需要忽略的服务,Zuul会在自动创建服务路由时会根据该表达式判断,如果服务名匹配则跳过不创建默认路由规则。下面在application.yml
中新增以下配置,指定忽略默认路由规则的服务:
zuul:
# 指定忽略默认路由规则的服务,*表示所有服务都不创建默认路由规则
ignored-services: eureka-server,eureka-client,ribbon,hystrix,openfeign
访问http://localhost:1500/actuator/routes查看路由信息,下图为路由信息,为空说明指定服务都成功忽略默认路由规则。
传统路由配置就是不依赖于服务发现机制的方式,直接在配置文件中指定每个路由表达式与服务实例的映射关系来实现API网关对外请求的路由。
通过zuul.routes.<route>.path
与zuul.routes.<route>.url
的方式进行配置,其中<route>
为路由名称,不能有相同的路由名称,每个<route>
对应了一条路由规则,即匹配客户端请求的路径表达式与具体实例地址或服务名的映射。下面在application.yml
中进行如下配置:
zuul:
# 传统路由配置之单实例配置
routes:
# 配置路由规则,route-eureka-client为路由名,每一个路由名对应一条路由规则
# 路由规则为将与/eureka-client-api/**匹配的请求路径转发到http://localhost:9001/
route-eureka-client:
# 指定匹配客户端请求的路径表达式
path: /eureka-client-api/**
# 指定匹配客户端请求的路径表达式映射的具体实例地址
url: http://localhost:9001/
不断访问http://localhost:1500/eureka-client-api/home,根据下面动图中的测试过程和结果,说明成功路由到了eureka-client
服务的9001端口节点的/home
接口。
通过zuul.routes.<route>.path
与zuul.routes.<route>.serviceId
的方式进行配置并通过ribbon.eureka.enabled=false
停用Eureka,<client>.ribbon.listOfServers
手动配置服务列表,这里的<client>
与serviceId
对应。下面在application.yml
中进行如下配置:
zuul:
# 传统路由配置之多实例配置
routes:
# 配置路由规则,route-eureka-client为路由名,每一个路由名对应一条路由规则
# 路由规则为将与/eureka-client-api/**匹配的请求路径转发到eureka-client
route-eureka-client:
# 指定匹配客户端请求的路径表达式
path: /eureka-client-api/**
# 指定匹配客户端请求的路径表达式映射的服务名
serviceId: eureka-client
ribbon:
eureka:
# 是否使用Eureka,true:使用,false:禁用,默认为true,禁用后需手动配置服务列表
enabled: false
eureka-client:
ribbon:
# 禁用Eureka后手动配置服务列表
listOfServers: localhost:9001,localhost:9002,localhost:9003
不断访问http://localhost:1500/eureka-client-api/home,根据下面动图中的测试过程和结果,说明成功路由到了eureka-client
服务的/home
接口并实现了负载均衡。
通过zuul.routes.<route>.path
与zuul.routes.<route>.serviceId
的方式进行配置,由于Zuul结合使用了Eureka,所以不需要手动指定服务地址列表。下面在application.yml
中进行如下配置:
zuul:
# 服务路由配置
routes:
# 配置路由规则,route-eureka-client为路由名,每一个路由名对应一条路由规则
# 路由规则为将与/eureka-client-api/**匹配的请求路径转发到eureka-client
route-eureka-client:
# 指定匹配客户端请求的路径表达式
path: /eureka-client-api/**
# 指定匹配客户端请求的路径表达式映射的服务名
serviceId: eureka-client
以上配置还有一种更简单的写法,通过zuul.routes.<serviceId>=<path>
来实现,其中<serviceId>
为服务名称,<path>
为匹配客户端请求的表达式。下面在application.yml
中进行如下配置:
zuul:
routes:
# 服务路由配置的简写方式,eureka-client为服务名,/eureka-client-api/**为匹配客户端请求的路径表达式
eureka-client: /eureka-client-api/**
不断访问http://localhost:1500/eureka-client-api/home,根据下面动图中的测试过程和结果,说明成功路由到了eureka-client
服务的/home
接口并实现了负载均衡。
如果想自定义路由映射规则,可以使用regexmapper
在serviceId
和路由之间提供约定。它使用名为groups的正则表达式从serviceId
中提取变量并将它们注入到路由模式中。下面是一个自定义路由映射规则的配置:
@Bean
public PatternServiceRouteMapper serviceRouteMapper() {
return new PatternServiceRouteMapper(
"(?<name>^.+)-(?<version>v.+$)",
"${version}/${name}");
}
根据以上配置,如果serviceId
为appname-v1
,会被映射到路由/v1/appname/**
。任何正则表达式都被接受,但所有命名组都必须存在于servicePattern
和routePattern中
。如果servicePattern
与serviceId
不匹配,则使用默认规则。比如serviceId
为appname
,检测不到版本,会被映射到/appname/**
,使用默认规则,仅适用于已发现的服务。具体的测试这里就省略了。
在Zuul中,匹配客户端请求的路径表达式<path>
有三种通配符,?
匹配任意单个字符,*
匹配任意数量的字符,**
匹配任意数量的字符并支持多及目录。
当一个url路径被多个不同路由规则的表达式匹配,匹配结果取决于路由规则的保存顺序,即路由规则配置的顺序,优先级按路由规则配置的顺序由高到低。由于需要路由规则的保存顺序,需要使用YAML文件,因为properties文件会丢失路由规则的保存顺序。例如以下配置,先判断url是否与/eureka-client-api/a/**
匹配,匹配就选择/eureka-client-api/a/**
路由,如果不匹配就判断是否与/eureka-client-api/**
匹配,匹配就选择/eureka-client-api/**
路由,不匹配就往下依次判断直到最后一个路由规则。
zuul:
routes:
hello-service-ext:
path: /eureka-client-api/a/**
serviceId: eureka-client-a
hello-service:
path: /eureka-client-api/**
serviceId: eureka-client
通过zuul.ignored-patterns
可以指定匹配忽略客户端请求的路径表达式,与这些表达式匹配的请求路径不会被API网关进行路由转发,该配置对所有路由有效。下面在application.yml
中进行如下配置:
zuul:
# 指定匹配忽略客户端请求的路径表达式,对所有路由有效
ignored-patterns: /**/home/**
访问http://localhost:1500/eureka-client-api/home,结果见下图,访问失败说明忽略成功。
通过zuul.prefix
可以为全局的路由规则增加前缀,指定了路由前缀之后在访问API网关时需要加上前缀。下面在application.yml
中进行如下配置:
zuul:
# 指定路由前缀,为全局的路由规则增加前缀信息
prefix: /api
访问http://localhost:1500/api/eureka-client-api/home,根据下图中的测试结果,说明成功路由到了eureka-client
服务的/home
接口。
前缀在代理转发时会默认从路径中移除,可以通过zuul.stripPrefix=false
指定全局的路由规则在代理转发时不移除前缀,也可以通过zuul.routes.<route>.strip-prefix=false
来指定该路由规则在代理转发时不移除前缀。下面在application.yml
中进行如下配置:
zuul:
# 指定全局的路由规则在转发时是否移除前缀,true:移除,false:不移除
strip-prefix: false
访问http://localhost:1500/api/eureka-client-api/home,结果见下图,访问失败说明转发时没有移除前缀。
通过zuul.routes.<route>.url
中配置forward形式的路径可以实现本地跳转。下面在application.yml
中新增以下配置:
zuul:
routes:
# 以forward形式的服务跳转配置
# 路由规则为将与/zuul-api/**匹配的请求路径转发到API网关中以/local为前缀的请求
route-local:
# 指定匹配客户端请求的路径表达式