资讯专栏INFORMATION COLUMN

spring-cloud-zuul原理解析(一)

qingshanli1988 / 2497人阅读

摘要:是开源的微服务网关,它可以和,等组件配合使用,网上也有很多如何使用的文章,我们也在生产环境使用了,所以读了下的源码,下面把它分享出来,与大家探讨下核心原理。

Zuul是Netflix开源的微服务网关,它可以和Eureka,consul,Ribbon,Hystrix等组件配合使用,网上也有很多如何使用zuul的文章,我们也在生产环境使用了,所以读了下zuul的源码,下面把它分享出来,与大家探讨下zuul核心原理。

一、spring-cloud-zuul是如何映射路由的?

zuul的路由映射是使用springMVC功能,我们知道springMVC有两大核心组件:

HandlerMapping:映射器

HandlerAdapter:适配器

具体的springMVC原理这里不做讲解,我们来看下zuul是如何自定义HandlerMapping来注册路由映射的?下图是springMVC的类继承关系

很清晰看到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 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);
        }
    }
}

梳理一下,以上方法的核心几步:

判断urlPath是否被忽略,如果忽略则返回null

判断路由规则有没有加载过或者更新过,没有加载或者有更新则重新加载

注册处理器的时候,使用的是ZuulController,是Controller的子类,对应的适配器是SimpleControllerHandlerAdapter,也就说每一个路由规则公共处理器都是ZuulController,这个处理器最终会调用ZuulServlet经过zuul定义的和自定义的拦截器,这个zuul的核心,后面我们作详细讲解。

根据url找到处理器,返回

二、路由定位器

在上面我们注册了路由规则,而路由规则是由路由定位器获取,那么zuul给我们提供哪些路由定位器,类图如下:

SimpleRouteLocator:主要加载配置文件的路由规则

DiscoveryClientRouteLocator:服务发现的路由定位器,去注册中心如Eureka,consul等拿到服务名称,以这样的方式/服务名称/**映射成路由规则

CompositeRouteLocator:复合路由定位器,主要集成所有的路由定位器(如配置文件路由定位器,服务发现定位器,自定义路由定位器等)来路由定位。

RefreshableRouteLocator:路由刷新,只有实现了此接口的路由定位器才能被刷新

扩展

1、这里我们可以实现自己的路由定位器,扩展自己想要的功能,如从数据库加载路由规则,可以参考文章

2、利用服务发现的路由定位器去加载理由规则的时候,我们只是简单的是把serviceId映射成路由规则,有的时间我们还是想在serviceId和路由之间提供约定 ,于是我们可以使用PatternServiceRouteMapper来实现

@Bean
public PatternServiceRouteMapper serviceRouteMapper() {
    return new PatternServiceRouteMapper(
        "(?^.+)-(?v.+$)",
        "${version}/${name}");
}

这样serviceId:myusers-v1将被映射到路由/ v1 / myusers / **,这里任何正则表达式都可以接受,根据自己需要自己设定。

三、过滤器

前面提到,所以路由请求都会被控制器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把过滤器分为四个阶段,分别是

pre:主要是在请求路由之前调用,很多验证可以在这里做

route:在路由请求时候被调用,主要用来转发请求

post:主要用来处理响应请求

error:当错误发生时,会经由这个类型的过滤器处理

zuul为我们提供了各个阶段的过滤器一共10个

这里我们来着重看下路由阶段的两个过滤器

SimpleHostRoutingFilter:主要提供当路由设置url方式时,由这个路由器来转发请求,使用的是apache的CloseableHttpClient来发送http请求

RibbonRoutingFilter:当路由设置serviceId时,由此过滤器来转发请求,这里集成了ribbon,Hystrix,实现负载均衡,熔断的功能;默认情况下也是使用apache的HttpClient来转发请求

未完待续......

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/76653.html

相关文章

  • 使用servlet3.0异步特性改造spring-cloud-zuul

    摘要:不过在出来之后支持异步了,可以把业务操作放到独立的线程池里面去,这样可以尽快释放线程,本身也支持异步了,本篇文章将带你如何使用的异步特性来改造优化其性能。 ​ 我们知道spring-cloud-zuul是依赖springMVC来注册路由的,而springMVC又是在建立在servlet之上的(这里微服务专家杨波老师写过一篇文章讲述其网络模型,可以参考看看),在servlet3.0...

    HmyBmny 评论0 收藏0
  • 起学习使用Spring Cloud Netflix之Zuul

    摘要:前言在体系中扮演着统一网关的角色,负责与外部交互。与结合使用,可以根据服务名来访问后端的服务,对于而言,也是一个。这段代码表示,如果请求中没有信息,就会报错。 前言 Zuul在Spring Cloud 体系中扮演着统一网关的角色,负责与外部交互。用户可以通过不同的URL特征来访问不同的后端服务,类似于Nginx代理的效果。Zuul与Eureka结合使用,可以根据服务名来访问后端的服务,...

    FullStackDeveloper 评论0 收藏0
  • JavaScript 工作原理之十四-解析,语法抽象树及最小化解析时间的 5 条小技巧

    摘要:事实是只是部分语言的不同表示法。基于这些,解析器会进行立即或者懒解析。然而,解析器做了完全不相关的额外无用功即解析函数。这里不解析函数,该函数声明了却没有指出其用途。所以之前的例子,解析器实际上 原文请查阅这里,本文采用知识共享署名 4.0 国际许可协议共享,BY Troland。 本系列持续更新中,Github 地址请查阅这里。 这是 JavaScript 工作原理的第十四章。 概...

    ZweiZhao 评论0 收藏0
  • JavaScript 工作原理之十四-解析,语法抽象树及最小化解析时间的 5 条小技巧

    摘要:事实是只是部分语言的不同表示法。基于这些,解析器会进行立即或者懒解析。然而,解析器做了完全不相关的额外无用功即解析函数。这里不解析函数,该函数声明了却没有指出其用途。所以之前的例子,解析器实际上 原文请查阅这里,本文采用知识共享署名 4.0 国际许可协议共享,BY Troland。 本系列持续更新中,Github 地址请查阅这里。 这是 JavaScript 工作原理的第十四章。 概...

    xuxueli 评论0 收藏0

发表评论

0条评论

qingshanli1988

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<