模拟实战(2) - 一文秒懂Ribbon、Hystrix实现服务负载均衡、资源隔离与熔断降级

前言:本篇为 SpringCloud Greenwich版本模拟业务实战系列文章第二篇,上篇文章主要讲了服务治理、高可用安全的服务注册中心,那么本文将对ribbon、hystrix进行介绍,对其如何实现服务负载均衡,以及如何保证服务之间调用资源的隔离、以及发生紧急情况时对服务进行熔断降级操作进行详细讲解

一、组件介绍


1、Ribbon

ribbon是属于Netflix的一款基于客户端http、tcp负载均衡的一个小框架,何为客户端负载均衡,简单理解就是客户端获取了服务注册中心的所有注册数缓存在本地,并定时维护本地缓存的注册数据;客户端发起远程服务请求,则采用一系列的负载均衡策略从本地获取一个服务的地址数据并对其发送网络请求。
我们平常所了解的SpringCloud ribbon则是对Netflix的ribbon进行了一系列的封装,使其无缝对接SpringCloud环境开发,并且能够无缝对接各大主流服务治理框架,如Eureka、Consul等;就比如我们采用RestTemplate结合Ribbon对远程服务进行访问只需要加一个注解@LoadBalanced就可以起到对注册中心的所有服务进行负载均衡的作用,我们根本察觉不到其实他们已经自动整合了,这也归功于SpringCloud对其进行了如此完美的封装。也正因如此,许多入门小白会满头雾水,后续我也会针对SpringCloud各个组件的源码进行一系列的讲解。

2、Hystrix

Hystrix也是Netflix开源的一款,针对服务之间调用限制其可使用的 线程资源 、以及服务调用失败可以调用指定的 降级处理 方法、包括调用的目标服务实例如果不可访问了,会针对设定的熔断阈值(默认调用失败超过50%)对该服务实例进行 熔断处理 ,所有的服务在短期设定的时间内直接返回调用失败,如果设置了降级方法则会调用降级方法。

二、为何使用?


1、为什么要使用ribbon

其实这个问题相信大家已经都明白了,ribbon的主要功能就是客户端负载均衡,并且是对服务注册中心已经注册了的服务进行负载均衡,没有ribbon的话 我们得自己编写一套负载均衡机制,除非你的服务实例是单个的,但是这不太现实!还有 就是后面的feign 也是基于ribbon来进行服务实例的负载均衡调用。

2、为什么要使用hystrix

很简单
1、熔断机制 保证了阻断了不可用的服务,直接响应请求失败,不会再去重重复请求占用资源。
2、资源隔离 可以保证对某个依赖服务进行调用时哪怕请求再多,只耗费属于其的线程资源,并不会影响别的服务的调用。
3、降级策略 可设置服务调用失败后执行的方法。
所以正应为hystrix的强大,才使其成为高可用、稳定、健壮的SpringCloud微服务系统中必不可少的组件之一。

三、ribbon的使用


在使用ribbon之前(这里指SpringCloud 封装过后的ribbon),我们可以先了解一下原始的netfix ribbon大概是如何使用的
编码API调用方式访问
注解方式访问
整合eureka 的服务列表访问方式

看上去还是挺简单的,但是当我们的SpringCloud给ribbon画龙点睛了一下,你会发现只要短短两三行代码,外加两三行配置,就可以无缝对接eureka乃至consul,并且只需要一个注解就可以为注册中心的各个服务进行负载均衡操作!

1、restTemplate + @LoadBalanced 使用ribbon负载均衡调用服务

在之前的 user-server 服务稍作修改,引入restTemplate,restTemplate是spring提供的一个非常强大的调用http rest接口的一款工具,不了解的可以去Google一下,这里不做赘述。

@RestController
@SpringBootApplication
@EnableEurekaClient
public class App {

    @Bean
    @LoadBalanced //加上此注解增加负载均衡功能,并使其能直接用服务名来进行调用
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }

    @Autowired
    RestTemplate restTemplate;

    @GetMapping("/test")
    @ResponseBody
    public Object test(){
        //使用restTemplate根据服务名调用,默认的restTemplate是没有此功能的
       String str = restTemplate.getForObject("http://provider-goods-service/test",String.class);
       return str;
    }
   
    public static void main(String[] args) {
        SpringApplication.run(App.class, args);
    }
}
2、给 goods-server 加上一个测试controller,再复制一个goods-server run,并设置不同启动端口
测试controller
    @GetMapping("/test")
    @ResponseBody
    public Object test(HttpServletRequest request){
        return "调用成功 当前服务端口为:"+ request.getServerPort();
    }
启动多个 goods-server
指定启动端口
启动并查看eureka server是否注册上
检查是否注册上
3、多次访问 user-server /test 测试地址,查看结果

第一次访问
第二次访问

从请求结果我们可以看出我们的请求已经被负载均衡了。这里并不需要重复引用ribbon的依赖,因为spring-cloud-starter-netflix-eureka-client 已经引入ribbon依赖了。
整个负载均衡请求流程为 (前方高能)

看不太懂的小伙伴别急,先学会如何使用, 后续会有专门的源码讲解篇。

4、配置

--负载均规则略--
上面我们实现了对服务实例进行负载均衡,我们重复进行访问可以发现ribbon默认使用的轮询请求的方式来进行负载均衡,除了轮询请求(RoundRobinRule)外,还有一些其他比较常用的

  • RandomRule - 随机选择服务
  • WeightedResponseTimeRule 根据响应时间来配置权重,响应最快的权重高,反之则少
  • BestAvailableRule 最少请求的服务

--常用配置项-

#针对单个服务配置路由规则,注意 配置的值 需要类全名(包名+类名);
provider-goods-service: #目标服务名
  ribbon:
    ConnectionTimeout: 400 #链接超时
    ReadTimeout: 400 #读取超时
    MaxAutoRetries: 1 #重试当前实例的次数
    MaxAutoRetriesNextServer: 1 #服务实例切换重试次数
    ServerListRefreshInterval: 30000 #刷新所服务列表间隔时间
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule #配置对应的规则,其他ribbon自带的规则 可查看IRule接口的实现类

#ribbon:
#  ConnectionTimeout: 400 #链接超时
#  ReadTimeout: 400 #读取超时
#  MaxAutoRetries: 1 #重试当前实例的次数
#  MaxAutoRetriesNextServer: 1 #服务实例切换重试次数
#  ServerListRefreshInterval: 30000 #刷新所服务列表间隔时间

设为全局配置只需要把服务名去掉 使ribbon变成顶级配置即可,注意:NFLoadBalancerRuleClassName 在全局配置中无效,全局负载均衡规则配置如下 ⬇️

    //针对所有服务配置路由规则,配置config
    @Bean
    public IRule iRule(){
        return new RandomRule();//具体的负载均衡规则类
    }

四、Hystrix的使用


1、添加依赖

虽然eureka依赖中也进行hystrix的引用,但是里面的引用在SpringCloud新版本中是无效的,依赖引用如下⬇️

     <dependency>
          <groupId>org.springframework.cloud</groupId>
          <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
     </dependency>

2、注解方式使用hystrix、降级回调

  • 我们之前的服务调用放到一个service中,创建 GoodsService类
@Service
public class GoodsService {

    @Autowired
    RestTemplate restTemplate;

    @HystrixCommand(fallbackMethod = "testFallback")//服务调用失败后 执行降级回调方法,方法名不需要加括号
    public String test(){
        return restTemplate.getForObject("http://provider-goods-service/test",String.class);
    }

    //降级回调方法,返回参数和调用参数需要一致
    public String testFallback(){
        return "请求失败,此次请求已记录!";
    }

}

如果在@HystrixCommand修饰的方法中有多个服务调用的话,任何一个服务的调用发生异常或者产生异常都会触发fallbackMethod的调用,如果触发了某些特定的异常不想触发fallback,则需要指定抛出的特定异常继承HystrixBadRequestException,或者在方法内部对可能抛出的异常进行try掉

  • controller改动一下
    @Autowired
    GoodsService goodsService;

    @GetMapping("/test")
    @ResponseBody
    public Object test(){
       String str = goodsService.test();
       return str;
    }
  • 启动类加如下注解 开启hystrix
@EnableCircuitBreaker //开启hystrix

3、hystrix熔断器、资源隔离、其他常用配置项

  • 局部配置(方法1),作用于单个 @HystrixCommand 修饰的方法不太推荐使用
    @HystrixCommand(
            fallbackMethod = "testFallback", // 请求失败降级回调方法,值为方法名,不需要括号
            commandProperties ={// 针对单个方法的配置
                    @HystrixProperty(name = "circuitBreaker.enabled", value = "true"),//开启熔断器,可不加默认为true
                    @HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "50"),//请求错误超过50%,开启熔断器
                    @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "10"),//一个周期(十秒)内超过10个请求才进行进行容错率判断
                    @HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds", value = "10000"),//开启熔断器后过10秒再尝试访问 
                    ...
            })

  • 局部配置(方法2),基于hystrix commandKey,和配置文件来配置(推荐使用
    我们把之前@HystrixCommand 里面的配置都去掉,改动结果如下
@HystrixCommand(
            commandKey = "testCommand",//为修饰的方法定义一个 commandKey,不设置默认取方法名为commandKey
            fallbackMethod = "testFallback"// 请求失败降级回调方法,值为方法名,不需要括号
    )
hystrix:
  command:
    testCommand: #commandKey,配置作用于指定的commandKey

      # **********************==》》  常用的熔断器配置  《《==**********************
      circuitBreaker:
        enabled: true #默认为true,可不用配置
        errorThresholdPercentage: 50 #一个监测周期(默认10s),请求失败率超过50%开启熔断器
        requestVolumeThreshold: 10 #一个监测周期内,超过10个请求才进行进行容错率判断
        sleepWindowInMilliseconds: 10000 #开启熔断器后过10s再尝试访问,默认5s
      metrics:
        rollingStats:
          timeInMilliseconds: 10000 #请求监测周期时长(单位 ms),默认10000
          numBuckets: 10 #每个监测周期,分为10个buckets,说白了就是 10秒的监测周期 分为十个buckets,每个buckets 1秒;每过一秒进行一次请求计算
                         #注意 timeInMilliseconds % numBuckets 必须为0 否则会触发异常

      # **********************==》》  常用的资源隔离配置  《《==**********************
      execution:
        isolation:
          strategy: THREAD # THREAD:线程隔离, SEMAPHORE:信号量隔离;默认线程隔离
          thread:
            timeoutInMilliseconds: 400 #占用线程调用接口的超时时间
            interruptOnTimeout: true #占用线程超时 是否中断线程的执行
          timeout:
            enabled: true #开启超时限制
          semaphore:
            maxConcurrentRequests: 20 #信号量隔离下才有效,最大的信号量值,可以理解为 最大支持的并发数
      fallback:
        isolation:
          semaphore:
            maxConcurrentRequests: 20 #降级回调方法允许的最大调用
  • 全局配置,在commandKey同级配置下 添加default的配置
hystrix:
  command:
    testCommand: #配置作用于指定的commandKey 
      # 配置项...
    default: #配置作用于全局,但是优先级会比指定commandKey 的低
      # 配置项...

4、总结

得益于springcloud的功劳才使得我们使用hystrix能够这么方便,当然hystrix的本质是不变的,对于一些大部分常用的功能配置都列出来了,可对实际的业务进行设置并测试;除了这些常用的配置 还有 线程池、请求合并 等配置

  • 线程池(ThreadPoolProperties) 一般不太建议随便设置,除非你对自己的实力有 足够的自信,使用默认的配置在百分之八九十的业务场景中是完全没问题的
  • 请求合并(CollapserProperties) 这个配置还是比较牛逼的,但是这一块的话除非的业务场景是并发请求量是特别大的可能会用得到,而且都是利弊性的;不过后续也会结合源码一起进行讲解,这样才能对其原理进行更深入的了解

有不足或者疑惑之处欢迎在下方留言~~

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,607评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,047评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,496评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,405评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,400评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,479评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,883评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,535评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,743评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,544评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,612评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,309评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,881评论 3 306
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,891评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,136评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,783评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,316评论 2 342

推荐阅读更多精彩内容