1. 程式人生 > >springCloud(F版)(6)——Hystrix斷路器、HystrixDashboard斷路器監控及Turbine斷路器聚合監控

springCloud(F版)(6)——Hystrix斷路器、HystrixDashboard斷路器監控及Turbine斷路器聚合監控

前面博文我們實現了一個簡單的分散式架構,通過服務叢集實現高可用,通過sleuth實現服務跟蹤。下面通過Hystrix斷路器對服務叢集某介面不可用的情況進行識別和反饋,並進一步優化使用HystrixDashboard對斷路器進行監控、使用Turbine對斷路器監控情況進行聚合分析。

首先,我們將介面服務的消費者進行優化,新增Hystrix斷路器來識別介面不可用情況並給出反饋。前面博文中的系統結構,使用zuul叢集對介面進行消費,使用一個zuul工程實現balance負載均衡,那麼我們首先看看zuul如何實現斷路器功能。然後再依次學習一下Ribbon和Feign如何整合Hystrix斷路器。

一、zuul的FallbackProvider功能

由於springCloud在不斷的更新,因此不同版本的zuul中實現熔斷的interface也在不斷變化,我們前面博文裡搭建的springCloud系統使用的是SpringCloud F版本,Springboot 2.0 版本。經過無數的坑之後,終於找到了zuul在的熔斷方法。。。如果你的工程springCloud版本或springboot版本跟我不一致,下面就當沒看見吧。。。

在之前做好的zuul工程中新增熔斷控制類 client1fallback 實現 FallbackProvider介面,原始碼如下:

package com.testzuul.zuul;

import org.springframework.cloud.netflix.zuul.filters.route.FallbackProvider;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.stereotype.Component;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;

@Component
public class client1fallback implements FallbackProvider {
    @Override
    public String getRoute() {
        return "service-client1";
    }
    @Override
    public ClientHttpResponse fallbackResponse(String route, Throwable cause) {
        System.out.println("route:"+route);
        System.out.println("exception:"+cause.getMessage());
        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("service-client1 fallback .".getBytes());
            }

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

為了少走彎路,我連import都粘進來了。。。

其他內容全都沒有修改,留意getRoute函式,返回值要跟application name(serviceID)保持一致,用來識別這是哪個微服務的熔斷控制類。當然,這也意味著你有多少微服務,就要建立多少個熔斷控制類了。

編譯執行後,關閉service-client1這個服務叢集中的所有服務,測試一下。

啟動service-client1服務,再試一次。

zuul整合的斷路機制只要是識別微服務是不是還在(剛剛識別的是service-client1這個微服務),而不是具體哪個介面的宕機,下面學習一下在Ribbon中整合Hystrix斷路器來進一步處理介面級別的熔斷機制。

二、Ribbon整合Hystrix斷路器

pom檔案中新增Hystrix起步依賴(spring-cloud-starter-netflix-hystrix)

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.testribbon</groupId>
    <artifactId>testribbon</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>testribbon</name>
    <description>Demo project for Spring Boot</description>

    <parent>
        <groupId>com.springcloud</groupId>
        <artifactId>testspringcloud</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <relativePath>../pom.xml</relativePath>
    </parent>

    <dependencies>
        <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-netflix-hystrix</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-zipkin</artifactId>
        </dependency>

    </dependencies>

</project>

修改properties配置檔案

eureka.client.serviceUrl.defaultZone=http://10.48.8.231:8761/eureka/
#eureka.client.serviceUrl.defaultZone=http://localhost:8761/eureka/
eureka.instance.prefer-ip-address=true
spring.zipkin.base-url=http://10.48.8.231:9411
#spring.zipkin.base-url=http://localhost:9411
#spring.sleuth.sampler.percentage=1.0
spring.sleuth.sampler.probability=1

server.port: 8751
spring.application.name=testRibbon

**application.java入口類添加註解

@SpringBootApplication
@EnableEurekaClient
@EnableDiscoveryClient
@EnableHystrix
public class TestribbonApplication {

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

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

新增介面呼叫的控制類 clientControler

@RestController
public class clientControler {
    @Autowired
    clientService client;

    @GetMapping(value = "/client1")
    public String client1(@RequestParam String name) {
        return client.client1Service( name );
    }

    @GetMapping(value = "/client2")
    public String client2(@RequestParam String name) {
        return client.client2Service( name );
    }

}

建立介面呼叫的service類 clientService

@Service
public class clientService {

    @Autowired
    RestTemplate restTemplate;

    @HystrixCommand(fallbackMethod = "client1Error")
    public String client1Service(String name) {
        return restTemplate.getForObject("http://SERVICE-CLIENT1/hi?name="+name,String.class);
    }

    public String client1Error(String name) {
        return "hi,"+name+",sorry,fallback!";
    }

    @HystrixCommand(fallbackMethod = "client2Error")
    public String client2Service(String name) {
        return restTemplate.getForObject("http://SERVICE-CLIENT2/hi?name="+name,String.class);
    }

    public String client2Error(String name) {
        return "hi,"+name+",sorry,fallback!";
    }

}

編譯執行後瀏覽器輸入 http://localhost:8751/client1?name=qftest 和  http://localhost:8751/client2?name=qftest

下圖是我開了client2,沒開client1的效果。

三、Feign整合Hystrix斷路器

Feign是自帶hystrix斷路器的,因此pom檔案無需新增新的依賴包。SpringCloud D版本以上斷路器預設是關閉的,因此修改配置檔案,新增feign.hystrix.enabled=true開啟斷路器即可

eureka.client.serviceUrl.defaultZone=http://localhost:8761/eureka/ 
eureka.instance.prefer-ip-address=true
spring.zipkin.base-url=http://localhost:9411 
#spring.sleuth.sampler.percentage=1.0
spring.sleuth.sampler.probability=1

server.port: 8752
spring.application.name=testFeign

#啟動feign自帶的hystrix斷路器
feign.hystrix.enabled=true

**application.java入口類添加註解

@SpringBootApplication
@EnableEurekaClient
@EnableDiscoveryClient
@EnableHystrix

控制類testcontroler也沒有變化

@RestController
public class testcontroler {
    @Autowired
    client1service test1;
    @Autowired
    client2service test2;

    @GetMapping(value = "/client1")
    public String client1(@RequestParam String name) {
        return test1.testFunction( name );
    }

    @GetMapping(value = "/client2")
    public String client2(@RequestParam String name) {
        return test2.testFunction( name );
    }
}

client1service介面則不能只宣告個interface函數了,要新增fallback的指定

@FeignClient(value = "service-client1",fallback = Client1serviceimpl.class)
public interface client1service {
    @RequestMapping(value = "/hi",method = RequestMethod.GET)
    String testFunction(@RequestParam(value = "name") String name);
}

其中,FeignClient註解指定了介面對應的微服務service-client1和熔斷處理類client1serviceimpl,介面中的每個函式宣告都對應著一個微服務介面,這一點沒什麼變化。

下面新增client1serviceimpl類實現client1service介面,其實是實現fallback。

@Component
public class Client1serviceimpl implements client1service {
    @Override
    public String testFunction(String name) {
         return "hi,"+name+",sorry,fallback!";
    }
}

當feign程式呼叫介面時,如果介面沒有反饋則會快速執行client1serviceimpl中對應的熔斷處理函式。

編譯執行,效果與Ribbon相同。

四、HystrixDashboard斷路器監控

訪問介面提示報錯資訊,說明我們的Hystrix斷路器已生效,下面我們以zuul工程為例,通過HystrixDashboard對服務的熔斷情況進行檢視監控。

pom檔案新增基礎依賴

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

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

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

properties配置檔案中新增必要的配置資訊

#配置 Hystrix Dashboard
management.server.port=9001
management.endpoints.web.base-path=/actuator
management.endpoints.web.exposure.include='*'

入口類添加註解

@EnableHystrix
@EnableHystrixDashboard

重新編譯執行zuul程式,瀏覽器呼叫zuul介面(反覆關閉service-client1和service-client2)看到成功呼叫和熔斷資訊後就可以嘗試檢視熔斷情況了。

瀏覽器輸入 http://localhost:8751/hystrix 注意localhost如果不是本地測試的化換成自己的ip、8751是服務的埠號,並不是剛剛配置的management.server.port對應的埠號。如下圖

輸入要要看的檢視的監控資訊 http://localhost:9001/actuator/hystrix.stream 這個時候的埠號才是剛剛配置的9001,讀取的資料地址是management.endpoints.web.base-path配置的actuator,資料流是hystrix.stream。當然這個url也可以直接貼上到瀏覽器上去看具體的資料流內容。如下圖:

至此我們已經可以檢視某個微服務的熔斷資訊了,Ribbon和Feign的改造方式基本相同。但是,這種改造方式只能檢視某個微服務的熔斷情況,當微服務較多時呢?我們希望聚合所有的微服務,將視覺化資訊集中起來檢視,引入下面章節。

五、Turbine斷路器聚合監控

Turbine是一個獨立的server,同樣作為eureka client將自己註冊到註冊中心,通過配置監控的serviceID從註冊中心獲取到各service的ip,然後去拉取hystrix.stream資料流進行統一監控。

我將上面建立的Ribbon和Feign都配置了HystrixDashboard,然後啟動了這兩個負載均衡介面呼叫程式,下面新建立一個springboot工程,配置成TurbineServer實現斷路器的聚合監控。

建立springboot工程命名為turbineserver,修改pom檔案

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.turbine</groupId>
    <artifactId>turbineserver</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>turbineserver</name>
    <description>Demo project for Spring Boot</description>

    <parent>
        <groupId>com.springcloud</groupId>
        <artifactId>testspringcloud</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <relativePath>../pom.xml</relativePath>
    </parent>

    <dependencies>
        <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-eureka-client</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-zipkin</artifactId>
        </dependency>

        <!-- hystrix起步依賴包 -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
        </dependency>
        <!-- hystrix dashboard 依賴包 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
        </dependency>
        <!-- turbine 依賴包 -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-turbine</artifactId>
        </dependency>
    </dependencies>
</project>

**application.java入口類添加註解

@SpringBootApplication
@EnableEurekaClient
@EnableDiscoveryClient
@RestController
@EnableHystrix
@EnableHystrixDashboard
@EnableCircuitBreaker
@EnableTurbine

修改application.properties配置檔案,新增turbine相關配置

#配置eureka client
eureka.client.serviceUrl.defaultZone=http://localhost:8761/eureka/ 
eureka.instance.prefer-ip-address=true
#配置zipkin服務跟蹤
spring.zipkin.base-url=http://localhost:9411  
spring.sleuth.sampler.probability=1

#配置自己的埠和serviceID
server.port: 9004
spring.application.name=turbine-server

#配置 Hystrix Dashboard
management.server.port=9003
management.endpoints.web.base-path=/actuator
management.endpoints.web.exposure.include="*"
management.endpoints.web.cors.allowed-origins="*"
management.endpoints.web.cors.allowed-methods="*"

#配置 turbine
turbine.app-config=testfeign,testribbon
turbine.aggregator.cluster-config=default
turbine.cluster-name-expression=new String("default")
turbine.combine-host-port=true 
turbine.instanceUrlSuffix.default=actuator/hystrix.stream

OK,編譯執行看下結果

瀏覽器輸入 http://localhost:9004/turbine.stream看一下資料流情況

瀏覽器輸入 http://localhost:9004/hystrix

頁面輸入http://localhost:9004/turbine.stream

收工睡覺。