【SpringCloud】Zuul源码解析

94bbb8a2220693f096e9094e7c205f55.png

Zuul是一个网关组件,是微服务的入口,网关会根据配置将请求转发给指定的服务。本章分析Zuul组件是如何实现请求过滤和转发的

参考源码:<spring-cloud.version>Hoxton.SR9</spring-cloud.version>

 

1、过滤

spring-cloud-netflix-zuul依赖包下有一个spring.factories文件,文件内容如下

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.netflix.zuul.ZuulServerAutoConfiguration,\
org.springframework.cloud.netflix.zuul.ZuulProxyAutoConfiguration

根据springboot自动配置的原理可知,ZuulServerAutoConfiguration会被标记成了一个自动配置类,其中和Zuul组件核心功能相关的是ZuulServlet

@Bean
@ConditionalOnMissingBean(
	name = {"zuulServlet"}
)
@ConditionalOnProperty(
	name = {"zuul.use-filter"},
	havingValue = "false",
	matchIfMissing = true
)
public ServletRegistrationBean zuulServlet() {
	ServletRegistrationBean<ZuulServlet> servlet = new ServletRegistrationBean(new ZuulServlet(), new String[]{this.zuulProperties.getServletPattern()});
	servlet.addInitParameter("buffer-requests", "false");
	return servlet;
}

ZuulServlet会处理所有的请求,在service方法内依次调用preRoute、route、postRoute方法,分别对应前置,路由和后置处理器

public class ZuulServlet extends HttpServlet {
 
    public ZuulServlet() {
    }

    public void init(ServletConfig config) throws ServletException {
        super.init(config);
        String bufferReqsStr = config.getInitParameter("buffer-requests");
        boolean bufferReqs = bufferReqsStr != null && bufferReqsStr.equals("true");
        this.zuulRunner = new ZuulRunner(bufferReqs);
    }

    // 处理请求
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
        try {
            this.init((HttpServletRequest)servletRequest, (HttpServletResponse)servletResponse);
            RequestContext context = RequestContext.getCurrentContext();
            context.setZuulEngineRan();

            try {
                this.preRoute();
            } catch (ZuulException var12) {
                this.error(var12);
                this.postRoute();
                return;
            }

            try {
                this.route();
            } catch (ZuulException var13) {
                this.error(var13);
                this.postRoute();
                return;
            }

            try {
                this.postRoute();
            } catch (ZuulException var11) {
                this.error(var11);
            }
        } catch (Throwable var14) {
            this.error(new ZuulException(var14, 500, "UNHANDLED_EXCEPTION_" + var14.getClass().getName()));
        } finally {
            RequestContext.getCurrentContext().unset();
        }
    }

    void preRoute() throws ZuulException {
        this.zuulRunner.preRoute();
    }
}

以preRoute方法为例,zuul网关会对请求做前置处理,遍历ZuulFilter接口的实现类执行runFilter方法

调用链:
-> ZuulServlet.preRoute
-> ZuulRunner.preRoute
-> FilterProcessor.preRoute
-> runFilters
public Object runFilters(String sType) throws Throwable {
	if (RequestContext.getCurrentContext().debugRouting()) {
		Debug.addRoutingDebug("Invoking {" + sType + "} type filters");
	}

	boolean bResult = false;

	// 获取指定类型的filter
	List<ZuulFilter> list = FilterLoader.getInstance().getFiltersByType(sType);
	if (list != null) {
		for(int i = 0; i < list.size(); ++i) {
			ZuulFilter zuulFilter = (ZuulFilter)list.get(i);

			// 遍历ZuulFilter实现类,前置处理请求
			Object result = this.processZuulFilter(zuulFilter);
			if (result != null && result instanceof Boolean) {
				bResult |= (Boolean)result;
			}
		}
	}

	return bResult;
}

public Object processZuulFilter(ZuulFilter filter) throws ZuulException {
	...
	// 调用ZuulFilter实现类的runFilter方法
	ZuulFilterResult result = filter.runFilter();
	...
}

public ZuulFilterResult runFilter() {
	...
	// run方法来自IZuulFilter接口
	Object res = this.run();
	...
}

请求传递给了ZuulFitler的实现类,遍历这些实现类时调用它们的run方法,请求过滤的逻辑一般就写在run方法内

 

2、转发

2.1 获取路由信息

PreDecorationFilter是zuul依赖包下的一个Filter,它实现了ZuulFilter抽象类,会在处理请求时被调用

public Object run() {
	RequestContext ctx = RequestContext.getCurrentContext();
	String requestURI = this.urlPathHelper.getPathWithinApplication(ctx.getRequest());

	// 获取请求的路由信息
	Route route = this.routeLocator.getMatchingRoute(requestURI);
	String location;
	...
}

this.routeLocator指向的是SimpleRouteLocator对象,查看它的getMatchingRoute方法

public Route getMatchingRoute(final String path) {
	return this.getSimpleMatchingRoute(path);
}

protected Route getSimpleMatchingRoute(final String path) {
	if (log.isDebugEnabled()) {
		log.debug("Finding route for path: " + path);
	}

	this.getRoutesMap();
	if (log.isDebugEnabled()) {
		log.debug("servletPath=" + this.dispatcherServletPath);
		log.debug("zuulServletPath=" + this.zuulServletPath);
		log.debug("RequestUtils.isDispatcherServletRequest()=" + RequestUtils.isDispatcherServletRequest());
		log.debug("RequestUtils.isZuulServletRequest()=" + RequestUtils.isZuulServletRequest());
	}

	String adjustedPath = this.adjustPath(path);
	
	// 获取路由信息
	ZuulProperties.ZuulRoute route = this.getZuulRoute(adjustedPath);
	return this.getRoute(route, adjustedPath);
}

protected ZuulProperties.ZuulRoute getZuulRoute(String adjustedPath) {
   
	// 匹配路由地址
	if (!this.matchesIgnoredPatterns(adjustedPath)) {

		// 遍历路由map
		Iterator var2 = this.getRoutesMap().entrySet().iterator();

		while(var2.hasNext()) {
			Map.Entry<String, ZuulProperties.ZuulRoute> entry = (Map.Entry)var2.next();
			String pattern = (String)entry.getKey();
			log.debug("Matching pattern:" + pattern);
			if (this.pathMatcher.match(pattern, adjustedPath)) {
				return (ZuulProperties.ZuulRoute)entry.getValue();
			}
		}
	}

	return null;
}

原始的路由信息来源于SimpleRouteLocator的routes成员,是一个map对象,调用getRoutesMap方法时如果routes对象为空会对它进行初始化

protected Map<String, ZuulProperties.ZuulRoute> getRoutesMap() {
	if (this.routes.get() == null) {
		// 初始化routes
		this.routes.set(this.locateRoutes());
	}

	return (Map)this.routes.get();
}

protected Map<String, ZuulProperties.ZuulRoute> locateRoutes() {
	LinkedHashMap<String, ZuulProperties.ZuulRoute> routesMap = new LinkedHashMap();
	
	// 获取配置文件中的路由信息
	Iterator var2 = this.properties.getRoutes().values().iterator();

	while(var2.hasNext()) {
		ZuulProperties.ZuulRoute route = (ZuulProperties.ZuulRoute)var2.next();
		routesMap.put(route.getPath(), route);
	}

	return routesMap;
}

配置文件中的路由信息封装在了ZuulProperties对象内,供初始化使用。拿到route后,PreDecorationFilter会在请求上下文中放入路由信息,这些路由信息会为后续ZuulFilter转发服务提供依据

public Object run() {
	...
	if (route != null) {
		location = route.getLocation();
		if (location != null) {
			ctx.put("requestURI", route.getPath());
			ctx.put("proxy", route.getId());
			...

			if (route.getRetryable() != null) {
				ctx.put("retryable", route.getRetryable());
			}

			if (!location.startsWith("http:") && !location.startsWith("https:")) {
				if (location.startsWith("forward:")) {
					ctx.set("forward.to", StringUtils.cleanPath(location.substring("forward:".length()) + route.getPath()));
					ctx.setRouteHost((URL)null);
					return null;
				} 

				// 置入serviceId
				ctx.set("serviceId", location);
				ctx.setRouteHost((URL)null);
				ctx.addOriginResponseHeader("X-Zuul-ServiceId", location);
			} else {

				// 置入路由url
				ctx.setRouteHost(this.getUrl(location));
				ctx.addOriginResponseHeader("X-Zuul-Service", location);
			}

			if (this.properties.isAddProxyHeaders()) {
				this.addProxyHeaders(ctx, route);
				String xforwardedfor = ctx.getRequest().getHeader("X-Forwarded-For");
				String remoteAddr = ctx.getRequest().getRemoteAddr();
				if (xforwardedfor == null) {
					xforwardedfor = remoteAddr;
				} else if (!xforwardedfor.contains(remoteAddr)) {
					xforwardedfor = xforwardedfor + ", " + remoteAddr;
				}

				ctx.addZuulRequestHeader("X-Forwarded-For", xforwardedfor);
			}

			if (this.properties.isAddHostHeader()) {
				ctx.addZuulRequestHeader("Host", this.toHostHeader(ctx.getRequest()));
			}
		}
	} 
	...
}

 

2.2 具体转发

Zuul支持以下三种类型的转发

转发类型ZuulFilter实现类配置文件案例
serviceId转发RibbonRoutingFilterzuul.routes.test-service.serviceId=test-service
url转发SimpleHostRoutingFilterzuul.routes.test-service.url=http://localhost:8080/
forward转发SendForwardFilterforward:/test

 

以RibbonRoutingFilter为例,当请求上下文中包含serviceId时,会调用RibbonRoutingFilter对象的run方法转发请求,具体的请求和负载均衡相关,就不加赘述了

// 判断是否启用过滤器
public boolean shouldFilter() {
	RequestContext ctx = RequestContext.getCurrentContext();
	// 上下文包含serviceId
	return ctx.getRouteHost() == null && ctx.get("serviceId") != null 
			 && ctx.sendZuulResponse();
}

public Object run() {
	RequestContext context = RequestContext.getCurrentContext();
	this.helper.addIgnoredHeaders(new String[0]);

	try {
		// 转发请求,获取响应信息
		RibbonCommandContext commandContext = this.buildCommandContext(context);
		ClientHttpResponse response = this.forward(commandContext);
		this.setResponse(response);
		return response;
	} catch (ZuulException var4) {
		throw new ZuulRuntimeException(var4);
	} catch (Exception var5) {
		throw new ZuulRuntimeException(var5);
	}
}

 

3、总结

Zuul组件以ZuulServlet为核心处理请求,将处理请求的过程分化成了请求前置、路由、后置三个阶段,而具体的处理逻辑则下放在ZuulFilter的实现类中

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值