1.1 简介
1.1.1 概述
Zuul 包含了对请求的路由和过滤两个最主要的功能:其中路由功能负责将外部请求转发到具体的微服务实例上,是实现外部访问统一入口的基础而过滤器功能则负责对请求的处理过程进行干预,是实现请求校验、服务聚合等功能的基础。Zuul 和 Eureka 进行整合,将 Zuul 自身注册为 Eureka 服务治理下的应用,同时从 Eureka 中获得其他微服务的消息,也即以后的访问微服务都是通过 Zuul 跳转后获得。继 Zuul 1.x 之后奈菲公司想推出 Zuul 2.x 但因为开发人员意见不合迟迟未推出,Spring 官方无法等待就自己研发了 Gateway 替代了 Zuul。Spring Cloud F 版推荐使用 Zuul,之后推荐使用 Gateway。
1.1.2 相关依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
1.2 简单使用
1.2.1 基础工程
☞ 基础工程 搭建完毕之后,创建 zuul 模块
1.2.2 配置文件
spring:
application:
name: zuul-server
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:8081/eureka
instance:
instance-id: zuul-8091
prefer-ip-address: true
lease-renewal-interval-in-seconds: 1
lease-expiration-duration-in-seconds: 2
zuul:
routes:
# 路由名称,随便取
info-service:
# 匹配请求 URL 的规则
path: /info/**
# 请求的服务名
serviceId: info-service
1.2.3 启动类
/**
* Created with IntelliJ IDEA.
*
* @author Demo_Null
* @date 2020/11/9
* @description Zuul 启动类
*/
@SpringBootApplication
@EnableZuulProxy // 开启 zuul 网关
public class ZuulApplication {
public static void main(String[] args) {
SpringApplication.run(ZuulApplication.class, args);
}
}
1.2.4 请求服务
我们可以发现直接请求 info 服务与通过 zuul 请求服务都成功了。当前配置下需要注意几个问题,第一个需要注意的是直接请求的路径是 info/get
而通过 zuul 是 info/info/get
但是服务获取的请求路径又是 info/get
。这是因为通过 zuul 时匹配 path 没有将匹配的前缀带上。第二个需要注意的是通过 zuul 之后在服务端直接通过 request.getRemoteAddr()
获取的远端地址其实是 zuul 的地址而不是真实地址。第三个就是经过网关的头信息会丢失。
1.2.5 Zuul 详细配置
zuul:
# 默认情况下,Eureka 上所有注册的服务都会被 Zuul 创建映射关系来进行路由
# 忽略默认路由,只认我们自己配置的路由,多个服务用逗号分开,使用 * 代表所有服务
ignored-services: xxx-server
# 忽略的接口,屏蔽接口
ignored-patterns: /**/xxx/**
# 添加统一前缀
prefix: /api
# 解决后端服务重定向后地址与网关地址不一致,暴露内部服务地址
add-proxy-headers: true
# 第一个路由 id 随意,一般与服务名称一致
aaa-server:
# 匹配请求 URL 的规则
path: /aaa/**
# 请求的服务名
serviceId: aaa-server
# 是否去掉前缀,即 info/info/get ☞ info/get
strip-prefix: true
# 不向后传递以下敏感头信息
sensitiveHeaders: Cookie,Set-Cookie,Authorization
# 开启转发头信息,被 sensitiveHeaders 禁止传递的不会被转发
use-forward-headers: true
# 第二个路由 id 随意
bbb-server:
# 匹配请求 URL 的规则
path: /bbb/**
# 请求的服务名
serviceId: bbb-server
1.3 Zuul 过滤器
/**
* Created with IntelliJ IDEA.
*
* @author Demo_Null
* @date 2020/11/9
* @description 自定义过滤器
*/
@Component
public class MyZuulFilter extends ZuulFilter {
/**
* 什么时候开始过滤
* @author Demo_Null
* @date 2020/11/9
* @param
* @return java.lang.String
**/
@Override
public String filterType() {
// pre:可以在请求被路由之前调用
// route:在路由请求时候被调用
// error:处理请求时发生错误时被调用
// post:在 route 和 error 过滤器之后被调用
return "pre";
}
/**
* 过滤器优先级
* @author Demo_Null
* @date 2020/11/9
* @param
* @return int
**/
@Override
public int filterOrder() {
// 返回的数值越小优先级越高
return 0;
}
/**
* 是否开启该过滤器
* @author Demo_Null
* @date 2020/11/9
* @param
* @return boolean
**/
@Override
public boolean shouldFilter() {
// 返回 false 表示不启用
return false;
}
/**
* 过滤器具体逻辑
* @author Demo_Null
* @date 2020/11/9
* @param
* @return java.lang.Object
**/
@Override
public Object run() throws ZuulException {
// 过滤器内 return Object 都表示继续执行
// 执行 RequestContext.getCurrentContext().sendZuulResponse(false) 这个请求最终不会被 zuul 转发到后端服务器
// 但是如果当前 Filter 后面还存在其他 Filter,那么其他 Filter 仍然会被调用到
return null;
}
}
1.4 Zuul 回退机制
使用 Zuul 进行请求分发的过程中,当后端服务出现异常时,我们并不希望将异常抛出给最外层暴露给客户端,期望服务可以像 hystrix 中一样能够进行降级操作,提供一个兜底的操作。当某个服务出现异常,直接返回我们的预设信息。要想使用回退机制,我们需要实现 FallbackProvider
接口,ZuulFallbackProvider
接口已经停止使用。
/**
* Created with IntelliJ IDEA.
*
* @author Demo_Null
* @date 2020/11/9
* @description
*/
@Component
public class MyZuulFallback implements FallbackProvider {
@Override
public String getRoute() {
// getRoute 方法返回 "info-server",即为 "info-server" 提供回退
// 如果要为所有路由提供默认回退,可以返回 * 或 null
return "*";
}
@Override
public ClientHttpResponse fallbackResponse(String route, Throwable cause) {
return new ClientHttpResponse() {
// 响应状态码
@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() {
}
// 异常体信息
@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;
}
};
}
}