平常使用zuul网关,主要就是用来做路由请求,和通过过滤器做相应的增强限制处理。其实,zuul就是一组过滤器,有pre,routing,post,error不同类型的过滤器。执行的时机也不一样。而路由功能也是通过过滤器来实现的。我们来看下源码,大概了解下他是如何做的吧。
首先,老规矩还是找到spring.factories,找到自动配置的类ZuulProxyAutoConfiguration和父类ZuulServerAutoConfiguration
public class ZuulProxyAutoConfiguration extends ZuulServerAutoConfiguration {
//继承自SimpleRouteLocator,路由定位器
@Bean
@ConditionalOnMissingBean(DiscoveryClientRouteLocator.class)
public DiscoveryClientRouteLocator discoveryRouteLocator(
ServiceRouteMapper serviceRouteMapper) {
return new DiscoveryClientRouteLocator(this.server.getServlet().getContextPath(),
this.discovery, this.zuulProperties, serviceRouteMapper,
this.registration);
}
//pre 过滤器
@Bean
@ConditionalOnMissingBean(PreDecorationFilter.class)
public PreDecorationFilter preDecorationFilter(RouteLocator routeLocator,
ProxyRequestHelper proxyRequestHelper) {
return new PreDecorationFilter(routeLocator,
this.server.getServlet().getContextPath(), this.zuulProperties,
proxyRequestHelper);
}
//route过滤器
@Bean
@ConditionalOnMissingBean(RibbonRoutingFilter.class)
public RibbonRoutingFilter ribbonRoutingFilter(ProxyRequestHelper helper,
RibbonCommandFactory<?> ribbonCommandFactory) {
RibbonRoutingFilter filter = new RibbonRoutingFilter(helper, ribbonCommandFactory,
this.requestCustomizers);
return filter;
}
}
public class ZuulServerAutoConfiguration {
//组合的路由定位器,将容器中的路由定位器组合一起,默认使用此路由定位器
@Bean
@Primary
public CompositeRouteLocator primaryRouteLocator(
Collection<RouteLocator> routeLocators) {
return new CompositeRouteLocator(routeLocators);
}
//Zuul创建的一个Controller,用于将请求交由ZuulServlet处理。
@Bean
public ZuulController zuulController() {
return new ZuulController();
}
//SpringMvc的HandlerMapping处理器映射器,只有被此映射器处理的请求才能出发到Zuul的后续流程
@Bean
public ZuulHandlerMapping zuulHandlerMapping(RouteLocator routes,
ZuulController zuulController) {
ZuulHandlerMapping mapping = new ZuulHandlerMapping(routes, zuulController);
mapping.setErrorController(this.errorController);
mapping.setCorsConfigurations(getCorsConfigurations());
return mapping;
}
}
//zuul请求处理核心
@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;
}
//注册过滤器
@Bean
@ConditionalOnMissingBean(name = "zuulServletFilter")
@ConditionalOnProperty(name = "zuul.use-filter", havingValue = "true",
matchIfMissing = false)
public FilterRegistrationBean zuulServletFilter() {
final FilterRegistrationBean<ZuulServletFilter> filterRegistration = new FilterRegistrationBean<>();
filterRegistration.setUrlPatterns(
Collections.singleton(this.zuulProperties.getServletPattern()));
filterRegistration.setFilter(new ZuulServletFilter());
filterRegistration.setOrder(Ordered.LOWEST_PRECEDENCE);
// The whole point of exposing this servlet is to provide a route that doesn't
// buffer requests.
filterRegistration.addInitParameter("buffer-requests", "false");
return filterRegistration;
}
大致列了几个个人感觉比较重要的组件,功能注释都有说明。在一个请求进入spring mvc的DispatcherServlet后,首先会获取遍历handlerMapping,调用getHandler方法,而getHandler方法会调用getHandlerInternal方法去获取具体handler。我们先来看ZuulHandlerMapping的父类方法AbstractUrlHandlerMapping#getHandlerInternal
protected Object getHandlerInternal(HttpServletRequest request) throws Exception {
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
request.setAttribute(LOOKUP_PATH, lookupPath);
Object handler = lookupHandler(lookupPath, request);
if (handler == null) {
// We need to care for the default handler directly, since we need to
// expose the PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE for it as well.
Object rawHandler = null;
if ("/".equals(lookupPath)) {
rawHandler = getRootHandler();
}
if (rawHandler == null) {
rawHandler = getDefaultHandler();
}
if (rawHandler != null) {
// Bean name or resolved handler?
if (rawHandler instanceof String) {
String handlerName = (String) rawHandler;
rawHandler = obtainApplicationContext().getBean(handlerName);
}
validateHandler(rawHandler, request);
handler = buildPathExposingHandler(rawHandler, lookupPath, lookupPath, null);
}
}
return handler;
}
首先获取请求地址,调用重写的ZuulHandlerMapping#lookupHandler方法来获取匹配的handler
protected Object lookupHandler(String urlPath, HttpServletRequest request)
throws Exception {
if (this.errorController != null
&& urlPath.equals(this.errorController.getErrorPath())) {
return null;
}
if (isIgnoredPath(urlPath, this.routeLocator.getIgnoredPaths())) {
return null;
}
RequestContext ctx = RequestContext.getCurrentContext();
if (ctx.containsKey("forward.to")) {
return null;
}
if (this.dirty) {
synchronized (this) {
if (this.dirty) {
registerHandlers();
this.dirty = false;
}
}
}
return super.lookupHandler(urlPath, request);
}
首先判断是否是errorController的路径,是否是忽略的路径,如果都不是,则调用ZuulHandlerMapping#registerHandlers
private void registerHandlers() {
Collection<Route> routes = this.routeLocator.getRoutes();
if (routes.isEmpty()) {
this.logger.warn("No routes found from RouteLocator");
}
else {
for (Route route : routes) {
registerHandler(route.getFullPath(), this.zuul);
}
}
}
这个方法委托调用routeLocator的getRoutes方法获取route集合,实际调用的就是CompositeRouteLocator#getRoutes,而CompositeRouteLocator是个路由定位器的组合,会遍历所有的路由定位器,调用SimpleRouteLocator#getRoutes
public List<Route> getRoutes() {
List<Route> values = new ArrayList<>();
for (Entry<String, ZuulRoute> entry : getRoutesMap().entrySet()) {
ZuulRoute route = entry.getValue();
String path = route.getPath();
try {
values.add(getRoute(route, path));
}
catch (Exception e) {
if (log.isWarnEnabled()) {
log.warn("Invalid route, routeId: " + route.getId()
+ ", routeServiceId: " + route.getServiceId() + ", msg: "
+ e.getMessage());
}
if (log.isDebugEnabled()) {
log.debug("", e);
}
}
}
return values;
}
先跟进getRoutesMap方法
protected Map<String, ZuulRoute> getRoutesMap() {
if (this.routes.get() == null) {
this.routes.set(locateRoutes());
}
return this.routes.get();
}
locateRoutes方法会调用子类DiscoveryClientRouteLocator#locateRoutes
protected LinkedHashMap<String, ZuulRoute> locateRoutes() {
LinkedHashMap<String, ZuulRoute> routesMap = new LinkedHashMap<>();
//调用父类将配置的路由放入map,Path为key
routesMap.putAll(super.locateRoutes());
if (this.discovery != null) {
Map<String, ZuulRoute> staticServices = new LinkedHashMap<>();
for (ZuulRoute route : routesMap.values()) {
String serviceId = route.getServiceId();
if (serviceId == null) {
serviceId = route.getId();
}
if (serviceId != null) {
staticServices.put(serviceId, route);
}
}
// 通过注册中心获取所有服务id
List<String> services = this.discovery.getServices();
//获取忽略列表
String[] ignored = this.properties.getIgnoredServices()
.toArray(new String[0]);
//遍历所有注册的服务id,将拼接路径path/服务id/**放入map
for (String serviceId : services) {
// Ignore specifically ignored services and those that were manually
// configured
String key = "/" + mapRouteToService(serviceId) + "/**";
if (staticServices.containsKey(serviceId)
&& staticServices.get(serviceId).getUrl() == null) {
// Explicitly configured with no URL, cannot be ignored
// all static routes are already in routesMap
// Update location using serviceId if location is null
ZuulRoute staticRoute = staticServices.get(serviceId);
if (!StringUtils.hasText(staticRoute.getLocation())) {
staticRoute.setLocation(serviceId);
}
}
if (!PatternMatchUtils.simpleMatch(ignored, serviceId)
&& !routesMap.containsKey(key)) {
// Not ignored
routesMap.put(key, new ZuulRoute(key, serviceId));
}
}
}
if (routesMap.get(DEFAULT_ROUTE) != null) {
ZuulRoute defaultRoute = routesMap.get(DEFAULT_ROUTE);
// Move the defaultServiceId to the end
routesMap.remove(DEFAULT_ROUTE);
routesMap.put(DEFAULT_ROUTE, defaultRoute);
}
LinkedHashMap<String, ZuulRoute> values = new LinkedHashMap<>();
//处理Prefix
for (Entry<String, ZuulRoute> entry : routesMap.entrySet()) {
String path = entry.getKey();
// Prepend with slash if not already present.
if (!path.startsWith("/")) {
path = "/" + path;
}
if (StringUtils.hasText(this.properties.getPrefix())) {
path = this.properties.getPrefix() + path;
if (!path.startsWith("/")) {
path = "/" + path;
}
}
values.put(path, entry.getValue());
}
return values;
}
首先会调用父类将配置的路由信息放入map,Path为key,route为value,然后遍历通过注册中心获取所有服务id,拼接key/服务id/**,并将其作为path放入map。获取完route集合后,回到ZuulHandlerMapping#registerHandlers方法,接下来遍历所有route,注册到handlerMap,
路径为key,ZuulController为值。
for (Route route : routes) {
registerHandler(route.getFullPath(), this.zuul);
}
注册完之后,ZuulHandlerMapping#lookupHandler方法会调用父类的lookupHandler方法,根据url从handlerMap中拿到对应handler也就是ZuulController具体看AbstractUrlHandlerMapping#lookupHandler
protected Object lookupHandler(String urlPath, HttpServletRequest request) throws Exception {
// Direct match?
Object handler = this.handlerMap.get(urlPath);
...
}
如果url匹配,会进入zuul执行流程,执行ZuulController#handleRequest方法
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();
}
}
protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response)
throws Exception {
Assert.state(this.servletInstance != null, "No Servlet instance");
this.servletInstance.service(request, response);
return null;
}
servletInstance实际就是ZuulServlet,在afterPropertiesSet方法中初始化的。所以,这里实际会调用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();
}
}
这里可以看出整个zuul的过滤器执行流程
- pre异常: pre -> error -> post
- route异常: pre -> route -> error -> post
- post异常: pre -> route -> post -> error
- 正常: pre -> route -> post
为什么最后都要走post,因为post最后,才能直接给用户响应数据。
pre:表示路由的前置过滤器链,route:表示路由的过滤器链,post:表示路由的后置过滤器链,error:表示路由错误过滤器链。
而各种过滤器的获取执行过程是想同的zuulRunner->FilterProcessor,这里以pre举例,看FilterProcessor#preRoute
public void preRoute() throws ZuulException {
try {
runFilters("pre");
} catch (ZuulException e) {
throw e;
} catch (Throwable e) {
throw new ZuulException(e, 500, "UNCAUGHT_EXCEPTION_IN_PRE_FILTER_" + e.getClass().getName());
}
}
最终都会调用runFilters方法
public Object runFilters(String sType) throws Throwable {
if (RequestContext.getCurrentContext().debugRouting()) {
Debug.addRoutingDebug("Invoking {" + sType + "} type filters");
}
boolean bResult = false;
List<ZuulFilter> list = FilterLoader.getInstance().getFiltersByType(sType);
if (list != null) {
for (int i = 0; i < list.size(); i++) {
ZuulFilter zuulFilter = list.get(i);
Object result = processZuulFilter(zuulFilter);
if (result != null && result instanceof Boolean) {
bResult |= ((Boolean) result);
}
}
}
return bResult;
}
首先通过FilterLoader#getFiltersByType方法获取过滤器
public List<ZuulFilter> getFiltersByType(String filterType) {
List<ZuulFilter> list = hashFiltersByType.get(filterType);
if (list != null) return list;
list = new ArrayList<ZuulFilter>();
//获取所有过滤器
Collection<ZuulFilter> filters = filterRegistry.getAllFilters();
for (Iterator<ZuulFilter> iterator = filters.iterator(); iterator.hasNext(); ) {
ZuulFilter filter = iterator.next();
//遍历匹配
if (filter.filterType().equals(filterType)) {
list.add(filter);
}
}
//排序
Collections.sort(list); // sort by priority
hashFiltersByType.putIfAbsent(filterType, list);
return list;
}
从filterRegistry中获取所有过滤器,遍历执行过滤器的filterType方法,这里是匹配filterType方法返回的是不是pre如果是则添加到list中,最后将过滤器排序(根据filterOrder方法返回值,越小越在前)返回。
拿到过滤器列表后,遍历执行FilterProcessor#processZuulFilter
public Object processZuulFilter(ZuulFilter filter) throws ZuulException {
RequestContext ctx = RequestContext.getCurrentContext();
boolean bDebug = ctx.debugRouting();
final String metricPrefix = "zuul.filter-";
long execTime = 0;
String filterName = "";
try {
long ltime = System.currentTimeMillis();
filterName = filter.getClass().getSimpleName();
RequestContext copy = null;
Object o = null;
Throwable t = null;
if (bDebug) {
Debug.addRoutingDebug("Filter " + filter.filterType() + " " + filter.filterOrder() + " " + filterName);
copy = ctx.copy();
}
ZuulFilterResult result = filter.runFilter();
ExecutionStatus s = result.getStatus();
execTime = System.currentTimeMillis() - ltime;
...
}
跟进过滤器实际执行方法ZuulFilter#runFilter
public ZuulFilterResult runFilter() {
ZuulFilterResult zr = new ZuulFilterResult();
if (!isFilterDisabled()) {
if (shouldFilter()) {
Tracer t = TracerFactory.instance().startMicroTracer("ZUUL::" + this.getClass().getSimpleName());
try {
Object res = run();
zr = new ZuulFilterResult(res, ExecutionStatus.SUCCESS);
} catch (Throwable e) {
t.setName("ZUUL::" + this.getClass().getSimpleName() + " failed");
zr = new ZuulFilterResult(ExecutionStatus.FAILED);
zr.setException(e);
} finally {
t.stopAndLog();
}
} else {
zr = new ZuulFilterResult(ExecutionStatus.SKIPPED);
}
}
return zr;
}
可以看到,这里会先去调用过滤器的shouldFilter方法,如果返回为true,则去调用过滤器的run方法,执行业务逻辑。我们先看默认的pre过滤器PreDecorationFilter的run方法
public Object run() {
RequestContext ctx = RequestContext.getCurrentContext();
final String requestURI = this.urlPathHelper
.getPathWithinApplication(ctx.getRequest());
// 根据请求路径获取route
Route route = this.routeLocator.getMatchingRoute(requestURI);
if (route != null) {
//获取目标地址(serviceId或者url)
String location = route.getLocation();
if (location != null) {
ctx.put(REQUEST_URI_KEY, route.getPath());
ctx.put(PROXY_KEY, route.getId());
//忽略headers信息的处理
if (!route.isCustomSensitiveHeaders()) {
this.proxyRequestHelper.addIgnoredHeaders(
this.properties.getSensitiveHeaders().toArray(new String[0]));
}
else {
this.proxyRequestHelper.addIgnoredHeaders(
route.getSensitiveHeaders().toArray(new String[0]));
}
if (route.getRetryable() != null) {
ctx.put(RETRYABLE_KEY, route.getRetryable());
}
//判断目标地址是否http开头或者https开头
if (location.startsWith(HTTP_SCHEME + ":")
|| location.startsWith(HTTPS_SCHEME + ":")) {
ctx.setRouteHost(getUrl(location));
ctx.addOriginResponseHeader(SERVICE_HEADER, location);
}
//是否forward:
else if (location.startsWith(FORWARD_LOCATION_PREFIX)) {
ctx.set(FORWARD_TO_KEY,
StringUtils.cleanPath(
location.substring(FORWARD_LOCATION_PREFIX.length())
+ route.getPath()));
ctx.setRouteHost(null);
return null;
}
else {
// set serviceId for use in filters.route.RibbonRequest
ctx.set(SERVICE_ID_KEY, location);
ctx.setRouteHost(null);
ctx.addOriginResponseHeader(SERVICE_ID_HEADER, location);
}
if (this.properties.isAddProxyHeaders()) {
addProxyHeaders(ctx, route);
String xforwardedfor = ctx.getRequest()
.getHeader(X_FORWARDED_FOR_HEADER);
String remoteAddr = ctx.getRequest().getRemoteAddr();
if (xforwardedfor == null) {
xforwardedfor = remoteAddr;
}
else if (!xforwardedfor.contains(remoteAddr)) { // Prevent duplicates
xforwardedfor += ", " + remoteAddr;
}
ctx.addZuulRequestHeader(X_FORWARDED_FOR_HEADER, xforwardedfor);
}
if (this.properties.isAddHostHeader()) {
ctx.addZuulRequestHeader(HttpHeaders.HOST,
toHostHeader(ctx.getRequest()));
}
}
}
else {
log.warn("No route found for uri: " + requestURI);
String forwardURI = getForwardUri(requestURI);
ctx.set(FORWARD_TO_KEY, forwardURI);
}
return null;
}
- 首先会获取请求路径,调用routeLocator的getMatchingRoute方法,最终调用SimpleRouteLocator#getSimpleMatchingRoute
protected Route getSimpleMatchingRoute(final String path) {
// This is called for the initialization done in getRoutesMap()
//初始化RoutesMap
getRoutesMap();
//对请求路径进行处理(dispatcherServletPath,zuulServletPath处理)
String adjustedPath = adjustPath(path);
//从routeMap中根据路径key获取对应的ZuulRoute
ZuulRoute route = getZuulRoute(adjustedPath);
//对路径prefix,StripPrefix处理
return getRoute(route, adjustedPath);
}
初始化RoutesMap,对请求路径进行处理,从routeMap中根据路径key获取对应的ZuulRoute,对路径prefix,StripPrefix处理。
- 接着获取目标地址(serviceId或者url),判断是否是http或者https或者forward开头,如果都不是则
else {
// set serviceId for use in filters.route.RibbonRequest
ctx.set(SERVICE_ID_KEY, location);
ctx.setRouteHost(null);
ctx.addOriginResponseHeader(SERVICE_ID_HEADER, location);
}
这里设置到全局的请求上下文中,key为serviceId,值为对应目标地址。这一步将会为了接下来的route过滤器选择做准备,如果进入当前的分支,则将会调用RibbonRoutingFilter作为route过滤器,通过注册中心获取路径请求,可以看下RibbonRoutingFilter的shouldFilter
public boolean shouldFilter() {
RequestContext ctx = RequestContext.getCurrentContext();
return (ctx.getRouteHost() == null && ctx.get(SERVICE_ID_KEY) != null
&& ctx.sendZuulResponse());
}
可以看到只有当全局的请求上下文中RouteHost为空,serviceId有值时才返回true。所以我们route会走RibbonRoutingFilter的run方法
public Object run() {
RequestContext context = RequestContext.getCurrentContext();
this.helper.addIgnoredHeaders();
try {
//构建RibbonCommandContext
RibbonCommandContext commandContext = buildCommandContext(context);
ClientHttpResponse response = forward(commandContext);
//设置Response
setResponse(response);
return response;
}
catch (ZuulException ex) {
throw new ZuulRuntimeException(ex);
}
catch (Exception ex) {
throw new ZuulRuntimeException(ex);
}
}
构建RibbonCommandContext,调用RibbonRoutingFilter#forward方法发送请求
protected ClientHttpResponse forward(RibbonCommandContext context) throws Exception {
Map<String, Object> info = this.helper.debug(context.getMethod(),
context.getUri(), context.getHeaders(), context.getParams(),
context.getRequestEntity());
//构建HttpClientRibbonCommand
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);
}
}
构建HttpClientRibbonCommand,然后调用HystrixCommand#execute方法执行实际请求
public R execute() {
try {
return queue().get();
} catch (Exception e) {
throw Exceptions.sneakyThrow(decomposeException(e));
}
}
这一步就是ribbon的调用了。设置调用获取到的response。再来看post过滤器,默认是SendResponseFilter
public Object run() {
try {
//添加响应头
addResponseHeaders();
//向客户端写数据
writeResponse();
}
catch (Exception ex) {
ReflectionUtils.rethrowRuntimeException(ex);
}
return null;
}
这个过滤器主要将调用返回的response写到客户端。
小结
- zuul通过注入ZuulHandlerMapping处理器映射器,路径如果在handlerMap中(会从routeLocator里取出所有的route(路径key为配置的路由路径或者是注册在注册中心的所有服务id/*),一个一个注册到handlerMap)。会走ZuulController,而ZuulController会将请求委托给ZuulServlet的service方法处理.
- ZuulServlet中就是进行了不同过滤器的调用执行,正常流程为pre -> route -> post,如果有异常则error,error后如果post没有执行也会继续走post,因为需要post过滤器将最终结果返回出去。
- 而请求路由的功能则是由pre和route过滤器进行的。在PreDecorationFilter中,主要解析url地址,获取到当前要使用的是哪个 route,如果是route中设置的是serviceId走ribbon,会设置一个serviceId的key,这会决定使用哪个route过滤器。
- 由于,我们在pre过滤器中设置了serviceId的key,所以符合route过滤器RibbonRoutingFilter,而在RibbonRoutingFilter中会构建构建HttpClientRibbonCommand,去调用ribbon执行具体请求。
- 在post过滤器默认SendResponseFilter中会向客户端写执行返回的response。
所以我们可以设置自定义的RouteLocator,来设置我们想要的handlerMap,决定哪些请求是走zuul的。并且自定义pre过滤器(禁用PreDecorationFilter),解析url,用自定义的逻辑来获取serviceId决定使用哪个route过滤器。