Zuul的核心
- 上文也提到了Zuul的核心是一系列过滤器,用来实现对外服务请求的控制。
- Zuul包括以下4种过滤器,分别是“PRE”、“ROUTING”、“POST”、“ERROR”
- PRE:在请求被路由之前调用。我们可利用这种过滤器实现身份验证、在集群中选择请求的微服务、记录调试信息等。
- ROUTING:将请求路由到微服务。这种过滤器用于构建发送给微服务的请求,并使用Apache HttpClient或Netfilx Ribbon请求微服务。
- POST:在路由到微服务以后执行。这种过滤器可用来为响应添加标准的HTTP Header、收集统计信息和指标、将响应从微服务发送给客户端等。
- ERROR:在其他阶段发生错误时执行该过滤器。
- 执行顺序:pre -> routing -> post -> error
- Zuul采用动态读取、编译和运行过滤器。过滤器质检不能直接通信,通过RequestContext对象来共享数据,每个请求都会创建一个RequestContext对象。
- Zuul过滤器包含以下属性
- Type:类型 ;即上述4种类型
- Execution Order:执行吮吸,Order越小,越先执行
- Criteria (shouldFilter):Filter执行所需的条件,指定Filter是否执行
- Run:行动,执行Filter的具体逻辑内容
Zuul的工作原理
执行机制
- request进入Zuul网关服务,先进PreFilter,进行一些列的验证、操作或判断;
- 交给RoutingFilter进行路由转发,到具体的服务实例进行逻辑处理,返回数据;
- 服务处理完,由PostFilter进行处理,该类型的Filter处理完
- 将Response信息返回给客户端。
ZuulServlet
- ZuulServlet是Zuul的核心Servlet
- 作用是初始化ZuulFilter,编排ZuulFilter的执行顺序。
- 以下是ZuulServlet的service()方法
public void service(javax.servlet.ServletRequest servletRequest, javax.servlet.ServletResponse servletResponse) throws ServletException, IOException {
try {
init((HttpServletRequest) servletRequest, (HttpServletResponse) servletResponse);
// Marks this request as having passed through the "Zuul engine", as opposed to servlets
// explicitly bound in web.xml, for which requests will not have the same data attached
RequestContext context = RequestContext.getCurrentContext();
context.setZuulEngineRan();
try {
preRoute();
} catch (ZuulException e) {
error(e);
postRoute();
return;
}
try {
route();
} catch (ZuulException e) {
error(e);
postRoute();
return;
}
try {
postRoute();
} catch (ZuulException e) {
error(e);
return;
}
} catch (Throwable e) {
error(new ZuulException(e, 500, "UNHANDLED_EXCEPTION_" + e.getClass().getName()));
} finally {
RequestContext.getCurrentContext().unset();
}
}
- 从代码中看出来,先执行preRoute();出异常则执行error(e)和postRoute()。然后执行route();最后执行postRoute();中间出现异常执行error()
自定义Filter
- 集成ZuulFilter类
- 实现filterType()、 filterOrder()、 shouldFilter()、 run() 4个方法
public class CustomizeFilter extends ZuulFilter {
@Override
public String filterType() {
// 定义filter的类型,有pre、route、post、error四种
return null;
}
@Override
public int filterOrder() {
// 定义filter的顺序,数字越小表示顺序越高,越先执行
return 0;
}
@Override
public boolean shouldFilter() {
// 表示是否需要执行该filter,true表示执行,false表示不执行
return false;
}
@Override
public Object run() throws ZuulException {
// ilter需要执行的具体操作
return null;
}
}
自定义PreFilter
- 自定义一个Pre类型的Filter,可以做校验。实验程序中校验token必传
/**
* 路由前置Filter:<Br>
* pre:可以在请求被路由之前调用 <br>
* 一般作用在路由之前做一些鉴权 校验的功能
*
*
* @author yanbin
* @since 2019/9/5 14:16
*
*/
@Component
public class PreFilter extends ZuulFilter {
@Override
public String filterType() {
// 过滤器类型
return FilterConstants.PRE_TYPE;
}
@Override
public int filterOrder() {
// 过滤器顺序,数值越小,越先执行
return 0;
}
@Override
public boolean shouldFilter() {
// 是否启用这个过滤器,return true 说明执行run()方法
return false;
}
@Override
public Object run() throws ZuulException {
RequestContext requestContext = RequestContext.getCurrentContext();
HttpServletRequest request = requestContext.getRequest();
String token = request.getParameter("token");
if (null == token) {
requestContext.setSendZuulResponse(false);
requestContext.setResponseStatusCode(403);
try {
requestContext.getResponse().getWriter().write("token is empty");
} catch (IOException e) {
return null;
}
}
return null;
}
}
- 启动调试: 访问:http://127.0.0.1:8888/provider/hello?name=yanbin 不带token
自定义PostFilter
- 定义个Post过滤器,最后改变Response的输出
/**
* 后置过滤器<br>
* post:在routing之后被调用<br>
* 一般作用是打赢响应的时间,返回日志 ;
*
*
* @author yanbin
* @since 2019/9/5 14:40
*
*/
@Component
public class PostFilter extends ZuulFilter {
@Override
public String filterType() {
return FilterConstants.POST_TYPE;
}
@Override
public int filterOrder() {
return 0;
}
@Override
public boolean shouldFilter() {
return true;
}
@Override
public Object run() throws ZuulException {
RequestContext ctx = RequestContext.getCurrentContext();
System.out.println("POST:" + ctx.getResponseBody());
ctx.setResponseBody("post后置数据");
// int i = 1 / 0;
return null;
}
}
- 启动调试:访问http://127.0.0.1:8888/provider/hello?name=yanbin&token=1123
自定义ErrorFilter
- 定义Error过滤器,在请求到响应过程中出现异常,进行处理输出
/**
* 异常过滤器<br>
* error:处理请求时发生错误时被调用;在post之后<br>
* 一般用于请求过程中出现异常之后统一的跳转
*
*
* @author yanbin
* @since 2019/9/5 15:18
*
*/
@Component
public class ErrorFilter extends ZuulFilter {
@Override
public String filterType() {
return FilterConstants.ERROR_TYPE;
}
@Override
public int filterOrder() {
return 0;
}
@Override
public boolean shouldFilter() {
return true;
}
@Override
public Object run() throws ZuulException {
RequestContext ctx = RequestContext.getCurrentContext();
System.out.println("Error:" + ctx.getResponseBody());
ctx.setResponseBody("出现异常");
return null;
}
}
- 启动调试:访问 http://127.0.0.1:8888/provider/hello?name=yanbin&token=1123
- 报错了
- 也输出了ErrorFilter中的逻辑
- 介于界面不友好,增加一个 /Error 的请求
/**
* 异常页面
*
*
* @author yanbin
* @since 2019/9/5 16:03
*
*/
@RestController
public class ErrorHandlerController implements ErrorController {
@Override
public String getErrorPath() {
return "/error";
}
@RequestMapping("/error")
public String error() {
return "出现异常";
}
}