写在前面
本篇文章仅作为近日参考其他文章后,自己实践的记录和总结,场景到细节尚有很多不足,有待补充和修正。
概述
Zuul使用一系列不同类型的过滤器,使我们能够快速灵活地将功能应用于我们的边缘服务。 这些过滤器可帮助我们执行
以下功能:
身份验证和安全 - 识别每个资源的身份验证要求,并拒绝不满足他们的请求。
洞察和监测 - 跟踪边缘的有意义的数据和统计数据,以便我们准确地了解生产情况。
动态路由 - 根据需要将请求动态路由到不同的后端集群。
压力测试 - 逐渐增加到集群的流量,以衡量性能。
负载分配 - 为每种类型的请求分配容量并删除超出限制的请求。
静态响应处理 - 直接在边缘构建一些响应,而不是将它们转发到内部集群
多区域弹性 - 跨AWS地区的路由请求,以使我们的ELB使用多样化
简单使用
zuul网关的使用往往需要新建一个单独的微服务作为网管服务,新建服务首先需要引入依赖
<dependencies>
<!--zuul核心依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zuul</artifactId>
</dependency>
<!--需要单独引入eureka client依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
</dependencies>
然后简单配置一下yml文件即可
spring:
application:
name: common-zuul
# profiles:
# active: zuul1
server:
port: 8888
eureka:
client:
serviceUrl:
defaultZone: http://user:[email protected]:8761/eureka
instance:
instance-id: ${spring.application.name}:${spring.cloud.client.ipAddress}:${server.port}
这个时候就可以直接启动并使用网关服务啦。
zuul默认以其他微服务的spring.application.name作为路径前缀
比如先现有微服务 consumer-a :
spring:
application:
name: consumer-a
该微服务提供接口为:
@GetMapping("/c/getByFeign/{id}")
public String getByFeign(@PathVariable String id) {
return producerFeign.get(id);
}
则直接访问上面的接口路径应为http://127.0.0.1:8888/c/getByFeign/{id}
通过默认网关的方式路径应为http://127.0.0.1:8888/consumer-a/c/getByFeign/{id}
开启日志打印
为了查看网关路由转换的处理详情,我们仅须在yml文件添加如下配置即可
logging:
level:
org.springframework.cloud.netflix.zuul: debug
zuul反向代理
上面我们了解了zuul默认的代理方式,下面是通过yml配置实现更多样的代理方式
- 为指定的微服务添加代理规则(方式一)
zuul:
routes:
#单独设置某一个服务的反向代理规则
consumer-a: /consumer/**
按照上面这样设置,那么
除了默认的 http://127.0.0.1:8888/consumer-a/c/getByFeign/{id} 路径可以访问之外,也可以使用 http://127.0.0.1:8888/consumer/c/getByFeign/{id} 路径访问
-
为指定的微服务添加代理规则(方式二)
按照这种方式可以为添加的规则命名
zuul:
routes:
#单独设置某一个服务的反向代理规则,名字可以随便,只要不重复即可
consumer1:
#代理路径
path: /consumer1/**
#需要反向代理的serviceId
serviceId: consumer
#单独设置某一个服务的反向代理规则,名字可以随便,只要不重复即可
consumer2:
#代理路径
path: /consumer2/**
#需要反向代理的serviceId
serviceId: consumer
- 为指定的服务实例(ip+port)代理
zuul:
routes:
#单独设置某一个服务的反向代理规则,名字可以随便,只要不重复即可
byIp:
#代理路径
path: /comsumerIp/**
#可以不是用serviceId,而是使用需要反向代理的服务地址
#使用这种url的方式将不会支持Hystrix断路器和Ribbon负载均衡
url: http://127.0.0.1:9010
按照上面的配置方式,例如请求网管地址 http://127.0.0.1:8888 /consumerIp/c/getByFeign/{id} 时,将会转到地址 http://127.0.0.1:9010/c/getByFeign/{id}
- 为所有服务的代理路径添加前缀
zuul:
#给所有的代理路径增加前缀,需要使用/api/${serviceId}/**进行访问
prefix: /api
#是否保留额外的前缀,默认为true,设置为false将会移除zuul.prefix的配置
#strip-prefix: false
- 关闭默认的代路径
上面我们可以知道默认以代理微服务的spring.application.name值作为代理路径前缀,可以通过下面的yml配置关闭默认的代理路径,需要注意的是,如果关闭了服务的默认代理路径却不为其设置自定义的代理路径,那么zuul网关代理将无法转发该微服务的请求
zuul:
#设置某几个service不使用默认的反向代理
#ignored-services: consumer1,consumer2
#设置所有service都不使用默认的反向代理
ignored-services: '*'
zuul监控端点
通过访问微服务的指定路径,即可查看zull网关服务代理的节点信息有哪些。
观察spring-cloud-starter-zuul依赖的内部关系,我们不难发现zuul网关服务默认自带spring cloud actuator安全组件。
因此,为了方便查看监控端点,我们可以先关闭安全控制(不关闭的话访问监控端点会提示401,不关闭的情况下如何访问,不属于zuul范畴,有待更深入探究),yml配置如下:
management:
security:
#关闭安全设置,默认为true, 否则无法访问/routes管理端点
#使用如下两个地址可以查看zuul的代理信息
#/routes
#/routes?format=details
enabled: false
访问路径 http://127.0.0.1:8888/routes 可以查看代理的概览
访问路径 http://127.0.0.1:8888/routes?format=details 可以查看代理的详细信息
zuul过滤器
首先了解一下zuul网关服务请求的生命周期
根据生命周期我们可以看出zuul过滤器主要分为四类
filterType | 说明 |
---|---|
pre | 在收到请求后最先执行 |
route | 将请求路径转化为实际的目标服务请求路径时用到的过滤器 |
post | 目标服务返回结果后用到的过滤器 |
error | 请求发生异常时,用到的过滤器 |
zuul过滤器的核心接口类为com.netflix.zuul.ZuulFilter,概览如下
public abstract class ZuulFilter implements IZuulFilter, Comparable<ZuulFilter> {
private final DynamicBooleanProperty filterDisabled = DynamicPropertyFactory.getInstance().getBooleanProperty(this.disablePropertyName(), false);
public ZuulFilter() {
}
public abstract String filterType();
public abstract int filterOrder();
......
}
包括实现类com.netflix.zuul.IZuulFilter
package com.netflix.zuul;
public interface IZuulFilter {
boolean shouldFilter();
Object run();
}
从上面可以看出,zuul过滤器核心的四个方法为
实现方法 | 说明 |
---|---|
filterType | 返回当前过滤器的过滤器类型 |
filterOrder | 返回当前过滤器的过滤器的执行顺序 |
shouldFilter | 用于判断过滤器是否执行 |
run | 执行过滤器的实际操作 |
zuul过滤器执行操作时,主要是围绕着请求的上下文对象(com.netflix.zuul.context.RequestContext),主要的过滤器如下:
过滤器具体的执行逻辑,可以参考实现类源码的四个关键方法
下面是手动禁用zuul自带的核心过滤器的方法,以DebugFilter为例
zuul:
#过滤器名称
DebugFilter:
#过滤器的filterType
pre:
disable: false
自定义过滤器
在了解zuul过滤器工作原理之后,我们可以自己定义一个或多个过滤器来实现我们的想法。其中关键点是要实现com.netflix.zuul.ZuulFilter接口类,并使用@Component或其他方式将过滤器注入到spring容器。
其次便是了解com.netflix.zuul.context.RequestContext类的使用,下面将列举出部分重要的方法并说明,更有待补充
方法 | 说明 |
---|---|
set(String key) | 设置一个属性,key为属性名称,属性值默认为true |
set(String key, Object value) | 设置一个属性,key为属性名称,属性值默认为value |
getRequest() | 获取HttpServletRequest对象 |
setRequest(HttpServletRequest request) | 设置HttpServletRequest对象 |
getResponse() | 获取HttpServletResponse对象 |
setResponse(HttpServletResponse response) | 设置HttpServletResponse对象 |
sendZuulResponse() | 判断是否经过route环节,为false的话,将不会转发请求到微服务 |
setSendZuulResponse(boolean bSend) | 设置是否经过route环节,为false的话,将不会转发请求到微服务 |
getResponseStatusCode() | 获取响应编码 |
setResponseStatusCode(int nStatusCode) | 设置响应编码 |
zuul使用hystrix
zuul默认使用hystrix
那么如何设置fallback(服务降级)呢?首先来了解org.springframework.cloud.netflix.zuul.filters.route.ZuulFallbackProvider接口类
package org.springframework.cloud.netflix.zuul.filters.route;
import org.springframework.http.client.ClientHttpResponse;
/**
* Provides fallback when a failure occurs on a route.
* @author Ryan Baxter
* @deprecated Use {@link FallbackProvider}
*/
@Deprecated
public interface ZuulFallbackProvider {
/**
* The route this fallback will be used for.
* @return The route the fallback will be used for.
*/
public String getRoute();
/**
* Provides a fallback response.
* @return The fallback response.
*/
public ClientHttpResponse fallbackResponse();
}
主要方法包括
实现方法 | 说明 |
---|---|
getRoute | 返回路由名称,及微服务的名称,返回所有微服务则返回 * |
fallbackResponse | 异常时返回的响应对象 |
ps:可以发现该类为Deprecated,而现在应该使用什么类来替代尚且不知道,有待补充
下面是fallbak样例
@Component
public class ConsumerZuulFallbackProvider implements ZuulFallbackProvider {
@Override
public String getRoute() {
//只针对一个微服务,针对所有微服务时使用 return "*";
return "consumer";
}
@Override
public ClientHttpResponse fallbackResponse() {
return new ClientHttpResponse() {
@Override
public InputStream getBody() throws IOException {
return new ByteArrayInputStream("fallback".getBytes());
}
@Override
public HttpHeaders getHeaders() {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
return headers;
}
@Override
public HttpStatus getStatusCode() throws IOException {
return HttpStatus.OK;
}
@Override
public int getRawStatusCode() throws IOException {
return 200;
}
@Override
public String getStatusText() throws IOException {
return "OK";
}
@Override
public void close() {
}
};
}
}
补充说明几点:
- 实现类须要使用@Component注解,以将其注入到spring容器
- 可以配置多个fallback类,如果同时存在针对所有微服务的fallback和针对某个微服务的fallback,将会以精确的为准,针对所有微服务的fallback(getRoute方法返回*)只视为默认的服务降级类