Zuul是Netflix开源的微服务网关,它能够和Eureka,consul,Ribbon,Hystrix等组件配合使用,网上也有不少如何使用zuul的文章,咱们也在生产环境使用了,因此读了下zuul的源码,下面把它分享出来,与你们探讨下zuul核心原理。java
1、spring-cloud-zuul是如何映射路由的?web
zuul的路由映射是使用springMVC功能,咱们知道springMVC有两大核心组件:正则表达式
具体的springMVC原理这里不作讲解,咱们来看下zuul是如何自定义HandlerMapping来注册路由映射的?下图是springMVC的类继承关系spring
很清晰看到Zuul提供的ZuulHandlerMapping是AbstractUrlHandlerMapping的子类,这个类是根据url来查找处理器,核心处理方法在lookupHandler里面:数据库
@Override protected Object lookupHandler(String urlPath, HttpServletRequest request) throws Exception { if (this.errorController != null && urlPath.equals(this.errorController.getErrorPath())) { return null; } //过滤忽略的路由规则 String[] ignored = this.routeLocator.getIgnoredPaths().toArray(new String[0]); if (PatternMatchUtils.simpleMatch(ignored, urlPath)) { return null; } RequestContext ctx = RequestContext.getCurrentContext(); if (ctx.containsKey("forward.to")) { return null; } if (this.dirty) { synchronized (this) { if (this.dirty) {//若是没有加载过路由或者路由有刷新,则加载路由 registerHandlers(); this.dirty = false; } } } //根据url调用父类获取处理器 return super.lookupHandler(urlPath, request); } private void registerHandlers() { //使用路由定位器获取路由规则 Collection<Route> routes = this.routeLocator.getRoutes(); if (routes.isEmpty()) { this.logger.warn("No routes found from RouteLocator"); } else { for (Route route : routes) { //调用父类,注册处理器 registerHandler(route.getFullPath(), this.zuul); } } }
梳理一下,以上方法的核心几步:apache
ZuulController
,是Controller
的子类,对应的适配器是SimpleControllerHandlerAdapter
,也就说每个路由规则公共处理器都是ZuulController
,这个处理器最终会调用ZuulServlet
通过zuul定义的和自定义的拦截器,这个zuul的核心,后面咱们做详细讲解。2、路由定位器segmentfault
在上面咱们注册了路由规则,而路由规则是由路由定位器获取,那么zuul给咱们提供哪些路由定位器,类图以下:app
/服务名称/**
映射成路由规则扩展:负载均衡
一、这里咱们能够实现本身的路由定位器,扩展本身想要的功能,如从数据库加载路由规则,能够参考文章ide
二、利用服务发现的路由定位器去加载理由规则的时候,咱们只是简单的是把serviceId映射成路由规则,有的时间咱们仍是想在serviceId和路由之间提供约定 ,因而咱们可使用PatternServiceRouteMapper来实现
@Bean public PatternServiceRouteMapper serviceRouteMapper() { return new PatternServiceRouteMapper( "(?<name>^.+)-(?<version>v.+$)", "${version}/${name}"); }
这样serviceId:myusers-v1
将被映射到路由/ v1 / myusers / **
,这里任何正则表达式均可以接受,根据本身须要本身设定。
3、过滤器
前面提到,因此路由请求都会被控制器ZuulControoler
拦截到,最终交由ZuulServlet
来处理,核心处理代码以下:
@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(); } }
这段代码体现了zuul过滤器的生命周期,官方提供了一张图很形象的展现:
zuul把过滤器分为四个阶段,分别是
zuul为咱们提供了各个阶段的过滤器一共10个
这里咱们来着重看下路由阶段的两个过滤器
CloseableHttpClient
来发送http请求HttpClient
来转发请求未完待续......