分布式系统面临的问题
服务雪崩
多个微服务之间调用的时候,假设微服务A调用微服务B和微服务C,微服务B和微服务C又调用其他的微服务,这就是所谓的“扇出"、如果扇出的链路上某个微服务的调用响应时间过长或者不可用,对微服务A的调用就会占用越来越多的系统资源,进而引起系统崩溃,所谓的“雪崩效应"。
对于高流量的应用来说,单一的后端依赖可能会导致所有服务器上的所有资源都在几秒中内饱和。比失败更糟糕的是,这些应用程序还可能导致服务之间的延迟增加,备份队列,线程和其他系统资源紧张,导致整个系统发生更多的级联故障,这些都表示需要对故障和延迟进行隔离和管理,以便单个依赖关系的失败,不能取消整个应用程序或系统。
服务雪崩的原因
应对策略
什么是Hystrix
- Hystrix是一个用于处理分布式系统的延迟和容错的开源库,在分布式系统中,许多的依赖不可避免的会调用失败,比如超时,异常等,Hystrix能够保证在一个依赖出问题的情况下,不会导致整体服务失败,避免级联故障,以提高分布式系统的弹性
- "熔断器"本身是一种开关装置,当某个服务单元发生故障时,通过断路器的故障监控,向调用方返回一个服务预期的,可处理的备选相应,而不是长时间的等待或者抛出调用方法无处理的异常,这样就保证了服务调用方的线程不会被长时间不必要的占用,从而避免了故障在分布式系统中的蔓延,乃至雪崩。
Hystrix的作用
- 提供保护并控制通过第三方客户端库(通常是通过网络)访问的依赖项的延迟和失败。
- 停止复杂的分布式系统中的级联故障。
- 快速失败并快速恢复。
- 回退并在可能的情况下正常降级。
- 启用近乎实时的监视,警报和操作控制。
服务熔断(服务端)
- 熔断机制是对应雪崩效应的一种微服务链路保护机制。
- 当下游的服务因为某种原因突然变得不可用或响应过慢,上游服务为了保证自己整体服务的可用性,不再继续调用目标服务,直接返回,快速释放资源。如果目标服务情况好转则恢复调用
- 当扇出链路的某个微服务不可用或者响应时间太长时,会进行服务的降级,进而熔断该节点微服务的调用,快速返回错误的响应信息。当检测到该节点微服务调用响应正常后恢复调用链路。在SpringCloud框架里熔断机制通过Hystrix实现。Hystrix会监控微服务间调用的状况,当失败的调用到一定阈值,缺省(默认)是5秒内20次调用失败就会启动熔断机制。熔断机制的注解是@HystrixCommand。
我们可以通过改变之间的8001代码来操作:
1.导入Hystrix jar包依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
<version>1.4.7.RELEASE</version>
</dependency>
2.在我们controller层中,添加熔断注解,并指定要熔断的方法;在这里我们只指定其中的一个方法!
@GetMapping("/get/{id}")
@HystrixCommand(fallbackMethod = "hystrixGet")//指定服务熔断执行的方法
private Dept get(@PathVariable("id") Integer id) {
Dept dept = service.queryById(id);
if (dept == null) {
throw new RuntimeException("用户不存在");
}
return dept;
}
/**
* 备选方法
*/
private Dept hystrixGet(@PathVariable("id") Integer id) {
return new Dept().setId(id)
.setName("没用对应信息")
.setDbSource("mysql中没有这个数据库");
}
3.在主启动类中开启服务熔断
@SpringBootApplication
@EnableEurekaClient //在服务启动后自动注册到eureka
@EnableDiscoveryClient//服务发现
@EnableCircuitBreaker //添加对熔断的支持
public class DeptProvider_8001 {
public static void main(String[] args) {
SpringApplication.run(DeptProvider_8001.class, args);
}
}
4.启动服务进行测试,此时出现错误不会直接弹出错误页面,而是查询会调用服务熔断指定的方法。
服务降级(客服端)
为什么服务降级使用在客服端呢?
这是因为服务端崩了,就没有办法返回,因此是在客户端访问服务端时,让客户端知道服务端崩了,再让客户端降级!
服务降级是进行在服务消费方的,配置Feign更方便使用,所以直接改造公共模块中关于Feign服务层接口的方法。
服务降级是当服务器压力剧增的情况下,根据当前业务情况及流量对一些服务和页面有策略的降级,以此释放服务器资源以保证核心任务的正常运行。
1.在公共模块(api)编写一个类实现FallbackFactory类,并重写create方法,重写的方法返回Feign的服务层接口类。
@Component
public class ClientFallbackFactory implements FallbackFactory {
@Override
public ClientService create(Throwable throwable) {
return new ClientService() {
@Override
public boolean addDept(Dept dept) {
return false;
}
@Override
public Dept queryById(Long id) {
return new Dept().setDeptno(id)
.setDname("服务已关闭!")
.setDb_source("mysql中没有这个数据库");//测试案例
}
@Override
public List<Dept> queryAll() {
return null;
}
};
}
}
2.在Feign的服务层接口类中配置Feign的返回工厂的类型
@FeignClient(value = "SPRINGCLOUD-SERVER",fallbackFactory = ClientFallbackFactory.class)
@Component
public interface ClientService {
@PostMapping("/dept/add")
boolean addDept(Dept dept);
@GetMapping("/dept/get/{id}")
Dept queryById(@PathVariable("id") Integer id);
@GetMapping("/dept/get")
List<Dept> queryAll();
}
3.在Feign的消费者模块中的yaml配置文件中开启降级服务
# 开启降级服务
feign:
hystrix:
enabled: true
4.启动测试
Dashboard流监控(与消费者相关)
Hystrix和客户端有相关的操作,就可以去监控这些请求,而Dashboard就是一个监控页面来监控这些信息的。
1.创建 “springcloud-consumer-hystrix-dashboard” 模块,这个模块就是Dashboard的监控页面,然后导入jar包。基本与前面的80类似。只需要导入额外的包!
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
<version>1.4.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix-dashboard</artifactId>
<version>1.4.7.RELEASE</version>
</dependency>
2.配置yaml文件,配置端口.
server:
port: 9001
hystrix:
dashboard:
proxy-stream-allow-list: "*"
3.在启动类上开启监控
@SpringBootApplication
@EnableHystrixDashboard//开启监控
public class DeptConsumerDashboard_9001 {
public static void main(String[] args) {
SpringApplication.run(DeptConsumerDashboard_9001.class, args);
}
}
4.保证每个服务提供者模块中有 “spring-boot-starter-actuator” 服务监控的依赖,然后启动测试,可以看到Dashboard默认的页面
5.现在Dashboard里面是空的,没有任何东西,接下来就往里面配置,在服务提供者模块中进行注册,并且在需要监控的接口上加上 “@HystrixCommand” 注解。
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
@SpringBootApplication
@EnableEurekaClient //在服务启动后自动注册到eureka
@EnableDiscoveryClient//服务发现
@EnableCircuitBreaker
public class Dept_Hystrix_8001 {
public static void main(String[] args) {
SpringApplication.run(Dept_Hystrix_8001.class, args);
}
//下面代码是固定的 为了配合监控使用
@Bean
public ServletRegistrationBean hystrixMetricsStreamServlet() {
ServletRegistrationBean registrationBean = new ServletRegistrationBean(new HystrixMetricsStreamServlet());
registrationBean.addUrlMappings("/actuator/hystrix.stream");
return registrationBean;
}
}
6.启动服务进行测试,浏览器访问 http://localhost:8001/actuator/hystrix.stream,实际端口根据自己的服务来访问,可以看到,当服务启动后,访问地址一直在ping某个东西,类似于心跳,当访问被监控的接口,页面就会有一些数据,相当于发送了一个心跳包。
7接下来访问Dashboard的地址,在页面去监控流.
进入监控页面,就可以看到监控的接口的状态信息了,当前是没有任何访问的
当我们访问接口时,快速点击刷新,就会发现出现以下的情况
接下来就详细聊聊关于这个信息界面
一圈
实心圆:共有两种含义,通过颜色的变化代表实例的健康程度,绿色<黄色<橙色<红色 递减;通过实例的请求流量它的大小也会随之变化,流量越大,实心圆就越大,所以通过实心圆表示,就可以在大量的 实例中快速的发现故障实例和高压实例。
一线
曲线:记录2分钟以内流量的相对变化,可以通过它来观测流量的上升和下降趋势。
其他
Geteway 网关
网关作为流量的入口,常用功能包括路由转发、权限校验、限流控制等;而SpringCloud Gateway 作为SpringCloud 官方推出的第二代网关框架,取代了Zuul网关。
特点:
- 建立在Spring Framework 5,Project Reactor和Spring Boot 2.0之上
- 能够匹配任何请求属性上的路由
- 路由有独特的断言和过滤器
- 集成断路器
- 集成Spring Cloud DiscoveryClient integration
- 易于编写断言和过滤器
- 请求限速
- 路径重写
在使用前需要了解几个基本的概念:
Route: 路由,网关的基本构建块。它由ID,目标URI,断言集合和过滤器集合定义。如果断言为true,则匹配路由
Predicate: 断言,Java8 断言函数,允许开发人员匹配当次请求的任何信息;它的作用,当满足断言规则则路由到指定路径
Filter: 过滤器,在发送请求之前或之后修改请求和响应
使用Gateway
网关,作为客户请求的第一层,直接通过网关转发客户请求到对应的路径,所以应该是一个单独的模块,并且为了方便建议使用80端口。
1.导入依赖
<!-- gateway -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!-- Nacos 服务注册发现 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!-- nacos 配置中心 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
2.编写yaml配置
spring:
cloud:
nacos:
discovery:
server-addr: 192.168.0.109:8848
gateway:
routes: # 路由规则,可以配置多种,以数组的方式配置
- id: test_route # 路由ID
uri: https://www.baidu.com # 跳转的路径
predicates: # 断言规则,数组形式配置多个
- Query=url,baidu # 精确匹配,如果携带的参数url等于 "baidu",则跳转
- id: qq_route
uri: https://www.qq.com
predicates:
- Query=url,qq
application:
name: gulimall-gateway
server:
port: 80
3.启动测试
@SpringBootApplication
@EnableDiscoveryClient //服务注册发现
public class GulimallGatewayApplication {
public static void main(String[] args) {
SpringApplication.run(GulimallGatewayApplication.class, args);
}
}