微服务架构➖SpringCloud➖Hystrix
关于作者
- 作者介绍
🍓 博客主页:作者主页
🍓 简介:JAVA领域优质创作者🥇、一名在校大三学生🎓、在校期间参加各种省赛、国赛,斩获一系列荣誉 🏆、阿里云专家博主 、51CTO专家博主
🍓 关注我:关注我学习资料、文档下载统统都有,每日定时更新文章,励志做一名JAVA资深程序猿👨💻
1. 前言
什么是服务雪崩?
- 当用户访问A的一个接口
- A的tomcat会给用户分配一个线程,支持用户访问
- A发现要完成用户的操作,需要访问B服务
- A去访问B
- B的会给A分配一个线程,支持A的访问
- B发现需要完成A的操作,必须访问C服务,但是C挂了
- B在访问C之前不知道C挂了,B去访问,直到超时,才知道C无法访问
结果: 因为C不可用,导致B的线程不能及时回收,从而导致A的线程也无法及时回收,导致整个服务链里面的线程池没有线程可用了。此时再有用户访问A,那么tomcat直接报503 ,服务不可用
服务雪崩的本质:线程没有及时回收。 不管是调用成功还是失败,只要线程可以及时回收,就可以解决服务雪崩
服务雪崩怎么解决?
- 修改调用的超时时长(不推荐)
将服务间的调用超时时长改小,这样就可以让线程及时回收,保证服务可用
优点:非常简单,也可以有效的解决服务雪崩
缺点:不够灵活,有的服务需要更长的时间去处理(写库,整理数据)
- 设置拦截器
简介
熔断器,也叫断路器!(正常情况下 断路器是关的 只有出了问题才打开)用来保护微服务不 雪崩的方法。思想和我们上面画的拦截器一样。
Hystrix 是 Netflix 公司开源的一个项目,它提供了熔断器功能,能够阻止分布式系统中出现联动故障。Hystrix 是通过隔离服务的访问点阻止联动故障的,并提供了故障的解决方案,从 而提高了整个分布式系统的弹性。微博 弹性云扩容 Docker K8。
快速入门
当有服务调用的时候,才会出现服务雪崩,所以 Hystrix 常和 OpenFeign,Ribbon 一起出现。
3.1 创建父项目 feign
3.2 创建子 module
- project-domain 公共实体类
- common-api 消费者接口
- user-center 消费者
- order-center 提供者
新建子moudle模块一定要选择 以 04-feign-project
为父项目。
pom文件的依赖关系
3.3 父项目 pom.xml
xml
<?xml version="1.0" encoding="UTF-8"?>
<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.0</modelVersion>
<packaging>pom</packaging>
<modules>
<module>project-domain</module>
<module>common-api</module>
<module>user-center</module>
<module>order-center</module>
</modules>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.12.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<!-- <packaging>pom</packaging>-->
<groupId>org.example</groupId>
<artifactId>04-feign-project</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<spring-boot.version>2.3.12.RELEASE</spring-boot.version>
<spring-cloud.version>Hoxton.SR12</spring-cloud.version>
</properties>
<!-- 这里所以的依赖 子模块都会有-->
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
<!--加载这里的依赖不会被直接引入该项目 只做了一个版本号的控制-->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<!-- 打包 仓库 等配置-->
<!-- <build>-->
<!-- </build>-->
</project>
3.4 子模块project-domain
Order.java
java
package com.zmz.domain;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @ProjectName: 04-hystrix
* @Package: com.zmz.domain
* @ClassName: Order
* @Author: 张晟睿
* @Date: 2022/10/8 18:57
* @Version: 1.0
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class Order {
private Integer orderId;
private String name;
private Double price;
}
pom.xml
xml
<?xml version="1.0" encoding="UTF-8"?>
<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">
<parent>
<artifactId>04-feign-project</artifactId>
<groupId>org.example</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>project-domain</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
</project>
3.5 子模块common-api
UserOrderFeign.java
java
package com.zmz.feign;
import com.zmz.domain.Order;
import com.zmz.feign.hystrix.UserOrderFeignHystrix;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
/**
* @ProjectName: 04-hystrix
* @Package: com.zmz.feign
* @ClassName: UserOrderFeign
* @Author: 张晟睿
* @Date: 2022/10/8 19:04
* @Version: 1.0
*/
@FeignClient(value="order-service", fallback = UserOrderFeignHystrix.class)
public interface UserOrderFeign {
@GetMapping("/order/getOrderById")
Order getOrderById(@RequestParam Integer userId);
}
UserOrderFeignHystrix.java
java
package com.zmz.feign.hystrix;
import com.zmz.domain.Order;
import com.zmz.feign.UserOrderFeign;
import org.springframework.stereotype.Component;
/**
* @ProjectName: 04-hystrix
* @Package: com.zmz.feign.hystrix
* @ClassName: UserOrderFeignHystrix
* @Author: 张晟睿
* @Date: 2022/10/9 9:09
* @Version: 1.0
*/
@Component
public class UserOrderFeignHystrix implements UserOrderFeign {
@Override
public Order getOrderById(Integer userId) {
return null;
}
}
pom.xml
xml
<?xml version="1.0" encoding="UTF-8"?>
<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">
<parent>
<artifactId>04-feign-project</artifactId>
<groupId>org.example</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>common-api</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.example</groupId>
<artifactId>project-domain</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
</dependencies>
</project>
3.6 子模块order-center
OrderController.java
java
package com.zmz.controller;
import com.zmz.domain.Order;
import com.zmz.feign.UserOrderFeign;
import org.springframework.web.bind.annotation.RestController;
/**
* @ProjectName: 04-hystrix
* @Package: com.zmz.controller
* @ClassName: Order
* @Author: 张晟睿
* @Date: 2022/10/8 19:03
* @Version: 1.0
*/
@RestController
public class OrderController
implements UserOrderFeign {
@Override
public Order getOrderById(Integer userId) {
Order order = Order.builder()
.name("西红柿鸡蛋面")
.orderId(0001)
.price(100D).build();
System.out.println(order);
return order;
}
}
OrderServiceApp.java 主启动类
java
package com.zmz;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
/**
* @ProjectName: 04-hystrix
* @Package: com.zmz
* @ClassName: OrderServiceApp
* @Author: 张晟睿
* @Date: 2022/10/8 19:02
* @Version: 1.0
*/
@SpringBootApplication
@EnableEurekaClient
public class OrderServiceApp {
public static void main(String[] args) {
SpringApplication.run(OrderServiceApp.class, args);
}
}
application.yml
yml
server:
port: 8080
spring:
application:
name: order-service
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka
instance:
hostname: localhost
instance-id: ${eureka.instance.hostname}:${spring.application.name}:${server.port}
pom.xml
xml
<?xml version="1.0" encoding="UTF-8"?>
<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">
<parent>
<artifactId>04-feign-project</artifactId>
<groupId>org.example</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>order-center</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.example</groupId>
<artifactId>common-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.6.0</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.3.12.RELEASE</version>
<configuration>
<mainClass>com.zmz.RentCarServiceApplication</mainClass>
</configuration>
<executions>
<execution>
<id>repackage</id>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
3.7 子模块user-center
UserController.java
java
package com.zmz.controller;
import com.zmz.domain.Order;
import com.zmz.feign.UserOrderFeign;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @ProjectName: 04-hystrix
* @Package: com.zmz.controller
* @ClassName: UserController
* @Author: 张晟睿
* @Date: 2022/10/8 19:22
* @Version: 1.0
*/
@RestController
public class UserController {
@Autowired
public UserOrderFeign userOrderFeign;
@GetMapping("find")
public Order findOrder() {
return userOrderFeign.getOrderById(11);
}
}
UserServiceApp.java 主启动类
java
package com.zmz;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
/**
* @ProjectName: 04-hystrix
* @Package: com.zmz
* @ClassName: UserServiceApp
* @Author: 张晟睿
* @Date: 2022/10/8 19:16
* @Version: 1.0
*/
@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients
public class UserServiceApp {
public static void main(String[] args) {
SpringApplication.run(UserServiceApp.class, args);
}
}
application.yml
yml
server:
port: 8081
spring:
application:
name: user-service
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka
instance:
hostname: localhost
instance-id: ${eureka.instance.hostname}:${spring.application.name}:${server.port}
feign:
hystrix:
enabled: true #开启熔断
pom.xml
xml
<?xml version="1.0" encoding="UTF-8"?>
<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">
<parent>
<artifactId>04-feign-project</artifactId>
<groupId>org.example</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>user-center</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.example</groupId>
<artifactId>common-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
</dependencies>
</project>
小结
Hystrix是Spring Cloud生态系统中的一款强大的容错工具,用于处理分布式系统中的故障和延迟。
- 容错机制:Hystrix提供了诸如断路器、超时控制、线程池隔离等容错机制,以保护系统免受故障的影响。通过配置这些机制,我们可以定义对于不同的服务调用,如果出现故障或超时,应该采取的容错行为。
- 断路器模式:Hystrix的核心概念之一是断路器模式。通过断路器的状态监控和切换,Hystrix能够在服务调用故障超过一定阈值时,迅速切换为执行备选方案,从而避免连锁故障并提高系统的可用性。
- 隔离策略:Hystrix通过线程池隔离和信号量隔离两种策略,实现了对不同服务调用的隔离。线程池隔离将每个服务调用放入独立的线程池中执行,从而避免了某个服务调用的故障影响其他服务。信号量隔离则通过限制同时执行的请求数量来保护系统。
- 监控和度量:Hystrix提供了丰富的监控和度量功能,可以实时监控服务调用的指标、熔断器状态以及执行结果。通过集成Hystrix Dashboard和Turbine等工具,我们可以实现对服务的可视化监控和报警。
- 异常处理和回退:使用Hystrix时,我们可以定义回退逻辑来处理服务调用的异常情况。当服务调用失败或超时时,Hystrix将执行预定义的回退逻辑,返回一个默认值或执行备选方案,保证系统的稳定性和可用性。