Zuul现在应用的理详已经非常少了,至少都在使用Spring Cloud Gateway。层实Zuul实现是现原基于Servlet这种阻塞是IO这种机制是通过创建更多的线程来弥补其不足;而Cloud Gateway则是基于反应式非阻塞式的,使用少量的理详线程来做更多的事。以下是层实官方对阻塞与非阻塞的对比图:
图片
public class ZuulServerAutoConfiguration { @Autowired protected ZuulProperties zuulProperties; @Autowired protected ServerProperties server; // 主 @Bean @Primary public CompositeRouteLocator primaryRouteLocator( Collection<RouteLocator> routeLocators) { return new CompositeRouteLocator(routeLocators); } @Bean @ConditionalOnMissingBean(SimpleRouteLocator.class) public SimpleRouteLocator simpleRouteLocator() { return new SimpleRouteLocator(this.server.getServlet().getContextPath(), this.zuulProperties); } }
public class SimpleRouteLocator implements RouteLocator, Ordered { private ZuulProperties properties; private String dispatcherServletPath = "/"; private String zuulServletPath; private AtomicReference<Map<String, ZuulRoute>> routes = new AtomicReference<>(); // servletPath = server.servlet.contextPath 配置属性值 public SimpleRouteLocator(String servletPath, ZuulProperties properties) { this.properties = properties; if (StringUtils.hasText(servletPath)) { this.dispatcherServletPath = servletPath; } // 默认为:/zuul this.zuulServletPath = properties.getServletPath(); } // 该方法会在CompositeRouteLocator中调用,而本例的现原作用就是将ZuulRoute转换为Route对象 @Override 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 { // 将配置文件中配置的ZuulRoute路由转换为Route对象 values.add(getRoute(route, path)); } } return values; } protected Map<String, ZuulRoute> getRoutesMap() { if (this.routes.get() == null) { this.routes.set(locateRoutes()); } return this.routes.get(); } protected Map<String, ZuulRoute> locateRoutes() { LinkedHashMap<String, ZuulRoute> routesMap = new LinkedHashMap<>(); // properties.getRouets获取配置文件中配置的所有路由 for (ZuulRoute route : this.properties.getRoutes().values()) { routesMap.put(route.getPath(), route); } return routesMap; } protected Route getRoute(ZuulRoute route, String path) { String targetPath = path; // 获取配置文件的zuul.prefix属性值 String prefix = this.properties.getPrefix(); if (prefix.endsWith("/")) { // 删除 '/' 结尾的字符 prefix = prefix.substring(0, prefix.length() - 1); } // 判断配置的路由path属性值的开始字符串是不是以 '/prefix/'开头,如:/api/ // 如果配置的理详path:/api/api-1/**则匹配第一个条件,再判断zuul.strip-prefix属性值是层实否为true if (path.startsWith(prefix + "/") && this.properties.isStripPrefix()) { // 成立则会截取字符串删除'/prefix/'开头的字符,则最终targetPath = /api-1/** targetPath = path.substring(prefix.length()); } // 如果配置为true if (route.isStripPrefix()) { // 知道path中第一个'*' int index = route.getPath().indexOf("*") - 1; // 如果存在 if (index > 0) { // 截取第一个'*'之前的现原字符串 // 如上:最终routePrefix = /api-1 String routePrefix = route.getPath().substring(0, index); // 结合上面:targetPath = /** targetPath = targetPath.replaceFirst(routePrefix, ""); // 结合上面:prefix = /api + /api-1 = /api/api-1 prefix = prefix + routePrefix; } } // 上面的路径处理就是将配置的zuul.prefix + zuul.routes.xx.path Boolean retryable = this.properties.getRetryable(); if (route.getRetryable() != null) { retryable = route.getRetryable(); } // 构建Route对象 return new Route(route.getId(), targetPath, route.getLocation(), prefix, retryable, route.isCustomSensitiveHeaders() ? route.getSensitiveHeaders() : null, route.isStripPrefix()); } }
public class CompositeRouteLocator implements RefreshableRouteLocator { private final Collection<? extends RouteLocator> routeLocators; private ArrayList<RouteLocator> rl; // 收集所有RouteLocator,如上面的理详SimpleRouteLocator public CompositeRouteLocator(Collection<? extends RouteLocator> routeLocators) { rl = new ArrayList<>(routeLocators); AnnotationAwareOrderComparator.sort(rl); this.routeLocators = rl; } public List<Route> getRoutes() { List<Route> route = new ArrayList<>(); for (RouteLocator locator : routeLocators) { route.addAll(locator.getRoutes()); } return route; } }
这里的RouteLocator有何用,下面会见到。层实
Zuul会注册自身特定的现原ZuulHandlerMapping对象。
public class ZuulServerAutoConfiguration { // Controller接口;一个controller接口可以通过实现Controller或AbstractController接口来实现 @Bean public ZuulController zuulController() { return new ZuulController(); } @Bean public ZuulHandlerMapping zuulHandlerMapping(RouteLocator routes,理详 ZuulController zuulController) { // HandlerMapping中注入了RouteLocator,也就是上面的CompositeRouteLocator对象 ZuulHandlerMapping mapping = new ZuulHandlerMapping(routes, zuulController); mapping.setErrorController(this.errorController); mapping.setCorsConfigurations(getCorsConfigurations()); return mapping; } }
public class ZuulController extends ServletWrappingController { public ZuulController() { // 调用父类的方法设置父类的成员变量servletClass 该Class是个Servlet // 在下面会看到如何使用ZuulServlet对象 setServletClass(ZuulServlet.class); setServletName("zuul"); // 支持任意的请求方法 setSupportedMethods((String[]) null); // Allow all } // 该方法是Controller接口的核心方法 @Override public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception { try { return super.handleRequestInternal(request, response); } } }
HandlerMapping作用就是从通过请求的URI查找到合适的处理句柄,找到就之间返回,接下来就是由HandlerAdapter进行实际的处理了
public class ZuulHandlerMapping extends AbstractUrlHandlerMapping { private volatile boolean dirty = true; private final ZuulController zuul; public ZuulHandlerMapping(RouteLocator routeLocator, ZuulController zuul) { this.routeLocator = routeLocator; this.zuul = zuul; // 优先级就很高了,值越小优先级越高 setOrder(-200); } 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) { // 首次时dirty为true,这时候就会注册处理句柄了 registerHandlers(); this.dirty = false; } } } // 调用父类的方法查找合适的请求处理器,从父类handlerMap属性(Map)集合中查找 return super.lookupHandler(urlPath, request); } private void registerHandlers() { // 这里就调用了上面CompositeRouteLocator#getRoutes,这样就得到了所有的路由信息 Collection<Route> routes = this.routeLocator.getRoutes(); if (routes.isEmpty()) { this.logger.warn("No routes found from RouteLocator"); } else { for (Route route : routes) { // 注意:所有的请求路径都对应同一个Handler也就是上面的ZuulController对象 // 到这就知道了所有的路由请求都会被ZuulController处理 // 调用父类方法,将路由信息添加到父类handlerMap属性中(Map)集合 registerHandler(route.getFullPath(), this.zuul); } } } }
一个请求过来时,通过上面的ZuulHandlerMapping对象找到了合适的处理句柄也就是ZuulController,接下来就是由合适的HandlerAdapter进行真正的调用了。
现在确定了handler对象是ZuulController,接下来就是查看那个HandlerAdatper能够进行处理了。
ZuulController实现了Controller接口,能够处理该类型的是:SimpleControllerHandlerAdaper。
public class SimpleControllerHandlerAdapter implements HandlerAdapter { public boolean supports(Object handler) { return (handler instanceof Controller); } @Override @Nullable public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 调用ZuulController#handleRequest方法 return ((Controller) handler).handleRequest(request, response); } }
回到ZuulController
public class ZuulController extends ServletWrappingController { public ZuulController() { // 记住这里我们给父类属性servletClass 设置为ZuulServlet setServletClass(ZuulServlet.class); setServletName("zuul"); // 支持任意方法 setSupportedMethods((String[]) null); // Allow all } @Override public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception { try { // 调用父类方法 return super.handleRequestInternal(request, response); } finally { // @see com.netflix.zuul.context.ContextLifecycleFilter.doFilter RequestContext.getCurrentContext().unset(); } } } public class ServletWrappingController extends AbstractController implements BeanNameAware, InitializingBean, DisposableBean { private Class<? extends Servlet> servletClass; public void setServletClass(Class<? extends Servlet> servletClass) { this.servletClass = servletClass; } protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception { // 调用ZuulServlet#service方法(具体的处理还是通过ZuulServlet处理) this.servletInstance.service(request, response); return null; } // 该Bean在初始化解决会通过serveltClass实例化ZuulServlet对象。 public void afterPropertiesSet() throws Exception { if (this.servletClass == null) { throw new IllegalArgumentException("'servletClass' is required"); } if (this.servletName == null) { this.servletName = this.beanName; } // 实例化 this.servletInstance = ReflectionUtils.accessibleConstructor(this.servletClass).newInstance(); // servlet初始化 this.servletInstance.init(new DelegatingServletConfig()); } }
public class ZuulServlet extends HttpServlet { public void init(ServletConfig config) throws ServletException { super.init(config); String bufferReqsStr = config.getInitParameter("buffer-requests"); boolean bufferReqs = bufferReqsStr != null && bufferReqsStr.equals("true") ? true : false; zuulRunner = new ZuulRunner(bufferReqs); } // 任意阶段发生了异常都会执行error,post阶段 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 { // pre节点的ZuulFilter执行 preRoute(); } catch (ZuulException e) { // error阶段 error(e); postRoute(); return; } try { // route阶段的路由执行 // 这里其实就是通过网络api调用目标服务的接口了 // 我们可以实现自己的处理api接口的调用 // 如果你真的自己实现了,那么请记得最后执行这行代码 // context.setRouteHost(null); // prevent SimpleHostRoutingFilter from running // 这样就阻止默认的执行了 // SimpleHostRoutingFilter 网络请求处理使用的Apache Client // 在该节点得到数据后我们可以通过ProxyRequestHelper工具类将结果(InputStream)保存到 // RequestContext中,一切顺利在post阶段就会由SendResponseFilter从RequestContext中获取InputStream,然后写到客户端 route(); } catch (ZuulException e) { error(e); // 发生了异常也会执行 postRoute(); return; } try { // post阶段 // 比如:系统提供的SendResponseFilter过滤器就是真正往客户端开始写数据了。 postRoute(); } catch (ZuulException e) { error(e); return; } } catch (Throwable e) { error(new ZuulException(e, 500, "UNHANDLED_EXCEPTION_" + e.getClass().getName())); } finally { RequestContext.getCurrentContext().unset(); } } void init(HttpServletRequest servletRequest, HttpServletResponse servletResponse) { // 将request,response都进行了Wrapper(HttpServletRequestWrapper, HttpServletResponseWrapper) zuulRunner.init(servletRequest, servletResponse); } }
在上面就看到了各个阶段的Filter的执行。这里需要注意一点,在每个阶段执行的时候都会通过FilterLoader来获取对应阶段的ZuulFilter。
这个FilterLoader对象并不是一个被容器管理的对象,下面来看这个FilterLoader是如何收集所有ZuulFilter。
public class ZuulServerAutoConfiguration { @Configuration(proxyBeanMethods = false) protected static class ZuulFilterConfiguration { // 获取容器中所有的ZuulFilter @Autowired private Map<String, ZuulFilter> filters; // 该Bean中有通过注解@PostConstruct标注的Bean在初始化阶段执行的方法。 @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 final Map<String, ZuulFilter> filters; 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; } // 该方法作用就是将所有的ZuulFilter添加到FilterRegistry中 @PostConstruct public void contextInitialized() { for (Map.Entry<String, ZuulFilter> entry : this.filters.entrySet()) { // 将所有的ZuulFilter注册到FilterRegistry中 filterRegistry.put(entry.getKey(), entry.getValue()); } } }
public class FilterLoader { public Object runFilters(String sType) throws Throwable { boolean bResult = false; // 获取对应阶段的ZuulFilter 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 List<ZuulFilter> getFiltersByType(String filterType) { List<ZuulFilter> list = hashFiltersByType.get(filterType); if (list != null) return list; list = new ArrayList<ZuulFilter>(); // 从FilterRegistry中获取所有的ZuulFilter;在上面看到了所有的ZuulFilter都被添加到了FilterRegistry中 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; } }
以上就是Zuul网关的底层实现原理。
责任编辑:武晓燕 来源: 实战案例锦集 ZuulIO反应式(责任编辑:探索)
创业板三季报:每家公司平均实现营收13.80亿元 板块成长性特色鲜明
一个多月内两家排队银行接连上会 A+H上市银行数量有望再度增加
568万元!四川省攀枝花市获省建筑领域绿色低碳循环发展专项资金支持
税收支持体系打造“升级版” 长三角税收一体化正跑出“加”速度
江山欧派(603208.SH)公布消息:公开发行可转债申请获审核通过
山东省陵城区财政优先保障环保资金投入 全面推进生态文明事业健康发展