Java基础之《微服务(7)—微服务雪崩解决方案2》

五、解决方案五:线程池隔离

1、示意图

2、结局

接口A请求10万次,接口B请求10次,由于他们在同一个线程池中,接口A会把接口B撑爆。A接口瘫痪必然导致B接口瘫痪
接口没有隔离
在同一个springboot,同一个tomcat,同一个线程池里面

3、线程池隔离
给每个接口单独分配一个线程,预计好有哪些接口并发量大

接口A不会影响接口B

4、service类添加方法

package com.example.mycloud.service;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.Future;

import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

import com.netflix.hystrix.contrib.javanica.annotation.HystrixCollapser;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import com.netflix.hystrix.contrib.javanica.conf.HystrixPropertiesManager;

@CacheConfig(cacheNames = {"com.example.mycloud"})
@Service
public class TestHystrixService {

	/********************
	 * 服务降级案例
	 ********************/
	
	@HystrixCommand(fallbackMethod = "fallback", //降级方法
			//配置参数
			commandProperties = {
					//默认10秒内,如果并发数达到该设置值,请求会被拒绝和抛出异常并且fallback不会被调用
					@HystrixProperty(name=HystrixPropertiesManager.FALLBACK_ISOLATION_SEMAPHORE_MAX_CONCURRENT_REQUESTS, value="15")
			})
	public String testHystrix1() {
		throw new RuntimeException("服务异常");
		//return "正常请求......";
	}
	
	/**
	 * 返回托底数据
	 * @return
	 */
	public String fallback() {
		return "fallback method......";
	}
	
	/********************
	 * 请求缓存案例
	 ********************/
	
	@Cacheable(key = "'get' + #id")
	public String get(Integer id) {
		System.out.println("==========get==========");
		return "测试缓存..." + id;
	}
	
	@CacheEvict(key = "'get' + #id")
	public void del(Integer id) {
		System.out.println("==========del==========");
	}
	
	/********************
	 * 请求合并案例
	 ********************/
	
	@HystrixCollapser(batchMethod = "batchIdInfo",
			scope = com.netflix.hystrix.HystrixCollapser.Scope.GLOBAL,
			collapserProperties = {
					//请求时间间隔在50ms之内的请求会被合并为一个请求
					@HystrixProperty(name = "timerDelayInMilliseconds", value = "20"),
					//设置触发批处理执行之前,在批处理中允许的最大请求数
					@HystrixProperty(name = "maxRequestsInBatch", value = "200")
			}
			)
	public Future<String> getIdInfo(Integer id) {
		//没有打印这里,因为请求合并了
		System.out.println("==========" + id + "==========");
		return null;
	}
	
	@HystrixCommand
	public List<String> batchIdInfo(List<Integer> id) {
		List<String> list = new ArrayList<>();
		for(Integer i : id) {
			list.add("id: " + i);
		}
		return list;
	}
	
	/********************
	 * 服务熔断案例
	 ********************/
	
	@HystrixCommand(fallbackMethod = "fallback2",
			commandProperties = {
					//默认20个;10s内请求数大于20个时就启动熔断器,当请求符合熔断条件时将触发getFallback()
					@HystrixProperty(name = HystrixPropertiesManager.CIRCUIT_BREAKER_REQUEST_VOLUME_THRESHOLD, value = "10"),
					//请求错误率大于50%时就熔断,然后for循环发起请求,当请求符合熔断条件时将触发getFallback()
					@HystrixProperty(name = HystrixPropertiesManager.CIRCUIT_BREAKER_ERROR_THRESHOLD_PERCENTAGE, value = "50"),
					//默认5秒;熔断多少秒后去尝试请求
					@HystrixProperty(name = HystrixPropertiesManager.CIRCUIT_BREAKER_SLEEP_WINDOW_IN_MILLISECONDS, value = "5000")
			}
			)
	public String testHystrix4() {
		Random random = new Random();
		int i = random.nextInt();
		if (i % 2 == 1) {
			throw new RuntimeException("服务异常");
		} 
		return "正常请求......";
	}
	
	public String fallback2() {
		return "fallback2 method......";
	}
	
	/********************
	 * 线程池隔离案例
	 ********************/
	
	@HystrixCommand(groupKey = "service-provider1", commandKey = "testHystrix5",
			threadPoolKey = "testHystrix5",
			threadPoolProperties = {
					@HystrixProperty(name = "coreSize", value = "30"), //线程池大小
					@HystrixProperty(name = "maxQueueSize", value = "100"), //最大队列长度
					@HystrixProperty(name = "keepAliveTimeMinutes", value = "2"), //线程存活时间
					@HystrixProperty(name = "queueSizeRejectionThreshold", value = "15") //拒绝请求
			},
			fallbackMethod = "falback3"
			)
	public String testHystrix5() {
		return "正常请求......";
	}
	
	public String fallback3() {
		return "fallback3 method......";
	}
	
}

resource添加:

	@RequestMapping("/testHystrix5")
	public String testHystrix5() {
		return testHystrixService.testHystrix5();
	}

5、线程池隔离参数配置
(1)
参数:groupKey
作用:服务名(相同服务用一个名称,如商品、用户等等)
默认值:getClass().getSimpleName();
备注:在consumer里面为每个provider服务,设置group标识,一个group使用一个线程池

(2)
参数:commandKey
作用:接口(服务下面的接口,如购买商品)
默认值:当前执行方法名
备注:consumer的接口名称

(3)
参数:threadPoolKey
作用:线程池的名称,配置全局唯一标识线程池的名称,相同线程池名称的线程池是同一个
默认值:默认是分组名groupKey
备注:

(4)
参数:coreSize
作用:线程池大小,这是最大的并发执行数量
默认值:10
备注:
每秒最大支撑的请求数(99%平均响应时间 + 一个换充值)
比如,每秒能处理1000个请求,99%的请求响应时间是60ms,那么公式是:1000 * (0.060 + 0.012)

(5)
参数:maxQueueSize
作用:最大队列长度,设置BlockingQueue的最大长度
默认值:-1
备注:默认值-1,如果使用正数,队列将从同步队列(SynchronousQueue)改为阻塞队列(LinkedBlockingQueue)

(6)
参数:keepAliveTimeMinutes
作用:线程存活时间,设置存活时间,单位分钟
默认值:1
备注:控制一个线程从使用完成到被释放的时间

(7)
参数:queueSizeRejectionThreshold
作用:拒绝请求,设置拒绝请求的临界值
默认值:5
备注:
此属性不适用于maxQueueSize=-1时
设置这个值的原因是maxQueueSize值运行时不能改变,我们可以通过修改这个变量动态修改允许排队的长度

六、解决方案六:信号量隔离

1、示意图

2、service类添加方法

package com.example.mycloud.service;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.Future;

import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

import com.netflix.hystrix.contrib.javanica.annotation.HystrixCollapser;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import com.netflix.hystrix.contrib.javanica.conf.HystrixPropertiesManager;

@CacheConfig(cacheNames = {"com.example.mycloud"})
@Service
public class TestHystrixService {

	/********************
	 * 服务降级案例
	 ********************/
	
	@HystrixCommand(fallbackMethod = "fallback", //降级方法
			//配置参数
			commandProperties = {
					//默认10秒内,如果并发数达到该设置值,请求会被拒绝和抛出异常并且fallback不会被调用
					@HystrixProperty(name=HystrixPropertiesManager.FALLBACK_ISOLATION_SEMAPHORE_MAX_CONCURRENT_REQUESTS, value="15")
			})
	public String testHystrix1() {
		throw new RuntimeException("服务异常");
		//return "正常请求......";
	}
	
	/**
	 * 返回托底数据
	 * @return
	 */
	public String fallback() {
		return "fallback method......";
	}
	
	/********************
	 * 请求缓存案例
	 ********************/
	
	@Cacheable(key = "'get' + #id")
	public String get(Integer id) {
		System.out.println("==========get==========");
		return "测试缓存..." + id;
	}
	
	@CacheEvict(key = "'get' + #id")
	public void del(Integer id) {
		System.out.println("==========del==========");
	}
	
	/********************
	 * 请求合并案例
	 ********************/
	
	@HystrixCollapser(batchMethod = "batchIdInfo",
			scope = com.netflix.hystrix.HystrixCollapser.Scope.GLOBAL,
			collapserProperties = {
					//请求时间间隔在50ms之内的请求会被合并为一个请求
					@HystrixProperty(name = "timerDelayInMilliseconds", value = "20"),
					//设置触发批处理执行之前,在批处理中允许的最大请求数
					@HystrixProperty(name = "maxRequestsInBatch", value = "200")
			}
			)
	public Future<String> getIdInfo(Integer id) {
		//没有打印这里,因为请求合并了
		System.out.println("==========" + id + "==========");
		return null;
	}
	
	@HystrixCommand
	public List<String> batchIdInfo(List<Integer> id) {
		List<String> list = new ArrayList<>();
		for(Integer i : id) {
			list.add("id: " + i);
		}
		return list;
	}
	
	/********************
	 * 服务熔断案例
	 ********************/
	
	@HystrixCommand(fallbackMethod = "fallback2",
			commandProperties = {
					//默认20个;10s内请求数大于20个时就启动熔断器,当请求符合熔断条件时将触发getFallback()
					@HystrixProperty(name = HystrixPropertiesManager.CIRCUIT_BREAKER_REQUEST_VOLUME_THRESHOLD, value = "10"),
					//请求错误率大于50%时就熔断,然后for循环发起请求,当请求符合熔断条件时将触发getFallback()
					@HystrixProperty(name = HystrixPropertiesManager.CIRCUIT_BREAKER_ERROR_THRESHOLD_PERCENTAGE, value = "50"),
					//默认5秒;熔断多少秒后去尝试请求
					@HystrixProperty(name = HystrixPropertiesManager.CIRCUIT_BREAKER_SLEEP_WINDOW_IN_MILLISECONDS, value = "5000")
			}
			)
	public String testHystrix4() {
		Random random = new Random();
		int i = random.nextInt();
		if (i % 2 == 1) {
			throw new RuntimeException("服务异常");
		} 
		return "正常请求......";
	}
	
	public String fallback2() {
		return "fallback2 method......";
	}
	
	/********************
	 * 线程池隔离案例
	 ********************/
	
	@HystrixCommand(groupKey = "service-provider1", commandKey = "testHystrix5",
			threadPoolKey = "testHystrix5",
			threadPoolProperties = {
					@HystrixProperty(name = "coreSize", value = "30"), //线程池大小
					@HystrixProperty(name = "maxQueueSize", value = "100"), //最大队列长度
					@HystrixProperty(name = "keepAliveTimeMinutes", value = "2"), //线程存活时间
					@HystrixProperty(name = "queueSizeRejectionThreshold", value = "15") //拒绝请求
			},
			fallbackMethod = "falback3"
			)
	public String testHystrix5() {
		return "正常请求......";
	}
	
	public String fallback3() {
		return "fallback3 method......";
	}
	
	/********************
	 * 信号量隔离案例
	 ********************/
	
	@HystrixCommand(fallbackMethod = "fallback4",
			commandProperties = {
					@HystrixProperty(name = HystrixPropertiesManager.EXECUTION_ISOLATION_STRATEGY, value = "SEMAPHORE"), //信号量隔离
					@HystrixProperty(name = HystrixPropertiesManager.EXECUTION_ISOLATION_SEMAPHORE_MAX_CONCURRENT_REQUESTS, value = "100") //信号量最大并发数
			}
			)
	public String testHystrix6() {
		return "正常请求......";
	}
	
	public String fallback4() {
		return "fallback4 method......";
	}
}

resource添加:

	@RequestMapping("/testHystrix6")
	public String testHystrix6() {
		return testHystrixService.testHystrix6();
	}

3、信号量隔离参数配置
(1)
参数:execution.isolation.strategy
作用:隔离策略配置
默认值:THREAD
备注:只有2种,THREAD和SEMAPHORE

(2)
参数:execution.isolation.thread.timeoutInMilliseconds
作用:超时时间
默认值:1000ms
备注:
在THREAD模式下,达到超时时间,自动中断
在SEMAPHORE模式中下,会等待执行完成后,再去判断是否超时

(3)
参数:execution.isolation.thread.interruptOnTimeout
作用:是否打开超时线程中断
默认值:TRUE
备注:THREAD模式有效

(4)
参数:execution.isolation.semaphore.maxConcurrentRequests
作用:信号量最大并发数
默认值:10
备注:SEMAPHORE模式有效

(5)
参数:fallback.isolation.semaphore.maxConcurrentRequests
作用:fallback最大并发数
默认值:10
备注:SEMAPHORE模式有效

4、信号量隔离其实就是队列里面最大并发量的数字

七、线程池隔离和信号量隔离的区别

线程池隔离信号量隔离
线程请求线程和调用provider线程不是同一条线程请求线程和调用provider线程是同一条线程
开销排队、调度、上下文开销等无线程切换,开销低
异步支持不支持
并发支持支持(最大线程池大小)支持(最大信号量上限)
传递Header无法传递Http Header可以传递Http Header
支持超时能支持超时不支持超时

1、什么情况下用线程池隔离
请求并发量大,并且耗时长(请求耗时长,一般是计算量大,或者读数据库)。采用线程隔离策略,这样的话,可以保证大量的容器(tomcat)线程可用,不会犹豫服务原因,一直处于阻塞或等待状态,快速失败返回

2、什么情况下用信号量隔离
请求并发量大,并且耗时短(请求耗时短,可能是计算量小,或者读缓存)。采用信号量隔离策略,因为这类服务的返回通常会非常的快,不会占用容器线程太长的时间,而且也减少了线程切换的一些开销,提高了缓存服务的效率

八、feign服务降级处理

1、修改配置文件

#feign默认是不开启Hystrix
feign.hystrix.enabled=true

2、增加fallback类
FeignClientServiceFallback.java

package com.example.mycloud.hystrix;

import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.PathVariable;

import com.example.mycloud.feign.FeignClientService;

@Component
public class FeignClientServiceFallback implements FeignClientService {

	@Override
	public String remoteApi(@PathVariable String id) {
		return "fallback......";
	}

}

3、关联feign服务类和fallback类

package com.example.mycloud.feign;

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;

import com.example.mycloud.hystrix.FeignClientServiceFallback;

@FeignClient(name = "inside-app1", fallback = FeignClientServiceFallback.class)
public interface FeignClientService {
	
	@GetMapping("/insideApp1/remoteApi/{id}")
	public String remoteApi(@PathVariable String id);
}

九、feign服务降级后的异常记录

1、增加fallback factory类
FeignClientServiceFallbackFactory.java

package com.example.mycloud.hystrix;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.example.mycloud.feign.FeignClientService;

import feign.hystrix.FallbackFactory;

public class FeignClientServiceFallbackFactory implements FallbackFactory<FeignClientService> {

	private Logger logger = LoggerFactory.getLogger(FeignClientServiceFallbackFactory.class);
	
	@Override
	public FeignClientService create(final Throwable cause) {
		return new FeignClientService() {

			@Override
			public String remoteApi(String id) {
				logger.warn("fallback exception: ", cause);
				return "fallback......";
			}
			
		};
	}

}

2、关联feign服务类和fallback factory类

package com.example.mycloud.feign;

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;

import com.example.mycloud.hystrix.FeignClientServiceFallback;
import com.example.mycloud.hystrix.FeignClientServiceFallbackFactory;

@FeignClient(name = "inside-app1", fallbackFactory = FeignClientServiceFallbackFactory.class) //fallback和fallbackFactory配置一个
public interface FeignClientService {
	
	@GetMapping("/insideApp1/remoteApi/{id}")
	public String remoteApi(@PathVariable String id);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值