zuul源码解析
zuul与springboot整合的依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
看到starter第一反应就是springboot的自动装配?
我们去到zuul的包下下边的spring.factories文件中可以看到文件内容
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.netflix.zuul.ZuulServerAutoConfiguration,\
org.springframework.cloud.netflix.zuul.ZuulProxyAutoConfiguration
可以看到配置的两个类ZuulServerAutoConfiguration,ZuulProxyAutoConfiguration
这两个类的区别是生效条件
@ConditionalOnBean(ZuulProxyMarkerConfiguration.Marker.class)
@ConditionalOnBean(ZuulServerMarkerConfiguration.Marker.class)
zuul网关在使用的时候一般在启动类上加@EnableZuulProxy注解
点进去看看
@EnableCircuitBreaker
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Import({ZuulProxyMarkerConfiguration.class})
public @interface EnableZuulProxy {
}
为我们导入了ZuulProxyMarkerConfiguration 类
只干了又1件事就是为我们导入mark使启动来生效
那么问题来了ZuulServerAutoConfiguration 和 ZuulProxyAutoConfiguration 的区别是啥
ZuulServerAutoConfiguration的静态内部类ZuulFilterConfiguration
@Configuration(
proxyBeanMethods = false
)
protected static class ZuulFilterConfiguration {
@Autowired
private Map<String, ZuulFilter> filters;
protected ZuulFilterConfiguration() {
}
@Bean
public ZuulFilterInitializer zuulFilterInitializer(CounterFactory counterFactory, TracerFactory tracerFactory) {
FilterLoader filterLoader = FilterLoader.getInstance();
FilterRegistry filterRegistry = FilterRegistry.instance();
return new ZuulFilterInitializer(this.filters, counterFactory, tracerFactory, filterLoader, filterRegistry);
}
}
上边代码就是初始化过滤器
其中
@Autowired
private Map<String, ZuulFilter> filters;
会把所有继承了ZuulFilter 并被spring管理的过滤器全部加在进来,这是spring 实现的
请求拦截器的确定
接下来问题来了,当我用postman请求zuul,会被哪个类处理呢?
框架处理请求用的是springMvc,熟悉springMvc源码的都知道请求是由DispatcherServlet处理的,这里我在DispatcherServlet.doDispatch方法上打了一个断点
getHandler方法:根据request请求获取Handler
回过来再看:ZuulServerAutoConfiguration
向容器中注入了ZuulHandlerMapping,ZuulController
ZuulController
ZuulController源码发现继承ServletWrappingController
于是搜索ServletWrappingController是做什么的
ServletWrappingController 会拦截请求,交给内部包装的的servlet进行处理
最终确定所有请求由ZuulController拦截,由内部包装的ZuulServlet进行处理
请求到ZuulController的handleRequest方法后会调用
这个servletInstance实例就是ZuulServlet
处理每次http请求
service方法
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 var13) {
this.error(var13);
this.postRoute();
return;
}
try {
this.route();
} catch (ZuulException var12) {
this.error(var12);
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();
}
}
我们看下preRoute方法
public void preRoute() throws ZuulException {
try {
this.runFilters("pre");
} catch (ZuulException var2) {
throw var2;
} catch (Throwable var3) {
throw new ZuulException(var3, 500, "UNCAUGHT_EXCEPTION_IN_PRE_FILTER_" + var3.getClass().getName());
}
}
runFilters方法
主要就是获取所有pre类型的过滤器并排序
然后滴哦用processZuulFilter方法
processZuulFilter方法
public Object processZuulFilter(ZuulFilter filter) throws ZuulException {
RequestContext ctx = RequestContext.getCurrentContext();
boolean bDebug = ctx.debugRouting();
String metricPrefix = "zuul.filter-";
long execTime = 0L;
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;
switch (s) {
case FAILED:
t = result.getException();
ctx.addFilterExecutionSummary(filterName, ExecutionStatus.FAILED.name(), execTime);
break;
case SUCCESS:
o = result.getResult();
ctx.addFilterExecutionSummary(filterName, ExecutionStatus.SUCCESS.name(), execTime);
if (bDebug) {
Debug.addRoutingDebug("Filter {" + filterName + " TYPE:" + filter.filterType() + " ORDER:" + filter.filterOrder() + "} Execution time = " + execTime + "ms");
Debug.compareContextState(filterName, copy);
}
}
if (t != null) {
throw t;
} else {
this.usageNotifier.notify(filter, s);
return o;
}
} catch (Throwable var15) {
if (bDebug) {
Debug.addRoutingDebug("Running Filter failed " + filterName + " type:" + filter.filterType() + " order:" + filter.filterOrder() + " " + var15.getMessage());
}
this.usageNotifier.notify(filter, ExecutionStatus.FAILED);
if (var15 instanceof ZuulException) {
throw (ZuulException)var15;
} else {
ZuulException ex = new ZuulException(var15, "Filter threw Exception", 500, filter.filterType() + ":" + filterName);
ctx.addFilterExecutionSummary(filterName, ExecutionStatus.FAILED.name(), execTime);
throw ex;
}
}
}
**最重要就是调用runfliter方法**
public ZuulFilterResult runFilter() {
ZuulFilterResult zr = new ZuulFilterResult();
if (!this.isFilterDisabled()) {
if (this.shouldFilter()) {
Tracer t = TracerFactory.instance().startMicroTracer("ZUUL::" + this.getClass().getSimpleName());
try {
Object res = this.run();
zr = new ZuulFilterResult(res, ExecutionStatus.SUCCESS);
} catch (Throwable var7) {
t.setName("ZUUL::" + this.getClass().getSimpleName() + " failed");
zr = new ZuulFilterResult(ExecutionStatus.FAILED);
zr.setException(var7);
} finally {
t.stopAndLog();
}
} else {
zr = new ZuulFilterResult(ExecutionStatus.SKIPPED);
}
}
循环执行过滤器的run方法
执行完preRoute()方法,若出现异常,则依次执行类型为error、post的过滤器,逻辑与pre过滤器一致,区别在于过滤器类型不一样;否则依次执行route()、postRoute()方法。
内部主要过滤器执行逻辑
pre类型
SevletDetectionFilter:决定由ZuulServlet还是DispatchServlet执行请求。
@Override
public Object run() {
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
if (!(request instanceof HttpServletRequestWrapper)
&& isDispatcherServletRequest(request)) {
//由DispatchServlet执行请求时,HttpServletRequest没有被包装过并且Attributes中包含
//DispatchServlet上下文参数
ctx.set(IS_DISPATCHER_SERVLET_REQUEST_KEY, true);
}
else {
//由ZuulServlet执行请求
ctx.set(IS_DISPATCHER_SERVLET_REQUEST_KEY, false);
}
return null;
}
FormBodyWrapperFilter:表单数据解析过滤器
@Override
public boolean shouldFilter() {
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
String contentType = request.getContentType();
//GET 请求不执行
if (contentType == null) {
return false;
}
//仅处理表单数据和在DispatchServlet中的Multipart数据。
try {
MediaType mediaType = MediaType.valueOf(contentType);
return MediaType.APPLICATION_FORM_URLENCODED.includes(mediaType)
|| (isDispatcherServletRequest(request)
&& MediaType.MULTIPART_FORM_DATA.includes(mediaType));
}
catch (InvalidMediaTypeException ex) {
return false;
}
}
@Override
public Object run() {
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
FormBodyRequestWrapper wrapper = null;
if (request instanceof HttpServletRequestWrapper) {
HttpServletRequest wrapped = (HttpServletRequest) ReflectionUtils
.getField(this.requestField, request);
//FormBodyRequestWrapper其实就是将request中的表单数据提取出来
wrapper = new FormBodyRequestWrapper(wrapped);
//把wrapper对象赋值给request中的request属性,供后续使用
ReflectionUtils.setField(this.requestField, request, wrapper);
//若请求已经被包装过,则将wrapper对象赋值给包装过的request中的request属性,供后续使用
if (request instanceof ServletRequestWrapper) {
ReflectionUtils.setField(this.servletRequestField, request, wrapper);
}
}
else {
wrapper = new FormBodyRequestWrapper(request);
ctx.setRequest(wrapper);
}
if (wrapper != null) {
ctx.getZuulRequestHeaders().put("content-type", wrapper.getContentType());
}
return null;
}
PreDecorationFilter:主要负责决定客户端请求的路由以及发送给下游服务的额外请求头。
@Override
public boolean shouldFilter() {
RequestContext ctx = RequestContext.getCurrentContext();
return !ctx.containsKey(FORWARD_TO_KEY) //还未经过决定请求转发的过滤器
&& !ctx.containsKey(SERVICE_ID_KEY); //还未经过决定serviceId的过滤器
}
@Override
public Object run() {
RequestContext ctx = RequestContext.getCurrentContext();
final String requestURI = this.urlPathHelper
.getPathWithinApplication(ctx.getRequest());
if (insecurePath(requestURI)) {
throw new InsecureRequestPathException(requestURI);
}
//根据URI以及配置的属性获取路由
Route route = this.routeLocator.getMatchingRoute(requestURI);
if (route != null) {
String location = route.getLocation();
if (location != null) {
ctx.put(REQUEST_URI_KEY, route.getPath());
ctx.put(PROXY_KEY, route.getId());
if (!route.isCustomSensitiveHeaders()) {
//将需要过滤的敏感头信息放入RequestContext
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());
}
//网关配置为url
if (location.startsWith(HTTP_SCHEME + ":")
|| location.startsWith(HTTPS_SCHEME + ":")) {
//设置下游服务的地址,供后续SimpleHostRoutingFilter(以httpClient的方式请求下游服务)使用
ctx.setRouteHost(getUrl(location));
ctx.addOriginResponseHeader(SERVICE_HEADER, location);
}
//网关配置的url以forward:开头,表明请求转发。
else if (location.startsWith(FORWARD_LOCATION_PREFIX)) {
//去除forward标识字符,重新设置下游服务url放入RequestContext,
//供后续SendForwardFilter过滤器使用
ctx.set(FORWARD_TO_KEY,
StringUtils.cleanPath(
location.substring(FORWARD_LOCATION_PREFIX.length()) + route.getPath()));
ctx.setRouteHost(null);
return null;
}
else {
//设置serviceId,供后续RibbonRoutingFilter使用
//由注册中心以及ribbon负载均衡决定最终下游服务地址
ctx.set(SERVICE_ID_KEY, location);
ctx.setRouteHost(null);
ctx.addOriginResponseHeader(SERVICE_ID_HEADER, location);
}
//配置文件是否设置加入代理头
//X-Forwarded-Host:请求的主机列表,用逗号隔开,越靠后越接近服务器
//X-Forwarded-Port:请求的端口列表,用逗号隔开,越靠后越接近服务器
//Forwarded-Proto:请求的协议列表,用逗号隔开,越靠后越接近服务器
//X-Forwarded-Prefix:请求网关的前缀
//X-Forwarded-For:请求的ip列表,用逗号隔开,越靠后越接近服务器
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;
}
route类型
RibbonRoutingFilter:由Ribbon负载均衡决定下游服务地址并且进行请求,将下游服务响应结果存入RequestContext
@Override
public boolean shouldFilter() {
RequestContext ctx = RequestContext.getCurrentContext();
//以serviceId的方式配置路由时执行该过滤器
return (ctx.getRouteHost() == null && ctx.get(SERVICE_ID_KEY) != null
&& ctx.sendZuulResponse());
}
@Override
public Object run() {
RequestContext context = RequestContext.getCurrentContext();
this.helper.addIgnoredHeaders();
try {
//根据请求头、请求参数、请求体、请求方法、serviceId、loadBalancerKey等构成
//ribbon上下文
RibbonCommandContext commandContext = buildCommandContext(context);
//创建command并且进行远程调用
ClientHttpResponse response = forward(commandContext);
//将下游服务响应结果存放在RequestContext,供后post过滤器SendResponseFilter使用。
setResponse(response);
return response;
}catch (ZuulException ex) {
throw new ZuulRuntimeException(ex);
}catch (Exception ex) {
throw new ZuulRuntimeException(ex);
}
}
SimpleHostRoutingFilter:以简单Apache HttpClient的方式调用下游服务
@Override
public boolean shouldFilter() {
//路由配置的是URL时执行该过滤器
return RequestContext.getCurrentContext().getRouteHost() != null
&& RequestContext.getCurrentContext().sendZuulResponse();
}
@Override
public Object run() {
RequestContext context = RequestContext.getCurrentContext();
HttpServletRequest request = context.getRequest();
//组装请求头,将忽略的请求头过滤
MultiValueMap<String, String> headers = this.helper
.buildZuulRequestHeaders(request);
//组装请求参数
MultiValueMap<String, String> params = this.helper
.buildZuulRequestQueryParams(request);
//组装请求方法
String verb = getVerb(request);
InputStream requestEntity = getRequestBody(request);
if (getContentLength(request) < 0) {
context.setChunkedRequestBody();
}
String uri = this.helper.buildZuulRequestURI(request);
this.helper.addIgnoredHeaders();
try {
//远程调用下游服务
CloseableHttpResponse response = forward(this.httpClient, verb, uri, request,
headers, params, requestEntity);
//保存下游服务响应结果
setResponse(response);
}
catch (Exception ex) {
throw new ZuulRuntimeException(handleException(ex));
}
return null;
}
post类型
SendResponseFilter:将响应信息从RequestContext中取出并组装,响应给浏览器。
@Override
public boolean shouldFilter() {
RequestContext context = RequestContext.getCurrentContext();
//不发生异常,下游服务有响应并且响应信息没被清空时执行
return context.getThrowable() == null
&& (!context.getZuulResponseHeaders().isEmpty()
|| context.getResponseDataStream() != null
|| context.getResponseBody() != null);
}
@Override
public Object run() {
try {
//组装下游服务的响应头和浏览器响应头
addResponseHeaders();
//响应给浏览器
writeResponse();
}
catch (Exception ex) {
ReflectionUtils.rethrowRuntimeException(ex);
}
return null;
}
```