Spring Cloud學(xué)習(xí)day100:Hystrix

一、雪崩效應(yīng)和解決方案

1.什么是災(zāi)難性的雪崩效應(yīng) ?

在微服務(wù)架構(gòu)中,一個(gè)請(qǐng)求需要調(diào)用多個(gè)服務(wù)是非常常見(jiàn)的。若有大量的請(qǐng)求涌入,容器的線(xiàn)程資源會(huì)被消耗完畢,導(dǎo)致服務(wù)癱瘓。服務(wù)與服務(wù)之間的依賴(lài)性,故障會(huì)傳播,造成連鎖反應(yīng),會(huì)對(duì)整個(gè)微服務(wù)系統(tǒng)造成災(zāi)難性的嚴(yán)重后果,這就是服務(wù)故障的“雪崩”效應(yīng)。

服務(wù)T出現(xiàn)故障后

導(dǎo)致的連鎖反應(yīng)

導(dǎo)致整個(gè)系統(tǒng)癱瘓

2.出現(xiàn)雪崩的原因:

(1)服務(wù)提供者不可用(硬件故障,程序bug,緩存擊穿,用戶(hù)的大量請(qǐng)求);
(2)重試加大流量(用戶(hù)重試,代碼邏輯重試);
(3)服務(wù)調(diào)用者不可用(同步等待造成的資源耗盡);
最終的結(jié)果就是一個(gè)服務(wù)不可用,導(dǎo)致一系列服務(wù)的不可用,而往往這種后果是無(wú)法預(yù)料的。

3.如何解決雪崩效應(yīng):

  • 降級(jí) :

超時(shí)降級(jí)、資源不足時(shí)(線(xiàn)程或信號(hào)量)降級(jí),降級(jí)后可以配合降級(jí)接口返回托底數(shù)據(jù)。 實(shí)現(xiàn)一個(gè) fallback 方法, 當(dāng)請(qǐng)求后端服務(wù)出現(xiàn)異常的時(shí)候, 可以使用 fallback 方法返回的值. 隔離(線(xiàn)程池隔離和信號(hào)量隔離) 限制調(diào)用分布式服務(wù)的資源使用,某一個(gè)調(diào)用的服務(wù)出現(xiàn)問(wèn)題不會(huì)影響其他服務(wù)調(diào)用。

  • 熔斷:

當(dāng)失敗率(如因網(wǎng)絡(luò)故障/超時(shí)造成的失敗率高)達(dá)到閥值自動(dòng)觸發(fā)降級(jí),熔斷器觸發(fā)的快 速失敗會(huì)進(jìn)行快速恢復(fù)。

  • 緩存:

提供了請(qǐng)求緩存。

  • 請(qǐng)求合并:

提供請(qǐng)求合并。


二、服務(wù)降級(jí)

1.什么是服務(wù)降級(jí)?

就是對(duì)不怎么重要的服務(wù)進(jìn)行低優(yōu)先級(jí)的處理。盡可能的把系統(tǒng)資源讓給優(yōu)先級(jí)高的服務(wù)。資源有限,而請(qǐng)求是無(wú)限的。如果在并發(fā)高峰期,不做服務(wù)降級(jí)處理,一方面肯定會(huì)影響整體服務(wù)的性能,嚴(yán)重的話(huà)可能會(huì)導(dǎo)致宕機(jī)某些重要的服務(wù)不可用。

  • 什么時(shí)候會(huì)觸發(fā)getFallback調(diào)用:

(1) 方法拋出非 HystrixBadRequestException 異常。
(2) 方法調(diào)用超時(shí)。
(3) 熔斷器開(kāi)啟攔截調(diào)用。
(4) 線(xiàn)程池/隊(duì)列/信號(hào)量是否跑滿(mǎn)。

2.如何使用服務(wù)降級(jí):

  • 創(chuàng)建簡(jiǎn)單測(cè)試降級(jí)實(shí)現(xiàn):

使用Eureka注冊(cè)中心注冊(cè)服務(wù),下面測(cè)試都使用這個(gè)Provider提供服務(wù)。

Consumer——服務(wù)的消費(fèi)者

Provider服務(wù)的生產(chǎn)者
1.1Consumer-hystrix:
  • 修改 pom 文件添加 hystrix 的坐標(biāo):
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Dalston.SR5</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-config</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-hystrix</artifactId>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
  • 修改全局配置文件:
spring.application.name=product-provider-hystix
server.port=9010

eureka.client.serviceUrl.defaultZone=http://admin:123456@eureka1:8761/eureka/,http://admin:123456@eureka2:8761/eureka/
  • 添加實(shí)體類(lèi):
public class Product {
    private int id;
    private String name;

    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Product(int id, String name) {
        super();
        this.id = id;
        this.name = name;
    }
    public Product() {
        super();
    }
}
  • 編寫(xiě)啟動(dòng)類(lèi)開(kāi)啟熔斷器:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;

@EnableCircuitBreaker // 開(kāi)啟熔斷器 斷路器
@EnableEurekaClient
@SpringBootApplication
public class ConsumerApplication {
    public static void main(String[] args) {
        SpringApplication.run(ConsumerApplication.class, args);
    }
}
  • 編寫(xiě)ProductService:
@Service
public class ProductService {
    @Autowired
    private LoadBalancerClient loadBalancerClient;// ribbon負(fù)載均衡器

    @HystrixCommand(fallbackMethod = "fallback")//當(dāng)下面方法服務(wù)出現(xiàn)異常停止會(huì)返回托底數(shù)據(jù)
    public List<Product> getProduct() {
        // 選擇調(diào)用的服務(wù)的名稱(chēng)
        // ServiceInstance 封裝了服務(wù)的基本信息,如 IP,端口
        ServiceInstance si = this.loadBalancerClient.choose("product-provider-hystix");
        // 拼接訪(fǎng)問(wèn)服務(wù)的URL
        StringBuffer sb = new StringBuffer();
        // http://localhost:9001/product/findAll
        sb.append("http://").append(si.getHost()).append(":").append(si.getPort()).append("/product/findAll");
        System.out.println(sb.toString());
        // springMVC RestTemplate
        RestTemplate rt = new RestTemplate();

        ParameterizedTypeReference<List<Product>> type = new ParameterizedTypeReference<List<Product>>() {
        };

        // ResponseEntity:封裝了返回值信息
        ResponseEntity<List<Product>> response = rt.exchange(sb.toString(), HttpMethod.GET, null, type);
        List<Product> list = response.getBody();
        return list;
    }
    //返回托底數(shù)據(jù)的方法
    public List<Product> fallback(){
        List<Product> list = new ArrayList<>();
        list.add(new Product(-1,"托底數(shù)據(jù)!"));
        return list;
    }
}
  • 編寫(xiě)Controller:
@RestController
public class ProductController {
    
    @Autowired
    private ProductService productService;
    
    @RequestMapping("/list")
    public List<Product> list(){
        return productService.getProduct();
    }
}
1.2Service-hystrix:
  • 修改POM文件:
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Dalston.SR5</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-config</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka</artifactId>
        </dependency>
    </dependencies>
  • 創(chuàng)建實(shí)體類(lèi):
public class Product {
    private int id;
    private String name;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
  • 編寫(xiě)Service:
@RequestMapping("/product")
public interface ProductService {
    // 查詢(xún)所有
    @RequestMapping(value = "/findAll", method = RequestMethod.GET)
    public List<Product> findAll();

    // ID查詢(xún)
    @RequestMapping(value = "/findById", method = RequestMethod.GET)
    public Product getProductById(@RequestParam("id") Integer id);

    // 傳遞多個(gè)參數(shù) 方式一 :GET方式
    @RequestMapping(value = "/add", method = RequestMethod.GET)
    public Product addProduct(@RequestParam("id") Integer id, @RequestParam("name") String name);

    // ----------------------Httpclient----------------------------------------------
    //傳遞多個(gè)參數(shù) 方式二 :POST方式
    @RequestMapping(value = "/add2", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE)
    public Product addProduct2(@RequestBody Product product);

    // 使用HttpClient工具 傳遞多個(gè)參數(shù) :基于GET方式
    @RequestMapping(value = "/add3", method = RequestMethod.GET, consumes = MediaType.APPLICATION_JSON_VALUE)
    public Product addProduct3(Product product);

}
1.3Provider- hystrix:
  • 修改POM文件:
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Dalston.SR5</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-config</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka</artifactId>
        </dependency>
        <!-- 添加product-service坐標(biāo) -->
        <dependency>
            <groupId>com.zlw</groupId>
            <artifactId>springcloud-eureka-ribbon-hystrix-service</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
  • 修改全局配置文件:
spring.application.name=product-provider-hystix
server.port=9011

eureka.client.serviceUrl.defaultZone=http://admin:123456@eureka1:8761/eureka/,http://admin:123456@eureka2:8761/eureka/
  • 編寫(xiě)啟動(dòng)類(lèi):
@EnableEurekaClient
@SpringBootApplication
public class ProviderApplication {

    public static void main(String[] args) {
        SpringApplication.run(ProviderApplication.class, args);
    }
}
  • 編寫(xiě)ProviderController:
/**
 * Product-Provider服務(wù)
 * @author zhang
 *
 */
@RestController
public class ProductController implements  ProductService{

    @Override
    public List<Product> findAll() {
        List<Product> list = new ArrayList<Product>();
        list.add(new Product(1,"手機(jī)"));
        list.add(new Product(2,"電腦"));
        list.add(new Product(3,"電視"));
        return list;
    }

    @Override
    public Product getProductById(Integer id) {
        return new Product(id,"Product Provider");
    }

    @Override
    public Product addProduct(Integer id, String name) {
        return new Product(id,name);
    }
    @Override
    public Product addProduct2(@RequestBody Product product) {
        return product;
    }
    @Override
    public Product addProduct3(@RequestBody Product product) {
        return product;
    }
}
  • 測(cè)試:


    示例

    示例

三、請(qǐng)求緩存

Hystrix 為了降低訪(fǎng)問(wèn)服務(wù)的頻率,支持將一個(gè)請(qǐng)求與返回結(jié)果做緩存處理。如果再次 請(qǐng)求的 URL 沒(méi)有變化,那么 Hystrix 不會(huì)請(qǐng)求服務(wù),而是直接從緩存中將結(jié)果返回。這樣可 以大大降低訪(fǎng)問(wèn)服務(wù)的壓力。

  • Hystrix 自帶緩存。有兩個(gè)缺點(diǎn):

(1)是一個(gè)本地緩存。在集群情況下緩存是不能同步的。
(2)不支持第三方緩存容器。Redis,memcache 不支持的。 可以使用 spring 的 cache。

1.使用Redis

安裝Redis:http://www.itdecent.cn/p/c3f2e374e62f

1.1創(chuàng)建Consumer-cache:
示例
  • 修改POM文件添加 springCache 坐標(biāo) :
        <!-- springCache Redis-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
  • 修改配置文件添加redis鏈接信息:
spring.application.name=product-consumer-redis
server.port=9012

#設(shè)置服務(wù)注冊(cè)中心地址,指向另一個(gè)注冊(cè)中心 
eureka.client.serviceUrl.defaultZone=http://admin:123456@eureka1:8761/eureka/,http://admin:123456@eureka2:8761/eureka/

#Redis
spring.redis.datasource=0
#Redis服務(wù)器地址
spring.redis.host=192.168.226.130
#Reids服務(wù)器連接的端口
spring.redis.port=6379
#Redis 服務(wù)器連接密碼(默認(rèn)為空) 
spring.redis.password=
#連接池最大連接數(shù)(負(fù)值表示沒(méi)有限制) 
spring.redis.pool.max-active=100
#連接池最大阻塞等待時(shí)間(負(fù)值表示沒(méi)有限制) 
spring.redis.pool.max-wait=3000
#連接池最大空閉連接數(shù) 
spring.redis.pool.max-idle=200
#連接漢最小空閑連接數(shù) 
spring.redis.pool.min-idle=50
#連接超時(shí)時(shí)間(毫秒)
spring.redis.pool.timeout=600 
  • 修改啟動(dòng)類(lèi)開(kāi)啟緩存:
@EnableCaching
@EnableEurekaClient
@SpringBootApplication
public class ConsumerApplication {
    public static void main(String[] args) {
        SpringApplication.run(ConsumerApplication.class, args);
    }
}
  • 創(chuàng)建實(shí)體類(lèi)(get和set方法):
    private int id;
    private String name;
  • 修改ProductService:
@CacheConfig(cacheNames = { "product.consumer" })
@Service
public class ProductService {
    @Autowired
    private LoadBalancerClient loadBalancerClient;// ribbon負(fù)載均衡器

    public List<Product> getProduct() {
        // 選擇調(diào)用的服務(wù)的名稱(chēng)
        // ServiceInstance 封裝了服務(wù)的基本信息,如 IP,端口
        ServiceInstance si = this.loadBalancerClient.choose("product-provider-hystix");
        // 拼接訪(fǎng)問(wèn)服務(wù)的URL
        StringBuffer sb = new StringBuffer();
        // http://localhost:9001/product/findAll
        sb.append("http://").append(si.getHost()).append(":").append(si.getPort()).append("/product/findAll");
        System.out.println(sb.toString());
        // springMVC RestTemplate
        RestTemplate rt = new RestTemplate();

        ParameterizedTypeReference<List<Product>> type = new ParameterizedTypeReference<List<Product>>() {
        };

        // ResponseEntity:封裝了返回值信息
        ResponseEntity<List<Product>> response = rt.exchange(sb.toString(), HttpMethod.GET, null, type);
        List<Product> list = response.getBody();
        return list;
    }

    // 根據(jù)ID查詢(xún)
    @Cacheable(key = "'product'+#id")
    public Product findById(Integer id) {
        System.out.println("------Find------" + id);
        return new Product(id, "Product Redis");
    }

    // 根據(jù)ID刪除
    @CacheEvict(key = "'product'+#id")
    public void delProduct(Integer id) {
        System.out.println("-------Delete--------"+id);
    }
}
  • 測(cè)試:


    控制臺(tái)打印
網(wǎng)頁(yè)

redis的存儲(chǔ)

四、請(qǐng)求合并

就是把請(qǐng)求合并起來(lái)一起發(fā)送給服務(wù)端(一次性批量發(fā)送請(qǐng)求),服務(wù)端批量處理完成后再返回給客戶(hù)端。
請(qǐng)求合并的功能是客戶(hù)端來(lái)控制的,由客戶(hù)端整合發(fā)起,然后由服務(wù)端統(tǒng)一處理,但是需要服務(wù)端支持批量處理請(qǐng)求的能力。

1.什么情況下使用請(qǐng)求合并:

在微服務(wù)架構(gòu)中,我們將一個(gè)項(xiàng)目拆分成很多個(gè)獨(dú)立的模塊,這些獨(dú)立的模塊通過(guò)遠(yuǎn)程 調(diào)用來(lái)互相配合工作,但是,在高并發(fā)情況下,通信次數(shù)的增加會(huì)導(dǎo)致總的通信時(shí)間增加, 同時(shí),線(xiàn)程池的資源也是有限的,高并發(fā)環(huán)境會(huì)導(dǎo)致有大量的線(xiàn)程處于等待狀態(tài),進(jìn)而導(dǎo)致 響應(yīng)延遲,為了解決這些問(wèn)題,我們需要來(lái)了解 Hystrix 的請(qǐng)求合并。

  • 請(qǐng)求合并的缺點(diǎn):

設(shè)置請(qǐng)求合并之后,本來(lái)一個(gè)請(qǐng)求可能 5ms 就搞定了,但是現(xiàn)在必須再等 10ms 看看還 有沒(méi)有其他的請(qǐng)求一起的,這樣一個(gè)請(qǐng)求的耗時(shí)就從 5ms 增加到 15ms 了,不過(guò),如果我們 要發(fā)起的命令本身就是一個(gè)高延遲的命令,那么這個(gè)時(shí)候就可以使用請(qǐng)求合并了,因?yàn)檫@個(gè) 時(shí)候時(shí)間窗的時(shí)間消耗就顯得微不足道了,另外高并發(fā)也是請(qǐng)求合并的一個(gè)非常重要的場(chǎng) 景。

2.創(chuàng)建Consumer-batch測(cè)試:

示例
  • 修改POM文件:
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Dalston.SR5</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-config</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-hystrix</artifactId>
        </dependency>
  • 修改配置文件:
spring.application.name=product-provider-batch
server.port=9009

#設(shè)置服務(wù)注冊(cè)中心地址,指向另一個(gè)注冊(cè)中心 
eureka.client.serviceUrl.defaultZone=http://admin:123456@eureka1:8761/eureka/,http://admin:123456@eureka2:8761/eureka/
  • 創(chuàng)建實(shí)體類(lèi):
    private Integer id;
    private String name;
  • 修改啟動(dòng)類(lèi):
@EnableCircuitBreaker
@EnableEurekaClient
@SpringBootApplication
public class ProviductApplication {
    public static void main(String[] args) {
        SpringApplication.run(ProviductApplication.class, args);
    }
}
  • 修改ProductService:
@Service
public class ProductService {
    // 使用hystrix進(jìn)行合并請(qǐng)求
    @HystrixCollapser(batchMethod = "productMethod", scope = com.netflix.hystrix.HystrixCollapser.Scope.GLOBAL, collapserProperties = {
            // 請(qǐng)求時(shí)間間隔在 20ms 之內(nèi)的請(qǐng)求會(huì)被合并為一個(gè)請(qǐng)求,默認(rèn) 為 10ms
            @HystrixProperty(name = "timerDelayInMilliseconds", value = "20"),
            // 設(shè)置觸發(fā)批處理執(zhí)行之前,在批處理中允許許的最大請(qǐng)求數(shù)。
            @HystrixProperty(name = "maxRequestsInBatch", value = "3") 
            })
    // consumer的Controller調(diào)用的方法,該方法返回值必須要返回 Future類(lèi)型
    public Future<Product> getFuture(Integer id) {
        System.out.println("-------" + id + "---------");
        return null;
    }
    
    //調(diào)用Provider服務(wù)的方法
    @HystrixCommand
    public List<Product> productMethod(List<Integer> id){
        System.out.println("---------productMethod---------");
        for (Integer num : id) {
            System.out.println("========id:"+num);
        }
        List<Product> list = new ArrayList<Product>();
        list.add(new Product(1,"手機(jī)"));
        list.add(new Product(2,"電腦"));
        list.add(new Product(3,"電視"));
        list.add(new Product(4,"音箱"));
        list.add(new Product(99,"...."));
        System.out.println("=================");
        return list;
    }
}
  • 修改Controller:
@RestController
public class ProductController {

    @Autowired
    private ProductService productService;

    @RequestMapping("/list")
    public void list() throws Exception {
        Future<Product> future = productService.getFuture(1);
        Future<Product> future2 = productService.getFuture(2);
        Future<Product> future3 = productService.getFuture(4);
        Future<Product> future4 = productService.getFuture(3);
        System.out.println(future.get().toString());
        System.out.println(future2.get().toString());
        System.out.println(future3.get().toString());
        System.out.println(future4.get().toString());
    }
}
  • 測(cè)試:


    示例

五、服務(wù)熔斷

熔斷機(jī)制相當(dāng)于電路的跳閘功能。

示例

1.創(chuàng)建Consumer

示例
  • 修改POM文件:
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Dalston.SR5</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-config</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-hystrix</artifactId>
        </dependency>
    </dependencies>
  • 修改全局配置文件:
spring.application.name=product-provider-fuse
server.port=9008

#設(shè)置服務(wù)注冊(cè)中心地址,指向另一個(gè)注冊(cè)中心 
eureka.client.serviceUrl.defaultZone=http://admin:123456@eureka1:8761/eureka/,http://admin:123456@eureka2:8761/eureka/
  • 編寫(xiě)啟動(dòng)類(lèi):
@EnableCircuitBreaker//開(kāi)啟熔斷器,斷路器
@EnableEurekaClient
@SpringBootApplication
public class FuseApplication {
    public static void main(String[] args) {
        SpringApplication.run(FuseApplication.class, args);
    }
}
  • 創(chuàng)建實(shí)體類(lèi):
public class Product {
    private int id;
    private String name;
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Product() {
        super();
    }
    public Product(int id, String name) {
        super();
        this.id = id;
        this.name = name;
    }
}
  • 編寫(xiě)Service:
@Service
public class ProductService {

    @Autowired
    private LoadBalancerClient loadBalancerClient;

    @HystrixCommand(fallbackMethod = "fallback", commandProperties = {
            // 默認(rèn) 20 個(gè);10s 內(nèi)請(qǐng)求數(shù)大于 20 個(gè)時(shí)就啟動(dòng)熔斷器,當(dāng)請(qǐng) 求符合熔斷條件時(shí)將觸發(fā) getFallback()
            @HystrixProperty(name = HystrixPropertiesManager.CIRCUIT_BREAKER_REQUEST_VOLUME_THRESHOLD, value = "10"),
            // 默認(rèn) 20 個(gè);10s 內(nèi)請(qǐng)求數(shù)大于 20 個(gè)時(shí)就啟動(dòng)熔斷器,當(dāng)請(qǐng) 求符合熔斷條件時(shí)將觸發(fā) getFallback()
            @HystrixProperty(name = HystrixPropertiesManager.CIRCUIT_BREAKER_ERROR_THRESHOLD_PERCENTAGE, value = "50"),
            // 默認(rèn) 5 秒,熔斷多少秒后去嘗試請(qǐng)求
            @HystrixProperty(name = HystrixPropertiesManager.CIRCUIT_BREAKER_SLEEP_WINDOW_IN_MILLISECONDS, value = "5000") })
    public List<Product> list(int flag) {
        System.out.println("fuse---productService---list:"+flag);
        if (flag == 1) {
            throw new RuntimeException();
        }
        // 選擇調(diào)用的服務(wù)的名稱(chēng)
        // ServiceInstance 封裝了服務(wù)的基本信息,如 IP,端口
        ServiceInstance si = this.loadBalancerClient.choose("eureka-provider");
        // 拼接訪(fǎng)問(wèn)服務(wù)的 URL
        StringBuffer sb = new StringBuffer();
        // http://localhost:9001/product/findAll
        sb.append("http://").append(si.getHost()).append(":").append(si.getPort()).append("/product/findAll");
        System.out.println(sb.toString());
        // springMVC RestTemplate
        RestTemplate rt = new RestTemplate();
        ParameterizedTypeReference<List<Product>> type = new ParameterizedTypeReference<List<Product>>() {
        };

        // ResponseEntity:封裝了返回值信息
        ResponseEntity<List<Product>> response = rt.exchange(sb.toString(), HttpMethod.GET, null, type);
        List<Product> list = response.getBody();
        return list;
    }

    public List<Product> fallback(int flag) {
        List<Product> list = new ArrayList<Product>();
        list.add(new Product(-1, "托底數(shù)據(jù)!"));
        return list;
    }
}
  • Controller:
@RestController
public class ProductController {

    @Autowired
    private ProductService productService;

    @RequestMapping("/list")
    public List<Product> list(@RequestParam("flag") Integer flag) {
        return productService.list(flag);
    }
}
  • 測(cè)試:


    示例

    示例

六、線(xiàn)程池隔離

什么是線(xiàn)程隔離,例如Customer通過(guò)線(xiàn)程池,訪(fǎng)問(wèn)服務(wù)接口,接口A(yíng)面對(duì)的10次的請(qǐng)求量,接口B是10次的訪(fǎng)問(wèn)量,比例是1:10000,此時(shí)接口A(yíng)和接口B的連接是在同一個(gè)的線(xiàn)程池中,如果接口A(yíng)因?yàn)榈脑L(fǎng)問(wèn)量過(guò)大的原因出現(xiàn)問(wèn)題,勢(shì)必影響線(xiàn)程池的效率,而線(xiàn)程池中的其他線(xiàn)程也會(huì)受到影響,從而造成雪崩效應(yīng)。

示例

1.如何使用線(xiàn)程隔離解決:

那就要用到線(xiàn)程的的隔離技術(shù)了。把可能出現(xiàn)問(wèn)題的服務(wù)獨(dú)立的運(yùn)行在一個(gè)獨(dú)立的線(xiàn)程池中。

示例

線(xiàn)程池的隔離如下圖中兩個(gè)線(xiàn)程池我們將服務(wù)量大的請(qǐng)求單獨(dú)的運(yùn)行在一個(gè)的獨(dú)立的線(xiàn)程池中,兩個(gè)線(xiàn)程池相互之間是不影響的。可以使用線(xiàn)程池隔離的技術(shù)將的線(xiàn)程池內(nèi)的可能預(yù)估出現(xiàn)問(wèn)題的線(xiàn)程和其他的線(xiàn)程的隔開(kāi)運(yùn)行在一個(gè)獨(dú)立的線(xiàn)程池中,一旦此線(xiàn)程出現(xiàn)問(wèn)題,不會(huì)影響到其他線(xiàn)程的運(yùn)行,解決雪崩效應(yīng)的產(chǎn)生。

示例
  • 使用線(xiàn)程隔離的優(yōu)缺點(diǎn):

(1)優(yōu)點(diǎn):
使用線(xiàn)程隔離可以完全隔離依賴(lài)的服務(wù)。
當(dāng)線(xiàn)程池出現(xiàn)問(wèn)題時(shí),線(xiàn)程池隔離是獨(dú)立的,不影響其他服務(wù)和接口。
當(dāng)失敗的服務(wù)再次可用時(shí),線(xiàn)程池將清理并可立即恢復(fù),不需要長(zhǎng)時(shí)間的恢復(fù)。
獨(dú)立的線(xiàn)程池提供了并發(fā)性。
(2)缺點(diǎn):
線(xiàn)程池隔離的主要缺點(diǎn)是他們?cè)黾佑?jì)算開(kāi)銷(xiāo),每個(gè)命令的執(zhí)行涉及到排隊(duì),調(diào)度和上下文切換都是在一個(gè)單獨(dú)的線(xiàn)程上運(yùn)行的。

2.創(chuàng)建Consumer測(cè)試:

示例
  • 修改POM文件:
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-hystrix</artifactId>
        </dependency>
  • 修改配置文件:
spring.application.name=product-provider-threadpool
server.port=9007

#設(shè)置服務(wù)注冊(cè)中心地址,指向另一個(gè)注冊(cè)中心 
eureka.client.serviceUrl.defaultZone=http://admin:123456@eureka1:8761/eureka/,http://admin:123456@eureka2:8761/eureka/
  • 修改啟動(dòng)類(lèi):
@EnableCircuitBreaker //開(kāi)啟熔斷器 斷路器 
@EnableEurekaClient 
@SpringBootApplication public class ConsumerApplication {  
    public static void main(String[] args) {   
          SpringApplication.run(ConsumerApplication.class, args);  
      }
 }
  • 修改ProductService:
@Service
public class ProductService {

    @Autowired
    private LoadBalancerClient loadBalancerClient;// ribbon 負(fù) 載均衡器

    @HystrixCommand(groupKey="ego-product-provider", commandKey = "getUsers",threadPoolKey="ego-product-provider",          
            threadPoolProperties = {               
                    @HystrixProperty(name = "coreSize", value = "30"),
                    //線(xiàn)程池大小               
                    @HystrixProperty(name = "maxQueueSize", value = "100"),
                    //最大隊(duì)列長(zhǎng)度               
                    @HystrixProperty(name = "keepAliveTimeMinutes", value = "2"),
                    //線(xiàn)程存活時(shí)間               
                    @HystrixProperty(name = "queueSizeRejectionThreshold", value = "15")//拒絕請(qǐng)求       
                    },
            fallbackMethod = "fallback")  
    public List<Product> getUsers() {   
        System.out.println(Thread.currentThread().getName());   
        // 選擇調(diào)用的服務(wù)的名稱(chēng)   
        // ServiceInstance 封裝了服務(wù)的基本信息,如 IP,端口   
        ServiceInstance si = this.loadBalancerClient.choose("ego-product-provider");   
        // 拼接訪(fǎng)問(wèn)服務(wù)的 URL   
        StringBuffer sb = new StringBuffer();   
        // http://localhost:9001/product/findAll 
        sb.append("http://").append(si.getHost()).append(":").appen d(si.getPort()).append("/product/findAll"); 
        System.out.println(sb.toString());   
        // springMVC RestTemplate   
        RestTemplate rt = new RestTemplate(); 
     
      ParameterizedTypeReference<List<Product>> type = new ParameterizedTypeReference<List<Product>>() {   }; 
     
      // ResponseEntity:封裝了返回值信息   
      ResponseEntity<List<Product>> response = rt.exchange(sb.toString(), HttpMethod.GET, null, type);
      List<Product> list = response.getBody();   
      return list;  } // 返回托底數(shù)據(jù)的方法

    public List<Product> fallback() {
        System.out.println(Thread.currentThread().getName());
        List<Product> list = new ArrayList<>();
        list.add(new Product(-1, "我是托底數(shù)據(jù)"));
        return list;
    }

    public void showThread() {
        System.out.println(Thread.currentThread().getName());
    }
}
  • 修改ProductController:

@RestController
public class ProductController {

    @Autowired
    private ProductService userService;

    @RequestMapping("/consumer")
    public List<Product> getUsers() {
        return this.userService.getUsers();
    }

    @RequestMapping("/consumer1")
    public void getUsers1() {
        this.userService.showThread();
    }
}

七、使用的注解

Hystrix 配置參數(shù)全解析

1.降級(jí)中使用的注解:

名稱(chēng) 作用
@HystrixCommand 當(dāng)下面方法服務(wù)出現(xiàn)異常停止會(huì)返回托底數(shù)據(jù)
@EnableCircuitBreaker 開(kāi)啟熔斷器,斷路器

2.請(qǐng)求緩存中使用的注解:

名稱(chēng) 作用
@CacheConfig 一個(gè)類(lèi)中可能會(huì)有多個(gè)緩存操作,而這些緩存操作可能是重復(fù)的。這個(gè)時(shí)候可以使用@CacheConfig
@Cacheable 根據(jù)一定條件對(duì)緩存添加數(shù)據(jù)
@CacheEvict 主要針對(duì)方法配置,能夠根據(jù)一定的條件對(duì)緩存進(jìn)行清空。
@EnableCaching 注解是spring framework中的注解驅(qū)動(dòng)的緩存管理功能,開(kāi)啟緩存。

3.請(qǐng)求合并:

@HystrixCollapser:利用 hystrix 實(shí)現(xiàn)請(qǐng)求合并。

屬性名稱(chēng) 作用
batchMethod 設(shè)置合并請(qǐng)求的方法
scope 設(shè)置請(qǐng)求的方式

@HystrixProperty:Hystrix 的命令屬性是由@HystrixProperty 注解數(shù)組構(gòu)成的,HystrixProperty 由 name 和 value 兩個(gè)屬性,數(shù)據(jù)類(lèi)型都是字符串。

屬性名稱(chēng) 作用
timerDelayInMilliseconds 請(qǐng)求時(shí)間間隔默認(rèn) 為 10ms之內(nèi)的請(qǐng)求會(huì)被合并為一個(gè)請(qǐng)求
maxRequestsInBatch 設(shè)置觸發(fā)批處理執(zhí)行之前,在批處理中允許許的最大請(qǐng)求數(shù)。

4.服務(wù)熔斷:

@HystrixProperty中的屬性HystrixPropertiesManager參數(shù)的作用。

參數(shù)名稱(chēng) 作用
circuitBreaker.requestVolumeThreshold 一個(gè)統(tǒng)計(jì)窗口內(nèi)熔斷觸發(fā)的最小個(gè)數(shù)。
circuitBreaker.enabled 是否開(kāi)啟熔斷。
circuitBreaker.sleepWindowInMiliseconds 熔斷多少秒后去嘗試請(qǐng)求。
circuitBreaker.errorThresholdPercentage 失敗率達(dá)到多少百分比后熔斷。
circuitBreaker.forceOpen 是否強(qiáng)制開(kāi)啟熔斷。
circuitBreaker.forceClosed 是否強(qiáng)制關(guān)閉熔斷。

5.線(xiàn)程隔離:

(1) @HystrixCommand注解中的threadPoolProperties屬性的作用配置與線(xiàn)程池相關(guān)的屬性。

參數(shù)名稱(chēng) 作用
groupKey 設(shè)置服務(wù)名(相同的服務(wù)用一個(gè)名稱(chēng)),默認(rèn)值getClass().getSimpleName()
commandKey 設(shè)置服務(wù)下面的接口,默認(rèn)當(dāng)前執(zhí)行方法名
threadPoolKey 設(shè)置線(xiàn)程池的名稱(chēng),全局唯一標(biāo)識(shí)線(xiàn)程池的名稱(chēng),相同線(xiàn)程池名稱(chēng)的線(xiàn)程池是同一個(gè)
coreSize 設(shè)置線(xiàn)程池大小
maxQueueSize 設(shè)置最大隊(duì)列長(zhǎng)度
queueSizeRejectionThreshold 設(shè)置拒絕請(qǐng)求的臨界值
keepAliveTimeMinutes 設(shè)置線(xiàn)程存活時(shí)間,默認(rèn)為1min
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀(guān)點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容