文章目录
一、Hystrix是什么
在分布式环境中,许多服务依赖(微服务间的调用)中的一些不可避免地会失败。Hystrix 可以通过添加延迟容错和容错逻辑来控制分布式服务之间的交互。Hystrix 通过隔离服务之间的访问点、阻止服务之间的级联故障并提供回退选项来实现这一点,这些操作可以提高系统的整体弹性。
Hystrix 旨在执行以下操作:
① 通常通过网络提供延迟和故障的保护和控制。
② 停止复杂分布式系统中的级联故障。(服务熔断)
③ 快速失败并快速恢复。
④ 在可能的情况下回退并优雅地降级。(服务降级)
⑤ 实现近乎实时的监控、警报和操作控制。(流量监控)
复杂分布式体系结构中的应用程序有几十个微服务,每个微服务都有宕机的可能性。如果主机应用程序没有与这些微服务(宕机)隔离开来,那么应用程序就有被这些微服务(宕机)破坏的风险。
当一切正常时,请求流可以如下所示:
当许多后端系统中的一个微服务异常,那么可能会导致用户请求失败:
倘若出现大量的请求访问该出现问题的微服务,那么极有可能导致"雪崩":
当使用 Hystrix 包装每个微服务时,每个依赖项彼此隔离,在一个微服务发生异常时,并不会直接返回错误,Hystrix 会回调设计人员提前定义好服务器异常响应:
二、服务熔断
1、简介
在微服务架构中,微服务之间的数据交互通过远程调用完成,微服务A 调用 微服务E 和 微服务G,微服务B 调用其它的 微服务M,此时如果链路上某个微服务的调用响应时间过长或者不可用,那么对 微服务A 的调用就会占用越来越多的系统资源,进而引起系统崩溃,导致“雪崩效应”。
客户端访问 A 、B 服务时,A 调用 E,E 调用 G;B 调用 M。当所有的服务都处于正常的状态,那么访问是成功的。
当 G服务 出现问题,无法提供服务时,那么可能会导致整个服系统出现"雪崩效应"。
当系统进入了服务熔断,即使 G服务 出现问题,熔断机制会调用 G服务 的副本,返回给用户相关的响应信息,不会使得整个系统出现雪崩。
熔断这一概念来源于电子工程中的断路器(Circuit Breaker)。其作用就是当某一个服务出现问题时,会通过熔断机制使该服务不可用并返回错误信息,从而避免服务雪崩。
熔断机制是应对服务雪崩效应
的一种微服务链路保护机制
,当扇出链路的某个微服务不可用或者响应时间太长时,会进行服务熔断,不再有该节点微服务的调用,快速返回错误的响应信息。当检测到该节点微服务调用响应正常后,恢复调用链路。
2、通过Hystrix实现服务熔断
服务熔断是对于服务提供者而言的,其主要是解决项目依赖的某个微服务中的某一个功能因为某种原因出现异常时,会直接在客户端显示 500 之类的错误页面。当我们通过 Hystrix 实现了服务熔断之后,上述的问题不会出现,而是将开发人员提前定义好的服务器异常时的响应内容返回给客户端。
实现服务熔断,我们是通过 Hystrix + Ribbon (RestTemplate)实现的。
第一步:创建一个服务提供者(springcloud-provider-dept-hystrix-8001)
参考:【SpringCloud】负载均衡 (Ribbon)【精讲】- 二 (3)
第二步:导入 hystrix 依赖
<!-- hystrix 依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
<version>2.2.5.RELEASE</version>
</dependency>
第三步:设置 fallbackMethod 方法
此方法的作用就是作为一个备用服务存在,当 A 服务出现异常时,Hystrix 会调用 A 方法的 fallbackMethod 方法,不至于使得用户得到 500 的响应页面。
@HystrixCommand(fallbackMethod = “B”):被该注解作用的方法A,当方法A出现异常,Hystrix 就会调用自定义的 B 方法,值得注意的是:A 与 B 的返回值类型,参数类型都要相同,否则无法生效。
package com.tiger.controller;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.tiger.pojo.Department;
import com.tiger.service.DepartmentService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
public class RouteController {
@Autowired
DepartmentService departmentService;
@GetMapping("/dept/select/{id}")
@HystrixCommand(fallbackMethod = "hystrixQueryOne")
public Department queryOne(@PathVariable("id") int dNum){
//手动制造异常
throw new RuntimeException("error");
}
public Department hystrixQueryOne(@PathVariable int dNum){
return new Department()
.setD_num(dNum)
.setD_name("服务升级中,抱歉给您带来不便")
.setD_source("服务升级中,抱歉给您带来不便");
}
}
@HystrixCommand 中的另一个常用的参数是 commandProperties,而commandProperties中是通过 @HystrixProperty进行设置的,如下:
@HystrixCommand(fallbackMethod = "xxxxx", commandProperties = {
@HystrixProperty(name="execution.isolation.semaphore.maxConcurrentRequests",value="20"),
@HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value="20000"),
})
@HystrixProperty中的属性,参看:【Hystrix】@HystrixProperty中 name-value 的说明
第四步:主启动类上开启服务熔断可用
@SpringBootApplication
...
@EnableCircuitBreaker //支持熔断(CircuitBreaker:断路器)
public class SpringcloudProviderDeptHystrix8001Application {
public static void main(String[] args) {
SpringApplication.run(SpringcloudProviderDeptHystrix8001Application.class, args);
}
}
测试
开启 springcloud-provider-dept-hystrix-8001
,访问 http://localhost:8081/dept/select/1
,因为我们在 queryOne()
中手动设置了异常,所以当访问 queryOne()
时,Hystrix 会调用 queryOne() 的 fallbackMethod 方法 —— hystrixQueryOne()
。
三、服务降级
1、简介
当服务器压力剧增的情况下,根据实际业务情况及流量,对一些服务或页面的某些功能做出暂时禁止使用的限制,从而释放服务器资源以保证核心业务的正常运作或高效运作。
举个例子你可能就会更加理解了。在大学期间,大学生都会进行选课,那么选课这个服务在短时间内会产生大量了流量(访问量),为了保证选课业务的正常进行,运维人员通常会将教务系统中的其他不重要的功能(评课、搜索…)设置为不可用,以缓解服务器压力,这就是一个简单的服务降级的使用场景。
使用场景
当整个微服务架构整体的负载超出了预设的上限阈值或即将到来的流量预计将会超过预设的阈值时,为了保证重要或基本的服务能正常运行,我们可以将一些 不重要 或 不紧急 的服务或任务进行服务的 延迟使用 或 暂停使用。注意,服务降级的操作是在服务消费者(springcloud-consumer)中实现的。
当一个服务的访问过大时,在没有进行降级服务的情况是这样的:
当一个服务的访问过大时,在进行降级服务的情况是这样的:
2、通过Hystrix实现服务降级
服务降级是在客户端(服务消费者)实现的,因为只有在客户端实现服务降级,当运维人员关闭不必要的服务时,即使用户再访问 已关闭 的服务也不会爆出 500 之类的页面,因为在客户端有响应的降级处理。
关于服务降级,我们通过 feign 与 hystrix 进行结合实现服务降级(feign 搭建可参照:【SpringCloud】负载均衡 (Ribbon & Feign)【精讲】- 三)。当然你也可以通过 ribbon 与 hystrix 与结合实现负载均衡。
使用 feign 的原因,一方面 feign 是面向接口编程实现的负载均衡,更加符合 java 思想;另一方面 feign 中存在 fallbackFactory ,可以一次性的对一个接口中的方法进行降级,更加简单方便。
第一步:创建一个 FallbackFactory 的实现类
该实现类的作用是对某个服务中的模块统一进行降级处理,要将该类注入到 spring 容器中去。
package com.tiger.service;
import feign.hystrix.FallbackFactory;
import org.springframework.stereotype.Component;
@Component
public class ServiceFallbackFactory implements FallbackFactory {
@Override
public Object create(Throwable throwable) {
return null;
}
}
第二步:自定义降级服务
将上面的 Object 换成你要进行降级操作的接口(DeptClientService ),并重写里面的方法
package com.tiger.service;
import com.tiger.pojo.Department;
import feign.hystrix.FallbackFactory;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
@Component
public class ServiceFallbackFactory implements FallbackFactory {
@Override
public DeptClientService create(Throwable throwable) {
return new DeptClientService() {
@Override
public boolean addDept(String dName) {
return false;
}
@Override
public Department queryByNum(int dNum) {
return new Department()
.setD_num(dNum)
.setD_name("当前流量过大,该服务暂时做降级处理")
.setD_source("当前流量过大,该服务暂时做降级处理");
}
@Override
public List<Department> queryAll() {
List<Department> list = new ArrayList<>();
list.add(new Department()
.setD_num(0)
.setD_name("当前流量过大,该服务暂时做降级处理")
.setD_source("当前流量过大,该服务暂时做降级处理"));
return list;
}
};
}
}
第三步:将 FallbackFactory 实现类绑定 DeptClientService
fallbackFactory :其值就是 FallbackFactory 实现类的 class 类型。经过fallbackFactory 的绑定,当真实的 DeptClientService 中的某一个方法的请求出问题时 [例如:服务提供者(服务端)关闭] ,hystrix 会执行 fallbackFactory 绑定的类中的对用的预备方法。
package com.tiger.service;
import com.tiger.pojo.Department;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import java.util.List;
@Service
@FeignClient(value = "SPRINGCLOUD-PROVIDER-DEPT", fallbackFactory = ServiceFallbackFactory.class)
public interface DeptClientService {
@GetMapping("/dept/insert/{name}")
boolean addDept(@PathVariable("name") String dName);
@GetMapping("/dept/select/{id}")
Department queryByNum(@PathVariable("id") int dNum);
@GetMapping("/dept/select")
List<Department> queryAll();
}
第四步:开启服务降级 feign.hystrix (application.yaml)
# 开启服务降级 feign.hystrix
feign:
hystrix:
enabled: true
测试:
开启springcloud-eureka-7001
、springcloud-provider-dept-8081
和springcloud-consumer-dept-feign
进行测试。访问 http://localhost:8080/list/1
。
当关闭 springcloud-consumer-dept-feign
服务,再次访问 http://localhost:8080/list/1
。
四、流量监控
1、简介
我们希望将服务中的每个子服务的流量情况(如:访问量、熔断量、超时、错误、成功)可视化。
2、通过Hystrix实现流量监控
我们需要创建一个服务(springcloud-consumer-hystrix-dashboard),由于流量监控使用
导入所需要的依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- hystrix-dashboard依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
<version>2.2.5.RELEASE</version>
</dependency>
配置配置文件:(application.yaml)
server:
port: 9001
# 将 localhost 添加到 proxyStreamAllowList 这个数组当中去
hystrix:
dashboard:
proxy-stream-allow-list: "localhost"
主启动类上添加 @EnableHystrixDashboard 注解
@SpringBootApplication
@EnableHystrixDashboard //开启Hystrix监控面板
public class SpringcloudConsumerHystrixDashboardApplication {
public static void main(String[] args) {
SpringApplication.run(SpringcloudConsumerHystrixDashboardApplication.class, args);
}
}
启动 springcloud-consumer-hystrix-dashboard ,访问 http://localhost:9001/hystrix
到此,我们的流量监控服务就已经搭好了。
下面我们需要将 被监控的服务 与 springcloud-consumer-hystrix-dashboard 建立联系。这里我们需要注意,被监控的服务通过 Hystrix 实现熔断,这是因为 hystrix-dashboard 是通过 @HystrixCommand 注解,对相关的方法进行监控的,而 @HystrixCommand 注解刚好是 hystrix 实现熔断的重要注解
。
假设我们要对 springcloud-provider-dept-hystrix-8001 服务进行监控,那么我们需要在其主启动类中添加一个 servlet,实现对该服务中相关熔断访问的监控。(在 springboot 中,如果需要自定义一个 servlet,那么需要用到 servletRegistrationBean,将其注入到 spring 容器中)
@SpringBootApplication
@EnableEurekaClient //在服务启动后,自动注册到 Eureka 的服务端
@EnableDiscoveryClient //服务发现
@EnableCircuitBreaker //支持熔断(CircuitBreaker:断路器)
public class SpringcloudProviderDeptHystrix8001Application {
public static void main(String[] args) {
SpringApplication.run(SpringcloudProviderDeptHystrix8001Application.class, args);
}
@Bean
public ServletRegistrationBean hystrixMetricsStreamServlet(){
ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(new HystrixMetricsStreamServlet());
//"/actuator/hystrix.stream"刚好与上图中标红的位置相对应
servletRegistrationBean.addUrlMappings("/actuator/hystrix.stream");
return servletRegistrationBean;
}
}
此时,我们已经将 被监控的服务 与 springcloud-consumer-hystrix-dashboard 建立联系。
下面我们进行测试,开启 springcloud-consumer-hystrix-dashboard
和 springcloud-provider-dept-hystrix-8001
。(我们只是简单的测试一下流量监控模块,所以并没有开起 注册中心 和 服务消费者/客户端)
访问http://localhost:9001/hystrix
✈ ❀ 希望平凡の我,可以给你不凡の体验 ☂ ✿ …