介绍
现在我们假设一下,服务提供者响应非常缓慢,那么消费者对提供者的请求就会被强制等待,直到服务返回。在高负载场景下,如果不做任何处理,这种问题很可能造成所有处理用户请求的线程都被耗竭,而不能响应用户的进一步请求。
雪崩效应
在微服务架构中通常会有多个服务层调用,大量的微服务通过网络进行通信,从而支撑起整个系统,各个微服务之间也难免存在大量的依赖关系。更何况每个服务都不是100%可用的,网络往往也是脆弱的,所以难免有些请求会失败,基础服务的故障导致【级联故障】,进而造成了整个系统的不可用,这种现象被称为雪崩效应,直接了当的讲,服务雪崩效应描述的是一种因服务提供者的不可用导致服务消费者的不可用,并将不可用逐渐扩大的过程。
C、D服务依赖B服务,B服务依赖A服务,如果A服务的不可用导致B服务慢慢的也不可用,B逐渐导致D、C不可用,这样逐渐扩大,导致整个系统的瘫痪。
解决方案
- 超时机制
通过网络请求其他服务时,都必须设置超时。正常情况下,一个远程调用一般在几十毫秒内就返回了。当依赖的服务不可用,或者因为网络问题,响应时间将会变得很长(几十秒)。而通常情况下,一次远程调用对应了一个线程/进程,如果响应太慢,那这个线程/进程就会得不到释放。而线程/进程都对应了系统资源,如果大量的线程/进程得不到释放,并且越积越多,服务资源就会被耗尽,从而导致资深服务不可用。所以必须为每个请求设置超时。 - 断路器模式
试想一下,家庭里如果没有断路器,电流过载了(例如功率过大、短路等),电路不断开,电路就会升温,甚至是烧断电路、起火。有了断路器之后,当电流过载时,会自动切断电路(跳闸),从而保护了整条电路与家庭的安全。当电流过载的问题被解决后,只要将关闭断路器,电路就又可以工作了。
同样的道理,当依赖的服务有大量超时,再让新的请求去访问已经没有太大意义,只会无谓的消耗现有资源。譬如我们设置了超时时间为1秒,如果短时间内有大量的请求,在1秒内都得不到响应,就往往意味着异常。此时就没有必要让更多的请求去访问这个依赖了,我们应该使用断路器避免资源浪费。
断路器(Hystrix)
- 断路器可以实现快速失败,如果它在一段时间内侦测到许多类似的错误(如超时),就会强迫其以后的多个调用【快速失败】,不再请求所依赖的服务,从而防止应用程序不断地尝试执行可能会失败的操作,这样应用程序可以继续执行而不用等待修正错误,或者浪费CPU时间去等待长时间的超时。
-
断路器也可以使应用程序能够诊断错误是否已经修正,如果已经修正,应用程序会再次尝试调用操作。断路器模式就像是那些容易导致错误的操作的一种代理。这种代理能够记录最近调用发生错误的次数,然后决定使用允许操作继续,或者立即返回错误。
简单解释一下:
- 关闭:如果一个请求一直都是成功的,那么断路器是关闭的;
- 打开:如果一个请求失败率达到了阈值,断路器会打开;
- 半开:在断路器打开的同时,它会【分流】,就是将请求的一小部分放开,让他去请求,如果失败率低于阈值,就会关闭断路器,如果还是高于阈值,继续保持在打开状态【快速失败】。
Hystrix
介绍
针对上述问题,Spring Cloud Hystrix实现了断路器、线程隔离等一系列保护功能,他也是基于Netflix的开源框架Hystrix实现的,该框架的目标在于通过控制那些访问远程系统、服务和第三方库的节点,从而对延迟和故障提供强大的容错能力,Hystrix具备服务降级、服务熔断、线程和信号的隔离、请求缓存、请求合并以及服务监控等强大功能。
具体实现
-
普通实现
- 启动类添加@EnableCircuitBreaker注解
@SpringBootApplication @EnableEurekaClient @EnableFeignClients @EnableCircuitBreaker public class MovieConsumeApplication { public static void main(String[] args) { SpringApplication.run(MovieConsumeApplication.class, args); } }
- 添加注解@HystrixCommand和fallback方法(@HystrixCommand(fallbackMethod = "getDefaultMovide"))
@RestController public class MovieController { @Autowired private UserClient userClient; @GetMapping("/movie/{id}") @HystrixCommand(fallbackMethod = "getDefaultMovide") public User getMovide(@PathVariable long id) { return userClient.getUser(id); } public User getDefaultMovide(long id) { User user = new User(); user.setId(-1L); user.setName("失败"); user.setAge(-1); user.setAddress("未知"); return user; } }
完事,就这么简单
-
feign配合使用hystrix
这里我直接介绍指定Client添加Hystrix,下面代码的前提是:我有来个服务提供者user-provider、payment-provide,我将让user使用Hystrix,payment不使用Hystrix.
-
yml中添加
feign: hystrix: enabled: true
默认说是开启的,刚开始没有添加,死活不走fallback,最后添加 上好了,我也很迷茫
-
Client、FallBack代码
-
UserClient
@FeignClient(name = "user-provider",fallback = HystrixClientFallback.class) public interface UserClient { @RequestMapping(value = "/getUser/{id}", method = RequestMethod.GET) public User getUser(@PathVariable("id") long id); }
-
User的Fallback类(这个类名不规范)
@Component public class HystrixClientFallback implements UserClient { @Override public User getUser(@PathVariable("id") long id) { User user = new User(); user.setName("123"); return user; } }
-
PaymentClient
@FeignClient(name = "payment-provide", configuration = PaymentConfig.class, fallback = PaymentClientFallback.class) public interface PaymentClient { @RequestMapping(value = "/getPayment/{id}", method = RequestMethod.GET) public Payment getPayment(@PathVariable("id") long id); }
-
PaymentClientFallback
@Component public class PaymentClientFallback implements PaymentClient { @Override public Payment getPayment(@PathVariable long id) { Payment payment = new Payment(); payment.setId(-1); payment.setManay(-1); payment.setUserId(-1); return payment; } }
-
PaymentConfig
@Configuration public class PaymentConfig { @Bean @Scope("prototype") public Feign.Builder feignBuilder() { return Feign.builder(); } }
注意:PaymentConfig这个类放【不能】放到到【启动类】所在【包】或是【所在包的子包下】,不然所有Client都会不使用Hystrix。
- Controller
@RestController public class MovieController { @Autowired private UserClient userClient; @Autowired private PaymentClient paymentClient; @GetMapping("/movie/{id}") public User getMovide(@PathVariable long id) { return userClient.getUser(id); } @GetMapping("/getPayment/{id}") public Payment allMovie(@PathVariable long id) { return paymentClient.getPayment(id); } }
-
结果:http://localhost:8002/movie/1 返回Fallback结果
http://localhost:8002/getPayment/1 报错 -
-
使用FallbackFactory这个我们只使用user-provider
- 添加fallbackFactory = UserFallbackFactory.class
@FeignClient(name = "user-provider", fallbackFactory = UserFallbackFactory.class) public interface UserClient { @RequestMapping(value = "/getUser/{id}", method = RequestMethod.GET) public User getUser(@PathVariable("id") long id); }
- UserFallbackFactory
@Component public class UserFallbackFactory implements FallbackFactory<UserClient> { Logger logger = LoggerFactory.getLogger(UserFallbackFactory.class); @Override public UserClient create(Throwable throwable) { logger.info(" User getUser(@PathVariable() long id)" + throwable.getMessage()); return new UserClient() { @Override public User getUser(@PathVariable("id") long id) { User user = new User(); user.setName("falbackFactory"); return user; } }; } }
FallbackFactroy相当于Fallback的增强版,可以在调用fallback方法前作一些处理
Hystrix Dashboard(断路器监控)
介绍
在微服务架构中为例保证程序的可用性,防止程序出错导致网络阻塞,出现了断路器模型。断路器的状况反应了一个程序的可用性和健壮性,它是一个重要指标。Hystrix Dashboard是作为断路器状态的一个组件,提供了数据监控和友好的图形化界面。
实现
- 创建Model
- yml中设置端口
``` server: port: 8030 ```
- 启动类添加注解 @EnableHystrixDashboard
@SpringBootApplication @EnableHystrixDashboard public class HystrixDashboardApplication { public static void main(String[] args) { SpringApplication.run(HystrixDashboardApplication.class, args); } }
按要求输入http://localhost:8002/hystrix.stream等信息
可以监控Hystrix
注意:出来这个图的前提是http://localhost:8002/hystrix.stream能访问的通,如果访问不通需要注意,启动类是否加了@EnableCircuitBreaker,
Hystrix Turbine
- 介绍
Turbine好似Bashboard加强版,Bashboard每次监控一个服务,但是Turbine却能同时监控多个服务, - 具体实现
- 启动类添加注解
@SpringBootApplication @EnableTurbine public class HystrixTurbineApplication { public static void main(String[] args) { SpringApplication.run(HystrixTurbineApplication.class, args); } }
- yml添加相应配置
eureka: client: serviceUrl: defaultZone: http://ralap:hjx969190@localhost:8761/eureka/ instance: prefer-ip-address: true server: port: 8040 turbine: aggregator: clusterConfig: default appConfig: movie-consume-feign-hystrix,movie-consume cluster-name-expression: "'default'" spring: application: name: hystrix-turbine
appConfig:配置监控的applicationName
clusterConfig: applicationName 全部大写,这里使用的default,配合cluster-name-expression使用,
cluster-name-expression:获取集群的名称 - 启动类添加注解