netflix zuul

优质
小牛编辑
129浏览
2023-12-01

introduction

zuul用来提供动态路由、监控、授权、安全、调度等等的边缘服务(edge service)

ZuulFilter

ZuulFilterZuul中核心组件,通过继承该抽象类,覆写几个关键方法达到自定义调度请求的作用,这里filter不是java web中的filter,不要混淆.

new ZuulFilter() {
                @Override
                public int filterOrder() {
                    return 0;
                }

                @Override
                public String filterType() {
                    return null;
                }

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

                @Override
                public Object run() {
                    return null;
                }
            }

filterOrder:filter执行顺序,通过数字指定 shouldFilter:filter是否需要执行 true执行 false 不执行 run : filter具体逻辑 filterType :filter类型,分为以下几种

pre:请求执行之前filter route: 处理请求,进行路由 post: 请求处理完成后执行的filter error:出现错误时执行的filter

quick start

直接给出一个简单demo,通过demo代码再具体解析

package com.lkl.springcloud.zuul;

import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.ContextLifecycleFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.filters.FilterRegistry;
import com.netflix.zuul.http.ZuulServlet;
import com.netflix.zuul.monitoring.MonitoringHelper;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.context.embedded.FilterRegistrationBean;
import org.springframework.boot.context.embedded.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;

import java.io.IOException;

/**
 * Created by liaokailin on 16/5/24.
 */
@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        new SpringApplicationBuilder(Application.class).web(true).run(args);
    }

    @Component
    public static class MyCommandLineRunner implements CommandLineRunner {
        @Override
        public void run(String... args) throws Exception {
            MonitoringHelper.initMocks();
            initJavaFilters();
        }

        private void initJavaFilters() {
            final FilterRegistry r = FilterRegistry.instance();

            r.put("javaPreFilter", new ZuulFilter() {
                @Override
                public int filterOrder() {
                    return 50000;
                }

                @Override
                public String filterType() {
                    return "pre";
                }

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

                @Override
                public Object run() {
                    System.out.println("running javaPreFilter");
                    RequestContext.getCurrentContext().set("name", "liaokailin");
                    return null;
                }
            });

            r.put("javaRoutingFilter", new ZuulFilter() {
                @Override
                public int filterOrder() {
                    return 50000;
                }

                @Override
                public String filterType() {
                    return "route";
                }

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

                @Override
                public Object run() {
                    System.out.println("running javaRoutingFilter");
                    try {
                        RequestContext.getCurrentContext().getResponse().sendRedirect("https://www.xnip.cn");
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                    return null;
                }
            });

            r.put("javaPostFilter", new ZuulFilter() {
                @Override
                public int filterOrder() {
                    return 50000;
                }

                @Override
                public String filterType() {
                    return "post";
                }

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

                @Override
                public Object run() {
                    System.out.println("running javaPostFilter");
                    System.out.println(RequestContext.getCurrentContext().get("name").toString());
                    return null;
                }

            });

        }

    }

    @Bean
    public ServletRegistrationBean zuulServlet() {
        ServletRegistrationBean servlet = new ServletRegistrationBean(new ZuulServlet());
        servlet.addUrlMappings("/test");
        return servlet;
    }

    @Bean
    public FilterRegistrationBean contextLifecycleFilter() {
        FilterRegistrationBean filter = new FilterRegistrationBean(new ContextLifecycleFilter());
        filter.addUrlPatterns("/*");
        return filter;
    }
}

servlet注册

通过ServletRegistrationBean构造ZuulServlet,该Servlet用以进行filter执行调度以及监控等等操作 访问 http://localhost:8080/test 进入该servlet

filter注册

通过FilterRegistrationBean进行filter注册,ContextLifecycleFilter的核心功能是为了清除RequestContext; 请求上下文通过ThreadLocal存储,因此需要在请求完成后删除该对象。

CommandLineRunner

CommandLineRunner接口很简单,知道spring boot的知道其功能,在工程启动后会执行对应run方法:

MonitoringHelper.initMocks(); 启动监控,这个再后续文章中具体再说 initJavaFilters()方法中注册三种类型filter

RequestContext

RequestContextzuul有很重作用,在不同组件传递数据都是通过它来实现的

启动工程后访问 http://localhost:8080/test 可以看到后台答应消息,页面也会定位到我的博客首页。

zuul执行流程

借用官网的一张图片

Lifecycle

通过图片可以清晰看出执行过程,在微服务中后端各种引用,利用zuul进行合理调用还是很有必要的,例如 负载、限流、监控、安全等等功能。

zuul with groovy

为了动态修改filter,zuul利用groovy,它是基于jvm的语言,语法简单而且和java很类似,可以简单的理解为在java语法上进行拓展,但groovy是可以动态加载的,应用发布到线上后可以在不重启情况下对业务逻辑进行修改。

为了简单起见,下面创建一个groovy filter

import com.netflix.zuul.ZuulFilter
import com.netflix.zuul.context.RequestContext

import javax.servlet.http.HttpServletRequest

class PreRequest extends ZuulFilter{

    @Override
    String filterType() {
        return "pre"
    }

    @Override
    int filterOrder() {
        return 1000
    }

    @Override
    boolean shouldFilter() {
        return true
    }

    @Override
    Object run() {
        HttpServletRequest req = RequestContext.currentContext.request as HttpServletRequest
        Iterator headerIt = req.getHeaderNames().iterator()
        while (headerIt.hasNext()) {
            String name = (String) headerIt.next()
            String value = req.getHeader(name)
            println("header: " + name + ":" + value)
        }
        return null
    }
}

创建一个pre类型的filter,在run方法中获取HttpServletRequest 然后答应header信息

在代码中加入groovy编译器,间隔10秒扫描一次groovy文件,其代码如下:

 <dependency>
            <groupId>org.codehaus.groovy</groupId>
            <artifactId>groovy-all</artifactId>
            <version>2.4.4</version>
        </dependency>
 FilterLoader.getInstance().setCompiler(new GroovyCompiler());
            try {
                FilterFileManager.setFilenameFilter(new GroovyFileFilter());
                FilterFileManager.init(10,"/Users/liaokailin/code/ieda/springcloud/myzuul/src/main/java/com/lkl/springcloud/zuul/filters/groovy/pre");
            } catch (Exception e) {
                throw new RuntimeException(e);
            }

这里groovy文件通过绝对路径指定,如果是实际开发中,可以通过db去存储groovy文件。

启动应用后再访问该工程,可以发现header信息全部答应出来。