hystrix合并请求_Hystrix中的批处理(折叠)请求

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的一些实例,一旦足够 (稍后会详细介绍),它将创建一个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

hystrix合并请求

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

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

相关文章

C语言项目:扫雷大战精简版

一直说写个几百行的小项目&#xff0c;于是我写了一个控制台的扫雷&#xff0c;没有想到精简完了代码才200行左右&#xff0c;不过考虑到这是我精简过后的&#xff0c;浓缩才是精华嘛&#xff0c;我就发出来大家一起学习啦&#xff0c;看到程序跑起来能玩&#xff0c;感觉还是蛮…

mysql ignore 1 lines_MYSQL使用笔记(1)

创建用户 用户名 dnsexpuser 密码&#xff1a;dnsexp1347insert into mysql.user(Host,User,Password) values (localhost,dnsexpuser,password(dnsexp1347)) ;创建数据库分配数据库权限create database wap_tagdb ;grant all privileges on wap_tagdb.*to waptaglocalhost ide…

Valhalla LW2的进展–内联类型

过去几周&#xff08;2019年6月/ 2019年7月&#xff09;&#xff0c; 瓦尔哈拉计划的LW2阶段/原型最初于2018年10月提出&#xff0c;在面向公众的方面取得了重大进展。 在这篇文章中&#xff0c;我简要总结了最近发布的一些消息&#xff0c;文档和Valhalla Early Access Build。…

mysql密码高级_mysql高级操作

连接数据库# mysql -uroot -p -h10.18.44.209 -p3306授权GRANT ALL PRIVILEGES ON *.* TO root% WITH GRANT OPTION;FLUSH PRIVILEGES;修改数据库密码# vim /etc/my.cnf 追加validate_passwordoff# systemctl restart mysqld方法一&#xff1a;mysql > SET PASSWORD FOR use…

javafx 和swing_Swing和JavaFX:使用JFXPanel

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

C语言十大经典例题:附答案

1、/*输出9*9口诀。共9行9列&#xff0c;i控制行&#xff0c;j控制列。*/#include <stdio.h>int main(){ int i,j,result;for (i1;i<10;i){ for(j1;j<10;j){resulti*j;printf("%d*%d%-3d",i,j,result);/*-3d表示左对齐&#xff0c;占3位*/}printf(&qu…

java正则表达式判断_Java正则表达式判断

/* 判断是否为数字 */ public static boolean isNumeric(String str) { if(str null || str.isEmpty()){ return false; } Pattern pattern Pattern.compile("[0-9]*"); Matcher isNum pattern.matcher(str); if…

C语言项目:图形马赛克处理技术

每个人都有讨厌的人&#xff0c;例如我就比较讨厌三个姓马的人&#xff0c;马云、马化腾和马赛克。马云骗女人的钱&#xff0c;马化腾骗孩子的钱&#xff0c;马赛克阻挡了人们的分享和交流 。那么大家是不是知道我们今天要分享的项目是什么啦&#xff1f;马赛克处理技术莫非就是…

在生产中配置和使用AWS EKS

到现在&#xff0c;我们已经完成了向Amazon EKS &#xff08; 工作地点&#xff09;的迁移&#xff0c;并且集群已经投入生产。 过去&#xff0c;我已经写了一些要点的简短摘要&#xff0c;您可以在这里找到。 当系统正在为实际流量提供服务时&#xff0c;我有了一些额外的信心…

java 监听 变量_[Java学习小记]使用PropertyChangeSupport来监听变量的变化

今天要处理的问题是&#xff1a;监听一个变量&#xff0c;当该变量的值出现变化时能够获知&#xff0c;并进行相应处理。使用java.bean.PropertyChangeSupport类。看如下的构造方法&#xff0c;其实就是将你要控制的对象绑定到该工具中。PropertyChangeSupport changes new Pr…

处理异常功能样式

Java从一开始就支持检查异常。 在Java 8中&#xff0c;语言元素lambda和支持流操作的RT库修改将功能编程风格引入了该语言。 函数样式和异常并不是真正的好朋友。 在本文中&#xff0c;我将描述一个简单的库&#xff0c;该库在某种程度上类似于使用Optional处理null方式处理异常…

C语言项目:灰度处理技术

Hello&#xff0c;今天给大家带来的是一个比较简单的图形处理技术-灰度处理技术。那么到底什么是灰度处理技术呢&#xff1f;简单来说&#xff0c;所谓的灰度处理技术就是把一张彩色的图片变成一张灰色的图片。如下图所示&#xff0c;左边是原图&#xff0c;右边则是已经被处理…

java 设置文本颜色_在Java中更改文本的颜色

添加到我的评论&#xff1a;1)您不应该通过调用paintComponent(..)方法的super.XXX实现来尊重paint链,它应该是覆盖方法中的第一个调用,否则可能发生异常&#xff1a;Overrideprotected void paintComponent(Graphics g) {super.paintComponent(g);Font font new Font("S…

C语言项目:推箱子大战

还记得大家小时候玩过的游戏吗&#xff1f;曾经的坦克大战、推箱子、贪吃蛇都是我们以前玩过的小游戏&#xff0c;然而现在随着大型单机、网络游戏的光芒照耀下&#xff0c;那些曾经的小游戏都渐渐消失了&#xff0c;也或许是我们都已经长大了吧。那么今天&#xff0c;我给大家…

在Spring@Component vs @Repository vs @Service

介绍&#xff1a; 借助Spring的自动扫描功能&#xff0c;它可以自动检测我们的应用程序中定义的各种bean。 我们通常使用可用的Spring注释之一来注释我们的bean- Component&#xff0c; Repository&#xff0c; Service&#xff0c; Controller 。 在检测到bean时&#xff0c;…

java项目加减乘除验证码_课堂Java小程序(加减乘除与验证码)

一、编写一个程序&#xff0c;用户输入两个数&#xff0c;求出其加减乘除&#xff0c;并用消息框显示计算结果。1.设计思想&#xff1a;从键盘输入两个数字和运算符&#xff0c;然后计算。将输入的数字及运算符由字符型转换为整型&#xff0c;再用if判断输入的运算符&#xff0…

C语言绘图:可爱叮当猫

大家对于叮当猫可以说是很熟悉了吧&#xff0c;他还有另外一个名字&#xff0c;也就是哆啦a梦。即便你没有看过他的电影动画&#xff0c;也总会听说过的。叮当猫神奇的口袋总是能够掏出我们幻想功能的任何道具&#xff0c;任意门能够带我们去到任何地方&#xff0c;以及插在头上…

jgit git pull_使用JGit API探索Git内部

jgit git pull您是否想过提交及​​其内容如何存储在Git中&#xff1f; 好吧&#xff0c;我有&#xff0c;在上一个下雨的周末我有一些空闲时间&#xff0c;所以我做了一些研究。 因为我对Java的感觉比对Bash的感觉要多&#xff0c;所以我使用JGit和一些学习测试来探究提交的G…

MFC实现Windows锁屏

编辑Windows锁屏锁屏软件相信大家都见过&#xff0c;以前我去网吧上网的时候也用过这个功能&#xff0c;当你有事情需要立即离开&#xff0c;而又不想让别人碰你的电脑&#xff0c;就需要用扫锁屏软件啦&#xff0c;锁住屏幕之后等回来的时候再输入密码解锁。同样的&#xff0c…

pdf 截图 java_java实现pdf文件截图的方法【附PDFRenderer.jar下载】

本文实例讲述了java实现pdf文件截图的方法。分享给大家供大家参考&#xff0c;具体如下&#xff1a;最近做的一个网站中&#xff0c;有个需求是上传pdf文件&#xff0c;显示pdf的封页&#xff0c;点击封页之后进行在线阅读&#xff0c;这里使用的是PDFRender对pdf进行截图。pub…