接着学习,之前学习过了feign和hystrix源码,发现还是有些概念不是很清晰,需要加强自身的基础。
这一篇学习zuul。
为了使用zuul,我们需要新建一个module,
,只需要在启动类上增加注解
即可。
配置文件中除了配置关键的zone,常规的timeout等,最关键的就是zuul的route配置。这里配置如下:
zuul:
routes:
feign:
path: /app1/**
serviceId: feign
another:
path: /app2/**
serviceId: another
这里配置对应的bean为ZuulProperties,
可以看到有一个变量LikedHashMap,保存了所有的route信息。
/**
* Map of route names to properties.
*/
private Map<String, ZuulRoute> routes = new LinkedHashMap<>();
ZuulRoute保存了上述对应的配置文件内容:
public static class ZuulRoute {
/**
* The ID of the route (the same as its map key by default).
*/
private String id;
/**
* The path (pattern) for the route, e.g. /foo/**.
*/
private String path;
/**
* The service ID (if any) to map to this route. You can specify a physical URL or
* a service, but not both.
*/
private String serviceId;
/**
* A full physical URL to map to the route. An alternative is to use a service ID
* and service discovery to find the physical address.
*/
private String url;
/**
* Flag to determine whether the prefix for this route (the path, minus pattern
* patcher) should be stripped before forwarding.
*/
private boolean stripPrefix = true;
/**
* Flag to indicate that this route should be retryable (if supported). Generally
* retry requires a service ID and ribbon.
*/
private Boolean retryable;
/**
* List of sensitive headers that are not passed to downstream requests. Defaults
* to a "safe" set of headers that commonly contain user credentials. It's OK to
* remove those from the list if the downstream service is part of the same system
* as the proxy, so they are sharing authentication data. If using a physical URL
* outside your own domain, then generally it would be a bad idea to leak user
* credentials.
*/
private Set<String> sensitiveHeaders = new LinkedHashSet<>();
private boolean customSensitiveHeaders = false;
对应了配置文件中的内容。
zuul关键的配置在于@EnableZuulProxy。
@EnableCircuitBreaker
@EnableDiscoveryClient
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(ZuulProxyConfiguration.class)
public @interface EnableZuulProxy {
}
该注解引入了类ZuulProxyConfiguration,该类加载了zuul的基本的配置,包括负载均衡,服务注册和发现,filter等。
负载均衡的Ribbon
@Import({ RibbonCommandFactoryConfiguration.RestClientRibbonConfiguration.class,
RibbonCommandFactoryConfiguration.OkHttpRibbonConfiguration.class,
RibbonCommandFactoryConfiguration.HttpClientRibbonConfiguration.class }
在它的父类ZuulConfiguration中配置了上面所说的 ZuulProperties配置,基本的filter等。
@Autowired
protected ZuulProperties zuulProperties;
@Autowired
protected ServerProperties server;
默认的pre filter:
@Bean
public PreDecorationFilter preDecorationFilter(RouteLocator routeLocator, ProxyRequestHelper proxyRequestHelper) {
return new PreDecorationFilter(routeLocator, this.server.getServletPrefix(), this.zuulProperties,
proxyRequestHelper);
}
// pre filters
@Bean
public ServletDetectionFilter servletDetectionFilter() {
return new ServletDetectionFilter();
}
@Bean
public FormBodyWrapperFilter formBodyWrapperFilter() {
return new FormBodyWrapperFilter();
}
@Bean
public DebugFilter debugFilter() {
return new DebugFilter();
}
@Bean
public Servlet30WrapperFilter servlet30WrapperFilter() {
return new Servlet30WrapperFilter();
}
默认的route filter:
// route filters
@Bean
public RibbonRoutingFilter ribbonRoutingFilter(ProxyRequestHelper helper,
RibbonCommandFactory<?> ribbonCommandFactory) {
RibbonRoutingFilter filter = new RibbonRoutingFilter(helper, ribbonCommandFactory, this.requestCustomizers);
return filter;
}
@Bean
public SimpleHostRoutingFilter simpleHostRoutingFilter(ProxyRequestHelper helper, ZuulProperties zuulProperties) {
return new SimpleHostRoutingFilter(helper, zuulProperties);
}
@Bean
public SendForwardFilter sendForwardFilter() {
return new SendForwardFilter();
}
默认的post filter:
// post filters
@Bean
public SendResponseFilter sendResponseFilter() {
return new SendResponseFilter();
}
@Bean
public SendErrorFilter sendErrorFilter() {
return new SendErrorFilter();
}
其中所有的filter初始化如下:
@Configuration
protected static class ZuulFilterConfiguration {
@Autowired
private Map<String, ZuulFilter> filters;
@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);
}
}
FilterRegistry是一个单例,其中包含了一个CurrentHashMap,保存了所有的zuulFilter,并提供了基本的查询,删除等方法。
在FilterLoader中持有FilterRegistry对象,做了一些基本操作的包装,对外提供操作。
最关键的是注入了zuul的核心servlet,
@Bean
@ConditionalOnMissingBean(name = "zuulServlet")
public ServletRegistrationBean zuulServlet() {
ServletRegistrationBean 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就是类似于spring的DispatchServlet,
@Override
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();
}
}
首先是初始化servletContext,看其init方法:
void init(HttpServletRequest servletRequest, HttpServletResponse servletResponse) {
zuulRunner.init(servletRequest, servletResponse);
}
public void init(HttpServletRequest servletRequest, HttpServletResponse servletResponse) {
RequestContext ctx = RequestContext.getCurrentContext();
if (bufferRequests) {
ctx.setRequest(new HttpServletRequestWrapper(servletRequest));
} else {
ctx.setRequest(servletRequest);
}
ctx.setResponse(new HttpServletResponseWrapper(servletResponse));
}
实际去执行的是zuulRunner,其会给每一个请求都会创建一个requestContext,该对象会被所有的filter共享。
看其preRoute()方法,实际也是调用的zuulRunner方法,
/**
* executes "pre" filterType ZuulFilters
*
* @throws ZuulException
*/
public void preRoute() throws ZuulException {
FilterProcessor.getInstance().preRoute();
}
最终交给FilterProcessor去执行,看其文档描述 This the the core class to execute filters.
具体执行逻辑为:
/**
* runs all filters of the filterType sType/ Use this method within filters to run custom filters by type
*
* @param sType the filterType.
* @return
* @throws Throwable throws up an arbitrary exception
*/
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中根据类型获取到所有的filter,然后执行。
这一篇主要看源码,下一篇继续调试一遍zuul,看一下各种对象的创建和状态。。。。。。