5 Hystrix
5.1 服务雪崩
在复杂的分布式架构中,服务之间都是相互依赖的,任何一个节点都不可避免会宕机。如果主节点不能从这些宕机节点中独立出来,那主节点将会面临被这些宕机的节点拖垮的风险。
-
完好情况下,请求流如下:
-
当一个依赖的节点坏掉时,将阻塞整个的用户请求:
-
随着高流量访问,单个后端服务发生错误,这可能导致所有服务器上的资源在短短几秒钟内变的饱和(并不是一定会造成资源饱和。例如,该故障请求返回超时,则可能造成线程数急剧增加,若请求不能即使释放,造成网络阻塞)。
应用程序接受到请求,通过网络对每个依赖服务进行访问,这时每个服务都有可能存在潜在的错误。比起当个服务宕机,更可怕的是,这些服务可能导致服务之间的延迟增加,从而导致备份队列,线程和其他资源被占用,从而导致整个系统的级联故障(整个服务集群宕机,甚至造成服务器不可达)。
多个微服务之间调用的时候,假设微服务A调用微服务B和微服务C,微服务B和微服务C又调用其他的微服务,这就是所谓的“扇出”,如果扇出的链路上某一微服务的调用响应时间过长或者不可用,对微服务A的调用就会占用越来越多的系统资源,进而引起系统崩溃,这就是所谓的雪崩效应。
5.2 Hystrix
Hystrix是一个用于处理分布式系统的延迟和容错的开源库,在分布式系统里,需要依赖不可避免的会调用失败,比如超时,异常等,Hystrix能够保证在一个依赖出问题的情况下,不会导致整体服务失败,避免级联故障,以提高分布式系统的弹性。
“断路器”本身是一种开关装置,当某个服务单元发生故障之后,通过断路器的故障监控(类似熔断保险丝),向调用方返回一个服务预期的,可处理的备选响应(FallBack),而不是长时间的等待或者抛出调用方法无法处理的异常,这样就可以保证了服务调用方的线程不会被长时间,不必要的占用,从而避免了故障在分布式系统中的蔓延,乃至雪崩。
5.3服务熔断
熔断机制是对应雪崩效应的一种微服务链路保护机制。
当扇出链路的某个微服务不可用或者响应时间太长时,会进行服务的降级,进而熔断该节点微服务的调用,快速返回错误的响应信息。当检测到该节点微服务调用响应正常后恢复调用链路。在SpringCloud框架里熔断机制通过Hystrix实现。Hystrix会监控微服务间调用的状况,当失败的调用到一定阈值,缺省是5秒内20次调用失败就会启动熔断机制。熔断机制的注解是@HystrixCommand。
使用
添加模块的通用步骤:
1、导入依赖maven
2、编写配置yml(可能不需要)
3、开启功能@EnableXXX
4、配置类
导入maven依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
不需要配置yml
开启熔断器
在主启动类上添加注解:@EnableCircuitBreaker
@SpringBootApplication
@EnableEurekaClient //在服务启动后自动注册到eureka中
@EnableDiscoveryClient //服务发现
@EnableCircuitBreaker //添加对熔断的支持
public class HystrixDeptProvider_8001 {
public static void main(String[] args) {
SpringApplication.run(HystrixDeptProvider_8001.class, args);
}
}
为接口方法添加备选方法
在需要熔断的方法上添加注释:@HystrixCommand(fallbackMethod = "hystrixGet")
,并指定备选方法。则当方法抛出异常时,会自动执行备选方法。
@HystrixCommand(fallbackMethod = "hystrixGet")
@GetMapping("/dept/get/{id}")
public Dept get(@PathVariable("id") Long id){
Dept dept = deptService.queryById(id);
if (dept == null) {
throw new RuntimeException("id=>" + id + ",不存在该用户,或者信息无法找到");
}
return dept;
}
/**
* get的备选方法
* @return
*/
public Dept hystrixGet(@PathVariable("id") Long id) {
return new Dept().setDeptno(id)
.setDname("id=>" + id + ",不存在该用户,或者信息无法找到")
.setDname("no this database in Mysql");
}
测试
查询数据库不存在的记录时,会自动输出空记录,而不报错。
5.4 服务降级
- 服务降级:在客户端,从整体网站请求负载考虑,当某个服务熔断或者关闭后,服务将不再被调用。此时在客户端,可以准备一个FallbaFactory,返回一个默认的值(缺省值),整体的服务水平下降了,但是好歹能用。
FallbackFactory
-
原本的service类
这里制定了服务关闭后的处理方法:
fallbackFactory = DeptClientServiceFallbaFactory.class
@Service
@FeignClient(value = "SPRINGCLOUD-PROVIDER-DEPT", fallbackFactory = DeptClientServiceFallbaFactory.class)
public interface DeptClientService {
@GetMapping("/dept/get/{id}")
public Dept queryById(@PathVariable("id") Long id);
@GetMapping("/dept/list")
public List<Dept> queryAll();
@PostMapping("/dept/add")
public boolean addDept(Dept dept);
}
-
DeptClientServiceFallbaFactory
当服务关闭后,会自动执行该方法
@Component
public class DeptClientServiceFallbaFactory implements FallbackFactory {
public DeptClientService create(Throwable throwable) {
return new DeptClientService() {
public Dept queryById(Long id) {
return new Dept().setDeptno(id)
.setDname("id=>" + id + ",不存在该用户,或者信息无法找到,客户端提供了降级服务,这个服务现在已经关闭")
.setDb_source("no data");
}
public List<Dept> queryAll() {
return null;
}
public boolean addDept(Dept dept) {
return false;
}
};
}
}
配置
- 在客户端模块开启降级
# 开启降级
feign:
hystrix:
enabled: true
效果
当关闭DeptConsumerController
调用的对应服务后,响应界面为:
服务降级功能成功实现。
5.5 Dashboard监控
1、导入依赖
-
新建项目
springcloud-consumer-hystrix-dashboard
-
导入依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix-dashboard</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
2、编写端口配置
server:
port: 9001
3、启动
- 在Springboot主启动类上添加
@EnableHystrixDashboard
注解,就可以启动监控面板
@SpringBootApplication
@EnableHystrixDashboard //开启
public class DeptConsumerDashboard_9001 {
public static void main(String[] args) {
SpringApplication.run(DeptConsumerDashboard_9001.class, args);
}
}
- 测试:访问http://localhost:9001/hystrix
4、被监控服务添加监控依赖
提示:被监控服务需要开启服务熔断,否则监控不到信息
-
springcloud-provider-dept-hystrix-8001
在该项目中添加依赖<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-hystrix</artifactId> <version>1.4.6.RELEASE</version> </dependency>
5、被监控服务添加servlet
-
springcloud-provider-dept-hystrix-8001
在该项目中的主启动类上添加Servlet -
要添加
@EnableCircuitBreaker
@SpringBootApplication
@EnableEurekaClient //在服务启动后自动注册到eureka中
@EnableDiscoveryClient //服务发现
@EnableCircuitBreaker //添加对熔断的支持
public class HystrixDeptProvider_8001 {
public static void main(String[] args) {
SpringApplication.run(HystrixDeptProvider_8001.class, args);
}
//增加一个Servlet p16测试只有ping 使用原本的8001服务没有数据,使用该有熔断的8001服务后可以正常输出数据
@Bean
public ServletRegistrationBean a() {
ServletRegistrationBean registrationBean = new ServletRegistrationBean(new HystrixMetricsStreamServlet());
registrationBean.addUrlMappings("/actuator/hystrix.stream");
return registrationBean;
}
}
- 并在被监控服务的Controller层的任一接口上添加
@HystrixCommand
注解。并指定备选方法。
6、添加监控
- 效果,监控面板,下图的详细含义请百度
6 Zuul路由网关
6.1 概述
Zuul包含了对请求的路由和过滤两个最主要的功能:
其中路由功能负责将外部请求转发到具体的微服务实例上,是实现外部访问统一入口的基础,而过滤器功能则负责对请求的处理过程进行干预,是实现请求校验,服务聚合等功能的基础。Zuul和Eureka进行整合,将Zuul自身注册为Eureka服务治理下的应用,同时从Eureka中获得其他微服务的消息,也即以后的访问微服务都是通过Zuul跳转后获得。
注意:Zuul服务最终还是会注册进Eureka。
提供:代理 + 路由 + 过滤 ,这三大功能。
6.2 使用
- 创建新项目
springcloud-zuul-9527
添加依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zuul</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
配置项目
server:
port: 9527
spring:
application:
name: springcloud-zuul
eureka:
client:
service-url:
defaultZone: http://localhost:7001/eureka/,http://localhost:7002/eureka/,http://localhost:7003/eureka/
instance:
instance-id: zuul9527 #修改eureka上的默认描述信
prefer-ip-address: true
info:
app.name: evan-springcloud
company.name: happy
zuul:
routes:
mydept.serviceId: springcloud-provider-dept-hystrix
mydept.path: /dept-hystrix/**
ignored-services: springcloud-provider-dept-hystrix # 不能再使用这个路径访问了
prefix: /evan #设置公共的前缀
主启动类开启
@SpringBootApplication
@EnableZuulProxy //
public class ZuulApplication_9527 {
public static void main(String[] args) {
SpringApplication.run(ZuulApplication_9527.class, args);
}
}
测试
P17 10:31 设置host
- 测试
存在的问题:微服务名称被暴露。
-
添加配置
zuul: routes: mydept.serviceId: springcloud-provider-dept-hystrix mydept.path: /dept-hystrix/**
-
隐藏后的效果
存在的问题,使用微服务名仍可以访问,需要屏蔽微服务名访问
-
添加配置
ignored-services: springcloud-provider-dept-hystrix # 不能再使用这个路径访问了
-
忽略后效果
ignored-services: springcloud-provider-dept-hystrix #隐藏指定微服务
ignored-services: "*" #隐藏全部
- 添加统一的前缀
prefix: /evan #设置公共的前缀
7 Config分布式配置
7.1 概述
分布式系统面临的配置文件问题
微服务意味着要单体应用中的业务拆分成一个个子服务,每个服务的粒度相对较小,因此系统中会出现大量的服务,由于每个服务都需要必要的配置信息才能运行,所以一套集中式的,动态的配置管理设施是必不可少的。
SpringCloud提供了ConfigServer来解决这个问题,我们每一个微服务自己带一个application.yml,那上百的配置文件要修改起来岂不是要发疯。
- SpringCloud Config分布式配置中心
SpringCloud Config为微服务架构中的微服务提供集中化的外部配置支持,配置服务器为各个不同微服务应用的所有环节提供了一个中心化的外部配置。
- SpringCloud Config分为服务端和客户端两部分
服务端也称为分布式配置中心,它是一个独立的微服务应用,用来连接配置服务器并为客户端提供获取配置信息,加密,解密信息等访问接口。
客户端则是通过指定的配置中心来管理应用资源,以及与业务相关的配置内容,并在启动的时候从配置中心获取和加载配置信息。配置服务器默认采用git来存储配置信息,这样就有助于对环境配置进行版本管理。并且可以通过git客户端工具来方便的管理和访问配置内容。
7.2 服务端搭建
新建项目springcloud-config-server-3344
添加依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
<version>2.1.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
配置
server:
port: 3344
spring:
application:
name: springcloud-config-server
# 连接远程仓库
cloud:
config:
server:
git:
uri: https://gitee.com/mrevan/springcloud-config.git
# 通过config-server可以连接到git,访问其中的资源以及配置
主启动类
@SpringBootApplication
@EnableConfigServer
public class ConfigServer_3344 {
public static void main(String[] args) {
SpringApplication.run(ConfigServer_3344.class, args);
}
}
测试
可以访问git远程仓库的application.yml
文件内容。
7.3 客户端搭建
新建项目:springcloud-config-client-3355
添加依赖
<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-config</artifactId>
<version>2.1.1.RELEASE</version>
</dependency>
</dependencies>
配置
- bootstrap.yml
- 类似上一节的向
http://localhost:3344
发起请求,获取git仓库的配置
# bootstrap:系统级别的配置
# application:用户级别的配置
spring:
cloud:
config:
uri: http://localhost:3344
name: config-client
profile: test
label: master #分支
- application.yml
spring:
application:
name: springcloud-config-client-3355
主启动类
@SpringBootApplication
public class ConfigClient_3355 {
public static void main(String[] args) {
SpringApplication.run(ConfigClient_3355.class, args);
}
}
测试
-
项目启动过程中,就会读取远程git仓库的配置,这个项目没有在本地配置访问端口号。项目读取配置后,端口号为8202.
-
增加访问接口
@RestController
public class ConfigClientController {
@Value(("${spring.application.name}"))
private String applicationName;
@Value("{eureka.client.service-url.defaultZone")
private String eurekaServer;
@Value("{server.port}")
private String port;
@RequestMapping("/config")
public String getConfig() {
return "applicationName: " + applicationName +
"eurekaServer" + eurekaServer +
"port:" + port;
}
}
- 访问该接口