自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Hystrix 實(shí)現(xiàn)資源隔離的“兩把利器”

開(kāi)發(fā) 架構(gòu)
小型電商網(wǎng)站的頁(yè)面展示采用頁(yè)面全量靜態(tài)化的思想。數(shù)據(jù)庫(kù)中存放了所有的商品信息,頁(yè)面靜態(tài)化系統(tǒng),將數(shù)據(jù)填充進(jìn)靜態(tài)模板中,形成靜態(tài)化頁(yè)面,推入 Nginx 服務(wù)器。

[[312493]]

小型電商網(wǎng)站的商品詳情頁(yè)系統(tǒng)架構(gòu)

小型電商網(wǎng)站的頁(yè)面展示采用頁(yè)面全量靜態(tài)化的思想。數(shù)據(jù)庫(kù)中存放了所有的商品信息,頁(yè)面靜態(tài)化系統(tǒng),將數(shù)據(jù)填充進(jìn)靜態(tài)模板中,形成靜態(tài)化頁(yè)面,推入 Nginx 服務(wù)器。用戶瀏覽網(wǎng)站頁(yè)面時(shí),取用一個(gè)已經(jīng)靜態(tài)化好的 html 頁(yè)面,直接返回回去,不涉及任何的業(yè)務(wù)邏輯處理。 

Hystrix 實(shí)現(xiàn)資源隔離的“兩把利器”

下面是頁(yè)面模板的簡(jiǎn)單 Demo 。

  1. <html> <body> 商品名稱:#{productName}<br> 商品價(jià)格:#{productPrice}<br> 商品描述:#{productDesc} </body></html> 

這樣做,好處在于,用戶每次瀏覽一個(gè)頁(yè)面,不需要進(jìn)行任何的跟數(shù)據(jù)庫(kù)的交互邏輯,也不需要執(zhí)行任何的代碼,直接返回一個(gè) html 頁(yè)面就可以了,速度和性能非常高。

對(duì)于小網(wǎng)站,頁(yè)面很少,很實(shí)用,非常簡(jiǎn)單,Java 中可以使用 velocity、freemarker、thymeleaf 等等,然后做個(gè) cms 頁(yè)面內(nèi)容管理系統(tǒng),模板變更的時(shí)候,點(diǎn)擊按鈕或者系統(tǒng)自動(dòng)化重新進(jìn)行全量渲染。

壞處在于,僅僅適用于一些小型的網(wǎng)站,比如頁(yè)面的規(guī)模在幾十到幾萬(wàn)不等。對(duì)于一些大型的電商網(wǎng)站,億級(jí)數(shù)量的頁(yè)面,你說(shuō)你每次頁(yè)面模板修改了,都需要將這么多頁(yè)面全量靜態(tài)化,靠譜嗎?每次渲染花個(gè)好幾天時(shí)間,那你整個(gè)網(wǎng)站就廢掉了。

大型電商網(wǎng)站的商品詳情頁(yè)系統(tǒng)架構(gòu)

大型電商網(wǎng)站商品詳情頁(yè)的系統(tǒng)設(shè)計(jì)中,當(dāng)商品數(shù)據(jù)發(fā)生變更時(shí),會(huì)將變更消息壓入 MQ 消息隊(duì)列中。緩存服務(wù)從消息隊(duì)列中消費(fèi)這條消息時(shí),感知到有數(shù)據(jù)發(fā)生變更,便通過(guò)調(diào)用數(shù)據(jù)服務(wù)接口,獲取變更后的數(shù)據(jù),然后將整合好的數(shù)據(jù)推送至 redis 中。Nginx 本地緩存的數(shù)據(jù)是有一定的時(shí)間期限的,比如說(shuō) 10 分鐘,當(dāng)數(shù)據(jù)過(guò)期之后,它就會(huì)從 redis 獲取到最新的緩存數(shù)據(jù),并且緩存到自己本地。

用戶瀏覽網(wǎng)頁(yè)時(shí),動(dòng)態(tài)將 Nginx 本地?cái)?shù)據(jù)渲染到本地 html 模板并返回給用戶。 

Hystrix 實(shí)現(xiàn)資源隔離的“兩把利器”

雖然沒(méi)有直接返回 html 頁(yè)面那么快,但是因?yàn)閿?shù)據(jù)在本地緩存,所以也很快,其實(shí)耗費(fèi)的也就是動(dòng)態(tài)渲染一個(gè) html 頁(yè)面的性能。如果 html 模板發(fā)生了變更,不需要將所有的頁(yè)面重新靜態(tài)化,也不需要發(fā)送請(qǐng)求,沒(méi)有網(wǎng)絡(luò)請(qǐng)求的開(kāi)銷,直接將數(shù)據(jù)渲染進(jìn)最新的 html 頁(yè)面模板后響應(yīng)即可。

在這種架構(gòu)下,我們需要保證系統(tǒng)的高可用性。

如果系統(tǒng)訪問(wèn)量很高,Nginx 本地緩存過(guò)期失效了,redis 中的緩存也被 LRU 算法給清理掉了,那么會(huì)有較高的訪問(wèn)量,從緩存服務(wù)調(diào)用商品服務(wù)。但如果此時(shí)商品服務(wù)的接口發(fā)生故障,調(diào)用出現(xiàn)了延時(shí),緩存服務(wù)全部的線程都被這個(gè)調(diào)用商品服務(wù)接口給耗盡了,每個(gè)線程去調(diào)用商品服務(wù)接口的時(shí)候,都會(huì)卡住很長(zhǎng)時(shí)間,后面大量的請(qǐng)求過(guò)來(lái)都會(huì)卡在那兒,此時(shí)緩存服務(wù)沒(méi)有足夠的線程去調(diào)用其它一些服務(wù)的接口,從而導(dǎo)致整個(gè)大量的商品詳情頁(yè)無(wú)法正常顯示。

這其實(shí)就是一個(gè)商品接口服務(wù)故障導(dǎo)致緩存服務(wù)資源耗盡的現(xiàn)象。

基于 Hystrix 線程池技術(shù)

上文提到,如果從 Nginx 開(kāi)始,緩存都失效了,Nginx 會(huì)直接通過(guò)緩存服務(wù)調(diào)用商品服務(wù)獲取最新商品數(shù)據(jù)(我們基于電商項(xiàng)目做個(gè)討論),有可能出現(xiàn)調(diào)用延時(shí)而把緩存服務(wù)資源耗盡的情況。這里,我們就來(lái)說(shuō)說(shuō),怎么通過(guò) Hystrix 線程池技術(shù)實(shí)現(xiàn)資源隔離。

資源隔離,就是說(shuō),你如果要把對(duì)某一個(gè)依賴服務(wù)的所有調(diào)用請(qǐng)求,全部隔離在同一份資源池內(nèi),不會(huì)去用其它資源了,這就叫資源隔離。哪怕對(duì)這個(gè)依賴服務(wù),比如說(shuō)商品服務(wù),現(xiàn)在同時(shí)發(fā)起的調(diào)用量已經(jīng)到了 1000,但是分配給商品服務(wù)線程池內(nèi)就 10 個(gè)線程,最多就只會(huì)用這 10 個(gè)線程去執(zhí)行。不會(huì)因?yàn)閷?duì)商品服務(wù)調(diào)用的延遲,將 Tomcat 內(nèi)部所有的線程資源全部耗盡。

Hystrix 進(jìn)行資源隔離,其實(shí)是提供了一個(gè)抽象,叫做 Command。這也是 Hystrix 最最基本的資源隔離技術(shù)。

利用 HystrixCommand 獲取單條數(shù)據(jù)

我們通過(guò)將調(diào)用商品服務(wù)的操作封裝在 HystrixCommand 中,限定一個(gè) key,比如下面的 GetProductInfoCommandGroup,在這里我們可以簡(jiǎn)單認(rèn)為這是一個(gè)線程池,每次調(diào)用商品服務(wù),就只會(huì)用該線程池中的資源,不會(huì)再去用其它線程資源了。

  1. public class GetProductInfoCommand extends HystrixCommand<ProductInfo> { 
  2.  private Long productId; 
  3.  public GetProductInfoCommand(Long productId) { super(HystrixCommandGroupKey.Factory.asKey("GetProductInfoCommandGroup")); this.productId = productId; } 
  4.  @Override protected ProductInfo run() { String url = "http://localhost:8081/getProductInfo?productId=" + productId; // 調(diào)用商品服務(wù)接口 String response = HttpClientUtils.sendGetRequest(url); return JSONObject.parseObject(response, ProductInfo.class); }} 

我們?cè)诰彺娣?wù)接口中,根據(jù) productId 創(chuàng)建 Command 并執(zhí)行,獲取到商品數(shù)據(jù)。

  1. @RequestMapping("/getProductInfo")@ResponseBodypublic String getProductInfo(Long productId) { HystrixCommand<ProductInfo> getProductInfoCommand = new GetProductInfoCommand(productId); 
  2.  // 通過(guò)command執(zhí)行,獲取最新商品數(shù)據(jù) ProductInfo productInfo = getProductInfoCommand.execute(); System.out.println(productInfo); return "success";} 

上面執(zhí)行的是 execute() 方法,其實(shí)是同步的。也可以對(duì) command 調(diào)用 queue() 方法,它僅僅是將 command 放入線程池的一個(gè)等待隊(duì)列,就立即返回,拿到一個(gè) Future 對(duì)象,后面可以繼續(xù)做其它一些事情,然后過(guò)一段時(shí)間對(duì) Future 調(diào)用 get() 方法獲取數(shù)據(jù)。這是異步的。

利用 HystrixObservableCommand 批量獲取數(shù)據(jù)

只要是獲取商品數(shù)據(jù),全部都綁定到同一個(gè)線程池里面去,我們通過(guò) HystrixObservableCommand 的一個(gè)線程去執(zhí)行,而在這個(gè)線程里面,批量把多個(gè) productId 的 productInfo 拉回來(lái)。

  1. public class GetProductInfosCommand extends HystrixObservableCommand<ProductInfo> { 
  2.  private String[] productIds; 
  3.  public GetProductInfosCommand(String[] productIds) { // 還是綁定在同一個(gè)線程池 super(HystrixCommandGroupKey.Factory.asKey("GetProductInfoGroup")); this.productIds = productIds; } 
  4.  @Override protected Observable<ProductInfo> construct() { return Observable.unsafeCreate((Observable.OnSubscribe<ProductInfo>) subscriber -> { 
  5.  for (String productId : productIds) { // 批量獲取商品數(shù)據(jù) String url = "http://localhost:8081/getProductInfo?productId=" + productId; String response = HttpClientUtils.sendGetRequest(url); ProductInfo productInfo = JSONObject.parseObject(response, ProductInfo.class); subscriber.onNext(productInfo); } subscriber.onCompleted(); 
  6.  }).subscribeOn(Schedulers.io()); }} 

在緩存服務(wù)接口中,根據(jù)傳來(lái)的 id 列表,比如是以 , 分隔的 id 串,通過(guò)上面的 HystrixObservableCommand,執(zhí)行 Hystrix 的一些 API 方法,獲取到所有商品數(shù)據(jù)。

  1. public String getProductInfos(String productIds) { String[] productIdArray = productIds.split(","); HystrixObservableCommand<ProductInfo> getProductInfosCommand = new GetProductInfosCommand(productIdArray); Observable<ProductInfo> observable = getProductInfosCommand.observe(); 
  2.  observable.subscribe(new Observer<ProductInfo>() { @Override public void onCompleted() { System.out.println("獲取完了所有的商品數(shù)據(jù)"); } 
  3.  @Override public void onError(Throwable e) { e.printStackTrace(); } 
  4.  /** * 獲取完一條數(shù)據(jù),就回調(diào)一次這個(gè)方法 * @param productInfo */ @Override public void onNext(ProductInfo productInfo) { System.out.println(productInfo); } }); return "success";} 

我們回過(guò)頭來(lái),看看 Hystrix 線程池技術(shù)是如何實(shí)現(xiàn)資源隔離的。 

Hystrix 實(shí)現(xiàn)資源隔離的“兩把利器”

從 Nginx 開(kāi)始,緩存都失效了,那么 Nginx 通過(guò)緩存服務(wù)去調(diào)用商品服務(wù)。緩存服務(wù)默認(rèn)的線程大小是 10 個(gè),最多就只有 10 個(gè)線程去調(diào)用商品服務(wù)的接口。即使商品服務(wù)接口故障了,最多就只有 10 個(gè)線程會(huì) hang 死在調(diào)用商品服務(wù)接口的路上,緩存服務(wù)的 Tomcat 內(nèi)其它的線程還是可以用來(lái)調(diào)用其它的服務(wù),干其它的事情。

基于 Hystrix 信號(hào)量

Hystrix 里面核心的一項(xiàng)功能,其實(shí)就是所謂的資源隔離,要解決的最最核心的問(wèn)題,就是將多個(gè)依賴服務(wù)的調(diào)用分別隔離到各自的資源池內(nèi)。避免說(shuō)對(duì)某一個(gè)依賴服務(wù)的調(diào)用,因?yàn)橐蕾嚪?wù)的接口調(diào)用的延遲或者失敗,導(dǎo)致服務(wù)所有的線程資源全部耗費(fèi)在這個(gè)服務(wù)的接口調(diào)用上。一旦說(shuō)某個(gè)服務(wù)的線程資源全部耗盡的話,就可能導(dǎo)致服務(wù)崩潰,甚至說(shuō)這種故障會(huì)不斷蔓延。

Hystrix 實(shí)現(xiàn)資源隔離,主要有兩種技術(shù):

•線程池•信號(hào)量

默認(rèn)情況下,Hystrix 使用線程池模式。

前面已經(jīng)說(shuō)過(guò)線程池技術(shù)了,這一小節(jié)就來(lái)說(shuō)說(shuō)信號(hào)量機(jī)制實(shí)現(xiàn)資源隔離,以及這兩種技術(shù)的區(qū)別與具體應(yīng)用場(chǎng)景。

信號(hào)量機(jī)制

信號(hào)量的資源隔離只是起到一個(gè)開(kāi)關(guān)的作用,比如,服務(wù) A 的信號(hào)量大小為 10,那么就是說(shuō)它同時(shí)只允許有 10 個(gè) tomcat 線程來(lái)訪問(wèn)服務(wù) A,其它的請(qǐng)求都會(huì)被拒絕,從而達(dá)到資源隔離和限流保護(hù)的作用。 

Hystrix 實(shí)現(xiàn)資源隔離的“兩把利器”

線程池與信號(hào)量區(qū)別

線程池隔離技術(shù),并不是說(shuō)去控制類似 tomcat 這種 web 容器的線程。更加嚴(yán)格的意義上來(lái)說(shuō),Hystrix 的線程池隔離技術(shù),控制的是 tomcat 線程的執(zhí)行。Hystrix 線程池滿后,會(huì)確保說(shuō),tomcat 的線程不會(huì)因?yàn)橐蕾嚪?wù)的接口調(diào)用延遲或故障而被 hang 住,tomcat 其它的線程不會(huì)卡死,可以快速返回,然后支撐其它的事情。

線程池隔離技術(shù),是用 Hystrix 自己的線程去執(zhí)行調(diào)用;而信號(hào)量隔離技術(shù),是直接讓 tomcat 線程去調(diào)用依賴服務(wù)。信號(hào)量隔離,只是一道關(guān)卡,信號(hào)量有多少,就允許多少個(gè) tomcat 線程通過(guò)它,然后去執(zhí)行。 

Hystrix 實(shí)現(xiàn)資源隔離的“兩把利器”

適用場(chǎng)景:

•線程池技術(shù),適合絕大多數(shù)場(chǎng)景,比如說(shuō)我們對(duì)依賴服務(wù)的網(wǎng)絡(luò)請(qǐng)求的調(diào)用和訪問(wèn)、需要對(duì)調(diào)用的 timeout 進(jìn)行控制(捕捉 timeout 超時(shí)異常)。•信號(hào)量技術(shù),適合說(shuō)你的訪問(wèn)不是對(duì)外部依賴的訪問(wèn),而是對(duì)內(nèi)部的一些比較復(fù)雜的業(yè)務(wù)邏輯的訪問(wèn),并且系統(tǒng)內(nèi)部的代碼,其實(shí)不涉及任何的網(wǎng)絡(luò)請(qǐng)求,那么只要做信號(hào)量的普通限流就可以了,因?yàn)椴恍枰ゲ东@ timeout 類似的問(wèn)題。

信號(hào)量簡(jiǎn)單 Demo

業(yè)務(wù)背景里,比較適合信號(hào)量的是什么場(chǎng)景呢?

比如說(shuō),我們一般來(lái)說(shuō),緩存服務(wù),可能會(huì)將一些量特別少、訪問(wèn)又特別頻繁的數(shù)據(jù),放在自己的純內(nèi)存中。

舉個(gè)栗子。一般我們?cè)讷@取到商品數(shù)據(jù)之后,都要去獲取商品是屬于哪個(gè)地理位置、省、市、賣家等,可能在自己的純內(nèi)存中,比如就一個(gè) Map 去獲取。對(duì)于這種直接訪問(wèn)本地內(nèi)存的邏輯,比較適合用信號(hào)量做一下簡(jiǎn)單的隔離。

優(yōu)點(diǎn)在于,不用自己管理線程池啦,不用 care timeout 超時(shí)啦,也不需要進(jìn)行線程的上下文切換啦。信號(hào)量做隔離的話,性能相對(duì)來(lái)說(shuō)會(huì)高一些。

假如這是本地緩存,我們可以通過(guò) cityId,拿到 cityName。

  1. public class LocationCache { private static Map<Long, String> cityMap = new HashMap<>(); 
  2.  static { cityMap.put(1L, "北京"); } 
  3.  /** * 通過(guò)cityId 獲取 cityName * * @param cityId 城市id * @return 城市名 */ public static String getCityName(Long cityId) { return cityMap.get(cityId); }} 

寫(xiě)一個(gè) GetCityNameCommand,策略設(shè)置為信號(hào)量。run() 方法中獲取本地緩存。我們目的就是對(duì)獲取本地緩存的代碼進(jìn)行資源隔離。

  1. public class GetCityNameCommand extends HystrixCommand<String> { 
  2.  private Long cityId; 
  3.  public GetCityNameCommand(Long cityId) { // 設(shè)置信號(hào)量隔離策略 super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("GetCityNameGroup")) .andCommandPropertiesDefaults(HystrixCommandProperties.Setter() .withExecutionIsolationStrategy(HystrixCommandProperties.ExecutionIsolationStrategy.SEMAPHORE))); 
  4.  this.cityId = cityId; } 
  5.  @Override protected String run() { // 需要進(jìn)行信號(hào)量隔離的代碼 return LocationCache.getCityName(cityId); }} 

在接口層,通過(guò)創(chuàng)建 GetCityNameCommand,傳入 cityId,執(zhí)行 execute() 方法,那么獲取本地 cityName 緩存的代碼將會(huì)進(jìn)行信號(hào)量的資源隔離。

  1. @RequestMapping("/getProductInfo")@ResponseBodypublic String getProductInfo(Long productId) { HystrixCommand<ProductInfo> getProductInfoCommand = new GetProductInfoCommand(productId); 
  2.  // 通過(guò)command執(zhí)行,獲取最新商品數(shù)據(jù) ProductInfo productInfo = getProductInfoCommand.execute(); 
  3.  Long cityId = productInfo.getCityId(); 
  4.  GetCityNameCommand getCityNameCommand = new GetCityNameCommand(cityId); // 獲取本地內(nèi)存(cityName)的代碼會(huì)被信號(hào)量進(jìn)行資源隔離 String cityName = getCityNameCommand.execute(); 
  5.  productInfo.setCityName(cityName); 
  6.  System.out.println(productInfo); return "success";} 

 

責(zé)任編輯:武曉燕 來(lái)源: 今日頭條
相關(guān)推薦

2022-11-21 06:55:08

golang協(xié)程

2010-09-17 20:46:11

2017-04-03 21:52:30

隔離線程池分布式

2023-11-09 08:18:31

Hystrix保護(hù)系統(tǒng)資源隔離

2017-05-20 15:07:02

大數(shù)據(jù)數(shù)字油田通用電氣

2017-05-23 14:31:59

大數(shù)據(jù)分析學(xué)數(shù)據(jù)

2017-04-13 09:57:39

機(jī)器學(xué)習(xí)人工智能AI

2021-07-12 08:39:14

程序員外賣小哥代碼

2023-09-20 10:07:26

Linux虛擬化

2014-01-07 14:29:14

HadoopYARN

2016-10-24 09:37:51

系統(tǒng)日志日志分析

2024-08-28 08:48:20

Linux資源隔離

2025-04-23 11:00:00

Hystrix隔離模式信號(hào)量

2015-11-24 16:59:13

2017-07-04 17:35:46

微服務(wù)架構(gòu)Spring Clou

2013-11-19 13:12:32

移動(dòng)互聯(lián)網(wǎng)

2018-05-14 08:36:53

微服務(wù)接入層動(dòng)靜資源

2009-03-05 13:36:10

沖突云計(jì)算SOA

2010-01-18 10:48:05

JBoss類隔離

2010-08-03 13:27:04

FlexBuilder
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)