微服务-服务容错:熔断、降级、隔离、限流

文章目录

SpringCloud请看:https://blog.csdn.net/qq_52681418/article/details/113247805

微服务-服务容错

再次之前已经探讨了服务注册、服务调用,它们只是解决了整个应用系统的各个服务的通信问题。

在微服务架构中,所需要应对的大型应用场景,因此需要使服务在大访问量下依然”坚挺“,因此需要进行服务容错的处理,防止过大的访问量导致服务瘫痪。

基本概念:服务降级、服务限流、服务隔离、服务熔断

  • 服务熔断:牺牲局部,保全全局,服务出问题时,切断该服务与系统的联系。
  • 服务降级:服务不可用时(如熔断后),提供一个低级服务返回信息。
  • 服务隔离:使服务之间相互隔离,防止出现雪崩效应
  • 服务限流:使某服务一段时间内只接收指定数量的请求,通过上面的三种手段,即可达到服务的限流。

雪崩效应:服务多级调用时,一个服务拥堵,从而导致多级服务都拥堵。

Hystrix

18年足够稳定,已不再更新。

1.服务熔断

Hystrix内部已经实现了熔断,并提供了默认的熔断配置。我们只需要修改配置即可。

超时设置:默认超过1s没获取到数据,就发生熔断。

hystrix:
	command:
		default:
 			execution:
  				isolation:
   					thread:
    					timeoutInMilliseconds: 2000 #修改为2s后熔断

服务熔断后,往要为该服务配置一个降级服务。

hystrix熔断器

断路器:即被@HystrixCommand注解的方法。

熔断器有3种状态:(默认关闭)

  • OPEN:打开:所有请求都进入降级方法。
  • CLOSED:关闭:可访问全部请求,统计失败次数,超过给定时间时半开,超过指定时间内失败次数上限时打开。
  • HALF_OPEN:半开:打开5s后,进入半开(尝试释放一个请求到服务,访问成功时关闭,否则保持打开)

原理:熔断器默认关闭,但统计对服务请求失败的次数,并设置一个阀值(假设10s内不能失败100次),如果达到这个阀值,熔断器就会进入打开状态,将所有对服务的请求都进行降级操作。但服务不能一直被降级,熔断器每隔5s会尝试释放一个请求到服务,此时为半开状态,如果这个请求访问成功,则关闭断路器。

测试熔断器:修改默认阀值。

#超时多久后熔断
hystrix:
 command:
   default:
     execution:
       isolation:
         thread:
           timeoutInMilliseconds: 2000 #修改为2s后熔断
     circuitBreaker:
       requestVolumeThreshold: 20 #触发熔断的最小请求次数,默认20次/10s
       sleepWindowInMilliseconds: 10000 #熔断几ms后请求尝试,默认5s
       errorThresholdPercentage: 50 #触发熔断的失败请求最小占比,默认50%

查看结果需要打开Hystrix实时监控平台并访问:http://localhost:8080/actuator/hystrix.stream
实时监控平台是什么,请参考第4小节。

2.服务降级

服务降级即服务不可用(被熔断)时,将一个请求的处理方法改为一个低级的处理方法,这样做可以在服务不可用时,提供一个低级服务返回信息。Hystrix可以对失败、拒绝、超时的请求进行统一降级处理。

服务降级需要为调用者服务添加依赖:

<!--引入hystrix依赖-->
<dependency>
	<groupId>org.springframework.cloud</groupId>
	<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<!-- 含@HystrixCommand-->
<dependency>
   <groupId>com.netflix.hystrix</groupId>
   <artifactId>hystrix-javanica</artifactId>
   <version>1.5.12</version>
</dependency>

启动类启动 @EnableCircuitBreaker 注解

@SpringBootApplication
@EnableFeignClients //激活Fegin (这里通过feign服务调用)
@EnableCircuitBreaker//激活Hystrix
public class MainApplication { 

   public static void main(String[] args) { 
       SpringApplication.run(MainApplication.class,args);
   }
   
}

进行服务降级,需要写降级方法,在服务不可用时去调用它,配置方式有2种:

  • 单接口降级:为每个接口单独设置降级方法(优先级较高)。
  • 统一降级:为整个类的所有接口设置一个降级方法。(防止写一大堆降级方法)

单接口降级:使用降级方法保护一个接口, @HystrixCommand注解

@Autowired
private ImgFeignClient ifc;

//配置熔断保护,fallbackMethod:指定熔断后的降级方法
@HystrixCommand(fallbackMethod = "himg")
@RequestMapping("/img4/{id}")
public Img fignimg(@PathVariable long id){ 
   Img img=ifc.findid(id);
   return img;
}

//降级方法,和需要受到保护的方法参数、返回值一致
public Img himg(long id){ 
   Img img=new Img();
   img.setName("触发降级方法");
   return img;
}

这种方法的好处是可以精准地为某接口降级,但接口往往很多,逐一保护,需要写大量降级方法。

统一降级:使用降级方法保护整个类的全部接口, @DefaultProperties注解

@RestController
@RequestMapping("/main")
@DefaultProperties(defaultFallback = "tyimg")//指定公共的熔断降级方法
public class MainController { 

   @Autowired
   private ImgFeignClient ifc;	

   //指定统一降级方法,不允许有参数
   public Img tyimg(){ 
       Img img=new Img();
       img.setName("触发统一降级方法");
       return img;
   }
   
   @HystrixCommand//统一指定时使用
   @RequestMapping("/img/{id}")
   public Img fignimg(@PathVariable long id){ 
       Img img=ifc.findid(id);
       return img;
   }
   ......
}

到此服务降级的2种方法已经说完了,不过对于fegin,服务降级可以写在接口实现类种。

Feign的调用方式为在调用者添加接口,对接口使用注解来实现服务调用,然后在控制层调用接口。这和一般的调用相比多了一层接口,因此在feign里,可以对调用的服务接口设置降级。

Feign已经集成了Hystrix,因此无需再导入依赖,只需要添加配置:

feign:
	hystrix: #开启对hystrix的支持
		enabled: true

实现要保护的接口,并将实现类注册到容器:假定调用服务创建的接口为ImgFeignClient 。

@Component
public class ImgFeignClientCallBack implements ImgFeignClient { 
  
   @Override // 熔断降级的方法
   public Img findid(long id) { 
       Img img=new Img();
       img.setName("触发降级方法");
       return img;
   }
}

调用服务的接口上添加注解,指定用来降级的实现类(因为此接口实现不止一个):

// fallback:指定服务降级方法,即实现类
@FeignClient(name="img-service",fallback= ImgFeignClientCallBack.class)
public interface ImgFeignClient { 
   
    // 配置需要调用的微服务接口
   @RequestMapping(value = "/img/findimg/{id}",method = RequestMethod.GET)
   public Img findid( @PathVariable long id);
   
}

为什么说此接口实现类不止一个呢,通过接口加注解来调用服务,不就相当于内置提供了一个实现吗。

3.服务隔离

服务隔离通常有2种方式:

  • 信号量隔离
  • 线程池隔离

信号量隔离

使用一个原子计数器记录当前运行的线程数,如果超过指定数量,丢弃请求。此方式严格控制线程且立即返回,无法应对突发流量。

信号量隔离配置:

hystrix:
 command:
   default:
     execution:
       isolation:
         strategy:
           ExecutionIsolationStrategy: SEMAPHORE #信号量隔离,线程池隔离为THREAD
           maxConcurrentRequests: 20 #最大信号量上限

线程池隔离

Tomcat以线程池方式处理请求,当某一服务堵塞,并且压力过大时,可能会造成整个系统的崩溃。
为了不影响其他接口的正常使用,需要对每个服务进行隔离:线程池隔离、信号量隔离。

使用一个线程池存储并处理当前请求,设置任务返回超时时间,堆积的请求堆积入线程池队列。此方式要为所有需要的服务请求线程池(资源消耗略高),可以应对突发流量。

实现服务隔离需要如下配置:

引入依赖

<dependency>
   <groupId>com.netflix.hystrix</groupId>
   <artifactId>hystrix-metrics-event-stream</artifactId>
   <version>1.5.12</version>
</dependency>
<dependency>
   <groupId>com.netflix.hystrix</groupId>
   <artifactId>hystrix-javanica</artifactId>
   <version>1.5.12</version>
</dependency>

配置线程池:实现HystrixCommand接口,并进行配置


public class OrderCommand extends HystrixCommand<String> { 
	
	private RestTemplate restTemplate;
	private Long id;
   
	public OrderCommand(RestTemplate restTemplate, Long id) { 
		super(setter());
		this.restTemplate = restTemplate;
		this.id = id;
	}
	
 	@Override//隔离的服务
 	protected String run() throws Exception { 
 		return restTemplate.getForObject("http://localhost/product/"+id, String.class);
	}

 	@Override//降级方法
 	protected String getFallback(){ 
 			return null;
	}

	//服务配置 
	private static Setter setter() { 
		// 服务分组、//服务标识 、// 线程池名称
		HystrixCommandGroupKey gk= HystrixCommandGroupKey.Factory.asKey("order_product");
		HystrixCommandKey ck=HystrixCommandKey.Factory.asKey("product");
		HystrixThreadPoolKey tpk=HystrixThreadPoolKey.Factory.asKey("order_product_pool");
 		HystrixThreadPoolProperties.Setter tpp= HystrixThreadPoolProperties.Setter()
 			.withCoreSize(50)	//线程池大小
         	.withKeepAliveTimeMinutes(15)		//线程存活时间15s
         	.withQueueSizeRejectionThreshold(100);	//队列等待的阈值为100,超过100执行拒绝 策略
 		// 命令属性配置Hystrix 开启超时
 		HystrixCommandProperties.Setter cp= HystrixCommandProperties.Setter()
		 	// 服务隔离方式:线程池隔离
       		.withExecutionIsolationStrategy(HystrixCommandProperties.ExecutionIsolationStrategy.THREAD)
 			.withExecutionTimeoutEnabled(false);// 禁止
 		
 		return HystrixCommand.Setter
 			.withGroupKey(gk)
 		   	.andCommandKey(ck)
           	.andThreadPoolKey(tpk)
          	.andThreadPoolPropertiesDefaults(tpp)
           	.andCommandPropertiesDefaults(cp);
 	}   
}

控制层,进行服务调用

@Autowired
private RestTemplate restTemplate;

@GetMapping("/buy/{id}")
public String order(@PathVariable Long id) throws Exception { 
 	return new OrderCommand(restTemplate,id).execute();
}

4.实时监控平台

HystrixCommand与HystrixObservableCommand在执行时,会生成执行结果和运行指标,比如每秒请求数等。这些状态会暴露在Actuator提供的/health的端点中。

导入依赖,并开启hystrix:

<!--引入actuator依赖-->
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>


<!--引入hystrix依赖-->
<dependency>
	<groupId>org.springframework.cloud</groupId>
	<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<!-- dashboard-->
<dependency>
	<groupId>org.springframework.cloud</groupId>
	<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
</dependency>

暴露所有actuator监控的端点:

management:
	endpoints:
		web:
			exposure:
				include: '*'

此时访问:http://localhost:8080/actuator/hystrix.stream 即可。

仪表板
但上面获取到的状态为文字,不方便观察,hystrix官方提供了图形化的dashboard(仪表板)监控平台,可以显示每个熔断器的状态。

在启动类上添加注解开启仪表板:@EnableHystrixDashboard

@SpringBootApplication
@EnableFeignClients //激活Fegin
@EnableCircuitBreaker//激活Hystrix
@EnableHystrixDashboard//激活仪表板
public class MainApplication { 

   public static void main(String[] args) { 
       SpringApplication.run(MainApplication.class,args);
   }
   
}

访问:http://localhost:8080/hystrix 即可打开仪表板。
《微服务-服务容错:熔断、降级、隔离、限流》
输入监控的服务地址:http://localhost:8080/actuator/hystrix.stream,点击按钮即可进入:
《微服务-服务容错:熔断、降级、隔离、限流》
上面的界面已经很好地解决了问题,可新问题出现了,此时只对一个服务进行了监控,如果服务很多时,如何进行统一监控呢?

断路器聚合监控Turbine
引入依赖:

<!--引入turbine依赖-->
<dependency>
	<groupId>org.springframework.cloud</groupId>
	<artifactId>spring-cloud-starter-netflix-turbine</artifactId>
</dependency>

<!--引入hystrix依赖-->
<dependency>
	<groupId>org.springframework.cloud</groupId>
	<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<!-- dashboard-->
<dependency>
	<groupId>org.springframework.cloud</groupId>
	<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
</dependency>

添加配置:

turbine:
	appConfig: img-service	#监控多个时用,分隔
	clusterNameExpression: "'default'"

开启Turbine:

@SpringBootApplication
@EnableFeignClients //激活Fegin
@EnableCircuitBreaker//激活Hystrix
@EnableHystrixDashboard//激活仪表板
@EnableTurbine//激活turbine
public class MainApplication { 

   public static void main(String[] args) { 
       SpringApplication.run(MainApplication.class,args);
   }
}

在图表工具页面输入:http://localhost:8185/turbine.stream

Sentinel

sentinel是阿里的开源项目,项目地址:https://github.com/alibaba/Sentinel/wiki
《微服务-服务容错:熔断、降级、隔离、限流》
与Hystrix相比:Sentinel支持的熔断降级维度更多,可对多种指标进行流控、熔断,且提供了实时监控和控制面板,功能更为强大。

Sentinel使用比较简单,基本上都是UI界面进行设置,使用ui就意味着你需要配置好才能使用。

1.管理面板

下载:https://github.com/alibaba/Sentinel/releases/download/1.6.3/sentinel-dashboard-1.6.3.jar

启动面板:

#启动面板
java -Dserver.port=8080 -Dcsp.sentinel.dashboard.server=localhost:8080 -Dproject.name=sentinel->dashboard -jar sentinel-dashboard-1.6.3.jar 

-Dserver.port指定控制台端口,启动成功后访问:http://localhost:8080/
从版本1.6.0开始,控制台引入登录功能,默认用户名+密码都为sentinel,jdk最低为1.8。
《微服务-服务容错:熔断、降级、隔离、限流》
注意:如果服务出不来刷新页面就可以了。

配置sentinel:

引入依赖:

<!--引入sentinel-->
<dependency>
  <groupId>com.alibaba.cloud</groupId>
  <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>

为服务提供者添加配置:

spring:
	cloud:
		sentinel:
			transport:
				dashboard:localhost: 8080

2.服务熔断、服务降级

服务熔断或服务异常后,就应当对接口做降级处理,此时应定义降级方法来处理请求。
sentinel中有两种降级方法:

  • 熔断降级
  • 抛出异常降级
//blockHandler = "熔断降级方法",fallback = "抛出异常降级方法"
//此注解还有value属性:自定义资源名(控制台看着顺眼)
@SentinelResource(blockHandler = "funa",fallback = "funb")
@RequestMapping("/img/{id}")
public Img findimg(@PathVariable long id){ 
   ......
}

可以看出,上面的资源保护只针对了一个接口,如果接口很多时,如何设置通用的降级方法来保护大量接口呢?

对于使用了RestTemplate对象的调用者,可以在RestTemplate对象注册时开启保护:

此时@SentinelResource的属性:

  • blockHandler :熔断降级方法
  • fallback:异常降级方法
  • blockHandlerClass :熔断降级方法所在类
  • fallbackClass :异常降级方法所在类
@SpringBootApplication
public class MainApplication { 

	//假设降级方法写在了Derated类中
	@SentinelResource(blockHandler = "funa",fallback = "funb",fallbackClass = Derated.class,blockHandlerClass = Derated.class)
	@Bean//将RestTemplate注册到容器
	public RestTemplate RestTemplate(){ 
 		return new RestTemplate();
	}

	public static void main(String[] args) { 
  	SpringApplication.run(MainApplication.class,args);
	}

}

此时默认资源名格式为:协议+主机+端口+[路径],降级的方法只需写在注解指定的位置即可。

通用降级方法示例:

//统一降级配置类
public class Derated{ 
	
	//统一熔断降级方法
	public Img funa(){ 
		return new Img.setName("熔断降级");
	}
	//统一异常降级方法
	public Img funb(){ 
		return new Img.setName("异常降级");
	}

}

接口的保护设置会在服务重启后被清空,不过可以设置一个默认值:

默认降级配置:

spring:
	cloud:
		sentinel:
			eager: true #立即刷新,比较带劲
			datasource:
				ds1:
					file:
						file: classpath:flowrule.json	#从本地资源找到flowrule.json
						data-type: json
						rule-type: flow
						

默认的配置是保存在json文本中,json内容如下:

{ 
	{ 
  		"resource":"Myname",	//资源名
   		"controlBehavior":0,	//流量效果
   		"count":1,				//流量阈值
   		"grade":1,				//流量类型
  		"limitApp":"default",	//策略
   		"strategy":0
	}
}
//具体有哪些属性,可在源码的RuleConstant.java中找到

OpenFeign中虽然内置了,Hystrix,但我们仍能够在使用OpenFeign的同时使用Sentinel来对请求进行服务降级,并且使用方式和Hystrix是一样的。

实现要保护的接口,并将实现类注册到容器:假定调用服务创建的接口为ImgFeignClient 。

@Component
public class ImgFeignClientCallBack implements ImgFeignClient { 
  
   @Override // 熔断降级的方法
   public Img findid(long id) { 
       Img img=new Img();
       img.setName("触发降级方法");
       return img;
   }
}

调用服务的接口上添加注解,指定用来降级的实现类(因为此接口实现不止一个):

// fallback:指定服务降级方法,即实现类
@FeignClient(name="img-service",fallback= ImgFeignClientCallBack.class)
public interface ImgFeignClient { 
   
    // 配置需要调用的微服务接口
   @RequestMapping(value = "/img/findimg/{id}",method = RequestMethod.GET)
   public Img findid( @PathVariable long id);
   
}

为什么说此接口实现类不止一个呢,通过接口加注解来调用服务,不就相当于内置提供了一个实现吗。

接口中方法XXX资源名为:GET:http://服务名/路径/{str}。

SpringBoot Admin 服务监控

Admin用于监控服务,分为服务端可客户端,客户端即服务,因此创建一个Admin server,并在需要监控的服务上添加依赖及简单配置,就可以实现监控了。

服务端

项目依赖,注意版本

		<dependency>
            <groupId>de.codecentric</groupId>
            <artifactId>spring-boot-admin-starter-server</artifactId>
            <version>2.2.2</version>
        </dependency>
        <dependency>
            <groupId>de.codecentric</groupId>
            <artifactId>spring-boot-admin-server-ui</artifactId>
            <version>2.2.2</version>
        </dependency>

添加依赖后,只需在启动类添加注解 @EnableAdminServer,并启动即可。(它也是服务,也可注册到注册中心)

客户端

客户端即要监控的服务,在需要被监控的服务上添加依赖和配置即可。

		<dependency>
            <groupId>de.codecentric</groupId>
            <artifactId>spring-boot-admin-starter-client</artifactId>
            <version>2.2.2</version>
        </dependency>

进行简单配置yml

spring:
 boot:
  admin:
     client:
       url: http://localhost:40000 #admin服务地址
management:
 endpoints:
   web:
     exposure:
       include: "*"

启动服务端、客户端,访问服务端地址就可以访问UI界面了。

《微服务-服务容错:熔断、降级、隔离、限流》

    原文作者:火恐龙
    原文地址: https://blog.csdn.net/qq_52681418/article/details/113351447
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞