熔断器Hystrix 及 服务监控Dashboard
本章知识:
1、服务雪崩效应
2、服务熔断服务降级
3、Hystrix默认超时时间设置
4、Hystrix服务监控Dashboard
服务雪崩效应
当一个请求依赖多个服务的时候:
正常情况下的访问 :
但是,当请求的服务中出现无法访问、异常、超时等问题时(图中的I),那么用户的请求将会被阻塞 :
如果多个用户的请求中,都存在无法访问的服务,那么他们都将陷入阻塞的状态中 :
Hystrix的引入,可以通过服务熔断和服务降级来解决这个问题,在下文有详细说明 。
服务熔断服务降级
Hystrix 断路器简介:
Hystrix 对应的中文名字是“豪猪”,豪猪周身长满了刺,能保护自己不受天敌的伤害,代表了一种防御机制,这与hystrix本身的功能不谋而合,因此Netflix团队将该框架命名为Hystrix,并使用了对应的卡通形象做作为logo。
在一个分布式系统里,许多依赖不可避免的会调用失败,比如超时、异常等。
那么,如何能够保证在一个依赖出问题的情况下,不会导致整体服务失败,这个就是Hystrix需要做的事情。
Hystrix 提供了熔断、隔离、Fallback、cache、监控等功能,能够在一个、或多个依赖同时出现问题时保证系统依然可用。
Hystrix 服务熔断服务降级 @HystrixCommand fallbackMethod
熔断机制 是应对雪崩效应的一种微服务链路保护机制。
当某个服务不可用或者响应时间超时,会进行服务降级,进而熔断该节点的服务调用,快速返回自定义的错误影响页面信息。
我们写个项目来测试下:
写一个新的带服务熔断的服务提供者模块 microservice-student-provider-hystrix-1004
把 配置和 代码 都复制一份到这个项目里;
修改配置:
-
pom.xml 加上 hystrix支持
<!--Hystrix相关依赖--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-hystrix</artifactId> </dependency>
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 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>com.dj</groupId> <artifactId>springcloud01</artifactId> <version>1.0-SNAPSHOT</version> </parent> <artifactId>microservice-student-provider-hystrix-1004</artifactId> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>com.dj</groupId> <artifactId>microservice-common</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> </dependency> <!-- 修改后立即生效,热部署 --> <dependency> <groupId>org.springframework</groupId> <artifactId>springloaded</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> </dependency> <!-- actuator监控引入 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <!--添加注册中心Eureka相关配置--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-config</artifactId> </dependency> <dependency> <groupId>com.dj</groupId> <artifactId>microservice-common</artifactId> <version>1.0-SNAPSHOT</version> <scope>compile</scope> </dependency> <!--Hystrix相关依赖--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-hystrix</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
-
application.yml修改下端口和实例名称
server: port: 1004 context-path: / spring: datasource: type: com.alibaba.druid.pool.DruidDataSource driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://localhost:3306/springcloud?useUnicode=true&characterEncoding=utf8 username: root password: 123 jpa: hibernate: ddl-auto: update show-sql: true application: name: microservice-student profiles: provider-hystrix-1004 eureka: instance: hostname: localhost appname: microservice-student instance-id: microservice-student:1004 prefer-ip-address: true client: service-url: defaultZone: http://eureka2001.dj.com:2001/eureka/,http://eureka2002.dj.com:2002/eureka/,http://eureka2003.dj.com:2003/eureka/ info: groupId: com.dj.testSpringcloud artifactId: microservice-student-provider-hystrix-1004 version: 1.0-SNAPSHOT userName: http://dj.com phone: 123
-
启动类 StudentProviderHystrixApplication_1004 加下注解支持 @EnableCircuitBreaker
package com.dj.microservicestudentproviderhystrix1004; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.domain.EntityScan; import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker; import org.springframework.cloud.netflix.eureka.EnableEurekaClient; @EnableCircuitBreaker @EntityScan("com.dj.*.*") @EnableEurekaClient @SpringBootApplication public class MicroserviceStudentProviderHystrix1004Application { public static void main(String[] args) { SpringApplication.run(MicroserviceStudentProviderHystrix1004Application.class, args); } }
-
服务提供者1004中controller新增
package com.dj.microservicestudentproviderhystrix1004.controller; import com.dj.microservicecommon.entity.Student; import com.dj.microservicestudentproviderhystrix1004.service.StudentService; import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.*; import java.util.HashMap; import java.util.List; import java.util.Map; @RestController @RequestMapping("/student") public class StudentProviderController { @Autowired private StudentService studentService; @Value("${server.port}") private String port; @PostMapping(value="/save") public boolean save(Student student){ try{ studentService.save(student); return true; }catch(Exception e){ return false; } } @GetMapping(value="/list") public List<Student> list(){ return studentService.list(); } @GetMapping(value="/get/{id}") public Student get(@PathVariable("id") Integer id){ return studentService.findById(id); } @GetMapping(value="/delete/{id}") public boolean delete(@PathVariable("id") Integer id){ try{ studentService.delete(id); return true; }catch(Exception e){ return false; } } @RequestMapping("/ribbon") public String ribbon(){ return "工号【"+port+"】正在为您服务"; } /** * 测试Hystrix服务降级 * @return * @throws InterruptedException */ @ResponseBody @GetMapping(value="/hystrix") @HystrixCommand(fallbackMethod="hystrixFallback") public Map<String,Object> hystrix() throws InterruptedException{ // Thread.sleep(2000); Map<String,Object> map=new HashMap<String,Object>(); map.put("code", 200); map.put("info","工号【"+port+"】正在为您服务"); return map; } public Map<String,Object> hystrixFallback() throws InterruptedException{ Map<String,Object> map=new HashMap<String,Object>(); map.put("code", 500); map.put("info", "系统【"+port+"】繁忙,稍后重试"); return map; } }
这里我正常访问 返回的是 200 业务数据xxxxx
但是我们这里Thread.sleep(2000) 模拟超时;
这里的话 我们加上@HystrixCommand注解 以及 fallbackMethod
表明这个方法我们再 没有异常以及没有超时(hystrix默认1秒算超时)的情况,才返回正常的业务数据;
否则,进入我们fallback指定的本地方法,我们搞的是500 系统出错,稍后重试,有效的解决雪崩效应,以及返回给用户界面
很好的报错提示信息;microservice-student-consumer-80 项目也要对应的加个方法
package com.dj.microservicestudentconsumer80.controller; import com.dj.microservicecommon.entity.Student; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import org.springframework.web.client.RestTemplate; import java.util.List; import java.util.Map; @RestController @RequestMapping("/student") public class StudentConsumerController { private final static String SERVER_IP_PORT = "http://MICROSERVICE-STUDENT"; @Autowired private RestTemplate restTemplate; @PostMapping(value="/save") private boolean save(Student student){ return restTemplate.postForObject(SERVER_IP_PORT+"/student/save", student, Boolean.class); } @GetMapping(value="/list") public List<Student> list(){ return restTemplate.getForObject(SERVER_IP_PORT+"/student/list", List.class); } @GetMapping(value="/get/{id}") public Student get(@PathVariable("id") Integer id){ return restTemplate.getForObject(SERVER_IP_PORT+"/student/get/"+id, Student.class); } @GetMapping(value="/delete/{id}") public boolean delete(@PathVariable("id") Integer id){ try{ restTemplate.getForObject(SERVER_IP_PORT+"/student/delete/"+id, Boolean.class); return true; }catch(Exception e){ return false; } } @RequestMapping("/ribbon") public String ribbon(){ return restTemplate.getForObject(SERVER_IP_PORT + "/student/ribbon", String.class); } /** * 测试Hystrix服务降级 * @return */ @GetMapping(value="/hystrix") @ResponseBody public Map<String,Object> hystrix(){ return restTemplate.getForObject(SERVER_IP_PORT+"/student/hystrix/", Map.class); } }
-
然后我们来测试下
先启动三个Eureka,再启动带Hystrix的provider,最后启动普通的consumer
浏览器:http://localhost/student/hystrix返回:
因为 Hystrix默认1算超时,所有 sleep了2秒 所以进入自定义fallback方法,防止服务雪崩;我们这里改sleep修改成100毫秒;
Hystrix默认超时时间设置
Hystrix 默认超时时间是1秒,我们可以通过Hystrix源码看到,
找到 hystrix-core.jar , com.netflix.hystrix 包下的 HystrixCommandProperties 类
default_executionTimeoutInMilliseconds 属性局势默认的超时时间
默认1000毫秒 = 1秒
我们系统里假如要自定义设置hystrix的默认时间的话,就在yml文件中修改就行;
application.yml配置文件加上
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutInMilliseconds: 3000
注意:这段配置idea居然没有提示功能的。
改成3秒 然后 我们代码里sleep修改成2秒测试;
测试OK
sleep修改成4秒;
报错提示表示ok…
Hystrix 服务监控 Dashboard
Hystrix 服务监控 Dashboard 仪表盘
Hystrix 提供了 准实时的服务调用监控项目 Dashboard,能够实时记录通过 Hystrix 发起的请求执行情况,
可以通过图表的形式展现给用户看。
我们 新建项目 :microservice-student-consumer-hystrix-dashboard-90
加依赖:
<!--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>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
application.yml配置
server:
port: 90
context-path: /
新建启动类:StudentConsumerDashBoardApplication_90,加注解:@EnableHystrixDashboard
package com.dj.microservicestudentconsumerhystrixdashboard90;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration;
import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard;
@SpringBootApplication(exclude={DataSourceAutoConfiguration.class, HibernateJpaAutoConfiguration.class})
@EnableHystrixDashboard
public class MicroserviceStudentConsumerHystrixDashboard90Application {
public static void main(String[] args) {
SpringApplication.run(MicroserviceStudentConsumerHystrixDashboard90Application.class, args);
}
}
这样就完事了。
我们启动这个项目;
然后浏览器输入:http://localhost:90/hystrix
出现这个 就说明OK;
然后我们来测试下;
我们启动三个eureka,然后再启动 microservice-student-provider-hystrix-1004
我们直接请求 http://localhost:1004/student/hystrix
返回正常业务
我们监控的话,输入http://localhost:1004/hystrix.stream 这个路径,点击 Monitor Stream 即可;
一直是ping,然后data返回数据;
用图形化的话
指标含义:
各种情况: