1.Hystrix说明
1.1 服务雪崩效应:是一种因 服务提供者 的不可用导致服务调用者 的不可用,并将不可用 逐渐放大 的过程。
如: A为服务提供者,B为A的服务调用者,C和D是B的服务调用者。当A的不可用,引起B的不可用,并将不可用逐渐放大C和D时,服务雪崩就形成了。
1.2 雪崩原因:
(1)服务提供者不可用
a.硬件故障:
硬件损坏造成的服务器主机宕机、网络硬件故障造成的服务提供者的不可访问。
b.程序Bug
c.缓存击穿:缓存应用重启,所有缓存被清空时,以及短时间内大量缓存失效时.。大量的缓存不命中, 使请求直击后端,造成服务提供者超负荷运行,引起服务不可用。
d.用户大量请求:在秒杀和大促开始前,如果准备不充分,用户发起大量请求造成服务提供者的不可用。
(2) 重试加大流量
a.用户重试:用户由于忍受不了界面上长时间的等待,而不断刷新页面甚至提交表单
b.代码逻辑重试:服务调用端的会存在大量服务异常后的重试逻辑
(3) 服务调用者不可用
a.同步等待造成的资源耗尽:使用 同步调用 时,会产生大量的等待线程占用系统资源。 一旦线程资源被耗尽,服务调用者提供的服务也将处于不可用状态,造成服务雪崩效应产生。
1.3 雪崩应对策略:
(1) 流量控制
a.网关限流
因为Nginx的高性能,,目前一线互联网公司大量采用Nginx+Lua的网关进行流量控制, 由此而来的OpenResty也越来越热门。
b.用户交互限流:采用加载动画,提高用户的忍耐等待时间、提交按钮添加强制等待时间机制。
c.关闭重试。
(2) 改进缓存模式
缓存预加载、同步改为异步刷新。
(3) 服务自动扩容
AWS的auto scaling。
(4) 服务调用者降级服务
a.资源隔离:主要是对调用服务的线程池进行隔离。
b.对依赖服务进行分类。
依赖服务分为:强依赖和若依赖,强依赖服务不可用会导致当前业务中止,而弱依赖服务的不可用不会导致当前业务的中止。
c.不可用服务的调用快速失败。
在微服务架构中,根据业务来拆分成一个个的服务,服务与服务之间可以相互调用(RPC),在Spring Cloud可以用RestTemplate+Ribbon和Feign来调用。为了保证其高可用,单个服务通常会集群部署。由于网络原因或者自身的原因,服务并不能保证100%可用,如果单个服务出现问题,调用这个服务就会出现线程阻塞,此时若有大量的请求涌入,Servlet容器的线程资源会被消耗完毕,导致服务瘫痪。服务与服务之间的依赖性,故障会传播,会对整个微服务系统造成灾难性的严重后果,这就是服务故障的“雪崩”效应。
为了解决这个问题,断路器模型便产生了。
图片引自spring官网。
2.Hystrix实例
前面说了一堆的熔断的理论,但是Hystrix该如何用呢?
2.1 准备
准备3个工程
springcloud-eureka-server
springcloud-eureka-client1
springcloud-eureka-client2
其中前面三个工程是前面几篇的介绍过的:
springcloud-eureka-server是服务注册中心,springcloud-eureka-client1和springcloud-eureka-client2是服务提供者,提供服务名为service-sayHello,服务mapping为sayHello。且服务提供者都将服务注册到注册中心上。
2.2 新建负载均衡器+熔断器
新建方法可参考前面的springcloud文章有详细的步骤和代码展示。
工程名:springcloud-eureka-hystrix
GroupId:com.tangjinyi
ArtifactId:springcloud-eureka-hystrix
2.3 pom.xml
<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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0modelVersion>
<groupId>com.tangjinyigroupId>
<artifactId>springcloud-eureka-hystrixartifactId>
<version>0.0.1-SNAPSHOTversion>
<packaging>jarpackaging>
<name>springcloud-eureka-hystrixname>
<description>Demo project for Spring Bootdescription>
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.0.1.RELEASEversion>
<relativePath/>
parent>
<properties>
<project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8project.reporting.outputEncoding>
<java.version>1.8java.version>
<spring-cloud.version>Finchley.M9spring-cloud.version>
properties>
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-serverartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-hystrixartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-ribbonartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-webartifactId>
<version>5.0.5.RELEASEversion>
dependency>
dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-dependenciesartifactId>
<version>${spring-cloud.version}version>
<type>pomtype>
<scope>importscope>
dependency>
dependencies>
dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
plugin>
plugins>
build>
<repositories>
<repository>
<id>spring-milestonesid>
<name>Spring Milestonesname>
<url>https://repo.spring.io/milestoneurl>
<snapshots>
<enabled>falseenabled>
snapshots>
repository>
repositories>
project>
使用ribbon+restTemplate作为负载均衡器,所以需要引入ribbon的依赖。同时引入hystrix依赖
2.4 application.properties
#将服务注册到eureka
eureka.client.service-url.defaultZone=http://localhost:8761/eureka/
#hystrix服务端口号
server.port=8766
#服务名
spring.application.name=service-hystrix
2.5 启动类
SpringcloudEurekaHystrixApplication
添加@EnableHystrix
注解,表示启动Hystrix服务。
package com.tangjinyi;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.netflix.hystrix.EnableHystrix;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
/** * 1.启动eureka服务 * 2.启动client1和client2服务,将两个服务注册到eureka服务器上。服务名为service-sayHello,具体请求为http://service-sayHello:8761/sayHello * 3.启动Hystrix微服务,将Hystrix服务注册到eureka * 4.通过Hystrix请求client1和client2服务(通过restTemplate请求service-sayHello服务)http://localhost:8766/syaHello * 5.依次关闭client1和client2两个服务 * 6.当client1和client2都停止后,再次请求,会跳转到熔断方法 * * 具体需要添加一些依赖,都在pom文件中 */
@SpringBootApplication
@EnableDiscoveryClient
@EnableHystrix
public class SpringcloudEurekaHystrixApplication {
public static void main(String[] args) {
SpringApplication.run(SpringcloudEurekaHystrixApplication.class, args);
}
@Bean
@LoadBalanced
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
2.6 Hystrix提供访问流程类
UserController
访问方法@HystrixCommand(fallbackMethod = "errorFallback")
注解,熔断后执行errorFallback方法。
package com.tangjinyi.controller;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.tangjinyi.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class UserController {
@Autowired
public UserService userService;
@HystrixCommand(fallbackMethod = "errorFallback")
@RequestMapping("/sayHello")
public String sayHello(@RequestParam String name){
return userService.sayHello(name);
}
public String errorFallback(String id) {
return "请求出现错误,将请求熔断成功";
}
}
UserService
package com.tangjinyi.service;
import com.tangjinyi.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class UserService {
@Autowired
public UserRepository userRepository;
public String sayHello(String name){
return userRepository.sayHello(name);
}
}
UserRepository
package com.tangjinyi.repository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import org.springframework.web.client.RestTemplate;
/** * 通过请求发现,凡是注册到eureka上面的服务,请求时的端口都是8761,也就是说请求的时候都使用的是eureka服务的端口,即(提供者的服务名+eureka服务的端口) */
@Repository
public class UserRepository {
@Autowired
public RestTemplate restTemplate;
public String sayHello(String name){
return restTemplate.getForObject("http://service-sayHello:8761/sayHello?name="+name,String.class);
}
}
最终的工程结构如下:
2.7 启动4个工程
分别启动springcloud-eureka-server,springcloud-eureka-client1,springcloud-eureka-client2,springcloud-eureka-hystrix工程:
2.8 访问服务:
打开浏览器,访问http://localhost:8766/sayHello?name=10
刷新浏览器,端口会在8762和8763之间轮询切换。
3 Hystrix熔断测试
关闭一个服务8763,刷新浏览器端口一直停留在8762。
再关闭一个服务8762,刷新浏览器。
访问已经跳转到熔断方法中。
在实际的应用中,可以跳转到其他处理的服务。