Hystrix中的批量(折叠)请求

Hystrix具有折叠(或批处理)请求的高级功能。 如果两个或多个命令同时运行相似的请求,Hystrix可以将它们组合在一起,运行一个批处理请求,并将拆分结果分派回所有命令。 首先让我们看看Hystrix如何工作而不会崩溃。 假设我们有一个StockPrice给定Ticker StockPrice的服务:

import lombok.Value;
import java.math.BigDecimal;
import java.time.Instant;@Value
class Ticker {String symbol;
}@Value
class StockPrice {BigDecimal price;Instant effectiveTime;
}interface StockPriceGateway {default StockPrice load(Ticker stock) {final Set<Ticker> oneTicker = Collections.singleton(stock);return loadAll(oneTicker).get(stock);}ImmutableMap<Ticker, StockPrice> loadAll(Set<Ticker> tickers);
}

为了方便起见, StockPriceGateway核心实现必须提供loadAll()批处理方法,而实现load()方法则是为了方便。 因此,我们的网关能够批量加载多个价格(例如,以减少延迟或网络协议开销),但是目前我们不使用此功能,始终一次加载一个股票的价格:

class StockPriceCommand extends HystrixCommand<StockPrice> {private final StockPriceGateway gateway;private final Ticker stock;StockPriceCommand(StockPriceGateway gateway, Ticker stock) {super(HystrixCommandGroupKey.Factory.asKey("Stock"));this.gateway = gateway;this.stock = stock;}@Overrideprotected StockPrice run() throws Exception {return gateway.load(stock);}
}

这样的命令将始终为每个Ticker调用StockPriceGateway.load() ,如以下测试所示:

class StockPriceCommandTest extends Specification {def gateway = Mock(StockPriceGateway)def 'should fetch price from external service'() {given:gateway.load(TickerExamples.any()) >> StockPriceExamples.any()def command = new StockPriceCommand(gateway, TickerExamples.any())when:def price = command.execute()then:price == StockPriceExamples.any()}def 'should call gateway exactly once when running Hystrix command'() {given:def command = new StockPriceCommand(gateway, TickerExamples.any())when:command.execute()then:1 * gateway.load(TickerExamples.any())}def 'should call gateway twice when command executed two times'() {given:def commandOne = new StockPriceCommand(gateway, TickerExamples.any())def commandTwo = new StockPriceCommand(gateway, TickerExamples.any())when:commandOne.execute()commandTwo.execute()then:2 * gateway.load(TickerExamples.any())}def 'should call gateway twice even when executed in parallel'() {given:def commandOne = new StockPriceCommand(gateway, TickerExamples.any())def commandTwo = new StockPriceCommand(gateway, TickerExamples.any())when:Future<StockPrice> futureOne = commandOne.queue()Future<StockPrice> futureTwo = commandTwo.queue()and:futureOne.get()futureTwo.get()then:2 * gateway.load(TickerExamples.any())}}

如果您不了解Hystrix,则通过将外部调用包装在命令中可以获得许多功能,例如超时,断路器等。但这不是本文的重点。 看一下最后两个测试:当两次,顺序或并行( queue() )两次询问任意行情的价格时,我们的外部gateway也被两次调用。 上一次测试特别有趣–我们几乎同时要求相同的报价,但Hystrix不能弄清楚。 这两个命令是完全独立的,将在不同的线程中执行,彼此之间一无所知-即使它们几乎同时运行。

折叠就是找到类似的请求并将其组合。 批处理(我将这个术语与崩溃互换使用)不会自动发生,并且需要一些编码。 但是首先让我们看看它的行为:

def 'should collapse two commands executed concurrently for the same stock ticker'() {given:def anyTicker = TickerExamples.any()def tickers = [anyTicker] as Setand:def commandOne = new StockTickerPriceCollapsedCommand(gateway, anyTicker)def commandTwo = new StockTickerPriceCollapsedCommand(gateway, anyTicker)when:Future<StockPrice> futureOne = commandOne.queue()Future<StockPrice> futureTwo = commandTwo.queue()and:futureOne.get()futureTwo.get()then:0 * gateway.load(_)1 * gateway.loadAll(tickers) >> ImmutableMap.of(anyTicker, StockPriceExamples.any())
}def 'should collapse two commands executed concurrently for the different stock tickers'() {given:def anyTicker = TickerExamples.any()def otherTicker = TickerExamples.other()def tickers = [anyTicker, otherTicker] as Setand:def commandOne = new StockTickerPriceCollapsedCommand(gateway, anyTicker)def commandTwo = new StockTickerPriceCollapsedCommand(gateway, otherTicker)when:Future<StockPrice> futureOne = commandOne.queue()Future<StockPrice> futureTwo = commandTwo.queue()and:futureOne.get()futureTwo.get()then:1 * gateway.loadAll(tickers) >> ImmutableMap.of(anyTicker, StockPriceExamples.any(),otherTicker, StockPriceExamples.other())
}def 'should correctly map collapsed response into individual requests'() {given:def anyTicker = TickerExamples.any()def otherTicker = TickerExamples.other()def tickers = [anyTicker, otherTicker] as Setgateway.loadAll(tickers) >> ImmutableMap.of(anyTicker, StockPriceExamples.any(),otherTicker, StockPriceExamples.other())and:def commandOne = new StockTickerPriceCollapsedCommand(gateway, anyTicker)def commandTwo = new StockTickerPriceCollapsedCommand(gateway, otherTicker)when:Future<StockPrice> futureOne = commandOne.queue()Future<StockPrice> futureTwo = commandTwo.queue()and:def anyPrice = futureOne.get()def otherPrice = futureTwo.get()then:anyPrice == StockPriceExamples.any()otherPrice == StockPriceExamples.other()
}

第一个测试证明,不是两次调用load() ,而是几乎一次调用loadAll() 。 还要注意,由于我们要求相同的Ticker (来自两个不同的线程),因此loadAll()仅请求一个代码。 第二个测试显示两个并发请求,两个不同的行情收录器被折叠为一个批处理调用。 第三次测试可确保我们仍能对每个单独的请求得到正确的响应。 相反,延长HystrixCommand我们必须扩展更加复杂HystrixCollapser 。 现在是时候看到StockTickerPriceCollapsedCommand实现,它无缝替换了StockPriceCommand

class StockTickerPriceCollapsedCommand extends HystrixCollapser<ImmutableMap<Ticker, StockPrice>, StockPrice, Ticker> {private final StockPriceGateway gateway;private final Ticker stock;StockTickerPriceCollapsedCommand(StockPriceGateway gateway, Ticker stock) {super(HystrixCollapser.Setter.withCollapserKey(HystrixCollapserKey.Factory.asKey("Stock")).andCollapserPropertiesDefaults(HystrixCollapserProperties.Setter().withTimerDelayInMilliseconds(100)));this.gateway = gateway;this.stock = stock;}@Overridepublic Ticker getRequestArgument() {return stock;}@Overrideprotected HystrixCommand<ImmutableMap<Ticker, StockPrice>> createCommand(Collection<CollapsedRequest<StockPrice, Ticker>> collapsedRequests) {final Set<Ticker> stocks = collapsedRequests.stream().map(CollapsedRequest::getArgument).collect(toSet());return new StockPricesBatchCommand(gateway, stocks);}@Overrideprotected void mapResponseToRequests(ImmutableMap<Ticker, StockPrice> batchResponse, Collection<CollapsedRequest<StockPrice, Ticker>> collapsedRequests) {collapsedRequests.forEach(request -> {final Ticker ticker = request.getArgument();final StockPrice price = batchResponse.get(ticker);request.setResponse(price);});}}

这里有很多事情要做,所以让我们逐步回顾StockTickerPriceCollapsedCommand 。 前三种通用类型:

  • BatchReturnType (在我们的示例中为ImmutableMap<Ticker, StockPrice> )是批处理命令响应的类型。 如您将在后面看到的那样,崩溃程序会将多个小命令变成批处理命令。 这是该批处理命令的响应的类型。 请注意,它与StockPriceGateway.loadAll()类型相同。
  • ResponseTypeStockPrice )是要折叠的每个命令的类型。 在我们的例子中,我们正在折叠HystrixCommand<StockPrice> 。 稍后,我们将BatchReturnTypeBatchReturnType为多个StockPrice
  • RequestArgumentTypeTicker )是我们将要折叠(批处理)的每个命令的输入。 当多个命令一起批处理时,我们最终将所有这些替换为一个批处理命令。 此命令应接收所有单个请求,以便执行一个批处理请求。

withTimerDelayInMilliseconds(100)将在稍后说明。 createCommand()创建一个批处理命令。 此命令应替换所有单个命令并执行批处理逻辑。 在我们的情况下,我们不会进行多次单独的load()调用:

class StockPricesBatchCommand extends HystrixCommand<ImmutableMap<Ticker, StockPrice>> {private final StockPriceGateway gateway;private final Set<Ticker> stocks;StockPricesBatchCommand(StockPriceGateway gateway, Set<Ticker> stocks) {super(HystrixCommandGroupKey.Factory.asKey("Stock"));this.gateway = gateway;this.stocks = stocks;}@Overrideprotected ImmutableMap<Ticker, StockPrice> run() throws Exception {return gateway.loadAll(stocks);}
}

此类与StockPriceCommand之间的唯一区别是,它需要一堆Ticker并返回所有价格。 Hystrix将收集几个StockTickerPriceCollapsedCommand实例,一旦它具有足够StockTickerPriceCollapsedCommand (稍后再介绍),它将创建一个StockPriceCommand 。 希望这很清楚,因为mapResponseToRequests()涉及的更多。 折叠后的StockPricesBatchCommand完成后,我们必须以某种方式拆分批处理响应,并将回复传达回各个命令,而不会崩溃。 从这个角度来看, mapResponseToRequests()实现非常简单:我们收到批处理响应和包装CollapsedRequest<StockPrice, Ticker>的集合。 现在,我们必须遍历所有正在等待的单个请求并完成它们( setResponse() )。 如果我们不完成某些请求,它们将无限期挂起并最终超时。

怎么运行的

这是描述如何实现折叠的正确时机。 我之前说过崩溃是在同时发生两个请求时发生的。 没有一样的时间了 。 实际上,当第一个可折叠请求进入时,Hystrix会启动一个计时器。 在我们的示例中,我们将其设置为100毫秒。 在此期间,我们的命令被挂起,等待其他命令加入。 在此可配置时间段之后,Hystrix将调用createCommand() ,收集所有请求键(通过调用getRequestArgument() )并运行它。 批处理命令完成后,它将使我们将结果分发给所有等待中的单个命令。 如果我们担心创建庞大的批处理,也可以限制已折叠请求的数量–另一方面,在此短时间内可以容纳多少并发请求?

用例和缺点

请求折叠应在承受高负载(请求频率很高)的系统中使用。 如果每个折叠时间窗口(在示例中为100毫秒)仅收到一个请求,则折叠只会增加开销。 这是因为每次您调用可折叠命令时,它都必须等待,以防万一其他命令想要加入并形成批处理。 仅当至少折叠了几个命令时,这才有意义。 节省网络等待时间和/或更好地利用合作者中的资源可以平衡浪费的等待时间(与单个呼叫相比,批处理请求通常要快得多)。 但是请记住,折叠是一把双刃剑,在特定情况下很有用。

最后要记住的一件事–为了使用请求折叠,您需要在try-finally块中使用HystrixRequestContext.initializeContext()shutdown()

HystrixRequestContext context = HystrixRequestContext.initializeContext();
try {//...
} finally {context.shutdown();
}

崩溃与缓存

您可能会认为可以通过适当的缓存来代替崩溃。 这不是真的。 在以下情况下使用缓存:

  1. 资源可能会被多次访问
  2. 我们可以安全地使用先前的值,它会在一段时间内保持有效, 或者我们确切地知道如何使它无效
  3. 我们可以提供对同一资源的并发请求以多次计算

另一方面,折叠不会强制数据的局部性(1),它总是命中实际服务,并且永远不会返回陈旧的数据(2)。 最后,如果我们从多个线程中请求相同的资源,我们将仅调用一次备份服务(3)。 在进行缓存的情况下,除非您的缓存真的很聪明,否则两个线程将独立地发现缓存中没有给定的资源,并两次请求支持服务。 但是,折叠可以与缓存一起使用-通过在运行可折叠命令之前咨询缓存。

摘要

请求折叠是一个有用的工具,但是用例非常有限。 它可以显着提高我们系统的吞吐量,并限制外部服务的负载。 崩溃可以神奇地平抑流量高峰,而不是将其散布到各处。 只要确保将其用于以极高频率运行的命令即可。

翻译自: https://www.javacodegeeks.com/2014/11/batching-collapsing-requests-in-hystrix.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/361714.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C#繁简转换

//1.using System.Runtime.InteropServices; //2.import kernel32.dll [DllImport("kernel32.dll",EntryPoint "LCMapStringA")]    public static extern int LCMapString(int Locale,int dwMapFlags,byte[] lpSrcStr,int cchSrc,byte[] lpDestStr,…

css笔记 2

定义一个类选择器.center {text-align: center} h1 有 center 类。这意味将遵守 ".center" 选择器中的规则。<h1 class"center">This heading will be center-aligned</h1> 类名的第一个字符不能使用数字&#xff01; 派生选择器td.fancy {…

合并购物车

合并购物车逻辑分析 合并方向&#xff1a;cookie 购物车数据合并到 Redis 购物车数据中。合并数据&#xff1a;购物车商品数据和勾选状态。合并方案&#xff1a; Redis 数据库中的购物车数据保留。如果 cookie 中的购物车数据在 Redis 数据库中已存在 将 cookie 购物车数据覆盖…

Swing和JavaFX:使用JFXPanel

我很快将不得不在基于Swing的胖客户端中处理JavaFX –哦&#xff0c;对不起&#xff0c;我的意思是“多层富客户端”&#xff01; 因此&#xff0c;这使我来看看JFXPanel 。 JFXPanel是一个javax.swing.JComponent&#xff0c;用于将JavaFX内容嵌入到Swing-UI中。 JFXPanel的用…

关于nodejs中npm命令没有反应的解决方法

最近在学习angularJS&#xff0c;正在做一个单页面应用&#xff0c;在安装nodejs之后&#xff0c;发现命令行传了输入npm -v能输出结果外&#xff0c;其余npm的任何操作都没有反应&#xff0c;一开始我以为是下载的比较慢&#xff0c;等了半小时还是没动静&#xff0c;只有下标…

Red Hat Enterprise 5 server 上安装 memcached 的问题记录

国内私募机构九鼎控股打造APP&#xff0c;来就送 20元现金领取地址&#xff1a;http://jdb.jiudingcapital.com/phone.html内部邀请码&#xff1a;C8E245J &#xff08;不写邀请码&#xff0c;没有现金送&#xff09;国内私募机构九鼎控股打造&#xff0c;九鼎投资是在全国股份…

统计一个panel中lable的个数

int n panel.Controls.OfType<Label>().Count();转载于:https://www.cnblogs.com/linji/archive/2012/09/20/2694640.html

Java扩展机制可加载所有JAR

Java扩展机制在Java教程中被描述为“一种标准的&#xff0c;可伸缩的方式&#xff0c;以使自定义API可供Java平台上运行的所有应用程序使用。” 如了解扩展类加载中所述 &#xff0c;“扩展框架利用类加载委托机制”&#xff0c;其中扩展类在rt.jar &#xff08;和相关的JAR&am…

js中的 return false;

总的来说return false 的作用就是阻止事件的默认行为 1、 function check() { if(form.title.value"") { alert("请输入文章标题!"); return false; //注意不能写成 return(false); } if(form.content.value"") { alert("文章正文不能为空…

C++的文艺复兴: Why C++? 王者归来

因为又有人叫我去Quora的C2C站去回答问题了&#xff0c;这回是 关于 《2012 不宜进入的三个技术点ActionScript&#xff0c;Thread 和 C&#xff0c; C争议的争议最大。(要我说&#xff0c;.NET比C更需要慎重进入&#xff0c;呵)。我就在这里回复一下这个问题吧。 正好我前段时…

PAT_B_1012 数字分类 (有待改进)

题目描述&#xff1a; 给定一系列正整数&#xff0c;请按要求对数字进行分类&#xff0c;并输出以下 5 个数字&#xff1a; A​1​​ 能被 5 整除的数字中所有偶数的和&#xff1b; A​2​​ 将被 5 除后余 1 的数字按给出顺序进行交错求和&#xff0c;即计算 n​1​​ −n…

Drools和jBPM KIE A​​pps平台

随着Drools和jBPM&#xff08;KIE&#xff09;6系列出现了一个新的工作台&#xff0c;并有望最终实现用户的可扩展性。 我终于有了一些预告片&#xff0c;以显示此工作原理以及所存储的内容。 确保选择1080p并全屏显示&#xff0c;以达到最佳效果。 &#xff08;点击放大&…

js 严格模式

一、概述 除了正常运行模式&#xff0c;ECMAscript 5添加了第二种运行模式&#xff1a;"严格模式"&#xff08;strict mode&#xff09;。顾名思义&#xff0c;这种模式使得Javascript在更严格的条件下运行。 设立"严格模式"的目的&#xff0c;主要有以下…

软件工程之系统建模

1、系统工程&#xff1a; 软件工程由系统工程演变而来&#xff0c;要了解软件工程应先了解系统工程。系统工程一般通过自顶向下、自底向上的方法&#xff0c;用层次结构来来分析整个系统。在系统工程层次图中自顶向下依次是全局视图&#xff08;业务或产品域&#xff09;——领…

采访田飞师兄有感 ——by 李皈颖

来MSRA后一个月了&#xff0c;终于迎来了现代软件工程&#xff0c;开课第一天&#xff0c;殷老师&#xff08;也是我的“馒头”&#xff09;要求我们去采访一下前任师兄&#xff0c;了解一下他们的感受。我们组联合采访了陈凯师兄&#xff0c;但是因为我忘记了&#xff0c;所以…

不要在facelets中重复表情

您是否曾经在JSF中看到过像这样的重复EL表达式&#xff1f; <h:inputText value"#{oneBean.name}" rendered"#{anotherBean.showPerson}"/> <h:inputText value"#{oneBean.birthday}" rendered"#{anotherBean.showPerson}"/…

模态对话框和全选反选

一、目标 制作一个表格&#xff0c;第一行分别为选择、主机名和端口增加一个按钮&#xff0c;名称为添加点击添加按钮&#xff0c;出现一个半透明的遮罩层&#xff0c;遮罩层中间有个弹出框弹出框中有两个输入框&#xff0c;分别为主机名和端口&#xff0c;还有两个按钮&#…

(转)iReaper for wp7正式发布

原文地址&#xff1a;http://www.cnblogs.com/AlexCheng/archive/2012/02/06/2339968.htmliReaper for windows phone 7今天正式发布了。有windows phone 7手机的朋友可以通过手机在线收听收看webcast中文课程。只要你有网络任何时间任何地点都可以学习微软技术&#xff0c;为您…

Neo4j:Cypher –避免热切

当心渴望的管道 尽管我喜欢Cypher的LOAD CSV命令使它容易地将数据获取到Neo4j中的方法&#xff0c;但它目前打破了最不惊奇的规则&#xff0c;因为它急切地在所有行中加载某些查询&#xff0c;即使是那些使用定期提交的查询。 这是我的同事Michael在第二篇博客文章中指出的&a…

一步步构建大型网站架构 [转]

来源: itivy 原文链接 之前我简单向大家介绍了各个知名大型网站的架构&#xff0c;MySpace的五个里程碑、Flickr的架构、YouTube的架构、PlentyOfFish的架构、WikiPedia的架构。这几个都很典型&#xff0c;我们可以从中获取很多有关网站架构方面的知识&#xff0c;看了之后你…