上篇我們模擬了高并發場景下,系統資源被耗盡導致其他接口訪問非常之慢。至此,這篇給出了五種解決方案(當然這個是次要的,主要還是理解原理)
上篇地址:https://blog.csdn.net/Kevinnsm/article/details/117302197?spm=1001.2014.3001.5501
文章目錄
- 一、雪崩效應是什么?
- 二、什么是Hystrix?
- 三、Hystrix用來解決什么問題?
- 四、雪崩效應的五大解決方案
- 1、請求緩存
- Ⅰ、redis安裝
- Ⅱ、代碼配置
- Ⅲ、啟動測試接口
- Ⅳ、模擬2500訪問量高并發測試
- 2、請求合并
- 3、服務隔離
- 4、服務熔斷
- 5、服務降級
- 五、總結
一、雪崩效應是什么?
在微服務項目當中,服務之間的調用是錯綜復雜的,服務之間相互依賴;一旦某個服務發生了問題,那么將可能出現鏈式反應,導致整個系統崩潰,這就是所謂的雪崩效應
(大白話說就是A服務依賴(調用)B服務,B服務依賴C服務,一旦C服務出了問題,那么A、B服務將一直處于阻塞狀態)
二、什么是Hystrix?
Hystrix是由Netflix開源的一個服務隔離組件,通過服務隔離來避免由于依賴延遲、異常,引起資源耗盡導致系統不可用的解決方案。
Hystrix地址:https://github.com/Netflix/Hystrix
三、Hystrix用來解決什么問題?
服務之間錯綜復雜的調用,如果某一個服務出現了問題,那么就會出現我們前面介紹的雪崩效應問題。而Hystrix為這個服務故障提供了一套解決方案。
例如:
1、請求緩存
2、請求合并
3、服務隔離
4、服務熔斷
5、服務降級
當你回答到這,面試官估計會對你嘿嘿一笑,說:來做CTO吧
上篇我們模擬了高并發場景下,系統資源被耗盡導致其他接口訪問非常慢。至此,這篇給出了五種解決方案
演示代碼免費下載地址:https://download.csdn.net/download/Kevinnsm/19098746
四、雪崩效應的五大解決方案
1、請求緩存
Ⅰ、redis安裝
此處為了方便,我直接在windows上進行快速安裝
另外我也使用了Reids Desktop Manager(圖形化界面),這個現在收費了,不過對于我們來說,肯定白嫖!
Ⅱ、代碼配置
在consumer模塊配置RedisConfig文件
@Configuration
public class RedisConfig {
@Beanpublic RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory
) {RedisTemplate<String, Object> template
= new RedisTemplate<>();
template
.setKeySerializer(new StringRedisSerializer());
template
.setValueSerializer(new GenericJackson2JsonRedisSerializer());
template
.setHashKeySerializer(new StringRedisSerializer());
template
.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());template
.setConnectionFactory(redisConnectionFactory
);return template
;}
public RedisCacheManager redisCacheManager(RedisTemplate redisTemplate
) {RedisCacheWriter redisCacheWriter
= RedisCacheWriter.nonLockingRedisCacheWriter(redisTemplate
.getConnectionFactory());RedisCacheConfiguration redisCacheConfiguration
= RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofMinutes(30))
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisTemplate
.getKeySerializer())).serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(redisTemplate
.getValueSerializer()));return new RedisCacheManager(redisCacheWriter
, redisCacheConfiguration
);}
}
redis配置
在啟動類上開啟緩存
在遠程調用接口上開啟緩存配置(第一個接口和第三個接口)
具體不明白的可以參考上一篇
@FeignClient(value
= "service-provider")
public interface ProductService {@GetMapping(value
= "/product/list")@Cacheable(cacheNames
= "orderService:product:list")List<Product> selectProductList();@GetMapping(value
= "/product/byIds")List<Product> selectProductListByIds(@RequestBody List<Integer> ids
);@GetMapping(value
= "/search/{id}")@Cacheable(cacheNames
= "orderService:product:single", key
= "#id")Product selectProductById(@PathVariable("id") Integer id
);
}
Ⅲ、啟動測試接口
啟動這四個服務訪問http://localhost:9090/select/1
由于我們在第一個消費接口的服務提供類的接口處暫停了兩秒,所以當我們訪問時,第一次會耗時>2s,后面就會非常快了,因為從緩存讀取了嘛!
之后我們從RedisManager查看結果
Ⅳ、模擬2500訪問量高并發測試
和上一篇操作一樣:https://blog.csdn.net/Kevinnsm/article/details/117302197?spm=1001.2014.3001.5501
此時我們可以看到結果
為什么2500個請求很快就完成了呢?究其原因還是緩存的功勞嘛!一般情況下從第二個請求開始就一直從緩存中取的數據。雖然我暫停了兩秒,但那和緩存沒毛線關系(就第一次走那個路線)。
在那2500個請求的過程中,訪問第三個接口,有可能2499個請求都找緩存去了,然后就不存在線程資源競爭嘛!當然就很快了
2、請求合并
什么是請求合并?
在高并發場景下,一個請求就需要一個線程進行處理;一旦訪問量很大,那么就會浪費很多的資源;所以引入了請求合并,將多個請求進行拼接,只進行一次訪問即可。
hystrix依賴
<dependency><groupId>org.springframework.cloud
</groupId><artifactId>spring-cloud-starter-netflix-hystrix
</artifactId><version>2.2.8.RELEASE
</version></dependency>
這兩個注解配置一下即可
@HystrixCollapser(batchMethod
= "searchOrderById", scope
= com.netflix.hystrix.HystrixCollapser.Scope.GLOBAL
, collapserProperties
= {@HystrixProperty(name
= "timeDelayInMilliseconds", value
= "30"),@HystrixProperty(name
= "maxRequestsInBatch", value
= "200")})
注意啟動類上需要開啟Hystrix熔斷。
3、服務隔離
1、線程隔離
什么是線程隔離?
我們以前沒有使用線程隔離的項目的所有接口都運行在一個ThreadPool中,一旦某一個接口壓力過大就會造成資源耗盡,從而導致其他接口的正常使用;如使用了線程隔離技術,我們可以將某個實例接口單獨使用ThreadPool隔離起來,一旦它出現故障,不會影響其他接口的使用,如下圖詳解兩種方式
異步,提高了系統的并發性;但是如果隔離的實例過多,還是推薦使用這種方式
使用注解方式實現線程隔離,注解方式和配置方式各有優缺點
配置方式可以在服務在線時進行修改配置,但是一個實例需要一個配置類,一旦需要隔離較多的實例就要寫很多類
注解方式雖然很方便,但是不能在服務在線時進行修改配置
@Service
public class OrderServiceImpl implements OrderService {@Autowiredprivate ProductService productService
;@Override@HystrixCommand(groupKey
= "consumer-product-pool-1", commandKey
= "selectOrderById", threadPoolKey
= "consumer-product-pool-1", commandProperties
= {@HystrixProperty(name
= "execution.isolation.thread.timeoutInMilliseconds",value
= "5000")},threadPoolProperties
= {@HystrixProperty(name
= "coreSize", value
= "6"),@HystrixProperty(name
= "maxQueueSize", value
= "100"),@HystrixProperty(name
= "keepAliveTimeMinutes", value
= "30"),@HystrixProperty(name
= "queueSizeRejectionThreshold", value
= "100")})public Order selectOrderById(Integer id
) {return new Order(id
, "one", "china", 199D,productService
.selectProductList());}private List<Product> selectProductListFallback() {return Arrays.asList(new Product(1, "默認數據1", 9, 100D),new Product(2, "默認數據2", 15, 200D),new Product(3, "默認數據3", 13, 300D));}@Overridepublic Order queryOrderById(Integer id
) {return new Order(id
, "two", "nyist", 11D, productService
.selectProductListByIds(Arrays.asList(1, 2)));}@Override@HystrixCommand(groupKey
= "consumer-product-pool-2", commandKey
= "searchOrderById", threadPoolKey
= "consumer-product-pool-1", commandProperties
= {@HystrixProperty(name
= "execution.isolation.thread.timeoutInMilliseconds",value
= "5000")},threadPoolProperties
= {@HystrixProperty(name
= "coreSize", value
= "3"),@HystrixProperty(name
= "maxQueueSize", value
= "100"),@HystrixProperty(name
= "keepAliveTimeMinutes", value
= "2"),@HystrixProperty(name
= "queueSizeRejectionThreshold", value
= "100")})public Order searchOrderById(Integer id
) {return new Order(id
, "three", "china", 110D, Arrays.asList(productService
.selectProductById(3)));}
}
在tomcat進行配置系統最多只能使用10個線程,方便查看結果
使用JMeter進行測試
由于我在注解中配置了該接口每次只能使用6個線程進行處理,可以發現每次會打印6個日志。(我暫停兩秒的那個接口)
2、信號量隔離
什么是信號量隔離?
信號量有一個大小,一旦請求的線程數超過了這個信號量的大小,直接獲取失敗做fallback處理。獲取到信號量的線程繼續訪問,訪問完成后歸還信號量;當然可以通過上調信號量的大小,來提高系統的性能
同步調用,信號量是不能用于網絡環境的;多用于本地環境。
@HystrixCommand(commandProperties
= {
@HystrixProperty(name
= "execution.isolation.thread.timeoutInMilliseconds",value
= "5000"),@HystrixProperty(name
= HystrixPropertiesManager.EXECUTION_ISOLATION_STRATEGY
, value
= "SEMAPHORE"),@HystrixProperty(name
= HystrixPropertiesManager.EXECUTION_ISOLATION_SEMAPHORE_MAX_CONCURRENT_REQUESTS
, value
= "10")})public Order selectOrderById(Integer id
) {return new Order(id
, "one", "china", 199D,productService
.selectProductList());}
4、服務熔斷
為什么要進行服務熔斷?
在微服務架構中,可能由于某種原因導致服務出現了過載現象,有可能引發整個系統發生故障;為防止這種情況發生引入服務熔斷技術;可以將熔斷理解為我們生活中的閘刀。
Hystrix熔斷技術原理:
服務模塊業務類代碼
為了模擬服務熔斷,我手動加入異常,使其開啟熔斷。
訪問id=1,下面會報運行時異常,然后就會走fallback函數。
@HystrixCommand(commandProperties
= {@HystrixProperty(name
= HystrixPropertiesManager.CIRCUIT_BREAKER_REQUEST_VOLUME_THRESHOLD
, value
= "10"),@HystrixProperty(name
= HystrixPropertiesManager.CIRCUIT_BREAKER_ERROR_THRESHOLD_PERCENTAGE
, value
= "50"),@HystrixProperty(name
= HystrixPropertiesManager.CIRCUIT_BREAKER_SLEEP_WINDOW_IN_MILLISECONDS
, value
= "5000")},fallbackMethod
= "selectProductListFallback")public Order queryOrderById(Integer id
) {if (id
==1 )throw new RuntimeException("id=1的信息異常,開始進行服務熔斷處理!");return new Order(id
, "two", "nyist", 11D, productService
.selectProductListByIds(Arrays.asList(1, 2)));}
服務熔斷異常執行函數fallback
private Order selectProductListFallback(Integer id
) {System.out
.println("------------》調用了默認數據000《---------------");return new Order(1, "系統故障,默認數據", "開啟熔斷", 11D, Arrays.asList(new Product(1,"vim", 2, 11D)));}
可以發現確實是走的fallback函數,服務熔斷模擬成功!
5、服務降級
如何理解服務降級?
服務降級是為了保留核心業務,舍棄邊緣業務。
@HystrixCommand(fallbackMethod
= "selectProductListFallback")
進行服務降級只需添加這一行代碼即可,前面服務熔斷已經使用過。
有幾種觸發條件
1、發生異常
2、調用超時
3、超出服務隔離設置的上限
4、開啟服務熔斷
五、總結
到這里基本就結束了,這里講述的都是基于Hystrix的雪崩解決方案,當然其他技術也能實現,比如Feign實現熔斷降級等等。雖然Hystrix已經進入維護之中,但是這個組件真的香,我一直都認為學習不能什么熱就學什么,為什么不去探究一下其中的奧妙呢?深入學習之后我發現
溜了溜了!
與50位技術專家面對面20年技術見證,附贈技術全景圖
總結
以上是生活随笔為你收集整理的面试官:了解雪崩效应吗?了解Hystrix吗?怎么解决雪崩效应吗?(大型社死现场,教你运筹帷幄之中)的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。