前言
Zuul是Netflix开源的微服务网关,他可以和Eureka,Ribbon,Hystrix等组件配合使用.目前被spring-cloud集成到体系里面。
- zuul源码里面一大堆test代码
- zuul好多代码都是11年的
网关是什么,为什么需要网关
百度百科是这样解释的:网关(Gateway)又称网间连接器、协议转换器。网关在网络层以上实现网络互连,是复杂的网络互连设备,仅用于两个高层协议不同的网络互连。仔细看下下面的图。
- 网关负责接收请求,并把请求转发给后端正确的服务。这是说明与上面的图是不是很像Nginx的功能,不错。网关最基础的功能是反向代理。直接用反向代理的软件不就行了吗?,那麻烦看第二点
- 对接收的请求进行校验,限流,熔断,统计等。市面上大多反向代理软件比如nginx,apache都是用c写的,扩展太难了。所以用容易扩展的语言实现。
zuul的基本架构设计
zuul核心模块是zuul-core。spring-cloud集成了zuul实现的jar是spring-cloud-netflix-zuul。
zuul-core
- filter
- FilterRegistry
- FilterLoader
- FilterProcessor
- ZuulRunner
当一个请求来了,这是类的调用链。
filter
filter是zuul核心执行流程,IZuulFilter是接口,主要是实现run方法。而ZuulFilter又两个抽象方法filterType( filter类型)和filterOrder(filter的排序),麻烦仔细看下ZuulFilter的runFilter方法,主要是执行run方法,并且对run的执行行为进行处理。
public interface IZuulFilter {
boolean shouldFilter();
Object run() throws ZuulException;
}
public abstract class ZuulFilter implements IZuulFilter, Comparable<ZuulFilter> {
private final AtomicReference<DynamicBooleanProperty> filterDisabledRef = new AtomicReference<>();
abstract public String filterType();
abstract public int filterOrder();
public boolean isStaticFilter() {
return true;
}
public String disablePropertyName() {
return "zuul." + this.getClass().getSimpleName() + "." + filterType() + ".disable";
}
public boolean isFilterDisabled() {
filterDisabledRef.compareAndSet(null, DynamicPropertyFactory.getInstance().getBooleanProperty(disablePropertyName(), false));
return filterDisabledRef.get().get();
}
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;
}
}
public class FilterConstants {
public static final String ERROR_TYPE = "error";
public static final String POST_TYPE = "post";
public static final String PRE_TYPE = "pre";
public static final String ROUTE_TYPE = "route";
}
FilterRegistry
filterRegistry是filter的注册对象,非常简单。只需要注意下静态成员变量INSTANCE。注意:zuul2.0以后可能会删除INSTANCE
public class FilterRegistry{
private static final FilterRegistry INSTANCE = new FilterRegistry();
public static final FilterRegistry instance() {
return INSTANCE;
}
private final ConcurrentHashMap<String, ZuulFilter> filters = new ConcurrentHashMap<String, ZuulFilter>();
public ZuulFilter remove(String key) {
return this.filters.remove(key);
}
public ZuulFilter get(String key) {
return this.filters.get(key);
}
public void put(String key, ZuulFilter filter) {
this.filters.putIfAbsent(key, filter);
}
public int size() {
return this.filters.size();
}
public Collection<ZuulFilter> getAllFilters() {
return this.filters.values();
}
}
FilterLoader
filterLoader 负责动态加载filter。加载方法有通过className,File对象,Groovy语言源代码。如果是Netflix这样的大公司有一套管理系统,负责FilterLoader就很鸡肋了。
public class FilterLoader {
final static FilterLoader INSTANCE = new FilterLoader();
private static final Logger LOG = LoggerFactory.getLogger(FilterLoader.class);
private final ConcurrentHashMap<String, Long> filterClassLastModified = new ConcurrentHashMap<String, Long>();
private final ConcurrentHashMap<String, String> filterClassCode = new ConcurrentHashMap<String, String>();
private final ConcurrentHashMap<String, String> filterCheck = new ConcurrentHashMap<String, String>();
private final ConcurrentHashMap<String, List<ZuulFilter>> hashFiltersByType = new ConcurrentHashMap<String, List<ZuulFilter>>();
private FilterRegistry filterRegistry = FilterRegistry.instance();
static DynamicCodeCompiler COMPILER;
static FilterFactory FILTER_FACTORY = new DefaultFilterFactory();
public void setCompiler(DynamicCodeCompiler compiler) {
COMPILER = compiler;
}
public void setFilterRegistry(FilterRegistry r) {
this.filterRegistry = r;
}
public void setFilterFactory(FilterFactory factory) {
FILTER_FACTORY = factory;
}
public static FilterLoader getInstance() {
return INSTANCE;
}
public ZuulFilter getFilter(String sCode, String sName) throws Exception {
if (filterCheck.get(sName) == null) {
filterCheck.putIfAbsent(sName, sName);
if (!sCode.equals(filterClassCode.get(sName))) {
LOG.info("reloading code " + sName);
filterRegistry.remove(sName);
}
}
ZuulFilter filter = filterRegistry.get(sName);
if (filter == null) {
Class clazz = COMPILER.compile(sCode, sName);
if (!Modifier.isAbstract(clazz.getModifiers())) {
filter = (ZuulFilter) FILTER_FACTORY.newInstance(clazz);
}
}
return filter;
}
public int filterInstanceMapSize() {
return filterRegistry.size();
}
public boolean putFilter(File file) throws Exception {
String sName = file.getAbsolutePath() + file.getName();
if (filterClassLastModified.get(sName) != null && (file.lastModified() != filterClassLastModified.get(sName))) {
LOG.debug("reloading filter " + sName);
filterRegistry.remove(sName);
}
ZuulFilter filter = filterRegistry.get(sName);
if (filter == null) {
Class clazz = COMPILER.compile(file);
if (!Modifier.isAbstract(clazz.getModifiers())) {
filter = (ZuulFilter) FILTER_FACTORY.newInstance(clazz);
List<ZuulFilter> list = hashFiltersByType.get(filter.filterType());
if (list != null) {
hashFiltersByType.remove(filter.filterType()); //rebuild this list
}
filterRegistry.put(file.getAbsolutePath() + file.getName(), filter);
filterClassLastModified.put(sName, file.lastModified());
return true;
}
}
return false;
}
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;
}
}
FilterProcessor
基本调用链postRoute,error,route,preRoute --> runFilters-->processZuulFilter【重点方法】 。获得Filter的方式: runFilters-> FilterLoader.getInstance().getFiltersByType(sType)
public class FilterProcessor {
static FilterProcessor INSTANCE = new FilterProcessor();
protected static final Logger logger = LoggerFactory.getLogger(FilterProcessor.class);
private FilterUsageNotifier usageNotifier;
public void postRoute() throws ZuulException {
try {
runFilters("post");
} catch (ZuulException e) {
throw e;
} catch (Throwable e) {
throw new ZuulException(e, 500, "UNCAUGHT_EXCEPTION_IN_POST_FILTER_" + e.getClass().getName());
}
}
public void error() {
try {
runFilters("error");
} catch (Throwable e) {
logger.error(e.getMessage(), e);
}
}
public void route() throws ZuulException {
try {
runFilters("route");
} catch (ZuulException e) {
throw e;
} catch (Throwable e) {
throw new ZuulException(e, 500, "UNCAUGHT_EXCEPTION_IN_ROUTE_FILTER_" + e.getClass().getName());
}
}
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());
}
}
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;
}
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;
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);
}
break;
default:
break;
}
if (t != null) throw t;
usageNotifier.notify(filter, s);
return o;
} catch (Throwable e) {
if (bDebug) {
Debug.addRoutingDebug("Running Filter failed " + filterName + " type:" + filter.filterType() + " order:" + filter.filterOrder() + " " + e.getMessage());
}
usageNotifier.notify(filter, ExecutionStatus.FAILED);
if (e instanceof ZuulException) {
throw (ZuulException) e;
} else {
ZuulException ex = new ZuulException(e, "Filter threw Exception", 500, filter.filterType() + ":" + filterName);
ctx.addFilterExecutionSummary(filterName, ExecutionStatus.FAILED.name(), execTime);
throw ex;
}
}
}
FilterUsageNotifier
每个filter执行完成或者异常,失败。都会调用FilterUsageNotifier.notify的方法。这样可以对执行结果进行监控。
public static class BasicFilterUsageNotifier implements FilterUsageNotifier {
private static final String METRIC_PREFIX = "zuul.filter-";
@Override
public void notify(ZuulFilter filter, ExecutionStatus status) {
DynamicCounter.increment(METRIC_PREFIX + filter.getClass().getSimpleName(), "status", status.name(), "filtertype", filter.filterType());
}
}
ZuulRunner
- zuulRunner代理了FilterProcessor
- zuulRunner.init是整个体系最核心的一个方法了,负责把servletRequest放到RequestContext.getCurrentContext上下文中,在当前线程可以获得HttpServletResponse,对象进行操作。同时可以传递其他参数。请自信看RibbonRoutingFilter。
public class ZuulRunner {
private boolean bufferRequests;
public ZuulRunner() {
this.bufferRequests = true;
}
public ZuulRunner(boolean bufferRequests) {
this.bufferRequests = bufferRequests;
}
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));
}
public void postRoute() throws ZuulException {
FilterProcessor.getInstance().postRoute();
}
public void route() throws ZuulException {
FilterProcessor.getInstance().route();
}
public void preRoute() throws ZuulException {
FilterProcessor.getInstance().preRoute();
}
public void error() {
FilterProcessor.getInstance().error();
}
}
spring-cloud-zuul
spring-cloud-zuul主要是两个类ZuulServerAutoConfiguration与ZuulProxyAutoConfiguration。分别向spring容器注入很多filter与其他管理对象。
注入的Filter如下
- ServletDetectionFilter
- FormBodyWrapperFilter
- DebugFilter
- Servlet30WrapperFilter
- SendResponseFilter
- SendErrorFilter
- SendForwardFilter
- PreDecorationFilter
- RibbonRoutingFilter
- SimpleHostRoutingFilter
RouteLocator
routeLocator是zuul route管理体系。SimpleRouteLocator是最简单的实现。如何大家需要维护的自己路由,向spring容器注入自己的实现就行了。
routeLocator是zuul route管理体系。SimpleRouteLocator是最简单的实现。如何大家需要维护的自己路由,向spring容器注入自己的实现就行了。
routeLocator是zuul route管理体系。SimpleRouteLocator是最简单的实现。如何大家需要维护的自己路由,向spring容器注入自己的实现就行了。
与Eureka集成
DiscoveryClientRouteLocator类负责与eureka进行集成。DiscoveryClientRouteLocator会从eureka服务器获取服务,并入住到SimpleRouteLocator
ZuulController
ZuulController负责向spring容器里面注入ZuulServlet对象
ZuulFilterConfiguration
ZuulFilterConfiguration是整个spring-cloud-zuul核心环节,ZuulFilterInitializer负责把FilterLoader,FilterRegistry,CounterFactory(统计模块),TracerFactory(felter执行跟踪模块),链接在一起。那么最基础的zuul体系组件完成
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);
}
}
public class ZuulFilterInitializer {
private static final Log log = LogFactory.getLog(ZuulFilterInitializer.class);
private final Map<String, ZuulFilter> filters;
private final CounterFactory counterFactory;
private final TracerFactory tracerFactory;
private final FilterLoader filterLoader;
private final FilterRegistry filterRegistry;
public ZuulFilterInitializer(Map<String, ZuulFilter> filters,CounterFactory counterFactory,TracerFactory tracerFactory, FilterLoader filterLoader,FilterRegistry filterRegistry) {
this.filters = filters;
this.counterFactory = counterFactory;
this.tracerFactory = tracerFactory;
this.filterLoader = filterLoader;
this.filterRegistry = filterRegistry;
}
@PostConstruct
public void contextInitialized() {
log.info("Starting filter initializer");
TracerFactory.initialize(tracerFactory);
CounterFactory.initialize(counterFactory);
for (Map.Entry<String, ZuulFilter> entry : this.filters.entrySet()) {
filterRegistry.put(entry.getKey(), entry.getValue());
}
}
@PreDestroy
public void contextDestroyed() {
log.info("Stopping filter initializer");
for (Map.Entry<String, ZuulFilter> entry : this.filters.entrySet()) {
filterRegistry.remove(entry.getKey());
}
clearLoaderCache();
TracerFactory.initialize(null);
CounterFactory.initialize(null);
}
private void clearLoaderCache() {
Field field = ReflectionUtils.findField(FilterLoader.class, "hashFiltersByType");
ReflectionUtils.makeAccessible(field);
@SuppressWarnings("rawtypes")
Map cache = (Map) ReflectionUtils.getField(field, filterLoader);
cache.clear();
}
ZuulServlet
zuulservlet 负责拦截所有请求,执行preRoute,route,postRoute,error行为。
public void service(javax.servlet.ServletRequest servletRequest, javax.servlet.ServletResponse servletResponse) throws ServletException, IOException {
try {
init((HttpServletRequest) servletRequest, (HttpServletResponse) servletResponse);
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();
}
}