分布式系统中,服务与服务之间的依赖错综复杂,不可避免,其中一些服务会出现故障 ,导致依赖他们的其他服务出现远程调度的线程问题(雪崩效应)。而Hystrix提供的熔断器,通过隔离服务的访问点,能阻止这种分布式系统中出现的联动故障,并提供故障的解决方案,从而提高了整个分布式系统的弹性。
什么是Hystrix
Hystrix是Netflix公司的开源项目,它提供熔断器功能,能够阻止分布式系统中出现联动故障。Hystrix是通过隔离服务的访问点阻止联动故障的,并提供故障的解决方案,从而提高整个分布式系统的弹性
Hystrix设计原则
工作机制
RestTemplate+Ribbon上使用熔断器
小demo(注:代码接微服务之Sring Cloud使用Feign实现负载均衡&Feign简介一期博客:
基本步骤:
细节实现:
1. 创建名为microService-provider-hystrix-8002的Maven项目
2. 配置pom.xml文件
cn.zf
microService-api
1.0-SNAPSHOT
org.springframework.cloud
spring-cloud-starter-netflix-eureka-client
org.springframework.boot
spring-boot-starter-web
org.mybatis.spring.boot
mybatis-spring-boot-starter
org.springframework.boot
spring-boot-starter-test
junit
junit
mysql
mysql-connector-java
com.alibaba
druid
org.springframework.cloud
spring-cloud-starter-netflix-hystrix
3. 在src/main/resources下创建application.yml配置文件
server:
port: 8002 # 设置端口号
mybatis:
cn.zf.springcloud.config-location: classpath:mybatis/mybatis.cfg.xml # mybatis配置文件所在路径
type-aliases-package: cn.zf.springcloud.entities # 所有Entity别名类所在包
mapper-locations: classpath:mybatis/mapper/**/*.xml # mapper映射文件
spring:
application:
name: microservice-product #微服务整合的命名,对外暴露的名字,非常重要
datasource:
type: com.alibaba.druid.pool.DruidDataSource # 当前数据源操作类型
driver-class-name: com.mysql.cj.jdbc.Driver # mysql驱动包
# springcloud_db02数据库名
url: jdbc:mysql://127.0.0.1:3306/springcloud_db02?serverTimezone=GMT%2B8
username: root # 数据库用户名
password: 123456 # 数据库密码
dbcp2:
min-idle: 5 # 数据库连接池的最小维持连接数
initial-size: 5 # 初始化连接数
max-total: 5 # 最大连接数
max-wait-millis: 150 # 等待连接获取的最大超时时间
eureka:
client: # 客户端注册进eureka服务列表内
register-with-eureka: true
fetch-registry: true
serviceUrl: # 注册到哪个服务中心
defaultZone: http://eureka6001.com:6001/eureka,http://eureka6002.com:6002/eureka
instance:
instanceId: ${spring.application.name}:${server.port}-hystrix # 在注册中心隐藏主机名
prefer-ip-address: true # 访问路径可以显示ip地址
4. 在src/main/resources下创建mybatis目录并引入mybatis.cfg.xml配置文件
5. 在src/main/resources/mybatis下创建mapper目录并引入productMapper.xml配置文件
INSERT INTO product(product_name,db_source) VALUES (#{productName},DATABASE())
6. 创建mapper接口
package cn.zf.springcloud.mapper;
import cn.zf.springcloud.entities.Product;
import java.util.List;
/*
dao持久层接口
*/
public interface ProductMapper {
Product findById(Long id);
List findAll();
boolean addProduct(Product product);
}
7. 创建service服务层,接口、实现类
package cn.zf.springcloud.service;
import cn.zf.springcloud.entities.Product;
import java.util.List;
/*
service业务层接口
*/
public interface ProductService {
boolean add(Product product);
Product get(Long id);
List list();
}
package cn.zf.springcloud.service.impl;
import cn.zf.springcloud.entities.Product;
import cn.zf.springcloud.mapper.ProductMapper;
import cn.zf.springcloud.service.ProductService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
/*
service业务层实现类
*/
@Service
public class ProductServiceImpl implements ProductService {
//注入dao层对象,调用dao层接口
@Autowired
private ProductMapper productMapper;
@Override
public boolean add(Product product) {
return productMapper.addProduct(product);
}
@Override
public Product get(Long id) {
return productMapper.findById(id);
}
@Override
public List list() {
return productMapper.findAll();
}
}
8. 创建controller控制层
package cn.zf.springcloud.controller;
import cn.zf.springcloud.entities.Product;
import cn.zf.springcloud.service.ProductService;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
//@RestController响应数据转json
@RestController
public class ProductController {
//注入service层的对象,调用service层的方法
@Autowired
private ProductService productService;
@RequestMapping(value = "/product/add",method = RequestMethod.POST)
//@RequestBody将传过来的json数据转java中的类型数据
public boolean add(@RequestBody Product product){
return productService.add(product);
}
//当get方法出现异常时 则会调用hystrixGet方法进行处理
@HystrixCommand(fallbackMethod = "getFallback")
@RequestMapping(value = "/product/get/{id}",method = RequestMethod.GET)
//@PathVariable获取路径中的参数
public Product get(@PathVariable("id") Long id){
Product product = productService.get(id);
if (product==null){
throw new RuntimeException("id"+id+"无效");
}
System.out.println(product);
return product;
}
//当出现异常是调用getFallback
public Product getFallback(@PathVariable("id")Long id){
return new Product(id,"id"+id+"无效--@HystrixCommand","无效的数据");
}
@RequestMapping(value = "/product/list",method = RequestMethod.GET)
public List list(){
return productService.list();
}
}
9. 创建ProductProvider_8002_hystrix主启动类
package cn.zf.springcloud;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.hystrix.EnableHystrix;
@EnableHystrix // 开启Hystrix 熔断机制的支持
@EnableEurekaClient
//@MapperScan指定要扫描的Mapper类的包的路径
@MapperScan("cn.zf.springcloud.mapper")
@SpringBootApplication
public class ProductProvider_8002_hystrix {
public static void main(String[] args) {
SpringApplication.run(ProductProvider_8002_hystrix.class,args);
}
}
10. 测试,先启动两个eureka注册中心的主启动类(两个注册中心顺序无所谓),再启动服务提供者ProductProvider_8002_hystrix主启动类,最后启动服务消费者ProductConsumer_80主启动类
Feign上使用熔断器
1. 创建名为microService-consumer-feign的Maven项目
2. 配置pom.xml文件
cn.zf
microService-api
1.0-SNAPSHOT
org.springframework.boot
spring-boot-starter-web
org.springframework.cloud
spring-cloud-starter-netflix-eureka-client
org.springframework.cloud
spring-cloud-starter-openfeign
3. 在src/main/resources下创建application.yml配置文件
server:
port: 80
eureka:
client:
register-with-eureka: false
fetch-registry: true #发现服务中 获得注册信息
serviceUrl:
defaultZone: http://eureka6001.com:6001/eureka,http://eureka6002.com:6002/eureka
feign:
hystrix:
enabled: true #在feign上使用hystrix
4. 在src/main/java下创建自定义Rest相关配置类ConfigBean
package cn.zf.springcloud.config;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
/**
* Rest相关配置类访问RestFull的方法(url,requestMap,ResponseBean.class)
*/
@Configuration
public class ConfigBean {
// 向容器中添加RestTemplate组件 直接通过该组件可以调用REST接口
@LoadBalanced
@Bean
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
}
5. 在src/main/java下创建ProductClientService接口、ProductClientServiceFallBack实现类
package cn.zf.springcloud.service;
import cn.zf.springcloud.entities.Product;
import cn.zf.springcloud.service.impl.ProductClientServiceFallBack;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import java.util.List;
@FeignClient(value = "MICROSERVICE-PRODUCT",fallback = ProductClientServiceFallBack.class)
public interface ProductClientService {
@RequestMapping(value = "/product/get/{id}",method = RequestMethod.GET)
//使用熔断器,加@PathVariable("id")
Product get(@PathVariable("id") Long id);
@RequestMapping(value = "/product/list",method = RequestMethod.GET)
List list();
@RequestMapping(value = "/product/add",method = RequestMethod.POST)
boolean add(Product product);
}
package cn.zf.springcloud.service.impl;
import cn.zf.springcloud.entities.Product;
import cn.zf.springcloud.service.ProductClientService;
import org.springframework.stereotype.Component;
import java.util.List;
@Component
public class ProductClientServiceFallBack implements ProductClientService {
@Override
public Product get(Long id) {
return new Product(id,"id="+id+"无数据--fegin&hystrix","无有效数据库");
}
@Override
public List list() {
return null;
}
@Override
public boolean add(Product product) {
return false;
}
}
6. 在src/main/java下创建ProductController_Fegin
package cn.zf.springcloud.controller;
import cn.zf.springcloud.entities.Product;
import cn.zf.springcloud.service.ProductClientService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
public class ProductorController_Feign {
@Autowired
private ProductClientService service;
@RequestMapping(value = "/consumer/product/add")
public boolean add(Product product){
return service.add(product);
}
@RequestMapping(value = "/consumer/product/get/{id}")
public Product get(@PathVariable("id") Long id){
Product product = service.get(id);
if (product==null){
throw new RuntimeException("id"+id+"无效数据");
}
return product;
}
@RequestMapping(value = "/consumer/product/list")
public List list(){
return service.list();
}
}
7. 创建ProductConsumer_80_Feign主启动类
package cn.zf.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
@EnableFeignClients(basePackages = {"cn.zf.springcloud"})
@EnableEurekaClient // 向注册中心进行注册
@SpringBootApplication
public class ProductConsumer_80_Feign {
public static void main(String[] args) {
SpringApplication.run(ProductConsumer_80_Feign.class,args);
}
}
8. 测试,先启动两个eureka注册中心的主启动类(两个注册中心顺序无所谓),再启动基于熔断器的服务提供者的主启动类,最后启动ProductConsumer_80_Feign服务消费者的主启动类
over~