11 Zuul学习
11.1 为什么要使用微服务网关
经过上面的学习,微服务架构已经初具雏形,但还有一些问题,不同的微服务一般会有不同的网络地址,而外部客户端(例如手机APP)可能需要调用多个服务的接口才能完成一个业务需求。
例如一个电影购票的手机APP,可能会调用多个微服务的接口,才能完成一次购票的业务流程。
如果让客户端直接与各个微服务通信,会有以下的问题:
- 客户端会多次请求不同的微服务,增加了客户端的复杂性。
- 存在跨域请求,在一定场景下处理相对复杂。
- 认证复杂,每个服务都需要独立认证。
- 难以重构,随着项目的迭代,可能需要重新划分微服务。例如,可能将多个服务合并成一个或者将一个服务拆分成多个。如果客户端直接与微服务通信,那么重构将会很难实施。
- 某些微服务可能使用了防火墙/浏览器不友好的协议,直接访问会有一定的困难。
以上问题可借助微服务网关解决。微服务网关是介于客户端和服务器端之间的中间层, 所有的外部请求都会先经过微服务网关。
使用微服务网关后,架构可演变成下图:
如图,微服务网关封装了应用程序的内部结构,客户端只须跟网关交互,而无须直接调 用特定微服务的接口。
这样,开发就可以得到简化。不仅如此,使用微服务网关还有以下优点:
- 易于监控。可在微服务网关收集监控数据并将其推送到外部系统进行分析。
- 易于认证。可在微服务网关上进行认证,然后再将请求转发到后端的微服务,而无须在每个微服务中进行认证。
- 减少了客户端与各个微服务之间的交互次数。
11.2 Zuul简介
Zuul是Netflix开源的微服务网关,它可以和Eureka、Ribbon、Hystrix等组件配合使用。
Zuul的核心是一系列的过滤器,这些过滤器可以完成以下功能。
- 身份认证与安全;识别每个资源的验证要求,并拒绝那些与要求不符的请求。
- 审查与监控:在边缘位置追踪有意义的数据和统计结果,从而带来精确的生产视图。
- 动态路由:动态地将请求路由到不同的后端集群。
- 压力测试:逐渐增加指向集群的流量,以了解性能。
- 负载分配:为每一种负载类型分配对应容量,并弃用超岀限定值的请求。
- 静态响应处理:在边缘位置直接建立部分响应,从而避免其转发到内部集群。
- 多区域弹性:跨越AWS Region进行请求路由,旨在实现ELB ( Elastic Load Balancing) 使用的多样化,以及让系统的边缘更贴近系统的使用者。
Spring Cloud对Zuul进行了整合与增强。目前,Zuul使用的默认HTTP客户端是Apache HTTPClient,也可以使用 RestClient 或者okhttp3.OkHttpClient。
如果想要使用 RestClient, 可以设置ribbon.restclient.enabled=true;
想要使用okhttp3.OkHttpClient,可以设置 ribbon .okhttp.enabled= true。
11.3 编写微服务网关
新建一个Maven工程microservice-gateway-zuul,pom.xml 如下:
<?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>edu.xja</groupId>
<artifactId>microservice-gateway-zuul</artifactId>
<version>1.0-SNAPSHOT</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.2.RELEASE</version>
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<spring-cloud.version>Hoxton.SR7</spring-cloud.version>
</properties>
<dependencies>
<!--引入网关依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
</dependencies>
<!--lombok所有的子工程都有,而下面的需要在子工程中手动添加。-->
<dependencyManagement>
<dependencies>
<!--springcloud-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
edu.xja 下新建启动类,ZuulApplication,代码如下:
@SpringBootApplication
@EnableZuulProxy
public class ZuulApplication {
public static void main(String[] args) {
SpringApplication.run(ZuulApplication.class,args);
}
}
代码说明:
在启动类上添加注解@EnableZuulProxy ,声明一个Zuul代理。
该代理使用Ribbon来定位注册在Eureka Server中的微服务;
同时,该代理还整合了 Hystrix,从而实现了容错,所有经过Zuul的请求都会在Hystrix命令中执行。
新建application.yml
server:
port: 8060
spring:
application:
name: microservice-gateway-zuul
# 网关会注册到注册中心
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
这样,一个简单的微服务网关就编写完成了。
从配置可知,此时仅是添加了Zuul的依赖,并将Zuul注册到Eureka Server上。
测试步骤:
启动项目 注册中心 eureka-server
启用服务提供者 microservice-provider-user
启动服务消费者 microservice-conumser-movie
启动网关 microservice-gateway-zuul
访问http://localhost:8060/user-provider/user/3
访问http://localhost:8060/consumer-movie/user/5
说明默认情况下,Zuul会代理所有注册到Eureka Server的微服务,并且Zuul的路由规则如下:
http://ZUUL_HOST:ZUUL_PORT/微服务在Eureka 注册中心上的serviced/** 会被转发到 serviceld 对应的微服务。
11.4 管理端点
当@EnableZuulProxy与Spring Boot Actuator配合使用时,Zuul会暴露两个端点:
一个路由管理端点/actuator/routes和/actuator/filters,借助这两个端点,可以方便、直观地查看以及管理Zuul的路由。
查看端点步骤:
spring-cloud-starter-netflix-zuul已经包含了spring-boot-starter-actuator,因此不需再次引入
11.4.1 查看端点步骤
修改application.yml,暴露端点
management:
endpoints:
web:
exposure:
include: "*"
重启网关
访问:http://localhost:8060/actuator/hystrix.stream 成功,
说明已经zuul已经整合了hystrix。
11.4.2 查看routes端点
访问:http://localhost:8060/actuator/routes 可以查看路由设置
11.4.3 查看filters端点
访问:http://localhost:8060/actuator/filters 可以查看过滤器端点
从SpringCloud Edgware版本开始,Zuul提供了filters端点。
访问该端点即可返回Zuul 中当前所有过滤器的详情,并按照类型分类。上面是filters端点的展示结果,从中,我们可以了解当前Zuul中,error、post、pre、route四种类型的过滤器分别有哪些,每个过滤器的order(执行顺序)是多少,以及是否启用等信息。这对Zul问题的定位很有用
11.5 路由配置详解
前文已经编写了一个简单的Zuul网关,并让该网关代理了所有注册到Eureka Server的 微服务。但在现实中可能只想让Zuul代理部分微服务,又或者需要对URL进行更加精确的控制。
Zuul的路由配置非常灵活、简单,下面通过几个示例,详细讲解Zuul的路由配置。
1.自定义指定微服务的访问路径
配置zul.routes.指定微服务的serviceId=指定路径 即可。例如∶
zuul:
routes:
user-provider: /usercenter/**
完成设置后,user-provider微服务就会被映射到/usercenter/*路径。
2.忽略指定微服务
忽略服务非常简单,可以使用zul.ignored-services配置需要忽略的服务,多个服间用逗号分隔。例如∶
zuul:
ignored-services: user-provider,consumer-movie
这样就可让Zuul忽略 user-provider和 consumer-movie微服务,只代理其他微服务。
3忽略所有微服务,只路由指定微服务
很多场景下,可能只想要让Zul代理指定的微服务,此时可以将zuul.ignored-services
设为’*’。
zuul:
ignored-services: '*' #使用'*'可忽略所有微服务
routes:
user-provider: /user/**
这样就可以让Zul只路由user-providerr微服务。
4同时指定微服务的serviceld和对应路径。例如∶
zuul:
routes:
#该配置方式中,user-route只是给路由一个名称,可以任意起名。
user-route:
service-id: user-provider
# service-id对应的路径
path: /user/**
11.6 Zuul的过滤器
过滤器是Zuul的核心组件,下面来学习一下Zuul的过滤器。
11.6.1 过滤器类型与请求生命周期
Zuul大部分功能都是通过过滤器来实现的。Zuul中定义了4种标准过滤器类型,这些过滤器类型对应于请求的典型生命周期。
- PRE:这种过滤器在请求被路由之前调用。可利用这种过滤器实现身份验证、在集群中选择请求的微服务、记录调试信息等。
- ROUTING:这种过滤器将请求路由到微服务。这种过滤器用于构建发送给微服务的请求,并使用Apache HttpClient或Netflix Ribbon请求微服务。
- POST:这种过滤器在路由到微服务以后执行。这种过滤器可用来为响应添加标准的 HTTP Header.收集统计信息和指标、将响应从微服务发送给客户端等。
- ERROR:在其他阶段发生错误时执行该过滤器。
除了默认的过滤器类型,Zuul还允许创建自定义的过滤器类型。例如,可以定制一种 STATIC类型的过滤器,直接在Zuul中生成响应,而不将请求转发到后端的微服务。
Zuul请求的生命周期如下图:
11.6.2 内置过滤器
Spring Cloud 默认为Zuul编写并启用了一些过滤器,这些过滤器有什么用呢?我们结合
@EnableZuulServer 和@EnableZuulProxy 两个注解来学习。
可将@EnableZuulProxy 简单理解为@EnableZuulServer的增强版。事实上,当Zuul与Eureka、Ribbon等组件配合使用时,@EnableZuulProxy 是我们最常用的注解。
@EnableZuulServer 和@EnableZuulProxy 所启用的过滤器,下面类型的过滤器都有。
- pre类型过滤器
- route类型过滤器
- post类型过滤器
- error类型过滤器
11.6.3 编写Zuul过滤器
理解过滤器类型和请求生命周期后,来编写一个Zuul过滤器。编写Zuul的过滤器非常简单,只须继承抽象类ZuulFilter,然后实现几个抽象方法就可以了。
下面来编写一个简单的Zuul过滤器,让该过滤器打印请求日志。
1.复制项目microservice-gateway-zuul, 将 Artifactld修改为microservice-gateway-zuul- filter。
2.编写自定义Zuul过滤器。
@Slf4j
public class MyZuulFilter extends ZuulFilter {
@Override
public String filterType() {
return FilterConstants.PRE_TYPE;
}
@Override
public int filterOrder() {
return FilterConstants.PRE_DECORATION_FILTER_ORDER - 1;
}
@Override
public boolean shouldFilter() {
return true;
}
@Override
public Object run() throws ZuulException {
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
log.info(String.format("send %s request to %s", request.getMethod(), request.getRequestURL().toString()));
return null;
}
}
由代码可知,自定义的Zuul Filter需实现以下几个方法。
- filterType:返回过滤器的类型。有pre、route、post、error等几种取值,分别对应上文的几种过滤器。详细可以参考com.netflix.zuul.ZuulFilter.filterType() 中的注释。
- filterOrder:返回一个int值来指定过滤器的执行顺序,不同的过滤器允许返回相同的数字。
- shouldFilter :返回一个boolean值来判断该过滤器是否要执行,true表示执行, false表示不执行。
- run:过滤器的具体逻辑。本例中让它打印了请求的HTTP方法以及请求的地址。
3。修改启动类,为启动类添加以下内容:
@Bean
public MyZuulFilter preRequestLogFilter() {
return new MyZuulFilter ();
}
测试步骤
启动注册中心
启动服务提供者
启动microservice-gateway-zuul- filter
访问http://localhost:8050/user-provider/user/1
上面只是演示了一个非常简单的过滤器。事实上,我们可以使用过滤器做很多事情,例如安全认证、灰度发布、限流等。
问题:
1、经过过滤器后,如果是一个非法访问,如果直接返回信息,而不是把请求发到后面的微服务上?
2、自学使用Spring Cloud Gateway 搭建网关。