SpringCloud学习-(6)熔断器(Hystrix)

1.Hystrix说明

1.1 服务雪崩效应:是一种因 服务提供者 的不可用导致服务调用者 的不可用,并将不可用 逐渐放大 的过程。

如: A为服务提供者,B为A的服务调用者,C和D是B的服务调用者。当A的不可用,引起B的不可用,并将不可用逐渐放大C和D时,服务雪崩就形成了。

1.2 雪崩原因:

(1)服务提供者不可用
a.硬件故障:
硬件损坏造成的服务器主机宕机、网络硬件故障造成的服务提供者的不可访问。
b.程序Bug
c.缓存击穿:缓存应用重启,所有缓存被清空时,以及短时间内大量缓存失效时.。大量的缓存不命中, 使请求直击后端,造成服务提供者超负荷运行,引起服务不可用。
d.用户大量请求:在秒杀和大促开始前,如果准备不充分,用户发起大量请求造成服务提供者的不可用。

(2) 重试加大流量
a.用户重试:用户由于忍受不了界面上长时间的等待,而不断刷新页面甚至提交表单
b.代码逻辑重试:服务调用端的会存在大量服务异常后的重试逻辑

(3) 服务调用者不可用
a.同步等待造成的资源耗尽:使用 同步调用 时,会产生大量的等待线程占用系统资源。 一旦线程资源被耗尽,服务调用者提供的服务也将处于不可用状态,造成服务雪崩效应产生。

1.3 雪崩应对策略:

(1) 流量控制
a.网关限流
因为Nginx的高性能,,目前一线互联网公司大量采用Nginx+Lua的网关进行流量控制, 由此而来的OpenResty也越来越热门。
b.用户交互限流:采用加载动画,提高用户的忍耐等待时间、提交按钮添加强制等待时间机制。
c.关闭重试。

(2) 改进缓存模式
缓存预加载、同步改为异步刷新。

(3) 服务自动扩容
AWS的auto scaling。

(4) 服务调用者降级服务
a.资源隔离:主要是对调用服务的线程池进行隔离。
b.对依赖服务进行分类。
依赖服务分为:强依赖和若依赖,强依赖服务不可用会导致当前业务中止,而弱依赖服务的不可用不会导致当前业务的中止。
c.不可用服务的调用快速失败。

在微服务架构中,根据业务来拆分成一个个的服务,服务与服务之间可以相互调用(RPC),在Spring Cloud可以用RestTemplate+Ribbon和Feign来调用。为了保证其高可用,单个服务通常会集群部署。由于网络原因或者自身的原因,服务并不能保证100%可用,如果单个服务出现问题,调用这个服务就会出现线程阻塞,此时若有大量的请求涌入,Servlet容器的线程资源会被消耗完毕,导致服务瘫痪。服务与服务之间的依赖性,故障会传播,会对整个微服务系统造成灾难性的严重后果,这就是服务故障的“雪崩”效应。

为了解决这个问题,断路器模型便产生了。
这里写图片描述
图片引自spring官网。

2.Hystrix实例
前面说了一堆的熔断的理论,但是Hystrix该如何用呢?

2.1 准备
准备3个工程
springcloud-eureka-server
springcloud-eureka-client1
springcloud-eureka-client2
其中前面三个工程是前面几篇的介绍过的:
springcloud-eureka-server是服务注册中心,springcloud-eureka-client1和springcloud-eureka-client2是服务提供者,提供服务名为service-sayHello,服务mapping为sayHello。且服务提供者都将服务注册到注册中心上。

2.2 新建负载均衡器+熔断器
新建方法可参考前面的springcloud文章有详细的步骤和代码展示。
工程名:springcloud-eureka-hystrix
GroupId:com.tangjinyi
ArtifactId:springcloud-eureka-hystrix

2.3 pom.xml

<?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.tangjinyi</groupId>
    <artifactId>springcloud-eureka-hystrix</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>springcloud-eureka-hystrix</name>
    <description>Demo project for Spring Boot</description>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.1.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
        <spring-cloud.version>Finchley.M9</spring-cloud.version>
    </properties>

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

        <!--在ribbon的基础上添加Hystrix-->
        <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-ribbon</artifactId>
        </dependency>

        <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</groupId>
            <artifactId>spring-web</artifactId>
            <version>5.0.5.RELEASE</version>
        </dependency>
    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

    <repositories>
        <repository>
            <id>spring-milestones</id>
            <name>Spring Milestones</name>
            <url>https://repo.spring.io/milestone</url>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </repository>
    </repositories>

</project>

使用ribbon+restTemplate作为负载均衡器,所以需要引入ribbon的依赖。同时引入hystrix依赖

2.4 application.properties

#将服务注册到eureka
eureka.client.service-url.defaultZone=http://localhost:8761/eureka/
#hystrix服务端口号
server.port=8766
#服务名
spring.application.name=service-hystrix

2.5 启动类
SpringcloudEurekaHystrixApplication
添加@EnableHystrix注解,表示启动Hystrix服务。

package com.tangjinyi;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.netflix.hystrix.EnableHystrix;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;

/**
 * 1.启动eureka服务
 * 2.启动client1和client2服务,将两个服务注册到eureka服务器上。服务名为service-sayHello,具体请求为http://service-sayHello:8761/sayHello
 * 3.启动Hystrix微服务,将Hystrix服务注册到eureka
 * 4.通过Hystrix请求client1和client2服务(通过restTemplate请求service-sayHello服务)http://localhost:8766/syaHello
 * 5.依次关闭client1和client2两个服务
 * 6.当client1和client2都停止后,再次请求,会跳转到熔断方法
 *
 *  具体需要添加一些依赖,都在pom文件中
 */
@SpringBootApplication
@EnableDiscoveryClient
@EnableHystrix
public class SpringcloudEurekaHystrixApplication {

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

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

2.6 Hystrix提供访问流程类
UserController

访问方法@HystrixCommand(fallbackMethod = "errorFallback")注解,熔断后执行errorFallback方法。

package com.tangjinyi.controller;

import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.tangjinyi.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class UserController {

    @Autowired
    public UserService userService;

    @HystrixCommand(fallbackMethod = "errorFallback")
    @RequestMapping("/sayHello")
    public String sayHello(@RequestParam String name){
        return userService.sayHello(name);
    }

    public String errorFallback(String id) {
        return "请求出现错误,将请求熔断成功";
    }

}

UserService

package com.tangjinyi.service;

import com.tangjinyi.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class UserService {

    @Autowired
    public UserRepository userRepository;

    public String sayHello(String name){
        return userRepository.sayHello(name);
    }
}

UserRepository

package com.tangjinyi.repository;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import org.springframework.web.client.RestTemplate;

/**
 * 通过请求发现,凡是注册到eureka上面的服务,请求时的端口都是8761,也就是说请求的时候都使用的是eureka服务的端口,即(提供者的服务名+eureka服务的端口)
 */
@Repository
public class UserRepository {

    @Autowired
    public RestTemplate restTemplate;

    public String sayHello(String name){
        return restTemplate.getForObject("http://service-sayHello:8761/sayHello?name="+name,String.class);
    }

}

最终的工程结构如下:

这里写图片描述

2.7 启动4个工程
分别启动springcloud-eureka-server,springcloud-eureka-client1,springcloud-eureka-client2,springcloud-eureka-hystrix工程:
这里写图片描述

2.8 访问服务:
打开浏览器,访问http://localhost:8766/sayHello?name=10
这里写图片描述
刷新浏览器,端口会在8762和8763之间轮询切换。

3 Hystrix熔断测试

关闭一个服务8763,刷新浏览器端口一直停留在8762。
再关闭一个服务8762,刷新浏览器。
这里写图片描述
访问已经跳转到熔断方法中。
在实际的应用中,可以跳转到其他处理的服务。

猜你喜欢

转载自blog.csdn.net/jinjin603/article/details/80436831