SpringCloud系列之Hystrix

作者 胡萝虎 日期 2018-05-15
SpringCloud系列之Hystrix

在分布式系统中,如果服务提供者响应缓慢,会导致消费者请求等待,直到收到响应或超时。如果在高并发、高负载的场景下,会导致消费者大量线程等待服务提供者响应,持续下去的话很容易导致整个系统因为资源耗尽而崩溃。比如,电影服务作为上层的消费者,需要从下层的用户服务查询用户信息,这时候用户服务因为某些原因导致响应非常缓慢,正常只需要10毫秒的响应被延长到几十秒。电影服务持续不断收到外部请求,并等待用户服务响应,很快,电影服务自身也崩溃了。

因为依赖的服务不可用或不稳定时,导致自身被拖垮,分布式系统中时常会上演,这种情况也被称为“雪崩效应”。

那么,该如何防止雪崩效应呢?一般需要做两点:

  • 设置网络请求超时时间,尽快释放系统资源,减少不必要的等待
  • 使用熔断器,为系统安装过载保护功能

下面我们要介绍的Hystrix就是熔断器的一个实现。

Hystrix

Hystrix时Netflix开发的一个容错库,用于隔离远程调用,防止引发级联失败,避免雪崩效应。它类似于电路中的断路器,当系统超过了容忍的错误限度时,开启断路保护,阻止错误放大,从而提升系统的稳定性和容错性。

Hystrix主要提供以下功能:

  • 代理请求,即使用HystrixCommand代理调用逻辑
  • 跳闸保护,当某个依赖服务错误率达到设定的阀值,可以自动或手动开启跳闸,阻止继续请求依赖的服务
  • 资源隔离,将依赖的服务放到单独的线程池中执行,避免过度资源占有和应用其他模块
  • 回退机制,即在请求失败、超时、被拒绝等场景下,多断路器打开状态下,执行回退逻辑(快速失败),开发者自定义回退逻辑
  • 自我修复,即断路器打开一段时间后会自动进入“半开”状态,并测试依赖的服务是否已经恢复,如果恢复,则将断路器关闭,即自动管理断路器的开启、半开和关闭状态

实战

修改用户服务

  • 服务provider-user,并改名为provider-user-hystrix

    image-20180515113118929

  • 修改用户服务代码

    将findById方法修改成 i>=3时,抛出异常。

    package com.huluohu.cloud.user.presentation.web;

    import com.huluohu.cloud.user.presentation.mode.UserDTO;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.web.bind.annotation.*;

    import java.util.Random;
    import java.util.concurrent.atomic.AtomicInteger;

    @Slf4j
    @RestController
    @RequestMapping("/user")
    public class UserController {
    private Random random = new Random();
    private static volatile AtomicInteger counter = new AtomicInteger();

    /**
    * 查询用户
    *
    * @param id
    * @return
    */
    @GetMapping("/{id}")
    public UserDTO findById(@PathVariable Integer id) {
    final int i = counter.incrementAndGet();
    log.info("i========={}", i);
    if (i >= 3) {
    throw new RuntimeException("测试断路器");
    }
    return UserDTO.builder()
    .id(id)
    .username("root_" + id)
    .name("huluohu_" + id)
    .age(random.nextInt(30) % (30 - 15 + 1) + 15)
    .balance(random.nextLong())
    .build();
    }
    }

    修改电影服务

  • 复制consumer-movie-feign,并改名为consumer-movie-hystrix

    image-20180515113450657

  • 添加依赖

    pom.xml中增加以下hystrix的依赖

    <dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-hystrix</artifactId>
    </dependency>
  • 修改配置

    在application-local.yml中增加hystrix配置

    feign:
    hystrix:
    enabled: true #开启hystrix
  • 修改启动类

    package com.huluohu.cloud.movie;

    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
    import org.springframework.cloud.netflix.feign.EnableFeignClients;
    import org.springframework.cloud.netflix.hystrix.EnableHystrix;

    @SpringBootApplication
    @EnableDiscoveryClient
    @EnableFeignClients
    @EnableHystrix //开启hystrix
    public class MovieApplication {
    public static void main(String[] args) {
    SpringApplication.run(MovieApplication.class, args);
    }
    }
  • 添加回退逻辑代码

    创建回退逻辑类,该类型需要实现UserServiceClient并增加@Component注解

    package com.huluohu.cloud.movie.application.client.hystrix;

    import com.huluohu.cloud.movie.application.client.UserServiceClient;
    import com.huluohu.cloud.movie.application.model.User;
    import org.springframework.stereotype.Component;
    import org.springframework.web.bind.annotation.PathVariable;

    @Component
    public class UserServiceClientHystrix implements UserServiceClient {
    @Override
    public User findById(@PathVariable("id") Integer id) {
    return User.builder()
    .username("random")
    .name("default")
    .age(0)
    .balance(0L)
    .build();
    }
    }

    设置回退逻辑类型

    package com.huluohu.cloud.movie.application.client;

    import com.huluohu.cloud.movie.application.client.hystrix.UserServiceClientHystrix;
    import com.huluohu.cloud.movie.application.model.User;
    import org.springframework.cloud.netflix.feign.FeignClient;
    import org.springframework.web.bind.annotation.*;

    @FeignClient(serviceId = "provider-user",fallback = UserServiceClientHystrix.class)//设置回退逻辑类
    public interface UserServiceClient {

    /**
    * 根据ID查询User
    *
    * @param id
    * @return
    */
    @RequestMapping(value = "/user/{id}", method = RequestMethod.GET)
    User findById(@PathVariable("id") Integer id);
    }

测试

  1. 启动Eureka Server(local profile)
  2. 启动Config Server(不需要区分profile)
  3. 启动provider-user-hystrix(local profile)
  4. 启动consumer-movie-hystrix(local profile)
  • 启动服务列表

    image-20180515114648649

  • 服务注册情况

    image-20180515114702291

  • 测试

    • 使用浏览器或Postman访问http://localhost:9009/test/user/13次,正常返回用户服务的结果

      image-20180515114906873

      此时,hystrix状态正常,断路器没有打开:

      image-20180515115108217

    • 使用Postman Runner快速请求http://localhost:9009/test/user/1

      image-20180515115157297

      每次返回的结果都是默认值:

      {
      "id": null,
      "username": "random",
      "name": "default",
      "age": 0,
      "balance": 0
      }

      此时,hystrix的断路器是打开的:

      image-20180515115430860

Hystrix监控

除了提供熔断功能外,Hystrix还提供了实时监控模块,可以对断路器的运行状态和指标进行监控。

查看Hystrix状态

在上面的例子中,consumer-movie-hystrix已经整合了Feign和Hystrix,并添加了spring-boot-starter-actuator依赖,可以在浏览器中请求http://localhost:9009/hystrix.stream,查看监控情况:

image-20180516113722747

Hystrix使用hystrix-metrics-event-stream模块实现将监控指标以text/evanet-stream格式输出。

整合Dashboard

直接访问http://localhost:9009/hystrix.stream返回的数据不太直观,我们可以通过整合Hystrix Dashboard,让数据可视化。

创建Maven项目

image-20180516114225734

添加依赖

pom.xml添加dashboard依赖

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix-dashboard</artifactId>
</dependency>
</dependencies>

添加配置

application.yml配置

spring:
application:
name: hystrix-dashboard


management:
security:
enabled: false

server:
port: 7777

创建启动类

package com.huluohu.cloud.hystrix;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard;

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

测试

  1. 启动Eureka Server(local profile)
  2. 启动Config Server(不需要区分profile)
  3. 启动provider-user-hystrix(local profile)
  4. 启动consumer-movie-hystrix(local profile)
  5. 启动hystrix-dashboard(不区分profile)
  • 应用列表

    image-20180516114519527

  • 浏览器中打开http://localhost:7777/hystrix

    image-20180516114705407

  • 在上面图中url输入框中输入http://localhost:9009/hystrix.stream,Title输入框随意输入一个名字,然后点击Monitor Stream按钮

    image-20180516114909908

到达监控页面,此时没用请求,监控数据都是初始值。

image-20180516115748805

  • 在Postman上执行查询用户接口测试,在查看监控页面

    image-20180516115224242

image-20180516115321177

怎么样,很简单吧!

你可能会喜欢

“扫一扫接着看”