`
y806839048
  • 浏览: 1121158 次
  • 性别: Icon_minigender_1
  • 来自: 上海
文章分类
社区版块
存档分类
最新评论

zuul自定义route源码分析

阅读更多

总括:

1,自定义zuulfilter 纳入bean中管理即可

2,自定义路由ZuulProxyAutoConfiguration中RouteLocator实例化为自定义即可

3,zuul的控制层也是用servlet-controller,这调用连会调用过滤器(先),定义好的路由(后)----调用过程

 

 

ZuulFilter定义

通过继承ZuulFilter我们可以定义一个新的过滤器,如下

public class IpAddressFilter extends ZuulFilter {
    @Autowired
    private IGatewayService iGatewayService;

    @Override
    public String filterType() {
        // pre类型的过滤器
        return PRE_TYPE;
    }

    @Override
    public int filterOrder() {
        // 排序
        return 1;
    }

    @Override
    public boolean shouldFilter() {
        return true;
    }

    @Override
    public Object run() {
        RequestContext ctx = RequestContext.getCurrentContext();
        String ip = ctx.getRequest().getRemoteAddr();
        Set<String> blackList = ConcurrentCache.getBlackSet();
        Set<String> whiteList = ConcurrentCache.getWhiteSet();

        blackList.removeAll(whiteList);

        // 在黑名单中禁用
        if (StringUtils.isNotBlank(ip)&& blackList.contains(ip)) {
            ctx.setSendZuulResponse(false);

            ctx.setResponseBody("Suspected flooding attack, IP blocked");
            ctx.setResponseStatusCode(HttpStatus.FORBIDDEN.value());
            ctx.addZuulResponseHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_UTF8_VALUE);
            return null;
        }
        return null;
    }
}

ZuulFilter中实现了compareTo()方法,根据它的值决定同类型的filter的执行顺序。compareTo()方法如下:

public int compareTo(ZuulFilter filter) {
    return Integer.compare(this.filterOrder(), filter.filterOrder());
}

注册ZuulFilter到spring容器中

ZuulFilter可以通过@Component,也可以通过@Bean实例化来纳入spring的生命周期中。

@Configuration
public class FilterConfig {

    @Bean
    public IpAddressFilter addIpAddressFilter() {
        return new IpAddressFilter();
    }
}    

ZuulServerAutoConfiguration中自动装配了filter,被spring实例化出来的所有的ZuulFilter都会被自动装配到Map中。

@Configuration
protected static class ZuulFilterConfiguration {
    // 根据类型,自动装配ZuulFilter到Map对象中
    @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);
    }

}

上面的代码会调用ZuulFilterInitializer的构造方法。

ZuulFilterInitializer中的contextInitialized()开启了@PostConstruct注解,在构造方法完成时,容器会调用contextInitialized()方法(注意:ZuulFilterInitializer对象要由spring管理才会调用到@PostConstruct),将所有的filter保存到filterRegistry中,filterRegistry是一个单例对象。

说明:PostConstruct 注释用于在依赖关系注入完成之后需要执行的方法上

contextInitialized()方法如下:

@PostConstruct
public void contextInitialized() {
    log.info("Starting filter initializer");

    TracerFactory.initialize(tracerFactory);
    CounterFactory.initialize(counterFactory);

    for (Map.Entry<String, ZuulFilter> entry : this.filters.entrySet()) {
        // 保存filter
        filterRegistry.put(entry.getKey(), entry.getValue());
    }
}

自定义路由转发规则

ZuulProxyAutoConfiguration类中注册了RouteLocatorbean@Bean会按照类型,自动注入RouteLocator的实现类。

@Bean
public PreDecorationFilter preDecorationFilter(RouteLocator routeLocator,
        ProxyRequestHelper proxyRequestHelper) {
    return new PreDecorationFilter(routeLocator, this.server.getServletPrefix(),
            this.zuulProperties, proxyRequestHelper);
}

RouteLocator实例化

@Configuration
public class AppConfig{
    //.....省略....
    @Bean(value = "discoveryRouteLocator")
    public DiscoveryClientRouteLocator discoveryClientRouteLocator(ServerProperties server, DiscoveryClient discovery, ZuulProperties properties,ServiceInstance localInstance) {
        return new CustomRouteLocator(server.getServletPath(), discovery,properties,localInstance);
    }

}

CustomRouteLocator实现自定义路由的功能,类如下。


public class CustomRouteLocator extends DiscoveryClientRouteLocator {
    // ....省略....

    @Override
    // 重写
    public Route getMatchingRoute(String path) {
        // ....省略....
        //可以从数据库中读取路由规则,并进行处理
    }

    // 重写
    @Override
    protected LinkedHashMap<String, ZuulRoute> locateRoutes() {
        // ....省略....
    }
}

Servlet初始化

为什么通过访问网关可以自动跳转到zuul中,其实是通过servlet的实现的,该servlet对根路径/进行过滤。下面说明servlet的初始化内容。

ZuulServerAutoConfiguration类中定义了ZuulController

@Bean
public ZuulController zuulController() {
    return new ZuulController();
}

ZuulController继承了ServletWrappingController

public class ZuulController extends ServletWrappingController {

    public ZuulController() {
        // 设置类为ZuulServlet
        setServletClass(ZuulServlet.class);
        setServletName("zuul");
        setSupportedMethods((String[]) null);
    }

    @Override
    public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
        try {
            return super.handleRequestInternal(request, response);
        }
        finally {
            RequestContext.getCurrentContext().unset();
        }
    }

}

ServletWrappingControllerZuulServlet进行实例化

@Override
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 = this.servletClass.newInstance();
    // 调用servlet的init方法
    this.servletInstance.init(new DelegatingServletConfig());
}    

当访问一个url的时候,服务请求会跳转到ZuulController中,执行handleRequest()方法。

@Override
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
    try {
        // 调用父类的handleRequestInternal方法
        return super.handleRequestInternal(request, response);
    }
    finally {
        RequestContext.getCurrentContext().unset();
    }
}

handleRequestInternal()方法如下:

protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response)
        throws Exception {

    this.servletInstance.service(request, response);
    return null;
}

servletInstanceZuulServlet的实例,上面的方法最终调用ZuulServlet中的service()方法。

service()方法如下:

@Override
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过滤器
            preRoute();
        } catch (ZuulException e) {
            error(e);
            postRoute();
            return;
        }
        try {
            // route过滤器
            route();
        } catch (ZuulException e) {
            error(e);
            postRoute();
            return;
        }
        try {
            // post过滤器
            postRoute();
        } catch (ZuulException e) {
            error(e);
            return;
        }

    } catch (Throwable e) {
        error(new ZuulException(e, 500, "UNHANDLED_EXCEPTION_" + e.getClass().getName()));
    } finally {
        RequestContext.getCurrentContext().unset();
    }
}

根据上面的源码我们知道,一个请求到来的时候,就要经历preRoute、route、postRoute几个阶段,用官方的图来说明

zuul请求的生命周期

网关请求执行的过程

根据第上面的内容,我们知道,当通过网关对服务进行请求的时候,要经历preRoute,route、postRoute阶段,这里以以preRoute()方法为例,对路由的处理过程进行说明。

preRoute()方法如下:

void preRoute() throws ZuulException {
    zuulRunner.preRoute();
}

ZuulRunner中的preRoute()方法如下:

public void preRoute() throws ZuulException {
    FilterProcessor.getInstance().preRoute();
}

FilterProcessor是一个单例模式,FilterProcessor中的preRoute()方法如下:

public void preRoute() throws ZuulException {
    try {
        // 运行pre过滤器
        runFilters("pre");
    } catch (ZuulException e) {
        throw e;
    } catch (Throwable e) {
        throw new ZuulException(e, 500, "UNCAUGHT_EXCEPTION_IN_PRE_FILTER_" + e.getClass().getName());
    }
}

执行过滤器,runFilters()方法如下:

 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;
}

根据类型获取过滤器列表,getFiltersByType()方法如下:

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();
        // 取得filterType的类型列表
        if (filter.filterType().equals(filterType)) {
            list.add(filter);
        }
    }
    // 对filter进行排序
    Collections.sort(list); // sort by priority
    // 保存列表
    hashFiltersByType.putIfAbsent(filterType, list);
    return list;
}

FilterRegistry类是一个单例模式,getAllFilters()方法如下

public class FilterRegistry {

    private static final FilterRegistry INSTANCE = new FilterRegistry();

    // ....省略....

    public Collection<ZuulFilter> getAllFilters() {
        return this.filters.values();
    }

}

过滤器的处理方法processZuulFilter()如下:

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();
      
        // ....省略....
        
        // 运行filter
        ZuulFilterResult result = filter.runFilter();
        ExecutionStatus s = result.getStatus();
        execTime = System.currentTimeMillis() - ltime;
        
        // .....省略....
 
        usageNotifier.notify(filter, s);
        return o;

    } catch (Throwable e) {
        // .....省略.....
    }
}

runFilter()方法如下,:

public ZuulFilterResult runFilter() {
    ZuulFilterResult zr = new ZuulFilterResult();
    if (!isFilterDisabled()) {
        // 判断过滤器是否需要执行
        if (shouldFilter()) {
            Tracer t = TracerFactory.instance().startMicroTracer("ZUUL::" + this.getClass().getSimpleName());
            try {
                // 调用filter的run方法。
                Object res = run();
                zr = new ZuulFilterResult(res, ExecutionStatus.SUCCESS);
            } catch (Throwable e) {
               // ....省略....
            } finally {
                t.stopAndLog();
            }
        } else {
            zr = new ZuulFilterResult(ExecutionStatus.SKIPPED);
        }
    }
    return zr;
}

最终调用到各ZuulFilter中的run()方法。

路由查找

pre类型的PreDecorationFilter过滤器,用来进行路由规则的匹配

如下:
根据请求路径寻找png

执行后,上下文内容中的内容如下,加入了requestURI
4.png-71.1kB

访问服务

根据下图可以知道,真正访问服务的是route阶段。如下:
image_1cbu5ucct12mlcro4u215s5vkk1u.png-39.4kB

对于正常的服务,比如:/xxx/service_name是通过RibbonRoutingFilter实现对服务的负载均衡访问,它的run()方法如下:

public Object run() {
    RequestContext context = RequestContext.getCurrentContext();
    this.helper.addIgnoredHeaders();
    try {
        RibbonCommandContext commandContext = buildCommandContext(context);
        ClientHttpResponse response = forward(commandContext);
        setResponse(response);
        return response;
    }
    catch (ZuulException ex) {
        throw new ZuulRuntimeException(ex);
    }
    catch (Exception ex) {
        throw new ZuulRuntimeException(ex);
    }
}

如果是固定的url链接,如:http://www.abc.com/xxx/service_name这种,则是通过SendForwardFilter过滤器实现转发。它的run()方法如下:

public Object run() {
    try {
        RequestContext ctx = RequestContext.getCurrentContext();
        String path = (String) ctx.get(FORWARD_TO_KEY);
        RequestDispatcher dispatcher = ctx.getRequest().getRequestDispatcher(path);
        if (dispatcher != null) {
            ctx.set(SEND_FORWARD_FILTER_RAN, true);
            if (!ctx.getResponse().isCommitted()) {
                // url转发
                dispatcher.forward(ctx.getRequest(), ctx.getResponse());
                ctx.getResponse().flushBuffer();
            }
        }
    }
    catch (Exception ex) {
        ReflectionUtils.rethrowRuntimeException(ex);
    }
    return null;
}
分享到:
评论

相关推荐

    Spring Cloud zuul自定义统一异常处理实现方法

    Spring Cloud zuul自定义统一异常处理实现方法 Spring Cloud zuul自定义统一异常处理实现方法是指在Spring Cloud微服务体系中,使用zuul提供filer和router功能时,实现自定义统一异常处理的方法。在zuul中,默认的...

    springcloud zuul 网关实现源码

    springcloud zuul 网关开发实践,模拟了在Spring Cloud微服务系统中,客户端的请求首先经过负载均衡(zuul、Ngnix),再到达服务网关(zuul集群),然后再到具体的服的实现过程。

    SpringCloud Zuul自定义filter代码实例

    SpringCloud Zuul 自定义 Filter 代码实例 SpringCloud Zuul 是一种基于 Java 的微服务网关框架,提供了负载均衡、反向代理、服务发现、安全认证、限流熔断等功能。 ZuulFilter 是 Zuul 框架中的一种核心组件,...

    Spring Cloud Zuul动态路由demo

    Zuul的过滤器机制允许我们在请求转发前后添加自定义逻辑,如认证、限流等。过滤器分为四种类型:pre、route、post和error。在"ZuulDemo"中,你可以根据需求创建自定义过滤器,并在其中实现动态路由逻辑。 7. **...

    资源前后端分离式分布式微服务架构项目用户认证Zuul讲义+源码+视频

    - **编写网关过滤器**:自定义Zuul过滤器来检查JWT令牌的有效性,确保请求的合法性。 ### 小结 本文档详细介绍了基于资源前后端分离式分布式微服务架构项目中的用户认证及Zuul网关的相关知识点,涵盖了用户登录、...

    zuul-gateway-route.rar

    在代码层面,我们可以通过实现自定义过滤器来扩展Zuul的功能,如动态获取和更新路由信息。 9. **监控与日志**: Zuul还支持集成Spring Boot Actuator进行性能监控,并可配置日志记录,帮助开发者分析请求流量和...

    YunaiV#Blog#2018_02_03_Zuul 原理与源码解析 —— 精品合集1

    1. 概述 1. RocketMQ / MyCAT / Sharding-JDBC 所有源码分析文章列表 2. RocketMQ / MyCAT / Shard

    sample-zuul-filters:用于Spring Cloud Netflix的自定义Zuul 1过滤器的示例

    Zuul过滤器样本 运行3个应用程序类作为spring boot应用程序。 ZuulGatewayApplication :在端口8080上运行zuul FooApplication :在端口9080上托管/foo服务 BarApplication :在端口7080上托管具有不同实现的/foo...

    SpringCloud zuul jar包

    2. **过滤器机制**:Zuul的过滤器功能允许开发者自定义逻辑,例如添加认证、日志记录、动态路由、限流、熔断等。过滤器分为四个阶段:pre(预处理)、route(路由)、post(后处理)和error(错误处理)。 3. **...

    zuul-2.1.3.zip

    学习Zuul源码有助于我们定制化开发,例如实现自定义过滤器,或对接特定的身份认证系统。同时,理解Zuul的工作原理也有助于我们在微服务架构中更好地部署和维护API网关。 总结,Zuul 2.1.3作为一款优秀的API网关,其...

    springcloud:Zuul动态网关demo源码案例演示

    springcloud:Zuul动态网关demo源码案例演示

    zuul网关登陆鉴权/动态路由

    Zuul 过滤器分为四种类型:pre(前置过滤器)、route(路由过滤器)、post(后置过滤器)和 error(错误过滤器)。对于登录鉴权,通常会在 pre 过滤器阶段进行处理,因为这一步发生在请求被路由到具体服务之前。 1....

    spring cloud zuul

    同时,Zuul 过滤器也支持自定义日志输出,便于追踪请求处理流程。 ### 7. 整合示例 压缩包中的 "spring cloud zuul" 可能包含了一个完整的 Spring Cloud Zuul 示例应用,它展示了如何配置和运行 Zuul 服务,包括...

    zuul-2.1.5:zuul原始码解析-源码解析

    Zuul是一种边缘服务,提供动态路由,监视,弹性,安全性等 原始解析目录 1,zuul使用场景,主要实现技术栈,系统架构整体概述(含系统架构图) 2,动态路由,静态路由,鉴权,限流等实战..... 3,从0到1实现一个...

    详解Spring Cloud Zuul重试机制探秘

    通过查看 Zuul 的源码,我们可以了解 Zuul 的重试机制的实现细节。 Zuul 的重试机制是基于 RibbonRoutingFilter 实现的,该 Filter 负责完成请求的路由转发。在 RibbonRoutingFilter 的 run 方法中, Zuul 会检查...

    18.Netflix之Zuul的高阶使用

    在Spring Cloud生态系统中,Zuul是作为边缘服务和API网关的角色存在,它负责路由转发、过滤器处理以及安全控制等任务。...通过对压缩包中的项目进行分析和实践,将进一步巩固和提升我们的Zuul实战能力。

    Apollo整合zuul

    开发者可以通过Zuul定义路由规则,将来自客户端的请求转发到相应的后端服务,并可以添加自定义的过滤器来实现权限验证、日志记录等功能。 将Apollo与Zuul整合,我们首先需要在Zuul应用中引入Apollo的客户端依赖,并...

    17.Spring Cloud集群中使用Zuul

    2. **过滤器**:Zuul的核心功能之一是过滤器,它可以自定义处理请求和响应的过程。过滤器分为四种类型:pre、route、error和post。我们可以根据需求创建自定义过滤器,实现诸如身份验证、限流等功能。 3. **动态...

Global site tag (gtag.js) - Google Analytics