1. 概述
1.1 Zuul 是什么
-
Zuul 包含了对请求的路由和过滤两个最主要的功能
-
其中路由功能负责将外部请求转发到具体的微服务实例上,是实现外部访问统一入口的基础而过滤器功能则负责对请求的处理过程进行干预,是实现请求校验、服务聚合等功能的基础。
-
Zuul 和 Eureka 进行整合,将 Zuul 自身注册为 Eureka 服务治理下的应用,同时从 Eureka 中获得其他微服务的消息,也即以后的访问微服务都是通过 Zuul 跳转后获得。
-
Zuul 服务最终还是会注册进 Eureka
2. Zuul 使用
- 项目已上传至 https://github.com/masteryourself/study-spring-cloud.git
2.1 配置文件
1. pom.xml
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
2. application.properties
# 此实例注册到eureka服务端的 name
spring.application.name=masteryourself-zuul
# 端口号
server.port=9001
# 与 eureka server 交互的地址
eureka.client.service-url.defaultZone=http://masteryourself-eureka-7001.com:7001/eureka
# 此实例注册到 eureka 服务端的唯一的实例 ID
eureka.instance.instance-id=masteryourself-zuul-9001
# 是否显示 IP 地址
eureka.instance.prefer-ip-address=true
# eureka 客户需要多长时间发送心跳给 eureka 服务器,表明它仍然活着,默认为 30 秒
eureka.instance.lease-renewal-interval-in-seconds=10
# eureka 服务器在接收到实例的最后一次发出的心跳后,需要等待多久才可以将此实例删除,默认为 90 秒
eureka.instance.lease-expiration-duration-in-seconds=30
# 请求连接的超时时间,默认的时间为 1000 ms
ribbon.ConnectTimeout=5000
# 请求处理的超时时间
ribbon.ReadTimeout=5000
# 命令执行超时时间,默认1000 ms
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=10000
# 配置 user 微服务地址的路由信息,将 /user 开头的请求转发到 MASTERYOURSELF-USER 微服务上
zuul.routes.user.service-id=MASTERYOURSELF-USER
zuul.routes.user.path=/user/**
# 禁用微服务名方式调用
zuul.ignored-services=MASTERYOURSELF-USER
# http 超时时间
zuul.host.connect-timeout-millis=10000
# socket 超时时间
zuul.host.socket-timeout-millis=10000
2.2 代码
1. ZuulApplication9001
@SpringBootApplication
@EnableEurekaClient
@EnableZuulProxy
public class ZuulApplication9001 {
public static void main(String [] args){
SpringApplication.run(ZuulApplication9001.class, args);
}
}
2.3 效果演示
- 通过 zuul 的路由转发,我们可以直接用 zuul 来访问微服务,比如说之前的地址是 http://192.168.20.1:5001/doOrderByFeignAndHystrix ,那么通过 zuul 访问地址就变成了 http://192.168.20.1:9001/user/doOrderByFeignAndHystrix
3. Zuul 使用自定义过滤器
-
过滤器(filter)是 zuul 的核心组件 zuul 大部分功能都是通过过滤器来实现的。
-
zuul 中定义了 4 种标准过滤器类型,这些过滤器类型对应于请求的典型生命周期。
-
PRE:这种过滤器在请求被路由之前调用。可利用这种过滤器实现身份验证、在 集群中选择请求的微服务、记录调试信息等。
-
ROUTING:这种过滤器将请求路由到微服务。这种过滤器用于构建发送给微服 务的请求,并使用 Apache HttpCIient 或 Netfilx Ribbon 请求微服务
-
POST:这种过滤器在路由到微服务以后执行。这种过滤器可用来为响应添加标准 的 HTTP Header、收集统计信息和指标、将响应从微服务发送给客户端等。
-
ERROR:在其他阶段发生错误时执行该过滤器。
-
3.1 代码
1. LogFilter
@Component
public class LogFilter extends ZuulFilter {
private AtomicInteger count = new AtomicInteger(0);
/**
* 返回过滤器的类型。有 pre、 route、 post、 error 等几种取值,分别对应上文的几种过滤器
*
* @return
*/
@Override
public String filterType() {
return FilterConstants.PRE_TYPE;
}
/**
* 返回一个 int 值来指定过滤器的执行顺序,不同的过滤器允许返回相同的数字
*
* @return
*/
@Override
public int filterOrder() {
return FilterConstants.PRE_DECORATION_FILTER_ORDER;
}
/**
* 返回一个 boolean 值来判断该过滤器是否要执行, true 表示执行, false 表示不执行
* @return
*/
@Override
public boolean shouldFilter() {
return true;
}
@Override
public Object run() throws ZuulException {
RequestContext currentContext = RequestContext.getCurrentContext();
if (count.getAndIncrement() % 3 == 0) {
// 不会继续往下执行 不会调用服务接口了 网关直接响应给客户了
currentContext.setSendZuulResponse(false);
currentContext.setResponseBody("{\"code\": \"400\",\"mg\": \"you were denied\"}");
currentContext.setResponseStatusCode(401);
return null;
}
HttpServletRequest request = currentContext.getRequest();
String remoteAddr = request.getRemoteAddr();
System.out.println("访问者IP:【" + remoteAddr + "】访问地址:【" + request.getRequestURI() + "】");
return null;
}
}
3.2 禁用 Filter
- 禁用 zuul 过滤器,Spring Cloud 默认为 Zuul 编写并启用了一些过滤器,例如 DebugFilter、 FormBodyWrapperFilter 等,这些过滤器都存放在
spring-cloud-netflix-core
这个 jar 包 里,一些场景下,想要禁用掉部分过滤器,该怎么办呢? 只需在 application.properties 里设置zuul...disable=true
,例如禁用上面的 LogFilter:zuul.LogFilter.pre.disable=true
4. Zuul 容错与回退
4.1 代码
1. MyFallbackProvider
@Component
public class MyFallbackProvider implements FallbackProvider {
@Override
public String getRoute() {
//指定为哪个微服务提供回退(这里写微服务名 写 * 代表所有微服务)
return "MASTERYOURSELF-USER";
}
@Override
public ClientHttpResponse fallbackResponse(String route, Throwable cause) {
//这里可以判断根据不同的异常来做不同的处理, 也可以不判断
//完了之后调用response方法并根据异常类型传入HttpStatus
if (cause instanceof HystrixTimeoutException) {
return response(HttpStatus.GATEWAY_TIMEOUT);
} else {
return response(HttpStatus.INTERNAL_SERVER_ERROR);
}
}
private ClientHttpResponse response(final HttpStatus status) {
//这里返回一个ClientHttpResponse对象 并实现其中的方法,关于回退逻辑的详细,便在下面的方法中
return new ClientHttpResponse() {
@Override
public HttpStatus getStatusCode() throws IOException {
//返回一个HttpStatus对象 这个对象是个枚举对象, 里面包含了一个status code 和reasonPhrase信息
return status;
}
@Override
public int getRawStatusCode() throws IOException {
//返回status的code 比如 404,500等
return status.value();
}
@Override
public String getStatusText() throws IOException {
//返回一个HttpStatus对象的reasonPhrase信息
return status.getReasonPhrase();
}
@Override
public void close() {
//close的时候调用的方法, 讲白了就是当降级信息全部响应完了之后调用的方法
}
@Override
public InputStream getBody() throws IOException {
//吧降级信息响应回前端
return new ByteArrayInputStream("降级信息".getBytes());
}
@Override
public HttpHeaders getHeaders() {
//需要对响应报头设置的话可以在此设置
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
return headers;
}
};
}
}
4.2 效果展示
- 把 masteryourself-user 微服务关掉,然后调用 http://192.168.20.1:9001/user/doOrderByFeignAndHystrix ,可看到浏览器显示如下内容