【Hystrix】实现服务隔离和降级

  • 一、背景

    • 1.1 服务熔断
    • 1.2 服务降级
    • 1.3 服务隔离
    • 1.4 总结
  • 二、使用Hystrix实现服务隔离和降级
    • 2.1 Hytrix 简介
    • 2.2 线程池方式
    • 2.3 信号量
  • 三、项目搭建
    • 3.1 订单工程

      • 1. 引入Maven依赖
      • 2. Service
      • 3.Controller
      • 4.工具类
    • 3.2 会员工程
  • 四、项目源码

一、背景

  在今天,基于SOA的架构已经大行其道。伴随着架构的SOA化,相关联的服务熔断、降级、限流等思想,也在各种技术讲座中频繁出现。
  伴随着业务复杂性的提高,系统的不断拆分,一个面向用户端的API,其内部的RPC调用层层嵌套,调用链条可能会非常长。这会造成以下问题:

  • API接口可用性降低:引用Hystrix官方的一个例子,假设tomcat对外提供的一个application,其内部依赖了30个服务,每个服务的可用性都很高,为99.99%。那整个applicatiion的可用性就是:99.99%的30次方 = 99.7%,即0.3%的失败率。这也就意味着,每1亿个请求,有30万个失败;按时间来算,就是每个月的故障时间超过2小时。

1.1 服务熔断

  为了解决上述问题,服务熔断的思想被提出来。类似现实世界中的“保险丝“,当某个异常条件被触发,直接熔断整个服务,而不是一直等到此服务超时。 熔断的触发条件可以依据不同的场景有所不同,比如统计一个时间窗口内失败的调用次数。

1.2 服务降级

  有了熔断,就得有降级。所谓降级,就是当某个服务熔断之后,服务器将不再被调用,此时客户端可以自己准备一个本地的fallback回调,返回一个缺省值。 这样做,虽然服务水平下降,但好歹可用,比直接挂掉要强,当然这也要看适合的业务场景。关于Hystrix中fallback的使用,见官网

1.3 服务隔离

  1. 雪崩效应:服务雪崩效应产生服务堆积在同一个线程池中,因为在同一个线程池中,所有请求全部到一个服务进行访问,这时候会导致其他服务没有线程接收请求访问,所以就会产生服务雪崩效应。
  2. Tomcat底层:http+线程池,每个线程都是独立的请求。
  3. 当大多数人在使用Tomcat时,多个http服务会共享一个线程池,假设其中一个http服务访问的数据库响应非常慢,这将造成服务响应时间延迟增加,大多数线程阻塞等待数据响应返回,导致整个Tomcat线程池都被该服务占用,甚至拖垮整个Tomcat。因此,如果我们能把不同http服务隔离到不同的线程池,则某个http服务的线程池满了也不会对其他服务造成灾难性故障。这就需要线程隔离或者信号量隔离来实现了。
  4. 服务隔离:每个服务接口互不影响,服务隔离有两种实现方式线程池方式计数器
  5. 作用:服务保护,当服务产生堆积的时候,对服务实现保护功能。(堆积请求:假设默认tomcat最大线程线程池是50。尝试第51一个请求,第51个请求会阻塞。大量请求正在等待,如果堆积请求过多,可能会造成服务器瘫痪。)
  6. 使用线程隔离或信号隔离的目的是为不同的服务分配一定的资源,当自己的资源用完,直接返回失败而不是占用别人的资源。

    ?

1.4 总结

  1. 服务隔离:保证每个服务互不影响,使用信号量线程池方式
  2. 服务降级:当服务不可用的时候,不会被等待,直接返回一个友好的提示
  3. 服务熔断:当服务器达到最大的承受能的之后,直接拒绝访问服务,最会调用服务降级方法,返回友好提示。目的是保证服务不被宕机掉

二、使用Hystrix实现服务隔离和降级

2.1 Hytrix 简介

  • Hystrix 是一个微服务关于服务保护的框架,是Netflix开源的一款针对分布式系统的延迟和容错解决框架,目的是用来隔离分布式服务故障。它提供线程和信号量隔离,以减少不同服务之间资源竞争带来的相互影响;提供优雅降级机制;提供熔断机制使得服务可以快速失败,而不是一直阻塞等待服务响应,并能从中快速恢复。Hystrix通过这些机制来阻止级联失败并保证系统弹性、可用。
  • Hystrix的资源隔离策略有两种,分别为:线程池和信号量

2.2 线程池方式

?

  1. 使用线程池隔离可以完全隔离第三方应用,请求线程可以快速放回。
  2. 请求线程可以继续接受新的请求,如果出现问题线程池隔离是独立的不会影响其他应用。
  3. 当失败的应用再次变得可用时,线程池将清理并可立即恢复,而不需要一个长时间的恢复。
  4. 独立的线程池提高了并发性

缺点: 线程池隔离的主要缺点是它们增加计算开销(CPU)。每个命令的执行涉及到排队、调度和上下文切换都是在一个单独的线程上运行的。

public class OrderHystrixCommand extends HystrixCommand<JSONObject> {
    @Autowired
    private MemberService memberService;

    /**
     * @param group
     */
    public OrderHystrixCommand(MemberService memberService) {
        super(setter());
        this.memberService = memberService;
    }

    protected JSONObject run() throws Exception {
        JSONObject member = memberService.getMember();
        System.out.println("当前线程名称:" + Thread.currentThread().getName() + ",订单服务调用会员服务:member:" + member);
        return member;
    }

    private static Setter setter() {

        // 服务分组
        HystrixCommandGroupKey groupKey = HystrixCommandGroupKey.Factory.asKey("orders");
        // 服务标识
        HystrixCommandKey commandKey = HystrixCommandKey.Factory.asKey("order");
        // 线程池名称
        HystrixThreadPoolKey threadPoolKey = HystrixThreadPoolKey.Factory.asKey("order-pool");
        // #####################################################
        // 线程池配置 线程池大小为10,线程存活时间15秒 队列等待的阈值为100,超过100执行拒绝策略
        HystrixThreadPoolProperties.Setter threadPoolProperties = HystrixThreadPoolProperties.Setter().withCoreSize(10)
                .withKeepAliveTimeMinutes(15).withQueueSizeRejectionThreshold(100);
        // ########################################################
        // 命令属性配置Hystrix 开启超时
        HystrixCommandProperties.Setter commandProperties = HystrixCommandProperties.Setter()
                // 采用线程池方式实现服务隔离
                .withExecutionIsolationStrategy(HystrixCommandProperties.ExecutionIsolationStrategy.THREAD)
                // 禁止
                .withExecutionTimeoutEnabled(false);
        return HystrixCommand.Setter.withGroupKey(groupKey).andCommandKey(commandKey).andThreadPoolKey(threadPoolKey)
                .andThreadPoolPropertiesDefaults(threadPoolProperties).andCommandPropertiesDefaults(commandProperties);

    }

//############   服务降级  ##########
    @Override
    protected JSONObject getFallback() {
        // 如果Hystrix发生熔断,当前服务不可用,直接执行Fallback方法
        System.out.println("系统错误!");
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("code", 500);
        jsonObject.put("msg", "系统错误!");
        return jsonObject;
    }
    }

应用场景:

  1. 第三方应用或者接口
  2. 并发量大

2.3 信号量

  • 计数器方式:底层使用原子计数器,针对于每个服务:都设置自己独立限制阈值,比如设置每个服务接口最多同时只能访问50次,超出缓存队列请求后,自己实现拒绝策略。
  • 使用一个原子计数器(或信号量)来记录当前有多少个线程在运行,当请求进来时先判断计数器的数值,若超过设置的最大线程个数则拒绝该请求,若不超过则通行,这时候计数器+1,请求返回成功后计数器-1。
  • 与线程池隔离最大不同在于执行依赖代码的线程依然是请求线程,信号量的大小可以动态调整, 线程池大小不可以

代码如下:

public class OrderHystrixCommand2 extends HystrixCommand<JSONObject> {
    @Autowired
    private MemberService memberService;

    /**
     * @param group
     */
    public OrderHystrixCommand2(MemberService memberService) {
        super(setter());
        this.memberService = memberService;
    }

    protected JSONObject run() throws Exception {

        // Thread.sleep(500);
        // System.out.println("orderIndex线程名称" +
        // Thread.currentThread().getName());
        // System.out.println("success");
        JSONObject member = memberService.getMember();
        System.out.println("当前线程名称:" + Thread.currentThread().getName() + ",订单服务调用会员服务:member:" + member);
        return member;
    }

    private static Setter setter() {
        // 服务分组
        HystrixCommandGroupKey groupKey = HystrixCommandGroupKey.Factory.asKey("order");
        // 命令属性配置 采用信号量模式
        HystrixCommandProperties.Setter commandProperties = HystrixCommandProperties.Setter()
                .withExecutionIsolationStrategy(HystrixCommandProperties.ExecutionIsolationStrategy.SEMAPHORE)
                // 使用一个原子计数器(或信号量)来记录当前有多少个线程在运行,当请求进来时先判断计数
                // 器的数值,若超过设置的最大线程个数则拒绝该请求,若不超过则通行,这时候计数器+1,请求返 回成功后计数器-1。
                .withExecutionIsolationSemaphoreMaxConcurrentRequests(50);
        return HystrixCommand.Setter.withGroupKey(groupKey).andCommandPropertiesDefaults(commandProperties);
    }

//############   服务降级  ##########
    @Override
    protected JSONObject getFallback() {
        // 如果Hystrix发生熔断,当前服务不可用,直接执行Fallback方法
        System.out.println("系统错误!");
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("code", 500);
        jsonObject.put("msg", "系统错误!");
        return jsonObject;
    }
    }

应用场景:

  1. 内部应用或者中间件(redis)
  2. 并发需求不大

三、项目搭建

需求:搭建一套分布式rpc远程通讯案例:比如订单服务调用会员服务实现服务隔离,防止雪崩效应案例

3.1 订单工程

1. 引入Maven依赖

<parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.0.RELEASE</version>
    </parent>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.apache.httpcomponents/httpclient -->
        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
        </dependency>
        <!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.47</version>
        </dependency>
        <dependency>
            <groupId>com.netflix.hystrix</groupId>
            <artifactId>hystrix-metrics-event-stream</artifactId>
            <version>1.5.12</version>
        </dependency>
        <dependency>
            <groupId>com.netflix.hystrix</groupId>
            <artifactId>hystrix-javanica</artifactId>
            <version>1.5.12</version>
        </dependency>
</dependencies>

2. Service

@Service
public class MemberService {
public JSONObject getMember() {
    JSONObject result = HttpClientUtils.httpGet("http://127.0.0.1:8081/member/memberIndex");
    return result;
}
}

3.Controller

@RestController
@RequestMapping("/order")
public class OrderController {
    @Autowired
    private MemberService memberService;

@RequestMapping("/orderIndex")
public Object orderIndex() throws InterruptedException {
    JSONObject member = memberService.getMember();
    System.out.println("当前线程名称:" + Thread.currentThread().getName() + ",订单服务调用会员服务:member:" + member);
    return member;
}

@RequestMapping("/orderIndexHystrix")
public Object orderIndexHystrix() throws InterruptedException {
    return new OrderHystrixCommand(memberService).execute();
}

@RequestMapping("/orderIndexHystrix2")
public Object orderIndexHystrix2() throws InterruptedException {
    return new OrderHystrixCommand2(memberService).execute();
}

@RequestMapping("/findOrderIndex")
public Object findIndex() {
    System.out.println("当前线程:" + Thread.currentThread().getName() + ",findOrderIndex");
    return "findOrderIndex";
}
}

@RestController
@RequestMapping("/order")
public class OrderController {
    @Autowired
    private MemberService memberService;

@RequestMapping("/orderIndex")
public Object orderIndex() throws InterruptedException {
    JSONObject member = memberService.getMember();
    System.out.println("当前线程名称:" + Thread.currentThread().getName() + ",订单服务调用会员服务:member:" + member);
    return member;
}

@RequestMapping("/orderIndexHystrix")
public Object orderIndexHystrix() throws InterruptedException {
    return new OrderHystrixCommand(memberService).execute();
}

@RequestMapping("/orderIndexHystrix2")
public Object orderIndexHystrix2() throws InterruptedException {
    return new OrderHystrixCommand2(memberService).execute();
}

@RequestMapping("/findOrderIndex")
public Object findIndex() {
    System.out.println("当前线程:" + Thread.currentThread().getName() + ",findOrderIndex");
    return "findOrderIndex";
}
}

4.工具类

public class HttpClientUtils {
    private static Logger logger = LoggerFactory.getLogger(HttpClientUtils.class); // 日志记录

private static RequestConfig requestConfig = null;

static {
    // 设置请求和传输超时时间
    requestConfig = RequestConfig.custom().setSocketTimeout(2000).setConnectTimeout(2000).build();
}

/**
 * post请求传输json参数
 *
 * @param url
 *            url地址
 * @param json
 *            参数
 * @return
 */
public static JSONObject httpPost(String url, JSONObject jsonParam) {
    // post请求返回结果
    CloseableHttpClient httpClient = HttpClients.createDefault();
    JSONObject jsonResult = null;
    HttpPost httpPost = new HttpPost(url);
    // 设置请求和传输超时时间
    httpPost.setConfig(requestConfig);
    try {
        if (null != jsonParam) {
            // 解决中文乱码问题
            StringEntity entity = new StringEntity(jsonParam.toString(), "utf-8");
            entity.setContentEncoding("UTF-8");
            entity.setContentType("application/json");
            httpPost.setEntity(entity);
        }
        CloseableHttpResponse result = httpClient.execute(httpPost);
        // 请求发送成功,并得到响应
        if (result.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
            String str = "";
            try {
                // 读取服务器返回过来的json字符串数据
                str = EntityUtils.toString(result.getEntity(), "utf-8");
                // 把json字符串转换成json对象
                jsonResult = JSONObject.parseObject(str);
            } catch (Exception e) {
                logger.error("post请求提交失败:" + url, e);
            }
        }
    } catch (IOException e) {
        logger.error("post请求提交失败:" + url, e);
    } finally {
        httpPost.releaseConnection();
    }
    return jsonResult;
}

/**
 * post请求传输String参数 例如:name=Jack&sex=1&type=2
 * Content-type:application/x-www-form-urlencoded
 *
 * @param url
 *            url地址
 * @param strParam
 *            参数
 * @return
 */
public static JSONObject httpPost(String url, String strParam) {
    // post请求返回结果
    CloseableHttpClient httpClient = HttpClients.createDefault();
    JSONObject jsonResult = null;
    HttpPost httpPost = new HttpPost(url);
    httpPost.setConfig(requestConfig);
    try {
        if (null != strParam) {
            // 解决中文乱码问题
            StringEntity entity = new StringEntity(strParam, "utf-8");
            entity.setContentEncoding("UTF-8");
            entity.setContentType("application/x-www-form-urlencoded");
            httpPost.setEntity(entity);
        }
        CloseableHttpResponse result = httpClient.execute(httpPost);
        // 请求发送成功,并得到响应
        if (result.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
            String str = "";
            try {
                // 读取服务器返回过来的json字符串数据
                str = EntityUtils.toString(result.getEntity(), "utf-8");
                // 把json字符串转换成json对象
                jsonResult = JSONObject.parseObject(str);
            } catch (Exception e) {
                logger.error("post请求提交失败:" + url, e);
            }
        }
    } catch (IOException e) {
        logger.error("post请求提交失败:" + url, e);
    } finally {
        httpPost.releaseConnection();
    }
    return jsonResult;
}

/**
 * 发送get请求
 *
 * @param url
 *            路径
 * @return
 */
public static JSONObject httpGet(String url) {
    // get请求返回结果
    JSONObject jsonResult = null;
    CloseableHttpClient client = HttpClients.createDefault();
    // 发送get请求
    HttpGet request = new HttpGet(url);
    request.setConfig(requestConfig);
    try {
        CloseableHttpResponse response = client.execute(request);

        // 请求发送成功,并得到响应
        if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
            // 读取服务器返回过来的json字符串数据
            HttpEntity entity = response.getEntity();
            String strResult = EntityUtils.toString(entity, "utf-8");
            // 把json字符串转换成json对象
            jsonResult = JSONObject.parseObject(strResult);
        } else {
            logger.error("get请求提交失败:" + url);
        }
    } catch (IOException e) {
        logger.error("get请求提交失败:" + url, e);
    } finally {
        request.releaseConnection();
    }
    return jsonResult;
}
}

3.2 会员工程

@RestController
@RequestMapping("/member")
public class MemberController {
@RequestMapping("/memberIndex")
public Object memberIndex() throws InterruptedException {
    Map<String, Object> hashMap = new HashMap<String, Object>();
    hashMap.put("code", 200);
    hashMap.put("msg", "memberIndex");
    Thread.sleep(1500);
    return hashMap;
}
}

四、项目源码

会员工程

订单工程

原文地址:https://www.cnblogs.com/haoworld/p/hystrix-shi-xian-fu-wu-ge-li-he-jiang-ji.html

时间: 2024-11-09 17:45:30

【Hystrix】实现服务隔离和降级的相关文章

外部服务的隔离及降级

本文的来源是之前的一次生产事故,当时由于某家医院的机房网络故障,造成整个某项业务的全部停止服务,也就是说其它医院虽说与平台的网络畅通,但是由于平台的资源全部被消耗,其它医院也不能再提供给患者提供任何服务.下图是用户.平台及医院的调用关系图: 我最近找了一些公共的开源框架,包扩大概的看了看apache commons和google guava工具包,都没有找到合适的工具,便花了两个晚上的时间,自己实现了一个简单的版本,源码可见于 https://github.com/mantuliu/distri

Hystrix 解决服务雪崩效应

1.服务雪崩效应 默认情况下tomcat只有一个线程池去处理客户端发送的所有服务请求,这样的话在高并发情况下,如果客户端所有的请求堆积到同一个服务接口上, 就会产生tomcat的所有线程去处理该服务接口,可能会导致其他服务接口访问延迟: 2.Hystrix服务保护框架,在微服务中Hystrix为我们解决了哪些事情? Hystrix 别名"豪猪" 1)断路器 2)服务降级 3)服务熔断 4)服务隔离机制 5)服务雪崩效应 -->连环雪崩效应,如果比较严重的话,可能会导致整个微服务接

Hystrix分布式系统限流、降级、熔断框架(二)

三.Hystrix容错 ???????Hystrix的容错主要是通过添加容许延迟和容错方法,帮助控制这些分布式服务之间的交互. 还通过隔离服务之间的访问点,阻止它们之间的级联故障以及提供回退选项来实现这一点,从而提高系统的整体弹性.Hystrix主要提供了以下几种容错方法: 资源隔离 熔断 降级 1.资源隔离-线程池 2.资源隔离-信号量 线程池和信号量隔离比较 ? 线程切换 支持异步 支持超时 支持熔断 限流 开销 信号量 否 否 否 是 是 小 线程池 是 是 是 是 是 大 ???????

windows中的服务隔离 service isolation and service SID(Virtual Account)

windows 中的服务隔离在windows vista 以及server 2008之后就有了,可以让管理员控制本地资源的使用(如文件.注册表等等).之前windows版本中,系统内置了一些高权限的服务账号,大家所熟悉的有Local System,Network,LocalService 为了最小化权限使用,通常我们需要创建账号来赋予最小权限,然后配置服务以这个账号运行,但是如果服务较多,那么有许许多多的账号要维护,而且如果你有严格的密码策略的话,比如定期要更改服务账号的密码,那真是头疼. wi

为什么架构设计要进行服务隔离?

前言 我们在做系统架构设计的时候,经常离不开的一个话题就是进行服务的隔离设计. 那什么是「服务隔离」呢? 顾名思义,它是指将系统按照一定的原则划分为若干个服务模块,各个模块之间相对独立,无强依赖.当有故障发生时,能将问题和影响隔离在某个模块内部,而不扩散风险,不波及其它模块,不影响整体的系统服务. 其实隔离设计并非软件行业独创,它是借鉴于造船行业. 如上图,造船行业有一个专业术语叫做「舱壁隔离」.利用舱壁将不同的船舱隔离起来,如果某一个船舱进了水,那么就可以立即封闭舱门,形成舱壁隔离,只损失那一

看懂架构设计中的服务隔离

前言 我们在做系统架构设计的时候,经常离不开的一个话题就是进行服务的隔离设计. 那什么是「服务隔离」呢? 顾名思义,它是指将系统按照一定的原则划分为若干个服务模块,各个模块之间相对独立,无强依赖.当有故障发生时,能将问题和影响隔离在某个模块内部,而不扩散风险,不波及其它模块,不影响整体的系统服务. 其实隔离设计并非软件行业独创,它是借鉴于造船行业.行业有一个专业术语叫做「舱壁隔离」.利用舱壁将不同的船舱隔离起来,如果某一个船舱进了水,那么就可以立即封闭舱门,形成舱壁隔离,只损失那一个船舱,其他船

Spring Cloud实战之初级入门(四)— 利用Hystrix实现服务熔断与服务监控

目录 1.环境介绍 2.服务监控 2.1 加入依赖 2.2 修改配置文件 2.3 修改启动文件 2.4 监控服务 2.5 小结 3. 利用hystrix实现消费服务熔断 3.1 加入服务熔断 3.2 测试服务熔断 4. 利用turbine监控所有应用 4.1 创建工程 4.2 修改配置文件 4.3 修改启动文件 4.4 启动 5.一点点重要的事情 1.环境介绍 本篇文章涉及到前面文章的工程,mirco-service-provider.mirco-service-consumer以及需要另外新建

【原创】谈谈怎么做服务隔离

引言 OK,如下图所示 那显而易见,做服务隔离的目的就是避免服务之间相互影响.毕竟谁也不能说自己的微服务百分百可用,如果不做隔离,一旦一个服务出现了问题,整个系统的稳定性都会受到影响! 因此,做服务隔离是很有必要的.那么怎么隔离呢?有如下两种方式 按种类隔离 按用户隔离 OK,接下来开始细说这两种方式! 正文 种类隔离 其实按照服务种类隔离要从两个纬度来说:即服务提供方和服务调用方! 假设我们一个系统有三个服务:订单服务,库存服务,支付服务!有如下调用关系: OK,我们先明确一点,上面有几个服务

spring-cloud-hystrix服务熔断与降级

Hystrix是一个用于处理分布式系统的延迟和容错的开源库,在分布式系统里,许多依赖不可避免的会调用失败,比如超时,异常等,Hystrix能保证在一个依赖出问题的情况下,不会导致整体服务失败,避免级联故障,以提高分布式系统的弹性. “断路器” 本身是一种开关设置,当某个服务单元发生故障之后,通过断路器的故障监控(类似熔断保险丝),向调用方返回一个符合预期的,可处理的备选相应(fallBack),而不是长时间的等待或者抛出调用方法无法处理的异常,这样就保证了服务调用方的线程不会长时间,不必要的占用