springcloud學習02

0.學習目標

  • 會配置Hystix熔斷
  • 會使用Feign進行遠程調用
  • 能獨立搭建Zuul網關
  • 能編寫Zuul的過濾器

1.Hystrix

1.1.簡介

Hystrix,英文意思是豪豬,全身是刺,看起來就不好惹,是一種保護機制。

Hystrix也是Netflix公司的一款組件。

主頁:https://github.com/Netflix/Hystrix/

那么Hystix的作用是什么呢?具體要保護什么呢?

Hystix是Netflix開源的一個延遲和容錯庫,用于隔離訪問遠程服務、第三方庫,防止出現(xiàn)級聯(lián)失敗。

1.2.雪崩問題

微服務中,服務間調用關系錯綜復雜,一個請求,可能需要調用多個微服務接口才能實現(xiàn),會形成非常復雜的調用鏈路:

如圖,一次業(yè)務請求,需要調用A、P、H、I四個服務,這四個服務又可能調用其它服務。

如果此時,某個服務出現(xiàn)異常:

例如微服務I發(fā)生異常,請求阻塞,用戶不會得到響應,則tomcat的這個線程不會釋放,于是越來越多的用戶請求到來,越來越多的線程會阻塞:

服務器支持的線程和并發(fā)數(shù)有限,請求一直阻塞,會導致服務器資源耗盡,從而導致所有其它服務都不可用,形成雪崩效應。

這就好比,一個汽車生產線,生產不同的汽車,需要使用不同的零件,如果某個零件因為種種原因無法使用,那么就會造成整臺車無法裝配,陷入等待零件的狀態(tài),直到零件到位,才能繼續(xù)組裝。 此時如果有很多個車型都需要這個零件,那么整個工廠都將陷入等待的狀態(tài),導致所有生產都陷入癱瘓。一個零件的波及范圍不斷擴大。

Hystix解決雪崩問題的手段有兩個:

  • 線程隔離
  • 服務熔斷

1.3.線程隔離,服務降級

1.3.1.原理

線程隔離示意圖:

解讀:

Hystrix為每個依賴服務調用分配一個小的線程池,如果線程池已滿調用將被立即拒絕,默認不采用排隊.加速失敗判定時間。

用戶的請求將不再直接訪問服務,而是通過線程池中的空閑線程來訪問服務,如果線程池已滿,或者請求超時,則會進行降級處理,什么是服務降級?

服務降級:優(yōu)先保證核心服務,而非核心服務不可用或弱可用。

用戶的請求故障時,不會被阻塞,更不會無休止的等待或者看到系統(tǒng)崩潰,至少可以看到一個執(zhí)行結果(例如返回友好的提示信息) 。

服務降級雖然會導致請求失敗,但是不會導致阻塞,而且最多會影響這個依賴服務對應的線程池中的資源,對其它服務沒有響應。

觸發(fā)Hystix服務降級的情況:

  • 線程池已滿
  • 請求超時

1.3.2.動手實踐

1.3.2.1.引入依賴

首先在itcast-service-consumer的pom.xml中引入Hystrix依賴:

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

1.3.2.2.開啟熔斷

可以看到,我們類上的注解越來越多,在微服務中,經常會引入上面的三個注解,于是Spring就提供了一個組合注解:@SpringCloudApplication

因此,我們可以使用這個組合注解來代替之前的3個注解。

@SpringCloudApplication
public class ItcastServiceConsumerApplication {

    @Bean
    @LoadBalanced
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }

    public static void main(String[] args) {
        SpringApplication.run(ItcastServiceConsumerApplication.class, args);
    }
}

1.3.2.3.編寫降級邏輯

我們改造itcast-service-consumer,當目標服務的調用出現(xiàn)故障,我們希望快速失敗,給用戶一個友好提示。因此需要提前編寫好失敗時的降級處理邏輯,要使用HystixCommond來完成:

@Controller
@RequestMapping("consumer/user")
public class UserController {

    @Autowired
    private RestTemplate restTemplate;

    @GetMapping
    @ResponseBody
    @HystrixCommand(fallbackMethod = "queryUserByIdFallBack")
    public String queryUserById(@RequestParam("id") Long id) {
        String user = this.restTemplate.getForObject("http://service-provider/user/" + id, String.class);
        return user;
    }

    public String queryUserByIdFallBack(Long id){
        return "請求繁忙,請稍后再試!";
    }
}

要注意,因為熔斷的降級邏輯方法必須跟正常邏輯方法保證:相同的參數(shù)列表和返回值聲明。失敗邏輯中返回User對象沒有太大意義,一般會返回友好提示。所以我們把queryById的方法改造為返回String,反正也是Json數(shù)據。這樣失敗邏輯中返回一個錯誤說明,會比較方便。

說明:

  • @HystrixCommand(fallbackMethod = "queryByIdFallBack"):用來聲明一個降級邏輯的方法

測試:

當itcast-service-provder正常提供服務時,訪問與以前一致。但是當我們將itcast-service-provider停機時,會發(fā)現(xiàn)頁面返回了降級處理信息:

1.3.2.4.默認FallBack

我們剛才把fallback寫在了某個業(yè)務方法上,如果這樣的方法很多,那豈不是要寫很多。所以我們可以把Fallback配置加在類上,實現(xiàn)默認fallback:

@Controller
@RequestMapping("consumer/user")
@DefaultProperties(defaultFallback = "fallBackMethod") // 指定一個類的全局熔斷方法
public class UserController {

    @Autowired
    private RestTemplate restTemplate;

    @GetMapping
    @ResponseBody
    @HystrixCommand // 標記該方法需要熔斷
    public String queryUserById(@RequestParam("id") Long id) {
        String user = this.restTemplate.getForObject("http://service-provider/user/" + id, String.class);
        return user;
    }

    /**
     * 熔斷方法
     * 返回值要和被熔斷的方法的返回值一致
     * 熔斷方法不需要參數(shù)
     * @return
     */
    public String fallBackMethod(){
        return "請求繁忙,請稍后再試!";
    }
}
  • @DefaultProperties(defaultFallback = "defaultFallBack"):在類上指明統(tǒng)一的失敗降級方法
  • @HystrixCommand:在方法上直接使用該注解,使用默認的剪輯方法。
  • defaultFallback:默認降級方法,不用任何參數(shù),以匹配更多方法,但是返回值一定一致

1.3.2.5.設置超時

在之前的案例中,請求在超過1秒后都會返回錯誤信息,這是因為Hystix的默認超時時長為1,我們可以通過配置修改這個值:

我們可以通過hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds來設置Hystrix超時時間。該配置沒有提示。

hystrix:
  command:
    default:
      execution:
        isolation:
          thread:
            timeoutInMilliseconds: 6000 # 設置hystrix的超時時間為6000ms

改造服務提供者

改造服務提供者的UserController接口,隨機休眠一段時間,以觸發(fā)熔斷:

@GetMapping("{id}")
public User queryUserById(@PathVariable("id") Long id) {
    try {
        Thread.sleep(6000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    return this.userService.queryUserById(id);
}

1.4.服務熔斷

1.4.1.熔斷原理

熔斷器,也叫斷路器,其英文單詞為:Circuit Breaker

熔斷狀態(tài)機3個狀態(tài):

  • Closed:關閉狀態(tài),所有請求都正常訪問。
  • Open:打開狀態(tài),所有請求都會被降級。Hystix會對請求情況計數(shù),當一定時間內失敗請求百分比達到閾值,則觸發(fā)熔斷,斷路器會完全打開。默認失敗比例的閾值是50%,請求次數(shù)最少不低于20次。
  • Half Open:半開狀態(tài),open狀態(tài)不是永久的,打開后會進入休眠時間(默認是5S)。隨后斷路器會自動進入半開狀態(tài)。此時會釋放部分請求通過,若這些請求都是健康的,則會完全關閉斷路器,否則繼續(xù)保持打開,再次進行休眠計時

1.4.2.動手實踐

為了能夠精確控制請求的成功或失敗,我們在consumer的調用業(yè)務中加入一段邏輯:

@GetMapping("{id}")
@HystrixCommand
public String queryUserById(@PathVariable("id") Long id){
    if(id == 1){
        throw new RuntimeException("太忙了");
    }
    String user = this.restTemplate.getForObject("http://service-provider/user/" + id, String.class);
    return user;
}

這樣如果參數(shù)是id為1,一定失敗,其它情況都成功。(不要忘了清空service-provider中的休眠邏輯)

我們準備兩個請求窗口:

當我們瘋狂訪問id為1的請求時(超過20次),就會觸發(fā)熔斷。斷路器會斷開,一切請求都會被降級處理。

此時你訪問id為2的請求,會發(fā)現(xiàn)返回的也是失敗,而且失敗時間很短,只有幾毫秒左右:

不過,默認的熔斷觸發(fā)要求較高,休眠時間窗較短,為了測試方便,我們可以通過配置修改熔斷策略:

circuitBreaker.requestVolumeThreshold=10
circuitBreaker.sleepWindowInMilliseconds=10000
circuitBreaker.errorThresholdPercentage=50

解讀:

  • requestVolumeThreshold:觸發(fā)熔斷的最小請求次數(shù),默認20
  • errorThresholdPercentage:觸發(fā)熔斷的失敗請求最小占比,默認50%
  • sleepWindowInMilliseconds:休眠時長,默認是5000毫秒

2.Feign

在前面的學習中,我們使用了Ribbon的負載均衡功能,大大簡化了遠程調用時的代碼:

String user = this.restTemplate.getForObject("http://service-provider/user/" + id, String.class);

如果就學到這里,你可能以后需要編寫類似的大量重復代碼,格式基本相同,無非參數(shù)不一樣。有沒有更優(yōu)雅的方式,來對這些代碼再次優(yōu)化呢?

這就是我們接下來要學的Feign的功能了。

2.1.簡介

有道詞典的英文解釋:

為什么叫偽裝?

Feign可以把Rest的請求進行隱藏,偽裝成類似SpringMVC的Controller一樣。你不用再自己拼接url,拼接參數(shù)等等操作,一切都交給Feign去做。

項目主頁:https://github.com/OpenFeign/feign

2.2.快速入門

改造itcast-service-consumer工程

2.2.1.導入依賴

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

2.2.2.開啟Feign功能

我們在啟動類上,添加注解,開啟Feign功能

@SpringCloudApplication
@EnableFeignClients // 開啟feign客戶端
public class ItcastServiceConsumerApplication {
    
    public static void main(String[] args) {
        SpringApplication.run(ItcastServiceConsumerApplication.class, args);
    }
}

刪除RestTemplate:feign已經自動集成了Ribbon負載均衡的RestTemplate。所以,此處不需要再注冊RestTemplate。

2.2.3.Feign的客戶端

在itcast-service-consumer工程中,添加UserClient接口:

內容:

@FeignClient(value = "service-provider") // 標注該類是一個feign接口
public interface UserClient {

    @GetMapping("user/{id}")
    User queryById(@PathVariable("id") Long id);
}
  • 首先這是一個接口,F(xiàn)eign會通過動態(tài)代理,幫我們生成實現(xiàn)類。這點跟mybatis的mapper很像
  • @FeignClient,聲明這是一個Feign客戶端,類似@Mapper注解。同時通過value屬性指定服務名稱
  • 接口中的定義方法,完全采用SpringMVC的注解,F(xiàn)eign會根據注解幫我們生成URL,并訪問獲取結果

改造原來的調用邏輯,調用UserClient接口:

@Controller
@RequestMapping("consumer/user")
public class UserController {

    @Autowired
    private UserClient userClient;

    @GetMapping
    @ResponseBody
    public User queryUserById(@RequestParam("id") Long id){
        User user = this.userClient.queryUserById(id);
        return user;
    }

}

2.2.4.啟動測試

訪問接口:

正常獲取到了結果。

2.3.負載均衡

Feign中本身已經集成了Ribbon依賴和自動配置:

因此我們不需要額外引入依賴,也不需要再注冊RestTemplate對象。

2.4.Hystrix支持

Feign默認也有對Hystrix的集成:

只不過,默認情況下是關閉的。我們需要通過下面的參數(shù)來開啟:(在itcast-service-consumer工程添加配置內容)

feign:
  hystrix:
    enabled: true # 開啟Feign的熔斷功能

但是,F(xiàn)eign中的Fallback配置不像hystrix中那樣簡單了。

1)首先,我們要定義一個類UserClientFallback,實現(xiàn)剛才編寫的UserClient,作為fallback的處理類

@Component
public class UserClientFallback implements UserClient {

    @Override
    public User queryById(Long id) {
        User user = new User();
        user.setUserName("服務器繁忙,請稍后再試!");
        return user;
    }
}

2)然后在UserFeignClient中,指定剛才編寫的實現(xiàn)類

@FeignClient(value = "service-provider", fallback = UserClientFallback.class) // 標注該類是一個feign接口
public interface UserClient {

    @GetMapping("user/{id}")
    User queryUserById(@PathVariable("id") Long id);
}

3)重啟測試:

2.5.請求壓縮(了解)

Spring Cloud Feign 支持對請求和響應進行GZIP壓縮,以減少通信過程中的性能損耗。通過下面的參數(shù)即可開啟請求與響應的壓縮功能:

feign:
  compression:
    request:
      enabled: true # 開啟請求壓縮
    response:
      enabled: true # 開啟響應壓縮

同時,我們也可以對請求的數(shù)據類型,以及觸發(fā)壓縮的大小下限進行設置:

feign:
  compression:
    request:
      enabled: true # 開啟請求壓縮
      mime-types: text/html,application/xml,application/json # 設置壓縮的數(shù)據類型
      min-request-size: 2048 # 設置觸發(fā)壓縮的大小下限

注:上面的數(shù)據類型、壓縮大小下限均為默認值。

2.6.日志級別(了解)

前面講過,通過logging.level.xx=debug來設置日志級別。然而這個對Fegin客戶端而言不會產生效果。因為@FeignClient注解修改的客戶端在被代理時,都會創(chuàng)建一個新的Fegin.Logger實例。我們需要額外指定這個日志的級別才可以。

1)設置com.leyou包下的日志級別都為debug

logging:
  level:
    cn.itcast: debug

2)編寫配置類,定義日志級別

內容:

@Configuration
public class FeignLogConfiguration {

    @Bean
    Logger.Level feignLoggerLevel(){
        return Logger.Level.FULL;
    }
}

這里指定的Level級別是FULL,F(xiàn)eign支持4種級別:

  • NONE:不記錄任何日志信息,這是默認值。
  • BASIC:僅記錄請求的方法,URL以及響應狀態(tài)碼和執(zhí)行時間
  • HEADERS:在BASIC的基礎上,額外記錄了請求和響應的頭信息
  • FULL:記錄所有請求和響應的明細,包括頭信息、請求體、元數(shù)據。

3)在FeignClient中指定配置類:

@FeignClient(value = "service-privider", fallback = UserFeignClientFallback.class, configuration = FeignConfig.class)
public interface UserFeignClient {
    @GetMapping("/user/{id}")
    User queryUserById(@PathVariable("id") Long id);
}

4)重啟項目,即可看到每次訪問的日志:

3.Zuul網關

通過前面的學習,使用Spring Cloud實現(xiàn)微服務的架構基本成型,大致是這樣的:

我們使用Spring Cloud Netflix中的Eureka實現(xiàn)了服務注冊中心以及服務注冊與發(fā)現(xiàn);而服務間通過Ribbon或Feign實現(xiàn)服務的消費以及均衡負載。為了使得服務集群更為健壯,使用Hystrix的融斷機制來避免在微服務架構中個別服務出現(xiàn)異常時引起的故障蔓延。

在該架構中,我們的服務集群包含:內部服務Service A和Service B,他們都會注冊與訂閱服務至Eureka Server,而Open Service是一個對外的服務,通過均衡負載公開至服務調用方。我們把焦點聚集在對外服務這塊,直接暴露我們的服務地址,這樣的實現(xiàn)是否合理,或者是否有更好的實現(xiàn)方式呢?

先來說說這樣架構需要做的一些事兒以及存在的不足:
先來說說這樣架構需要做的一些事兒以及存在的不足:

  • 破壞了服務無狀態(tài)特點。

    為了保證對外服務的安全性,我們需要實現(xiàn)對服務訪問的權限控制,而開放服務的權限控制機制將會貫穿并污染整個開放服務的業(yè)務邏輯,這會帶來的最直接問題是,破壞了服務集群中REST API無狀態(tài)的特點。

    從具體開發(fā)和測試的角度來說,在工作中除了要考慮實際的業(yè)務邏輯之外,還需要額外考慮對接口訪問的控制處理。
    
  • 無法直接復用既有接口。

    當我們需要對一個即有的集群內訪問接口,實現(xiàn)外部服務訪問時,我們不得不通過在原有接口上增加校驗邏輯,或增加一個代理調用來實現(xiàn)權限控制,無法直接復用原有的接口。

面對類似上面的問題,我們要如何解決呢?答案是:服務網關!

為了解決上面這些問題,我們需要將權限控制這樣的東西從我們的服務單元中抽離出去,而最適合這些邏輯的地方就是處于對外訪問最前端的地方,我們需要一個更強大一些的均衡負載器的 服務網關。

服務網關是微服務架構中一個不可或缺的部分。通過服務網關統(tǒng)一向外系統(tǒng)提供REST API的過程中,除了具備服務路由、均衡負載功能之外,它還具備了權限控制等功能。Spring Cloud Netflix中的Zuul就擔任了這樣的一個角色,為微服務架構提供了前門保護的作用,同時將權限控制這些較重的非業(yè)務邏輯內容遷移到服務路由層面,使得服務集群主體能夠具備更高的可復用性和可測試性。

3.1.簡介

官網:https://github.com/Netflix/zuul

Zuul:維基百科

電影《捉鬼敢死隊》中的怪獸,Zuul,在紐約引發(fā)了巨大騷亂。

事實上,在微服務架構中,Zuul就是守門的大Boss!一夫當關,萬夫莫開!

3.2.Zuul加入后的架構

不管是來自于客戶端(PC或移動端)的請求,還是服務內部調用。一切對服務的請求都會經過Zuul這個網關,然后再由網關來實現(xiàn) 鑒權、動態(tài)路由等等操作。Zuul就是我們服務的統(tǒng)一入口。

3.3.快速入門

3.3.1.新建工程

填寫基本信息:

添加Zuul依賴:

3.3.2.編寫配置

server:
  port: 10010 #服務端口
spring:
  application:
    name: api-gateway #指定服務名

3.3.3.編寫引導類

通過@EnableZuulProxy注解開啟Zuul的功能:

@SpringBootApplication
@EnableZuulProxy // 開啟網關功能
public class ItcastZuulApplication {

    public static void main(String[] args) {
        SpringApplication.run(ItcastZuulApplication.class, args);
    }
}

3.3.4.編寫路由規(guī)則

我們需要用Zuul來代理service-provider服務,先看一下控制面板中的服務狀態(tài):

  • ip為:127.0.0.1
  • 端口為:8081

映射規(guī)則:

server:
  port: 10010 #服務端口
spring:
  application:
    name: api-gateway #指定服務名
zuul:
  routes:
    service-provider: # 這里是路由id,隨意寫
      path: /service-provider/** # 這里是映射路徑
      url: http://127.0.0.1:8081 # 映射路徑對應的實際url地址

我們將符合path 規(guī)則的一切請求,都代理到 url參數(shù)指定的地址

本例中,我們將 /service-provider/**開頭的請求,代理到http://127.0.0.1:8081

3.3.5.啟動測試

訪問的路徑中需要加上配置規(guī)則的映射路徑,我們訪問:http://127.0.0.1:10010/service-provider/user/1

3.4.面向服務的路由

在剛才的路由規(guī)則中,我們把路徑對應的服務地址寫死了!如果同一服務有多個實例的話,這樣做顯然就不合理了。我們應該根據服務的名稱,去Eureka注冊中心查找 服務對應的所有實例列表,然后進行動態(tài)路由才對!

對itcast-zuul工程修改優(yōu)化:

3.4.1.添加Eureka客戶端依賴

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

3.4.2.添加Eureka配置,獲取服務信息

eureka:
  client:
    registry-fetch-interval-seconds: 5 # 獲取服務列表的周期:5s
    service-url:
      defaultZone: http://127.0.0.1:10086/eureka

3.4.3.開啟Eureka客戶端發(fā)現(xiàn)功能

@SpringBootApplication
@EnableZuulProxy // 開啟Zuul的網關功能
@EnableDiscoveryClient
public class ZuulDemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(ZuulDemoApplication.class, args);
    }
}

3.4.4.修改映射配置,通過服務名稱獲取

因為已經有了Eureka客戶端,我們可以從Eureka獲取服務的地址信息,因此映射時無需指定IP地址,而是通過服務名稱來訪問,而且Zuul已經集成了Ribbon的負載均衡功能。

zuul:
  routes:
    service-provider: # 這里是路由id,隨意寫
      path: /service-provider/** # 這里是映射路徑
      serviceId: service-provider # 指定服務名稱

3.4.5.啟動測試

再次啟動,這次Zuul進行代理時,會利用Ribbon進行負載均衡訪問:

3.5.簡化的路由配置

在剛才的配置中,我們的規(guī)則是這樣的:

  • zuul.routes.<route>.path=/xxx/**: 來指定映射路徑。<route>是自定義的路由名
  • zuul.routes.<route>.serviceId=service-provider:來指定服務名。

而大多數(shù)情況下,我們的<route>路由名稱往往和服務名會寫成一樣的。因此Zuul就提供了一種簡化的配置語法:zuul.routes.<serviceId>=<path>

比方說上面我們關于service-provider的配置可以簡化為一條:

zuul:
  routes:
    service-provider: /service-provider/** # 這里是映射路徑

省去了對服務名稱的配置。

3.6.默認的路由規(guī)則

在使用Zuul的過程中,上面講述的規(guī)則已經大大的簡化了配置項。但是當服務較多時,配置也是比較繁瑣的。因此Zuul就指定了默認的路由規(guī)則:

  • 默認情況下,一切服務的映射路徑就是服務名本身。例如服務名為:service-provider,則默認的映射路徑就 是:/service-provider/**

也就是說,剛才的映射規(guī)則我們完全不配置也是OK的,不信就試試看。

3.7.路由前綴

配置示例:

zuul:
  routes:
    service-provider: /service-provider/**
    service-consumer: /service-consumer/**
  prefix: /api # 添加路由前綴

我們通過zuul.prefix=/api來指定了路由的前綴,這樣在發(fā)起請求時,路徑就要以/api開頭。

3.8.過濾器

Zuul作為網關的其中一個重要功能,就是實現(xiàn)請求的鑒權。而這個動作我們往往是通過Zuul提供的過濾器來實現(xiàn)的。

3.8.1.ZuulFilter

ZuulFilter是過濾器的頂級父類。在這里我們看一下其中定義的4個最重要的方法:

public abstract ZuulFilter implements IZuulFilter{

    abstract public String filterType();

    abstract public int filterOrder();
    
    boolean shouldFilter();// 來自IZuulFilter

    Object run() throws ZuulException;// IZuulFilter
}
  • shouldFilter:返回一個Boolean值,判斷該過濾器是否需要執(zhí)行。返回true執(zhí)行,返回false不執(zhí)行。
  • run:過濾器的具體業(yè)務邏輯。
  • filterType:返回字符串,代表過濾器的類型。包含以下4種:
    • pre:請求在被路由之前執(zhí)行
    • route:在路由請求時調用
    • post:在route和errror過濾器之后調用
    • error:處理請求時發(fā)生錯誤調用
  • filterOrder:通過返回的int值來定義過濾器的執(zhí)行順序,數(shù)字越小優(yōu)先級越高。

3.8.2.過濾器執(zhí)行生命周期

這張是Zuul官網提供的請求生命周期圖,清晰的表現(xiàn)了一個請求在各個過濾器的執(zhí)行順序。

正常流程:

  • 請求到達首先會經過pre類型過濾器,而后到達route類型,進行路由,請求就到達真正的服務提供者,執(zhí)行請求,返回結果后,會到達post過濾器。而后返回響應。

異常流程:

  • 整個過程中,pre或者route過濾器出現(xiàn)異常,都會直接進入error過濾器,在error處理完畢后,會將請求交給POST過濾器,最后返回給用戶。
  • 如果是error過濾器自己出現(xiàn)異常,最終也會進入POST過濾器,將最終結果返回給請求客戶端。
  • 如果是POST過濾器出現(xiàn)異常,會跳轉到error過濾器,但是與pre和route不同的是,請求不會再到達POST過濾器了。

所有內置過濾器列表:

3.8.3.使用場景

場景非常多:

  • 請求鑒權:一般放在pre類型,如果發(fā)現(xiàn)沒有訪問權限,直接就攔截了
  • 異常處理:一般會在error類型和post類型過濾器中結合來處理。
  • 服務調用時長統(tǒng)計:pre和post結合使用。

3.9.自定義過濾器

接下來我們來自定義一個過濾器,模擬一個登錄的校驗?;具壿嫞喝绻埱笾杏衋ccess-token參數(shù),則認為請求有效,放行。

3.9.1.定義過濾器類

內容:

@Component
public class LoginFilter extends ZuulFilter {
    /**
     * 過濾器類型,前置過濾器
     * @return
     */
    @Override
    public String filterType() {
        return "pre";
    }

    /**
     * 過濾器的執(zhí)行順序
     * @return
     */
    @Override
    public int filterOrder() {
        return 1;
    }

    /**
     * 該過濾器是否生效
     * @return
     */
    @Override
    public boolean shouldFilter() {
        return true;
    }

    /**
     * 登陸校驗邏輯
     * @return
     * @throws ZuulException
     */
    @Override
    public Object run() throws ZuulException {
        // 獲取zuul提供的上下文對象
        RequestContext context = RequestContext.getCurrentContext();
        // 從上下文對象中獲取請求對象
        HttpServletRequest request = context.getRequest();
        // 獲取token信息
        String token = request.getParameter("access-token");
        // 判斷
        if (StringUtils.isBlank(token)) {
            // 過濾該請求,不對其進行路由
            context.setSendZuulResponse(false);
            // 設置響應狀態(tài)碼,401
            context.setResponseStatusCode(HttpStatus.SC_UNAUTHORIZED);
            // 設置響應信息
            context.setResponseBody("{\"status\":\"401\", \"text\":\"request error!\"}");
        }
        // 校驗通過,把登陸信息放入上下文信息,繼續(xù)向后執(zhí)行
        context.set("token", token);
        return null;
    }
}

3.9.2.測試

沒有token參數(shù)時,訪問失?。?/p>

添加token參數(shù)后:

3.10.負載均衡和熔斷

Zuul中默認就已經集成了Ribbon負載均衡和Hystix熔斷機制。但是所有的超時策略都是走的默認值,比如熔斷超時時間只有1S,很容易就觸發(fā)了。因此建議我們手動進行配置:

hystrix:
  command:
    default:
      execution:
        isolation:
          thread:
            timeoutInMilliseconds: 2000 # 設置hystrix的超時時間為6000ms
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容