SpringCloud入門(5)Zuul

網(wǎng)上很多資源都把API網(wǎng)關(guān)凝化,是什么,能做什么解釋得非常清楚俊鱼,但是對于初學(xué)者來說我覺得是不夠友好的,Zuul就是SpringCloud微服務(wù)中的網(wǎng)關(guān)畅买。

對于初學(xué)者入門來說并闲,只需要知道Zuul就是當(dāng)服務(wù)增多之后,就要對API進(jìn)行一個統(tǒng)一的管理谷羞,某個類型的API就會調(diào)用某個類型的服務(wù)帝火,除此之外還能對請求過來的API進(jìn)行一個過濾。更進(jìn)一步才是Zuul其它作用湃缎,具體有哪些作用如圖所示:

image

本文重點講解的是路由轉(zhuǎn)發(fā)過濾器 犀填。

1 如何引入Zuul

一樣的,建立一個Zuul模塊嗓违,本例中沒有什么消費端九巡,所以就沒有采取之前建立空父模塊再建立具體子模塊的方法。然后往Zuul中的pom文件中添加依賴:

<dependencies>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
    </dependency>
</dependencies>

到現(xiàn)在整個項目的目錄結(jié)構(gòu)如圖所示:

image

2 主啟動類和配置文件

因為不涉及服務(wù)消費等蹂季,只是做api的處理冕广,所以主啟動類還是比較簡單的

@SpringBootApplication
@EnableZuulProxy        //開啟Zuul
@EnableEurekaClient
public class ZuulMain9401 {
    public static void main(String[] args) {
        SpringApplication.run(ZuulMain9401.class, args);
    }
}

配置文件的話和常規(guī)的Eureka客戶端是一樣的

spring:
  application:
    name: zuul9401
server:
  port: 9401

eureka:
  client:
    fetch-registry: true
    register-with-eureka: true
    service-url:
      defaultZone: http://localhost:8001/eureka/
  instance:
    instance-id: zuul9401

3 路由轉(zhuǎn)發(fā)

路由轉(zhuǎn)發(fā)主要是通過配置文件來修改,往上面配置文件中增加內(nèi)容 偿洁,下面會講3種方式的轉(zhuǎn)發(fā)路由撒汉。

設(shè)置注冊Eureka的服務(wù)id

增加第一波配置文件,是在原有的配置文件上增加了以下內(nèi)容涕滋。

zuul:
  routes:
    user-a:
      path: /api-a/**
      serviceId: eureka-provide

user-a隨便定義 神凑,path 是外部訪問的路徑,serviceId是微服務(wù)配置文件的spring.application.name 的值何吝。

所以上面增加的配置文件整體意思是,當(dāng)外部訪問/api-a/ 相關(guān)路徑時候鹃唯,會轉(zhuǎn)發(fā)給名字為eureka-provid 的服務(wù)提供服務(wù)爱榕。

開啟Eureka服務(wù)注冊中心EurekaServer8001 ,服務(wù)提供者EurekaProvide7001/2/3 坡慌,API網(wǎng)關(guān)ZuulMain9401

image

接著訪問http://localhost:9401/api-a/eureka/provide黔酥,按照分析,應(yīng)該會被轉(zhuǎn)發(fā)到eureka-provide 服務(wù)里的eureka/provide路徑。

為了防止有點混跪者,貼一次第一個項目的代碼棵帽,詳情請看本系列的第一篇文章。

@SpringBootApplication
@RestController
@EnableEurekaClient
public class EurekaProvide7001 {

    @Value("${server.port}")
    int port;

    @GetMapping("/eureka/provide")
    public String getInfo() {
        return "hello, i am eureka provide, the provide service. My port: " + port;
    }

    @GetMapping("/eureka/delayProvide")
    public String delayGetInfo() throws InterruptedException {
        Thread.sleep(3000);
        return "hello, delay to do something";
    }
    public static void main(String[] args) {
        SpringApplication.run(EurekaProvide7001.class, args);
    }
}

可以看到能夠成功轉(zhuǎn)發(fā)路由

image

設(shè)置URL

增加第二波配置文件

zuul:
  routes:
#    user-a:
#      path: /api-a/**
#      serviceId: eureka-provide
    user-b:
      path: /api-b/**
      url: http://localhost:7101/

其它如上渣玲,url 需要轉(zhuǎn)發(fā)到哪個服務(wù)

通過Edit Configurations更改端口以及服務(wù)名以模擬新的服務(wù)逗概,具體操作同樣在第一篇文章中有清晰的gif圖。

image
image

其它服務(wù)不用關(guān)閉忘衍,繼續(xù)開啟剛新建的Provide7101 逾苫,重啟ZuulMain9401服務(wù),訪問http://localhost:9401/api-b/eureka/provide枚钓,同樣能過夠看到成功轉(zhuǎn)發(fā)

image

設(shè)置非注冊Eureka的服務(wù)id

之前在學(xué)習(xí)Ribbon的時候也說過铅搓,我們可以通過Ribbon設(shè)置訪問一些沒有注冊進(jìn)Eureka的服務(wù),同樣在API網(wǎng)關(guān)也能通過配置文件設(shè)置Ribbon來達(dá)到一樣的效果搀捷。

增加第三波配置文件

zuul:
  routes:
#    user-a:
#      path: /api-a/**
#      serviceId: eureka-provide
#    user-b:
#      path: /api-b/**
#      url: http://localhost:7101/
    user-c:
      path: /api-c/**
      serviceId: provide-without-eureka

#一定需要這個才行
ribbon:
  eureka:
    enabled: false
provide-without-eureka:
  ribbon:
    ServerListClassName: com.netflix.loadbalancer.ConfigurationBasedServerList
    listOfServers: localhost:7201, localhost:7202
    ConnectTimeout: 1000
    ReadTimeout: 3000
    MaxTotalHttpConnections: 500
    MaxConnectionsPerHost: 100

如果取消user-a 的相關(guān)注釋星掰,此時訪問user-a是無法轉(zhuǎn)發(fā)的,會報500的錯誤嫩舟,我猜是因為設(shè)置了ribbon.eureka.enabled = false 的緣故氢烘。

serviceId同樣是微服務(wù)的名稱,然后對這個微服務(wù)設(shè)置至壤,所以是設(shè)置微服務(wù)名[provid-without-eureka].ribbon 威始,其它屬性都是相關(guān)屬性,最重要的同樣是listOfServers 像街,表示這個訪問這個服務(wù)名會在這些服務(wù)列表中進(jìn)行分配黎棠。

為了簡單還是用同一個服務(wù),用上面的方法修改配置文件即可镰绎,修改端口號7201脓斩,修改eureka.client.register.with.eureka = false 來模擬沒有注冊進(jìn)Eureka的服務(wù)。

image

接著再復(fù)制一份配置畴栖,其它都不遍随静,把端口號改成7202,總共是新建了端口為7201吗讶,7202的兩個服務(wù)燎猛。

其它服務(wù)不用關(guān),開啟ProvideWithoutEureka7201/2 服務(wù)照皆,重啟ZuulMain9401服務(wù)重绷,此時所有的服務(wù)開啟如下

image

訪問http://localhost:9401/api-c/eureka/provide,服務(wù)還是一樣能進(jìn)行成功轉(zhuǎn)發(fā)

image

4 查看路由狀態(tài)

順便簡單說下查看路由狀態(tài)膜毁,首先還是需要增加配置文件昭卓,是一定要增加

management:
  endpoints:
    web:
      exposure:
        include: "*"
  endpoint:
    health:
      show-details: ALWAYS

接著訪問http://localhost:9401/actuator/routes愤钾,正常情況會出現(xiàn)以下

image

如果想得到詳細(xì)信息,那么只需要訪問http://localhost:9401/actuator/routes/details

image

5 轉(zhuǎn)發(fā)路由時的Fallback

和Hystrix候醒,當(dāng)轉(zhuǎn)發(fā)路由發(fā)現(xiàn)服務(wù)不能夠正常提供服務(wù)的時候能颁,就可以fallback。

新建一個類MyFallbackProvider 實現(xiàn)FallbackProvider 接口

@Component
public class MyFallbackProvider implements FallbackProvider {
    @Override
    public String getRoute() {
        //為所有的路由提供回退
        return "*"; 
    }

    @Override
    public ClientHttpResponse fallbackResponse(String route, Throwable throwable) {
        return new ClientHttpResponse() {
            @Override
            public HttpStatus getStatusCode() throws IOException {
                return HttpStatus.OK;
            }

            @Override
            public int getRawStatusCode() throws IOException {
                return 200;
            }

            @Override
            public String getStatusText() throws IOException {
                return "OK";
            }

            @Override
            public void close() {
            }

            @Override
            public InputStream getBody() throws IOException {
                //回退后顯示出來
                return new ByteArrayInputStream("something wrong, fallback now".getBytes());
            }

            @Override
            public HttpHeaders getHeaders() {
                HttpHeaders headers = new HttpHeaders();
                headers.setContentType(MediaType.APPLICATION_JSON);
                return headers;
            }
        };
    }
}

現(xiàn)在來手動關(guān)閉ProvideWithoutEureka7201/2服務(wù)模擬服務(wù)宕機倒淫,來看看是否能回退

image

6 過濾器

之所以Zuul能完成驗證伙菊、授權(quán)、靜態(tài)資源處理等昌简,就是得益于下面要講的過濾器占业,但是主要是講最基本的過濾,以后可能以后進(jìn)階的時候可能再深入講纯赎。

創(chuàng)建過濾器

首先創(chuàng)建filter包谦疾,然后創(chuàng)建一個過濾器類MyPreFilter需要實現(xiàn)ZuulFilter 接口

public class MyPreFilter extends ZuulFilter {
    @Override
    public String filterType() {    //過濾器類型
        return FilterConstants.PRE_TYPE;    //請求前處理
    }

    @Override
    public int filterOrder() {      //過濾器順序犬金,越小越優(yōu)先
        return 0;
    }

    @Override
    public boolean shouldFilter() { //是否開啟過濾
        return true;
    }

    @Override
    public Object run() throws ZuulException {  //執(zhí)行邏輯
        RequestContext currentContext = RequestContext.getCurrentContext();
        HttpServletRequest request = currentContext.getRequest();
        System.out.println("[ PreFilter" + " ]" + String.format("send %s request to %s",request.getMethod(),request.getRequestURL()));
        return null;
    }
}

FilterConstants 類中定義了一系列常量念恍,其中對于過濾器就是以下幾種

public static final String ERROR_TYPE = "error";    //出錯時執(zhí)行
public static final String POST_TYPE = "post";      //請求后請求
public static final String PRE_TYPE = "pre";        //請求前請求
public static final String ROUTE_TYPE = "route";    //處理目標(biāo)請求

同時再建立一個后置請求

public class MyPostFilter extends ZuulFilter {
    @Override
    public String filterType() {
        return FilterConstants.POST_TYPE;
    }

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

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

    @Override
    public Object run() throws ZuulException {
        RequestContext currentContext = RequestContext.getCurrentContext();
        HttpServletRequest request = currentContext.getRequest();
        //這里把PreFilter改為PostFilter
        System.out.println("[ PostFilter" + " ]" + String.format("send %s request to %s",request.getMethod(),request.getRequestURL())); 
        return null;
    }
}

注入容器中

新建一個config包,在包下創(chuàng)建一個類ZuulConfiguration

@Configuration
public class ZuulConfiguration {

    @Bean
    public MyPreFilter getZuulPreFilterBean() {
        return new MyPreFilter();
    }

    @Bean
    public MyPostFilter getZuulPostFilterBean() {
        return new MyPostFilter();
    }
}

此時Zuul模塊的目錄結(jié)構(gòu)如下

注意晚顷,這里有個坑峰伙,就是當(dāng)開啟了過濾器后,會發(fā)現(xiàn)前一小節(jié)的fallback失效该默。

image

重啟ZuulMain9401服務(wù)瞳氓,并且清空idea輸出控制臺

如果是接著上面一節(jié)的內(nèi)容,那么此時應(yīng)該是轉(zhuǎn)發(fā)的是非注冊進(jìn)Eureka服務(wù)的路由

訪問http://localhost:9401/api-c/eureka/provide栓袖,查看控制臺輸出

image

創(chuàng)作不易匣摘,如果對你有幫助,歡迎點贊裹刮,收藏和分享啦音榜!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市捧弃,隨后出現(xiàn)的幾起案子赠叼,更是在濱河造成了極大的恐慌,老刑警劉巖违霞,帶你破解...
    沈念sama閱讀 212,080評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件嘴办,死亡現(xiàn)場離奇詭異,居然都是意外死亡买鸽,警方通過查閱死者的電腦和手機涧郊,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,422評論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來癞谒,“玉大人底燎,你說我怎么就攤上這事〉猓” “怎么了双仍?”我有些...
    開封第一講書人閱讀 157,630評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長桌吃。 經(jīng)常有香客問我朱沃,道長,這世上最難降的妖魔是什么茅诱? 我笑而不...
    開封第一講書人閱讀 56,554評論 1 284
  • 正文 為了忘掉前任逗物,我火速辦了婚禮,結(jié)果婚禮上瑟俭,老公的妹妹穿的比我還像新娘翎卓。我一直安慰自己,他們只是感情好摆寄,可當(dāng)我...
    茶點故事閱讀 65,662評論 6 386
  • 文/花漫 我一把揭開白布失暴。 她就那樣靜靜地躺著,像睡著了一般微饥。 火紅的嫁衣襯著肌膚如雪逗扒。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,856評論 1 290
  • 那天欠橘,我揣著相機與錄音矩肩,去河邊找鬼。 笑死肃续,一個胖子當(dāng)著我的面吹牛黍檩,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播痹升,決...
    沈念sama閱讀 39,014評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼建炫,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了疼蛾?” 一聲冷哼從身側(cè)響起肛跌,我...
    開封第一講書人閱讀 37,752評論 0 268
  • 序言:老撾萬榮一對情侶失蹤症概,失蹤者是張志新(化名)和其女友劉穎憔足,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體命迈,經(jīng)...
    沈念sama閱讀 44,212評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡皮钠,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,541評論 2 327
  • 正文 我和宋清朗相戀三年稳捆,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片麦轰。...
    茶點故事閱讀 38,687評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡乔夯,死狀恐怖砖织,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情末荐,我是刑警寧澤侧纯,帶...
    沈念sama閱讀 34,347評論 4 331
  • 正文 年R本政府宣布,位于F島的核電站甲脏,受9級特大地震影響眶熬,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜块请,卻給世界環(huán)境...
    茶點故事閱讀 39,973評論 3 315
  • 文/蒙蒙 一娜氏、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧墩新,春花似錦贸弥、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,777評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至切省,卻和暖如春最岗,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背朝捆。 一陣腳步聲響...
    開封第一講書人閱讀 32,006評論 1 266
  • 我被黑心中介騙來泰國打工般渡, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人芙盘。 一個月前我還...
    沈念sama閱讀 46,406評論 2 360
  • 正文 我出身青樓驯用,卻偏偏與公主長得像,于是被迫代替她去往敵國和親儒老。 傳聞我的和親對象是個殘疾皇子蝴乔,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,576評論 2 349

推薦閱讀更多精彩內(nèi)容