简介
Hystrix断路器,又称Hystrix熔断器。它主要做SpringCloud里面的做服务熔断,服务降级的处理。它的思想和Spring里面的前置通知,环绕通知也有相似之处。
在复杂的分布式系统中,微服务之间可能有数十个依赖关系,而这些依赖关系中,总会不可避免的出现调用失败。比如A服务调用B服务,B服务又调用C服务…这也就是所谓的扇出,而在这扇出链路上,如果有一个环节出现了问题,就可能导致系统的资源被一直占用着,进而引起系统的崩溃,这并不是我们想看到的。
Hystrix就类似于保险丝,比如这会冬天到了,热得快,电热毯一下子全开,电路压力很大,如果承受不住,保险丝就会熔断,进而保证安全。Hystrix就是当某个单元出现故障,通过断路器的故障监控,向调用方法返回一个符合预期的、可处理的备选响应(fallback),而不是长时间的等待或抛出异常。SpringCloud框架里熔断机制通过Hystrix实现。Hystrix会监控微服务间调用的状况,当失败的调用到一定阈值,缺省是5秒内20次调用失败就会启动熔断机制。熔断机制的注解是@HystrixCommand。
熔断和降级
服务熔断:一般是某个服务故障或异常引起的,类似于现实世界的保险丝,为了防止系统雪崩,直接熔断整个服务,而不是一直等到此服务超时。服务熔断可能会带来方法膨胀和高度耦合。所以我们可以针对某个接口中的所有方法总的来做一次熔断处理。
服务降级:一般是从整体符合考虑。就是当某个服务熔断后,服务器将不再被调用,此时客户端可以自己准备一个本地的fallback回调,返回一个缺省值。这样做,虽然服务水平下降,但好歹可用,比直接挂掉要强。
服务降级是在客户端实现完成的,与服务端没有关系。也即客户端访问的时候,我们自身就带着一个准备好的本地的fallback回调,会返回一个缺省值。如果成功调用,则返回数据,如果不能成功调用,则返回fallback方法里的缺省值。 能访问,就访问对应的服务接口,不能访问就去找fallback回调。
搭建
Hystrix需要配置在服务提供端,即provider。我们可以拷贝之前的provider的服务的配置文件,进行一些修改。
第一步
修改pom.xml文件。在之前的pom.xml基础上,添加如下依赖。
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>
第二步
在provider的controller中,我们自定义 fallback 方法。
package com.zxb.springcloud.controller;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.zxb.springcloud.bean.User;
import com.zxb.springcloud.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
public class UserController {
@Autowired
private UserService userService;
@GetMapping(path = "/user/get/{id}")
@HystrixCommand(fallbackMethod = "getFallBackData")
public User getUserById(@PathVariable(value = "id") Integer id) {
User user = userService.findById(id);
if (user == null)
throw new RuntimeException("用户不存在");
else
return user;
}
// 出现异常,会自动调用此方法
public User getFallBackData(@PathVariable(value = "id") Integer id) {
User user = new User();
user.setId(id);
user.setName("查无此人");
user.setDbSource("查无此数据库");
return user;
}
}
第三步
配置主启动类,在主启动类上,需要加上 @EnableCircuitBreaker
注解,表示对 hystrix 熔断机制的支持。
@SpringBootApplication
@MapperScan(basePackages = {"com.zxb.springcloud.mapper"})
@EnableCircuitBreaker
@EnableEurekaClient
public class UserProvider8001_Hystrix_APP {
public static void main(String[] args) {
SpringApplication.run(UserProvider8001_Hystrix_APP.class, args);
}
}
好,provider微服务配置好了。
现在通过消费者查询一个不存在的id
这里要注意,我们写的Fallback方法的返回类型,要和标注了 @HystrixCommand 注解的返回类型一样或者是其子类,否则会有如下错误:
Hint: Fallback method 'public java.lang.String
com.zxb.springcloud.controller.UserController.getFallBackData(java.lang.Integer)'
must return: class com.zxb.springcloud.bean.User or its subclass错误
-----------------------------------------------------------华丽的分割线-----------------------------------------------------------
现在看似很完美,但是实则有些小问题。可能一个controller很多方法,我们一个一个去写 fallback 方法十分麻烦。并且在provider服务的controller中我们只想写一些业务逻辑相关的代码,并不想将熔断的代码也写在里面,耦合度过高。而且如果我们将provider服务停止了,这样消费者去访问,就不会出现熔断的信息,而是直接拒绝连接,出现500异常。这都是我们不想看到的。
接下来,就来解决这些问题。
在之前的博客中,已经配置搭建了用Feign来进行远程调用。而消费者调用provider,靠的就是feign去调用provider的接口,如果我们将服务熔断和降级定义在这,就可以完美解决以上问题。
创建一个 FallbackFactory 接口的实现类,该接口的泛型就是Feign接口类型。
package com.zxb.springcloud.service;
import com.zxb.springcloud.bean.User;
import feign.hystrix.FallbackFactory;
import org.springframework.stereotype.Component;
import java.util.List;
@Component
public class UserClientServiceFallbackFactory implements FallbackFactory<UserClientService> {
@Override
public UserClientService create(Throwable cause) {
return new UserClientService() {
@Override
public User getUserById(Integer id) {
User user = new User();
user.setId(id);
user.setName("该" + id + "没有对应的信息," +
"这是Consumer客户端提供的降级信息,此刻服务Provider已经关闭");
user.setDbSource("查无此库");
return user;
}
@Override
public Boolean addUser(User user) {return null;}
@Override
public Integer deleteUser(Integer id) {return null;}
@Override
public void updateUser(User user) {}
@Override
public List<User> getUsers() {return null;}
};
}
}
这里注意,一定要将该实现类加入到IOC容器中,我这里使用 @Component 注解。
随后,在Feign接口上标注的 @FeignClient
注解加上 fallbackFactory属性,值为我们刚定义的实现类。
@FeignClient(value = "springcloudservice-user",fallbackFactory = UserClientServiceFallbackFactory.class)
配置好后,重新将 feign 模块,mvn clean,mvn install打包,让其他引用的地方获取到最新值。
此时,我们启动注册中心,provider,consumer进行测试。
当我访问不存在的值:
此时假设请求过多,保持核心功能即可,关闭非核心微服务。将 provider 微服务停止。再去访问该接口。
服务监控
Hystrix还提供了准实时的调用监控(Hystrix Dashboard),Hystrix会持续地记录所有通过Hystrix发起的请求的执行信息,并以统计报表和图形的形式展示给用户,包括每秒执行多少请求多少成功,多少失败等。Netflix通过hystrix-metrics-event-stream项目实现了对以上指标的监控。Spring Cloud也提供了Hystrix Dashboard的整合,对监控内容转化成可视化界面。
了解了概念,就整起!
搭建
新建一个模块,专门用来做服务监控。
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>
<artifactId>springcloudservice</artifactId>
<groupId>org.example</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>springcloudservice-hystrix-dashboard</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- hystrix和 hystrix-dashboard相关-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix-dashboard</artifactId>
</dependency>
</dependencies>
</project>
application.properties 配置如下:设置端口即可
server.port=9001
在主启动类上加一个注解:@EnableHystrixDashboard
@SpringBootApplication
@EnableHystrixDashboard // 支持 hystrix 图形化界面
public class HystrixDashboard_App {
public static void main(String[] args) {
SpringApplication.run(HystrixDashboard_App.class, args);
}
}
接下来注意,我们要监控微服务,那么那些微服务也要情愿被我们监控,大家要约定一个规矩。所以,所有被监控的服务,都要加上如下配置:
<!-- actuator监控信息完善 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
我在三个provider微服务的pom.xml中都添加了这个依赖。配置完后,开始测试。
直接启动这个监控微服务,必须这个监控微服务启动成功没毛病,才能监控其他微服务。访问ip+port/hystrix 即可。我这里是 http://localhost:9001/hystrix
看到这个豪猪哥,则表示配置成功。
监控
搭建完毕,现在我们可以去监控其他微服务的健康状况、压力状况、调用状况以图形化的界面展示。
启动之前搭建好的Eureka-Server集群,再启动前面搭建的带有hystrix熔断的provider微服务。
先正常访问接口:http://localhost:8001/user/get/。然后我们看Hystrix Dashboard 主页,这里提示我们输入地址。
按照它的格式,在浏览器地址栏输入 http://localhost:8001/hystrix.stream并访问。
hystrix其实一直在ping,并且给我们返回data。但是这个界面十分的不友好。所以我们也可以设置成图形化界面。
在最上面的文本框输入被监控的微服务地址,我要监控谁,我就填谁。豪猪哥的placeholder也给了我们提示。我现在9001监控8001,那么我就填8001即可。那么什么格式呢?就是主机名+ip +端口号/hystrix.stream 即可。 即: http://localhost:8001/hystrix.stream。
1:Delay:该参数用来控制服务器上轮询监控信息的延迟时间,默认为2000毫秒,可以通过配置该属性来降低客户端的网络和CPU消耗。
2:Title:该参数对应了头部标题Hystrix Stream之后的内容,默认会使用具体监控实例的URL,可以通过配置该信息来展示更合适的标题。
随后单击 monitor,出现如下界面则成功。
这图怎么看呢? 七色一圈一线。
现在还没有任何请求到 provider,这里先记录洗状态:
接下来我再疯狂访问 http://localhost:8001/user/get/1 这个请求,看看这图会有啥变化。
我猛的访问了27次该路径,此时圆圈变大了,直线也上升了,并且27次都成功了,是绿色。
这会不访问了,圈又小了,波动也下来了。
图示分析:
再复杂点的微服务的监控图: