5. Zuul 源码
对 zuul 工程的请求是有一个 controller 来接收的
org.springframework.cloud.netflix.zuul.ZuulServerAutoConfiguration
@Bean
public ZuulController zuulController() {
return new ZuulController();
}
// 所有请求都会走到这个方法对应的 HandlerMapping
@Bean
public ZuulHandlerMapping zuulHandlerMapping(RouteLocator routes,
ZuulController zuulController) {
ZuulHandlerMapping mapping = new ZuulHandlerMapping(routes, zuulController);
mapping.setErrorController(this.errorController);
mapping.setCorsConfigurations(getCorsConfigurations());
return mapping;
}
org.springframework.cloud.netflix.zuul.web.ZuulController
public class ZuulController extends ServletWrappingController {
public ZuulController() {
setServletClass(ZuulServlet.class);
setServletName("zuul");
setSupportedMethods((String[]) null); // Allow all
}
@Override
public ModelAndView handleRequest(HttpServletRequest request,
HttpServletResponse response) throws Exception {
try {
// We don't care about the other features of the base class, just want to
// handle the request
return super.handleRequestInternal(request, response);
}
finally {
// @see com.netflix.zuul.context.ContextLifecycleFilter.doFilter
RequestContext.getCurrentContext().unset();
}
}
}
所有的请求会有这个 zuulServlet 来处理
org.springframework.web.servlet.mvc.ServletWrappingController#handleRequestInternal
protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response)
throws Exception {
Assert.state(this.servletInstance != null, "No Servlet instance");
this.servletInstance.service(request, response); // 这里
return null;
}
这个zuulServlet的装配位置 org.springframework.cloud.netflix.zuul.ZuulServerAutoConfiguration#zuulServlet
@Bean
@ConditionalOnMissingBean(name = "zuulServlet")
@ConditionalOnProperty(name = "zuul.use-filter", havingValue = "false",
matchIfMissing = true)
public ServletRegistrationBean zuulServlet() {
ServletRegistrationBean<ZuulServlet> servlet = new ServletRegistrationBean<>(
new ZuulServlet(), this.zuulProperties.getServletPattern());
// The whole point of exposing this servlet is to provide a route that doesn't
// buffer requests.
servlet.addInitParameter("buffer-requests", "false");
return servlet;
}
在 zuulServlet 中就涉及到 pre、route、post、error 过滤器的调用
com.netflix.zuul.http.ZuulServlet#service
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;
}
分析一个 route 过滤器,这个过滤器是 zuul 做路由的调用的
org.springframework.cloud.netflix.zuul.ZuulProxyAutoConfiguration#ribbonRoutingFilter
@Bean
@ConditionalOnMissingBean(RibbonRoutingFilter.class)
public RibbonRoutingFilter ribbonRoutingFilter(ProxyRequestHelper helper,
RibbonCommandFactory<?> ribbonCommandFactory) {
// route过滤器
RibbonRoutingFilter filter = new RibbonRoutingFilter(helper, ribbonCommandFactory,
this.requestCustomizers);
return filter;
}
org.springframework.cloud.netflix.zuul.filters.route.RibbonRoutingFilter#run
public Object run() {
RequestContext context = RequestContext.getCurrentContext();
this.helper.addIgnoredHeaders();
try {
RibbonCommandContext commandContext = buildCommandContext(context);
ClientHttpResponse response = forward(commandContext); // 转向
setResponse(response);
return response;
}
catch (ZuulException ex) {
throw new ZuulRuntimeException(ex);
}
catch (Exception ex) {
throw new ZuulRuntimeException(ex);
}
}
protected ClientHttpResponse forward(RibbonCommandContext context) throws Exception {
Map<String, Object> info = this.helper.debug(context.getMethod(),
context.getUri(), context.getHeaders(), context.getParams(),
context.getRequestEntity());
RibbonCommand command = this.ribbonCommandFactory.create(context);
try {
ClientHttpResponse response = command.execute(); // 命令执行
this.helper.appendDebug(info, response.getRawStatusCode(),
response.getHeaders());
return response;
}
catch (HystrixRuntimeException ex) {
return handleException(info, ex);
}
}
这里走的依然是 hystrix 那一套
com.netflix.hystrix.HystrixCommand#execute
public R execute() {
try {
return queue().get();
} catch (Exception e) {
throw Exceptions.sneakyThrow(decomposeException(e));
}
}