天天看點

Springboot 整合 SpringCloud元件-Feign(Ribbon/Hystrix) (三)

咱們已經把Eureka Server注冊中心搭建完畢,也把微服務成功注冊到了注冊中心裡去了。

接下來,我們整合的是,非常核心實用的元件-Feign,

Feign除了解決了微服務之間調用,裡面還囊括了 Ribbon負載均衡以及Hystrix 熔斷降級。

Ribbon 負載均衡:

Springboot 整合 SpringCloud元件-Feign(Ribbon/Hystrix) (三)

Hystrix 熔斷降級:

Springboot 整合 SpringCloud元件-Feign(Ribbon/Hystrix) (三)

同樣,建立一個springboot項目,起名feign作為一個微服務:

Springboot 整合 SpringCloud元件-Feign(Ribbon/Hystrix) (三)

(同樣,我們這裡選用的springcloud版本是:Finchley.RELEASE) 

 既然作為一個微服務,那自然也是需要注冊到注冊中心去的,是以pom.xml裡核心的依賴包為:

<dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-openfeign</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
    </dependency>      

 application.yml:

eureka:
  instance:
    preferIpAddress: true
    instance-id: ${spring.cloud.client.ip-address}:${server.port}
  client:
    serviceUrl:
      defaultZone: http://localhost:8761/eureka/
server:
  port: 8765

spring:
  application:
    name: feign
feign:
  hystrix:
    enabled: true      

(fegin: hystrix : enabled : true   這個配置項是指,開啟熔斷機制,也許在yml裡顯示沒有相關依賴,但是不用慌,也許是因為版本問題,這個設定項加上就行,是起作用的) 

 然後我們在啟動類上加上各種注解:

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.hystrix.EnableHystrix;
import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard;
import org.springframework.cloud.openfeign.EnableFeignClients;

@SpringBootApplication
@EnableEurekaClient
@EnableDiscoveryClient
@EnableHystrix
@EnableHystrixDashboard
@EnableFeignClients
public class FeignApplication {

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

}      

 OK,基本的條條框框,我們已經做完了,接下來進行 使用Feign去調用我們之前建立的微服務client-test。

我們來看看,我們準備調用的client-test裡面的接口:

@Value("${server.port}")
    String port;
    @RequestMapping("/haveatry")
    public String home(@RequestParam(value = "name", defaultValue = "forezp") String name) {

        return "微服務 client-test被調用, " + "name為:"+name + " ,被調用的服務端口 port:" + port;
    }      

那麼我們在feign服務裡,要這麼寫,才能成功調用:

 先建立一個interface ,SchedualServiceHi.java:

import com.cloud.feign.service.impl.SchedualServiceHiHystric;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;

/**
 * @Author:JCccc
 * @Description:
 * @Date: created in 14:42 2019/2/3
 */

@FeignClient(value = "client-test",fallback = SchedualServiceHiHystric.class)
@Component
public interface SchedualServiceHi {
    @RequestMapping(value = "/haveatry",method = RequestMethod.GET)
    String sayHiFromClientOne(@RequestParam(value = "name") String name);
}      

可以看到,

1. 需要保證@FeignClient 裡面,value的值是 調用的接口所屬微服務名稱;

2.需要保證請求的Url是一緻的;

3.需要保證接口請求方式是一緻的;

4.需要保證接口傳參是一緻的;

5.需要使用@RequestMapping這種方式(避免使用GetMapping/PostMapping)

6.額外提醒,隻要是傳參,請都加上@RequestParam("XXXX");如果傳對象就加上@RequestBody; 這樣你就不用踩Feign的傳參的坑了,不管是使用Feign的服務還是提供接口的client服務,請都加上這些傳參注解保持一緻;

看的仔細地人已經看到了fallback後面的類,這個類就是用于熔斷降級的,SchedualServiceHiHystric.java:

import com.cloud.feign.service.SchedualServiceHi;
import org.springframework.stereotype.Component;

/**
 * @Author:JCccc
 * @Description:
 * @Date: created in 14:48 2018/2/3
 */
@Component
public class SchedualServiceHiHystric implements SchedualServiceHi {
    @Override
    public String sayHiFromClientOne(String name) {

        return "sorry! 網絡異常,服務暫時無法通路。 請求的name為:"+name;
    }
}      

OK,到這裡,我們其實已經打通了跨服務直接的接口調用,那麼我們寫一個屬于feign服務的接口(因為feign也是一個微服務已經注冊到了注冊中心,跨服務調用接口直接因為我們分布式架構按照業務把服務拆分了,業務需求需要從feign服務這裡的接口調用到client-test服務接口,是以就需要用到了Feign元件):

HiController.java:

import com.cloud.feign.service.SchedualServiceHi;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

/**
 * @Author:JCccc
 * @Description:
 * @Date: created in 14:44 2019/2/3
 */
@RestController
public class HiController {


    @Autowired
    SchedualServiceHi schedualServiceHi;

    @GetMapping(value = "/feignTest")
    public String sayHi(@RequestParam String name) {

        return schedualServiceHi.sayHiFromClientOne(name);
    }

}      

好,現在我們先将項目跑起來(前提注冊中心以及client-test也是正在運作的),看看跨服務調用效果(熔斷和負載均衡一會略詳細講講,不慌):

Springboot 整合 SpringCloud元件-Feign(Ribbon/Hystrix) (三)

 我們調下feign的接口  ​​http://localhost:8765/feignTest?name=1323​​ :

Springboot 整合 SpringCloud元件-Feign(Ribbon/Hystrix) (三)

可以看到,跨服務間調用已經成功。

  然後,下面我們直接把 client-test服務關掉(狠心),feign的接口  ​​http://localhost:8765/feignTest?name=1323​​ :

Springboot 整合 SpringCloud元件-Feign(Ribbon/Hystrix) (三)

看到傳回值,顯示的内容, 沒錯就是我們剛剛配置的fallback類:

Springboot 整合 SpringCloud元件-Feign(Ribbon/Hystrix) (三)

這裡可以根據使用場景,擴充做資料存儲、降級調用等等。

到這裡,基本feign的使用也七七八八, 就剩一個負載均衡的使用:

那麼我們繼續, 既然是負載均衡,那麼也先得有東西均衡,是以我們直接再建立一個微服務,這個微服務跟上一篇的client-test微服務一模一樣,依賴、配置全都保持一樣, 唯一的改變是 端口!

上一篇的client-test微服務端口是8762, 我們這個是8763(注意,服務名保持一樣 client-test,因為負載均衡是通過擷取注冊中心的服務注冊資訊,根據服務名去比對的):

server:
  port: 8763

spring:
  application:
    name: client-test

  zipkin:
    base-url: http://localhost:9411
    sender:
      type: web
  sleuth:
    sampler:
      probability: 1.0

eureka:
#以IP位址方式顯示在注冊中心
  instance:
    preferIpAddress: true
    instance-id: ${spring.cloud.client.ip-address}:${server.port}

  client:
    serviceUrl:
      defaultZone: http://localhost:8761/eureka/      

然後也将這個新的client-test微服務跑起來,先通路注冊中心看看情況 ​​http://localhost:8761/​​:

Springboot 整合 SpringCloud元件-Feign(Ribbon/Hystrix) (三)

連續調下feign的接口  ​​http://localhost:8765/feignTest?name=1323​​ :

Springboot 整合 SpringCloud元件-Feign(Ribbon/Hystrix) (三)
Springboot 整合 SpringCloud元件-Feign(Ribbon/Hystrix) (三)

從調用的傳回值我們可以得知,已經負載均衡了,預設的負載機制是輪換地調用,我們隻需橫向擴充需要實作負載均衡的服務執行個體。

到了這裡,講道理是已經整合完畢了,但是,也許細心思考的人會有以下的想法:

又熔斷降級、又負載均衡,那麼如果現在有3個client-test服務執行個體, 其中有一個挂掉或者通路響應比較慢,那麼負載和均衡直接是怎麼樣的處理結果呢?

接下來我們用client-test服務執行個體 來簡單模拟下場景,

首先我們剛剛試過,2個client-test服務執行個體 都正常通路,響應都是正常的時候, 不可能出現熔斷降級,然後負載均衡是,輪流一次次地通路到了這兩個執行個體;

OK,我們在8762這個服務執行個體的接口上,加上一個sleep(5000):

Springboot 整合 SpringCloud元件-Feign(Ribbon/Hystrix) (三)

然後連續調下feign的接口  ​​http://localhost:8765/feignTest?name=1323​​ :

結果是優先負載均衡輪流調用執行個體,調用到正常運作的服務則正常調用;若調用到時間久的,就會出現熔斷降級。

那麼這種情況,其實我們是可以設定feign調用服務的連接配接逾時時間的,

我們建立一個FeignConfiugure.java:

import feign.Request;
import feign.Retryer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @Author:JCccc
 * @Description:
 * @Date: created in 10:56 2019/2/3
 */
@Configuration
public class FeignConfigure {
    public static int connectTimeOutMillis = 7000;//逾時時間
    public static int readTimeOutMillis =7000;
    @Bean
    public Request.Options options() {
        return new Request.Options(connectTimeOutMillis, readTimeOutMillis);
    }

    @Bean
    public Retryer feignRetryer() {
        return new Retryer.Default();
    }

}      

注意這裡設定的其實是負載均衡的連接配接逾時時間,熔斷預設是1000ms,也就是說連接配接超過1000ms都直接熔斷了,根本不會負載均衡。 是以在這裡建議,負載均衡的逾時時間設定最好是不要大于熔斷逾時時間, 這樣才是符合邏輯的。

那麼熔斷時間怎麼設定呢,

我們可以直接在yml設定(下面的設定項也許在yml檔案裡面也會顯示不存在,但是其實是有效的):

### Hystrix 配置
hystrix:
  # 這樣将會自動配置一個 Hystrix 并發政策插件的 hook,這個 hook 會将 SecurityContext 從主線程傳輸到 Hystrix 的指令。
  # 因為 Hystrix 不允許注冊多個 Hystrix 政策,是以可以聲明 HystrixConcurrencyStrategy
  # 為一個 Spring bean 來實作擴充。Spring Cloud 會在 Spring 的上下文中查找你的實作,并将其包裝在自己的插件中。
  shareSecurityContext: true
  command:
    default:
      circuitBreaker:
        # 當在配置時間視窗内達到此數量的失敗後,進行短路。預設20個
        requestVolumeThreshold: 1
        # 觸發短路的時間值,當該值設為5000時,則當觸發 circuit break 後的5000毫秒内都會拒絕request
        # 也就是5000毫秒後才會關閉circuit。預設5000
        sleepWindowInMilliseconds: 15000
        # 強制打開熔斷器,如果打開這個開關,那麼拒絕所有request,預設false
        forceOpen: false
        # 強制關閉熔斷器 如果這個開關打開,circuit将一直關閉且忽略,預設false
        forceClosed: false
      execution:
        isolation:
          thread:
            # 熔斷器逾時時間,預設:1000/毫秒
            timeoutInMilliseconds: 10000      

 我這邊是故意設定熔斷時間為10s,負載均衡時間為7秒, 然後接口sleep是5s,然後調用接口通路,就可以明顯觀察,不會存在熔斷傳回值隻是通路特别久才有傳回結果,因為sleep是5秒;

OK,兩個時間咱們都可以設定,具體就根據業務需求去設定吧。

Springboot整合SpringCloud -Feign的詳細介紹就到此。

以下是我自己設定各個時間後,調用接口的簡述(可以無視,自己親自去試試時間的設定搭配):

我們可以再把熔斷時間改為4秒,負載均衡時間為7秒,然後接口sleep是5s,調用接口通路,可以看到直接觸發了熔斷效果;

我們可以再把熔斷時間改為7秒,負載均衡時間為3秒,然後接口sleep是5s,調用接口通路,

可以看到當調用到超過負載時間的接口時,并沒直接傳回熔斷結果(因為還沒到熔斷的7s),而是傳回了正常沒有設定sleep的另一個執行個體的接口傳回值(因為這時候超過了負載的3秒,是以元件幫我們去正常的服務了)。

繼續閱讀