09 Spring Cloud的集群保护框架Hystrix

1.概述

  在很多系统架构中都需要考虑横向扩展、单点故障等问题,对于一个庞大的应用集群,部分服务或机器出现问题不可避免。在出现问题时,如何减少故障的影响、保障集群的高可用,成为一个重要的课题。在微服务集群中,不管是服务器,还是客户端,都支持集群部署,本节将介绍Spring Cloud中所用的集群保护框架:Hystrix.

 1.1实际问题

    假设有如下应用程序

  用户范围销售模块,服务通过Web接口或者其他方式访问会员模块,会员模块访问数据库。如果数据库因为某些原因变得不可用,会员模块就会得到“数据库无法访问”的信息,并且会将此信息告知销售模块。在实际问题中,用户会不断地向销售模块发请求,而销售模块这继续请求会员模块,会员模块会不断地请求连接有问题的数据库直到超时,但是还是会有大量的用户请求(包括重试的)会发过来,导致整个应用不堪重负。可能情况会比这个更糟糕,用户的请求不停的发送给销售模块,而由于数据库的原因,会员模块迟迟没有响应,有可能导致整个机房的网络阻塞,受害的不仅仅是这个应用程序,机房中的所有服务都有可能因为网络的原因而瘫痪。

 1.2传统的解决方式

    对于前面遇到的实际问题,可以选择在连接数据库的同时加上超时的配置,让会员模块快速响应。但这仅仅是解决了其中的一种情况,在实际情况中,会员模块有可能出现问题,例如部分线程阻塞、进程假死等,在这些情况下,对外的服务销售模块面对大量的用户与有故障的会员模块,仍然无法独善其身,前面所说的问题依旧会出现。

  在当今的互联网时代,面对大量的用户请求,传统或者单一的解决方式在复杂的急群中显得力不从心,我们需要跟优雅更完善的方案来解决这些问题。

2.集群容错框架Hystrix

  在分布式环境中,总会有一些被依赖的服务会失效,例如像网络短暂无法访问、服务器宕机等情况。Hystrix是Netflix下的一个java库,Spring Cloud将Hystrix整合到Netflix项目中,Hystrix通过添加延迟阈值以及容错的逻辑,来帮助我们控制分布式系统间组件的交互。Hystrix通过隔离服务间的访问点、停止他们之间的级联故障、提供可回退操作来实现容错。

  例如我们之前讲到的问题,如果数据库层面出现问题,销售模块在访问会员模块时必然会出现超时的情况,此时可以将会员模块隔离开来,销售模块短时间内不再调用会员模块,并且会快速响应用户的请求,从而保证销售模块自身乃至整个集群的稳定性,这是Hystrix可以解决的问题。加入了容错机制,当会员模块或者数据库不可用时,销售模块将对其进行“熔断”,在一定时间内,销售模块不会再调用会员模块,以维持自身的稳定,结构图就变成下面的图了

        

  Hystrix主要实现以下的功能:

  > 当所依赖的网络服务发生延迟或者失败时,对访问的客户端程序进行保护,就像上面的例子对销售模块进行保护一样;

  > 在分布式系统中停止级联故障;

  > 网络服务恢复正常后,可以快速恢复客户端的访问能力;

  > 调用失败时执行服务回退;

  > 可支持实时监控、报警和其他操作。

3.第一个Hystrix程序

  本例将编写一个简单的Hello World程序,展示Hystrix的基本功能。

 3.1 创建服务提供者

    使用SpringBoot的spring-boot-starter-web项目,创建一个普通的web项目,发布两个测试服务用于测试,项目目录及代码清单如下

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.triheart</groupId>
    <artifactId>hystrixserver</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <version>1.5.4.RELEASE</version>
        </dependency>
    </dependencies>
</project>

ServerApp.java

package com.triheart.hystrixserver;

import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;

/**
 * @author 阿遠
 * Date: 2018/8/29
 * Time: 21:16
 */
@SpringBootApplication
public class ServerApp {
    public static void main(String[] args) {
        new SpringApplicationBuilder(ServerApp.class).run(args);
    }
}

    MyController.java

package com.triheart.hystrixserver;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;

/**
 * @author 阿遠
 * Date: 2018/8/29
 * Time: 21:18
 */
@RestController
public class MyController {

    @GetMapping("/normalHello")
    public String normalHello(HttpServletRequest request) {
        return "normal Hello!";
    }

    @GetMapping("/errorHello")
    public String errorHello(HttpServletRequest request) throws Exception{
        // 模拟处理让线程睡眠10秒
        Thread.sleep(10000);
        return "error Hello";
    }
}

  在MyController控制器中,我们提供了一个正常的服务,提供了一个需要等待10秒才有返回的服务。

 3.2 创建客户端并使用Hystrix

  使用Hystri来请求Web服务,与原来的方式不太一样,新建项目hystrixclient,项目的目录结构如下

  

  添加相关的依赖,pom.xml的代码清单如下

  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.triheart</groupId>
    <artifactId>hystrixclient</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
        <dependency>
            <groupId>com.netflix.hystrix</groupId>
            <artifactId>hystrix-core</artifactId>
            <version>1.5.12</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <version>1.7.25</version>
            <artifactId>slf4j-log4j12</artifactId>
        </dependency>
        <dependency>
            <groupId>commons-logging</groupId>
            <artifactId>commons-logging</artifactId>
            <version>1.2</version>
        </dependency>
        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
            <version>4.5.2</version>
        </dependency>
    </dependencies>
</project>

  客户端除了要使用Hystrix外,还会使用HttpClient模块来访问Web服务,因此要加入httpclient的依赖。新建命令类

  HelloCommand.java

package com.triheart.hystrixclient;

import com.netflix.hystrix.HystrixCommand;
import com.netflix.hystrix.HystrixCommandGroupKey;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;

/**
 * @author 阿遠
 * Date: 2018/8/29
 * Time: 21:43
 */
public class HelloCommand extends HystrixCommand<String> {

    private String url;
    CloseableHttpClient httpclient;

    public HelloCommand(String url) {
        // 调用父类的构造器,设置命令组的key,默认用来作为线程池的key
        super(HystrixCommandGroupKey.Factory.asKey("ExampleGroup"));
        // 创建HttpClient客户端
        this.httpclient = HttpClients.createDefault();
        this.url = url;
    }

    protected String getFallback() {
        System.out.println("执行 HelloCommand 的回退方法");
        return "error";
    }

    protected String run() throws Exception {
        try {
            // 调用 GET 方法请求服务
            HttpGet httpget = new HttpGet(url);
            // 得到服务响应
            HttpResponse response = httpclient.execute(httpget);
            // 解析并返回命令执行结果
            return EntityUtils.toString(response.getEntity());
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "";
    }
}

  新建运行类,执行HelloCommand命令

  HelloMain.java

package com.triheart.hystrixclient;

/**
 * @author 阿遠
 * Date: 2018/8/29
 * Time: 21:49
 */
public class HelloMain {

    public static void main(String[] args) {
        // 请求正常的服务
        String normalUrl = "http://localhost:8080/normalHello";
        HelloCommand command = new HelloCommand(normalUrl);
        String result = command.execute();
        System.out.println("请求正常的服务,结果:" + result);
    }
}

  正常情况下,直接调用HttpClient的API来请求Web服务,而此处命令类和运行类则通过执行命令来执行调用的工作。在命令类HelloCommond中,实现了父类的run方法,使用HttpClient调用服务的过程,都放到了该方法中。运行HelloMain类,可以看到,结果与平常调用Web服务无异。

  假设我们所调用的Hello服务发生故障,导致无法正常访问,那么对于客户端来说,该如何自保呢?下面将演示调用异常服务的情况

  在HelloCommand类中,加入回退方法。在运行类中,调用发生故障的服务

  HelloErrorMain.java

package com.triheart.hystrixclient;

/**
 * @author 阿遠
 * Date: 2018/8/29
 * Time: 21:49
 */
public class HelloErrorMain {

    public static void main(String[] args) {
        // 请求异常的服务
        String normalUrl = "http://localhost:8080/errorHello";
        HelloCommand command = new HelloCommand(normalUrl);
        String result = command.execute();
        System.out.println("请求异常的服务,结果:" + result);
    }
}

  运行对应的类,可以看到控制台输入如下

  根据结果可知,回退方法被执行了。本例中调用的errorHello服务,会阻塞10秒才有返回。默认情况下,如果调用Web服务无法再一秒内完成,那么将触发回退。

  回退更像是一个备胎,当请求的服务无法正常返回时,就调用该备胎来实现。这样就可以很好的保护客户端,服务端所提供的服务受网络等条件的制约,如果有服务真的需要10秒才能返回结果,而客户端有没有容错的机制,后果就是客户端将一直等待返回,直到网络超时活着服务有响应,而外界会一直不停地发送请求给客户端,最终导致的结果就是客户端因为请求过多而瘫痪。

原文地址:https://www.cnblogs.com/a-yuan/p/9557313.html

时间: 2024-07-29 01:05:42

09 Spring Cloud的集群保护框架Hystrix的相关文章

docker-compose 完整打包发布, 多服务,多节点SPRING CLOUD ,EUREKA 集群

这里不再使用 端口映射的方式,因为不同主机上,Feign 根据 docker hostname访问会有问题. 把打包的好jar copy到docker镜像里 有几个服务,就复制几个dockerfile Dockerfile FROM registry.cn-hangzhou.aliyuncs.com/laizhenwei/jre:8u144 # MAINTAINER zhenweilai <181282945@qq.com> COPY jar /usr/local RUN cp /usr/sh

Spring Cloud Eureka集群搭建与注册

参考博文: 单元测试:https://blog.csdn.net/sz85850597/article/details/80427408 Eureka注册与发现:https://blog.csdn.net/qq_32529383/article/details/79951511 原文地址:https://www.cnblogs.com/free-wings/p/9687529.html

集群通信框架Tribes之整体介绍

接下来一系列文章会对集群通信框架tribes进行源码级别的分析,欢迎讨论. 把若干机器组合成一个集群,集群为了能协同工作,成员之间的通信是必不可少的,当然可以说这也是集群实现中重点需要解决的核心问题,一个强大的通信协同机制是集群的基础. 简约地说,Tribes是一个具备让你通过网络向组成员发送和接收信息.动态检测发现其他节点的组通信能力的高扩展性的独立的消息框架.在组成员之间进行信息复制及成员维护是一个相对复杂的事情,因为不仅要考虑各种通信协议还要有必要的机制提供不同的消息传输保证级别,且成员关

Spring集成redis集群

Spring集成redis集群 有密码 Maven <jedis.version>2.9.0</jedis.version> <spring-data-redis.version>1.7.1.RELEASE</spring-data-redis.version> <spring.version>3.2.17.RELEASE</spring.version> <dependency> <groupId>org.s

从 Spring Cloud 看一个微服务框架的「五脏六腑」

原文:https://webfe.kujiale.com/spring-could-heart/ Spring Cloud 是一个基于 Spring Boot 实现的微服务框架,它包含了实现微服务架构所需的各种组件. 注:Spring Boot 简单理解就是简化 Spring 项目的搭建.配置.组合的框架.因为与构建微服务本身没有直接关系,所以本文不对 Spring Boot 进行展开.另外本文有一些例子涉及到 Spring 和 Spring Boot,建议先了解一下 Spring 和 Spri

spring boot + quartz 集群

spring boot bean配置: @Configuration public class QuartzConfig { @Value("${quartz.scheduler.instanceName}") private String quartzInstanceName; @Value("${org.quartz.dataSource.myDS.driver}") private String myDSDriver; @Value("${org.q

spring 使用redis集群配置

上面两篇介绍了redis集群的配置合一些基本的概念,所以接下来当然是要在项目中使用咯,redis的java支持已经做的非常好了,所以我们来试着使用这些api来进行redis的操作,首先我们需要操作redis的架包: <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>2.7.3</version> <

Spring+监听器+Quartz集群(1)——基本配置

先说说项目背景,因为业务需求,项目中有很多定时任务,这自然用到了Quartz这个开源产品.而在这之前使用的Quartz主要是基于内存的,在应用程序启动时,由监听器创建定时任务,为了防止多个应用程序重新创建任务,只能在发布时,在另外的web.xml中禁用监听器.这样系统发布变得麻烦,因为不同的应用程序的配置不同. 除了发布麻烦,还有不能发挥集群的优势,一旦运行任务的服务器崩溃,集群中的其他服务器不能接受定时任务. 为了解决以上的两个问题,在系统中使用Quartz集群,同时考虑到系统中创建定时任务的

API网关spring cloud gateway和负载均衡框架ribbon实战

通常我们如果有一个服务,会部署到多台服务器上,这些微服务如果都暴露给客户,是非常难以管理的,我们系统需要有一个唯一的出口,API网关是一个服务,是系统的唯一出口.API网关封装了系统内部的微服务,为客户端提供一个定制的API.客户端只需要调用网关接口,就可以调用到实际的微服务,实际的服务对客户不可见,并且容易扩展服务. API网关可以结合ribbon完成负载均衡的功能,可以自动检查微服务的状况,及时剔除或者加入某个微服务到可用服务列表.此外网关可以完成权限检查.限流.统计等功能.下面我们将一一完