1. 程式人生 > >springcloud(第九篇)netflix zuul

springcloud(第九篇)netflix zuul

netflix zuul

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("http://blog.csdn.net/liaokailin/");
                    } 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有很重作用,在不同元件傳遞資料都是通過它來實現的

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資訊全部答應出來。

歡迎關注,您的肯定是對我最大的支援

這裡寫圖片描述