SpringCloud整合Hystrix

1、Hystrix简介

Hystrix是由Nefflix开源的一个延迟和容错库,用于隔离访问远程系统、服务或第三方库,防止级联失败,从而提升系统的可用性、容错性与局部应用的弹性,是一个实现了超时机制和熔断器模式的工具类库。

2、Hystrix设计原则

  • 防止任何单独的依赖耗尽资源(线程),过载立即切断并快速失败,防止排队。
  • 尽可能提供回退以保护用户免受故障。
  • 使用隔离技术(例如隔板、泳道和断路器模式)来限制任何一个依赖的影响。
  • 通过近实时的指标,监控和告警,确保故障被及时发现。
  • 通过动态修改配置属性,确保故障及时恢复。
  • 防止整个依赖客户端执行失败,而不仅仅是网络通信。

3、Hystrix工作原理

  • 使用命令模式将所有对外部服务(或依赖关系)的调用包装再HystrixCommand或HystrixObservableCommand对象中,并将该对象放在单独的线程中执行。
  • 每个依赖都维护一个线程池(或信号量),线程池被耗尽则拒绝请求(而不是让请求排队)。
  • 记录请求成功,失败,超时和线程拒绝。
  • 服务错误百分比超过阀值,熔断器开关自动打开,一段时间内停止对该服务的所有请求。
  • 请求失败,被拒绝,超时或熔断时执行降级逻辑。
  • 近实时地讲课指标和配置的修改。

当使用Hystrix封装每个基础依赖项时,每个依赖项彼此隔离,受到延迟时发生饱和的资源的限制,并包含回退逻辑,该逻辑决定了在依赖项中发生任何类型的故障时做出什么响应。

4、Hystrix整合

1)实现eureka-server

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">
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.2.RELEASE</version>
        <relativePath/>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>eureka-server</artifactId>
    <version>1.0</version>
    <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>Hoxton.RELEASE</spring-cloud.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </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.apache.maven.plugins</groupId>
                <artifactId>maven-jar-plugin</artifactId>
        <version>2.6</version>
                <configuration>
                    <excludes>
                        <exclude>*.**</exclude>
                        <exclude>*/*.xml</exclude>
                    </excludes>
                    <archive>
                        <manifest>
                            <addClasspath>true</addClasspath>
                            <classpathPrefix>lib/</classpathPrefix>
                            <useUniqueVersions>false</useUniqueVersions>
                            <mainClass>cn.kenlab.org.EurekaServerApplication</mainClass>
                        </manifest>
                        <manifestEntries>
                            <Class-Path>./resources/</Class-Path>
                        </manifestEntries>
                    </archive>
                    <outputDirectory>${project.build.directory}</outputDirectory>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-dependency-plugin</artifactId>
                <version>3.0.1</version>
                <executions>
                    <execution>
                        <id>copy-dependencies</id>
                        <phase>package</phase>
                        <goals>
                            <goal>copy-dependencies</goal>
                        </goals>
                        <configuration>
                            <outputDirectory>${project.build.directory}/lib/</outputDirectory>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
            <plugin>
            <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-resources-plugin</artifactId>
                <version>3.2.0</version>
                <executions>
                    <execution>
                        <id>copy-resources</id>
                        <phase>package</phase>
                        <goals>
                            <goal>copy-resources</goal>
                        </goals>
                        <configuration>
                            <resources>
                                <resource>
                                    <directory>src/main/resources</directory>
                                </resource>
                            </resources>
                            <outputDirectory>${project.build.directory}/resources</outputDirectory>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

 编写启动类,添加@EnableEurekaServer和@SpringBootApplication注解:

@EnableEurekaServer
@SpringBootApplication
public class EurekaServerApplication {
    public static void main(String[] args) {
        SpringApplication.run(EurekaServerApplication.class,args);
    }
}

配置文件application.properties内容:

server.port=8761
eureka.client.register-with-eureka=false
eureka.client.fetch-registry=false

2)创建服务提供者

pob文件添加依赖

<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>

实现启动类,添加@EnableEurekaClient和@SpringBootApplication注解。

@SpringBootApplication
@EnableEurekaClient
public class UserServicerApplication {
    public static void main(String[] args) {
        SpringApplication.run(UserServicerApplication.class,args);
    }
}

实现UserController接口:

@RestController
@RequestMapping("/User")
public class UserController {
    static Map<Integer, User> userMap = new HashMap<>();
    static {
        //模拟数据库
        User user1 = new User(1, "张三", "123456");
        userMap.put(1, user1);
        User user2 = new User(2, "李四", "123123");
        userMap.put(2, user2);
    }

    @RequestMapping(value = "/getUser",method = RequestMethod.GET)
    public User getUser(int id)
    {
        User user = userMap.get(id);
        return user;
    }
}

配置文件application.properties内容:

eureka.client.service-url.defaultZone=http://127.0.0.1:8761/eureka/
eureka.client.register-with-eureka=true
eureka.client.fetch-registry=true
server.port=8082
spring.application.name=service

3)在Ribbon中使用熔断器

pom中添加依赖,熔断器在spring-cloud-starter-netflix-hystrix包中。

<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>

启动类中加上@EnableHystrix注解,开启熔断器功能。

 

@EnableHystrix //在启动类上添加@EnableHystrix注解开启Hystrix的熔断器功能。
@SpringBootApplication
@EnableEurekaClient
public class ConsumerRibbonApplication {
    //当添加@LoadBalanced注解,就代表启动Ribbon,进行负载均衡
    @LoadBalanced
    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
    public static void main(String[] args) {
        SpringApplication.run(ConsumerRibbonApplication.class,args);
    }
}

实现HystrixRibbonController类,在需要有熔断机制的方法上添加@HystrixCommand,属性fallbackMethod是熔断时返回的方法。

@RestController
@RequestMapping("/Hystrix/User")
public class HystrixRibbonController {
    @Autowired
    private RestTemplate restTemplate;
    /**
     * 调用 user微服务
     */
    @HystrixCommand(fallbackMethod = "getDefaultUser")
    @RequestMapping(value = "getUser",method = RequestMethod.GET)
    public String getUser(Integer id) {
        String url = "http://service/User/getUser?id=" + id;
        return restTemplate.getForObject(url, String.class);
    }

    public String getDefaultUser(Integer id) {
        System.out.println("熔断,默认回调函数");
        return "{\"id\":-1,\"name\":\"熔断用户\",\"password\":\"123456\"}";
    }
}

测试。正常情况下,在postman中访问192.168.1.136:8080/Hystrix/User/getUser?id=1,服务正常。如下图所示:

然后停掉service服务,再次访问192.168.1.136:8080/Hystrix/User/getUser?id=1,此时会触发熔断。运行结果如下所示:

4)在Feign中使用熔断器

 pom文件中添加依赖。由于Feign依赖中已经加入了Hystrix依赖,因此,项目中添加Feign依赖后,无需添加Hstrix的其它依赖。只需要在application.properties文件中添加feign.hystrix.enabled=true,开启熔断机制即可,默认为false。

<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>

启动类与普通feign启动类实现方式相同。即加上@EnableFeignClients。

@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients
public class ConsumerFeignApplication {
    public static void main(String[] args) {
        SpringApplication.run(ConsumerFeignApplication.class,args);
    }
}

配置文件application.properties中加入feign.hystrix.enabled=true,开启熔断机制。

eureka.client.service-url.defaultZone=http://127.0.0.1:8761/eureka/
eureka.client.register-with-eureka=true
eureka.client.fetch-registry=true
server.port=8081
spring.application.name=Feign
feign.hystrix.enabled=true 

实现feign客户端,调用服务提供者service模块的getUser接口,并配置fallback快速失败处理类UserFeignBackImpl。

//表示"service"的服务,指定fallback
@FeignClient(value = "service",fallback = UserFeignBackImpl.class)
public interface UserInterface {

    @RequestMapping(value = "/User/getUser",method = RequestMethod.GET)
    public String getUser(@RequestParam("id") int id);
}

实现UserFeignBackImpl类。

@Component
public class UserFeignBackImpl implements UserInterface {
    @Override
    public String getUser(int id) {
        return "{\"id\":-1,\"name\":\"熔断用户\",\"msg\":\"请求异常,返回熔断用户!\"}";
    }
}

实现外部访问接口。

@RestController
@RequestMapping("/HystrixFeign/User")
public class FeignHystrixController {
    @Autowired
    private UserInterface userInterface;

    @RequestMapping(value = "getUser",method = RequestMethod.GET)
    public String getUser(int id) {
        return  userInterface.getUser(id);
    }
}

测试方法同上。

posted @ 2021-04-20 10:36  钟齐峰  阅读(961)  评论(0编辑  收藏  举报