(五)Hystrix微服务容错

1. 背景

在微服务分布式环境下,服务被我们拆分成了许多服务单元,服务之间通过注册和订阅机制相互依赖。系统间的依赖十分的庞大和复杂,一个请求可能会经过多个依赖服务,最后完成调用。
分布式应用中存在错综复杂的相互依赖。

在这里插入图片描述

1.1 微服务面临的问题

当系统中某个服务出现延迟或者不可用时,那么整个用户请求都被阻塞,最终导致该用户功能不可用。依赖的服务越多,那么不可用的风险就越大。

在这里插入图片描述

高请求量情况下,由于网络原因或者是服务自身的不可用,导致出现故障或者延时。这些问题会导致服务调用方对外提供的服务出现延迟,此时调用方的外来请求不断增加,任务不断的积压,资源不断被占用,最后导致服务调用方自身服务瘫痪。

高并发情况下如果其中一个服务不可用,那么整个系统都可能会面临崩溃的风险。对比传统高可用,传统高可用相互独立,互不影响。

在这里插入图片描述

举个例子,在我们的模拟交易系统中,我们会将我们的系统拆分成学生、交易、股票、教学等一系列模块。学生端发出交易请求到交易中心,交易中心又会发请求到股票服务查询一些股票的基本信息。这个时候如果股票服务由于自身或者网络原因出现延迟,那么交易中心的请求就会阻塞等待股票服务的返回。漫长的等待之后,股票服务调用失败,返回信息给交易中心,交易中心又将失败结果返回给学生端。在高并发的情况下,这些阻塞的线程就会导致交易中心资源被大量占用,最终导致交易中心不可用。

在这里插入图片描述

1.2 断路器

在我们的家里,都会安装断路器或者是保险丝保护电路过载。当电流过大时自动跳闸或者熔断,避免设备损坏甚至火灾等严重后果。

那么在我们微服务体系中有没有这么一个“断路器”,当服务不可用时及时切断调用链路,快速响应失败,来保护我们服务的安全呢?

在这里插入图片描述

在这里插入图片描述

2.1 Hystrix简介

对于以上的问题,Spring Clould Hystrix实现了断路器、依赖隔离、监控等一系列功能。Hystrix是由Netflix开源的一个延迟和容错库,用于隔离访问远程系统、服务或者第三方库,防止级联失败,从而提升系统的可用性与容错性。Hystrix/test/advisor主要通过以下几点实现可用性与容错性。

  • 包裹请求:使用HystrixCommand(或HystrixObservableCommand)包裹对依赖的调用逻辑,每个命令在独立线程中执行。这里使用了设计模式中的“命令模式”。
  • 跳闸机制:当某服务的错误率超过一定阈值时,Hystrix可以自动或者手动跳闸,停止请求该服务一段时间。
  • 资源隔离:Hystrix为每个依赖都维护了一个小型的线程池(或者信号量)。如果该线程池已满,发往该依赖的请求就被立即拒绝,而不是排队等候,从而加速失败判定。
  • 监控:Hystrix可以近乎实时地监控运行指标和配置的变化,例如成功、失败、超时和被拒绝的请求等。
  • 回退机制:当请求失败、超时、被拒绝,或当断路器打开时,执行回退逻辑。回退逻辑可由开发人员自行提供,例如返回一个缺省值。

在这里插入图片描述

2.2 如何使用

2.2.1 单独使用

  1. 引入依赖
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
        </dependency>
  1. 编码
@RestController
@RequestMapping("/stock")
public class StockController {
   

	@Autowired
	private StockService stockService;

	@GetMapping()
	public Stock getStock() {
   
		return StockService.getStock();
	}
}


@Service
public class StockService {
   

	@Autowired
	private RestTemplate restTemplate;

	@HystrixCommand(fallbackMethod = "getStockFallback")
	public Stock getStock() {
   
        // 如果调用多次调用失败,直接触发熔断,调用getStockFallback()
		return restTemplate.getForObeject("http://STOCK-SERVICE/stock", Stock.class);
	}

	@GetMapping()
	public Stock getStockFallback() {
   
        // 降级逻辑不依赖网络等其他有风险的渠道
		return new Stock();
	}
}
  1. 详细配置参考spring-cloud-netflix-hystrix-x.x.x.x.jar/MATA-INF/spring-configuration-metadata.json

2.2.2 结合Feign使用

  1. 引入依赖
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
        </dependency>
  1. 编码
@RestController
@RequestMapping("/stock")
public class StockController {
   

	@Autowired
	private RestTemplate restTemplate;

	@GetMapping()
	public Stock getStock() {
   
		return StockService.getStock();
	}
}


@FeignClient(value = "STOCK-SERVICE", fallbackFactory = StockServiceFallback.class)
@RequestMapping("/stock")
public class StockService {
   

    @GetMapping()
	public Stock getStock();

}


@Component
public class StockServiceFallback implements StockService {
   

	public Stock getStock() {
   
		return new Stock();
	}
}
  1. 详细配置参考spring-cloud-openfeign-core-x.x.x.x.jar/MATA-INF/spring-configuration-metadata.json

3 原理分析

3.1 Hystrix在整个体系中的位置

在这里插入图片描述

3.2 集成Feign源码分析

在Feign源码分析中我们知道了,@EnableFeignClients注解中导入了FeignClientsRegistrar.classregisterBeanDefinitions()为入口函数,我们从registerBeanDefinitions()函数开始分析。

class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {
   
	// 扫描到所有@FeignClient注解
	public void registerFeignClients(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
   
		
        // 省略...
        
		for (String basePackage : basePackages) {
   
			Set<BeanDefinition> candidateComponents = scanner.findCandidateComponents(basePackage);
			for (BeanDefinition candidateComponent : candidateComponents) {
   
				if (candidateComponent instanceof AnnotatedBeanDefinition) {
   
                    
					// 省略...
                    
                    Map<String, Object> attributes = annotationMetadata.getAnnotationAttributes(FeignClient.class.getCanonicalName());
					registerFeignClient(registry, annotationMetadata, attributes);
				}
			}
		}
	}

	// 解析@FeignClient,生产FeignClient工厂
	private void registerFeignClient(BeanDefinitionRegistry registry, AnnotationMetadata annotationMetadata, Map<String, Object> attributes) {
   
		// 省略...
        // 定义FeignClientFactoryBean
		BeanDefinitionBuilder definition = BeanDefinitionBuilder.genericBeanDefinition(FeignClientFactoryBean.class);
		// 解析@FeignClient,读取将其中的属性配置到FeignClientFactoryBean
		definition.addPropertyValue("url", getUrl(attributes));
		definition.addPropertyValue("path", getPath(attributes));
		
        // 省略...

		// 注册
		BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
	}
}

FeignClientsRegistrar中我们注册了FeignClientFactoryBean,在分析这个类之前我们先看一下配置类。

@Configuration
public class FeignClientsConfiguration {
   
	@Configuration
	@ConditionalOnClass({
    HystrixCommand.class, HystrixFeign.class })
	protected static class HystrixFeignConfiguration {
   
		@Bean
		@Scope("prototype")
		@ConditionalOnMissingBean
		@ConditionalOnProperty(name = "feign.hystrix.enabled")
		public Feign.Builder feignHystrixBuilder() {
   
			return HystrixFeign.builder();
		}
	}
}

@Configuration
@ConditionalOnClass(Feign.class)
@EnableConfigurationProperties({
   FeignClientProperties.class, FeignHttpClientProperties.class})
public class FeignAutoConfiguration {
   
	@Configuration
	@ConditionalOnClass(name = "feign.hystrix.HystrixFeign")
	protected static class HystrixFeignTargeterConfiguration {
   
		@Bean
		@ConditionalOnMissingBean
		public Targeter feignTargeter() {
   
			return new HystrixTargeter();
		}
	}

	@Configuration
	@ConditionalOnMissingClass("feign.hystrix.HystrixFeign")
	protected static class DefaultFeignTargeterConfiguration {
   
		@Bean
		@ConditionalOnMissingBean
		public Targeter feignTargeter() {
   
			return new DefaultTargeter();
		}
	}
}

看完配置之后我们分析一下FeignClientFactoryBean这个类。

FeignClientFactoryBean实现了FactoryBean接口。实现了FactoryBean接口代表他是一个工厂类,最终会通过getObject()方法返回真正的类。关于FactoryBean的知识大家可以自行去扩展学习。

class FeignClientFactoryBean implements FactoryBean<Object>, InitializingBean, ApplicationContextAware {
   
    @Override
	
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值