当前位置: 首页 > 网络应用技术

我很震惊!完整的未来存在性能问题!

时间:2023-03-08 01:47:25 网络应用技术

  实际上,我认为是一个灵活的负载平衡的想法并不糟糕,具体取决于谁可以收集关键信息并使用它。

  因为它基于Dubbo,在调试过程中,我写道我看到了这个地方:

  org.apache.dubbo.rpc.protocol.abstractinvoker#waitforresultifsync

  首先查看我构架的这一行代码。在iysncresult中有一个完整的未来。它随超时时间调用get()方法。超时时间为integer.max_value。从理论上讲,效果等同于get()方法。

  从我的直观角度来看,这里的get()方法甚至更好地理解都不应该存在问题。

  但是为什么不使用get()方法呢?

  实际上,该方法的注释是出于原因而写的,恐怕像我这样的人会有这样的问题:

  这些抓住我眼睛的话是:

  表现严重。

  表现认真下降。

  可能是我们必须致电而不是get()方法,因为事实证明该方法会导致性能大幅下降。

  对于Dubbo而言,Waitforresultifsync方法是主要链接上的一种方法。我个人认为,可以说,可以说,有90%以上的请求将会出现这种阻止结果的方法。将影响Dubbo的表现。

  Dubbo作为中间件,可能会以各种不同的JDK版本运行。对于特定的JDK版本,此优化确实对提高性能非常有帮助。

  即使我们不说Dubbo,当我们使用完整的未图时,get()方法是我们经常使用的方法。

  此外,我对此方法的调用链接非常熟悉。

  因为我两年前写的第一篇公共帐户文章被讨论了Dubbo的异步转换,“ Dubbo 2.7新功能的异步转换”

  当时,这个代码绝对不是这样,至少没有这样的提示。

  因为如果您有此提示,我必须在第一次编写它时注意到它。

  果然,我去了它。尽管图片已经模糊,但我仍然可以隐约地看到它。曾经调用的get()方法:

  我也称其为最“ SAO”的代码行。

  因为该行的代码是Dubbo异步的关键代码,以同步。

  这只是前面的引号。本文不会编写Dubbo相关的知识点。

  get()完整未来的问题是什么?

  放心,面试绝对不会接受采访。只是在您知道这一点之后,您的JDK版本在修复之前就无法修复。编写代码时,您可以注意它。

  在该方法调用相同通知的地方学习Dubbo,并直接推动GE。

  或者,当您不小心看到其他人以这种方式写作时,请轻轻地说:这里可能存在性能问题,您可以找出答案。

  根据Dubbo注释中的信息,我不知道问题是什么,但是我知道在哪里可以找到问题。

  这种问题必须记录在OpenJDK的错误列表中,因此第一站是在此处搜索以搜索关键字:

  https://bugs.openjdk.java.net/projects/jdk/issues/

  一般来说,这是一些旧的错误,需要搜索半天的时间才能找到所需的信息。

  但是,这次我很幸运,第一个突然出现的是我想要的。我有点不习惯。这是传奇的国庆礼物吗?我不敢考虑。

  标题是:完整未来的绩效提高。

  其中提到的数字8227019的错误。

  https://bugs.openjdk.java.net/browse/jdk-8227019

  让我们看一下这个错误所描述的内容。

  标题是翻译的,这意味着完整的future.waitingget方法中有一个循环。这个周期称为运行时。可瓦布尔处理器方法。此方法经常调用,因此不好。

  在详细说明中,它提到了另一个错误号8227006。此错误描述了为什么经常致电可用处理器不好,但我们首先按下它。

  他提到的第一次研究这样的代码:

  他说,在等待网中,我们去看看发生了什么。

  但是我本地的JDK版本是1.8.0_271,其WaitchGet源代码是:

  java.util.concurrent.completablefuture#waitchget

  无论这些行是什么意思,我都发现我没有看到错误中提到的代码。我只看到了。尽管旋转称为该方法,但该字段是通过静态和最终方法修改的,并且在描述“经常调用”的错误中没有错误。

  所以我意识到我的版本是错误的。这应该是修复后的代码,因此我下载了一些以前的版本。

  最后,该代码在JDK 1.8.0_202版本中找到:

  与上一个屏幕截图的源代码的区别在于,前者有一个额外的旋转字段,可以缓冲方法。

  我必须找到这条代码行的原因是要证明这种代码确实出现在某些JDK版本中。

  好吧,现在我们看看Waitchget方法在做什么。

  首先,当调用get()方法时,如果结果仍然为null,则意味着异步线程执行的结果尚未准备就绪,然后调用WaitchGet方法:

  当我们进入Waitchget方法时,我们只注意Bug的两个分支以判断:

  首先将旋转的值初始化为-1。

  然后,当结果为null时,始终执行while循环。

  因此,如果您输入周期,则首次致电可用处理方法。<<8 ,即 256。

  然后再次进行循环,走入到 spins>判断0的分支,然后进行随机操作。如果随机出现的值大于或等于0,则旋转将减少。

  只有将其简化为0时,它才能输入我被我构成的逻辑:

  换句话说,这是为了将旋转从256降低到0,并且由于存在随机函数,因此循环的数量必须大于256次。

  但是,还有另一个很大的前提,也就是说,每当周期循环时,都会确定是否仍然建立了周期条件。也就是说,判断结果是否仍然为null。只有无效将继续减少。

  那么,您说这个代码正在做什么?

  实际上,评论已经很清楚地写了:

  在多处理器上使用简短的自旋等待。

  简而言之,这是一个四级词汇,您必须记住,您必须参加测试。它的意思是“短”,这是一个不规则的动词,其最高级别是最简短的。

  顺便说一句,每个人都应该知道旋转一词。我忘了以前为每个人教这些单词,只是一起聊天,观看小黑板:

  因此,注释是:如果它是多处理器,请等待短暂的旋转。

  从256到0的过程是这个“简短的自旋等”。

  但是,如果您考虑一下,在等待旋转的过程中,首次输入循环时,可用的处理器方法只会调用一次。

  那么为什么会消耗性能呢?

  是的,它确实只有一次称为get()方法,但是您不会持有get()方法调用更多的地方。

  以Dubbo为例。在大多数情况下,每个人的调用方法使用默认同步调用方案。IS,将调用一次可用的处理器方法。

  那么解决方案是什么?

  我以前已经看过它,只需找到可用处理器方法的返回值即可找到缓存的字段:

  但随后是“预言”,此“ proplem”意味着,如果我们慢慢保存多处理器的值,假设程序在运行程序运行过程中从多处理器变为单个处理器,则该值它将被更改。这是不准确的,尽管这是不太可能的变化,但是即使这种“预言”确实发生了,也会导致较小的性能损失。

  因此,每个人都看到了这样的代码。这是“我们可以在字段中缓存此值”:

  特定的代码更改是:

  http://cr.openjdk.java.net/~shade/8227018/webrev.01/src/share/classes/java/java/chautil/completablefuture.java.html

  因此,当您去查看源代码的这一部分时,您会发现旋转字段上实际上有一个很长的段落,情况就是这样:

  为每个人翻译:

  1.在候补方法中,在阻塞操作之前旋转。

  2.无需在单个处理器上旋转。

  3.调用运行时间的成本。可利用的处理器非常高,因此此值在此处缓存。但是,此值是首次可用的CPU数量。如果系统启动时只有一个CPU可以使用,则可以使用旋转的值将初始化为0。即使更多的CPU在线也不会改变。

  当您在上一个错误的描述中有路面时,您会理解为什么在这里写这么大的段落。

  有些学生确实浏览了代码,也许您看到的是:

  情况是什么?我根本看不到与旋转相关的代码。这不是这个欺骗诚实的人吗?

  恐慌,猴子不着急,你还没有完成吗?

  我们注视着图片中的这句话:

  您只需要在JDK 8中执行此维修,因为JDK 9的代码和更高版本不是这样写的。

  例如,在JDK 9中,整个旋转的逻辑被直接删除,因此请勿等待此简短旋转:

  http://hg.openjdk.java.net/jdk9/jdk9/jdk9/jdk/rev/f3af17da360b

  尽管此短期旋转等待已被删除,但实际上是一个学习操作。

  问题:为什么在不引入时间的情况下做出旋转等待效果?

  答案是已删除的代码。

  但是有人说,当我第一次看到此代码时,我感到很尴尬。这种短旋转能够扩展多长时间?

  添加此旋转以在以下逻辑下执行公园代码,这有点重,但是我认为这个“短暂自旋等”的好处实际上是最小的。

  因此,我也理解为什么将来将删除整个代码。当删除此代码时,作者没有意识到这里有一个错误。

  这里提到的作者实际上是道格·莱(Doug Lea)。

  我为什么这么说?

  根据此错误链接中提到的错误编号8227018,它们实际上描述了同一件事:

  里面有这样的对话,大卫·霍尔姆斯(David Holmes)和道格·利(Doug Lea)出现了:

  福尔摩斯提到了“在字段中缓存此值”的解决方案,并获得了道格的同意。

  道格说:JDK 9不再使用旋转。

  因此,我个人理解道格(Doug)在不知道这个地方有错误的情况下删除了旋转的逻辑。考虑到什么考虑,我想收入确实不大,而代码令人困惑。删除后最好理解。

  每个人都熟悉道格·莉亚(Doug Lea)。谁是大卫·霍尔姆斯(David Holmes)?

  茶已经结束了“ Java并发编程”的作者之一。

  而且,如果您对我上一篇文章印象深刻,那么您会发现Netizens再次发现了“ J.U.C软件包中的Doug Lea”中写的错误。”在本文中,他出现了:

  老朋友再次出现,我建议铁汁在公共屏幕上互动。

  我说了这么大的段落。核心想法实际上是运行时的高呼叫成本。可利用的程序员,因此不应在完整的future.waitingget方法中经常调用此方法。

  但是,为什么可用的过程被称为高,什么是基础,您必须将其取出!

  本节,让我向您展示什么是基础。

  基础是此错误描述:

  https://bugs.openjdk.java.net/browse/jdk-8227006

  标题说:在Linux环境中,Runtime.available Processors的执行时间增加了100倍。

  增加了100次,必须进行两种不同的版本比较,那么哪个版本是?

  在1.8B191之前的JDK版本上,以下示例程序可以实现运行时的呼叫。AvalainProcessors每秒超过400万次。

  但是,在JDK Build 1.8B191和所有主要版本和次要版本(包括11)中,它可以实现的最大呼叫数量约为每秒40,000次,并且性能下降了100次。

  这导致了alltableFuture.Waitingget的性能问题,该策略在周期中调用Runtime.AvalainProcessor。由于我们的应用程序在异步代码中显示出明显的性能问题,因此Waitchget是我们最初发现该问题的地方。

  测试代码是:

  根据错误提交器的描述,如果您在64 -bit Linux上使用JDK 1.8B182和1.8B191运行,您会发现差异的近100倍。

  至于为什么有100倍的性能差异,一个名叫Fairoz Matte的兄弟说,当问题出现“ Oscontainer :: IS_Containerize()”时,他已经调试并将问题定位。

  他还将问题的初始版本编号定位为8U191 B02。此版本之后,代码将遇到此类问题。

  带来问题的问题的升级是改善Docker容器检测和资源分配。

  因此,如果您的JDK 8是8U191 B02之前的版本,并且系统打电话高,则恭喜您,您有机会踩这台坑。

  然后,以下大个子提供了许多基于此问题的解决方案,并讨论了各种解决方案。

  在某些解决方案中,听起来很麻烦,需要编写很多代码。

  最后,简·简(Jane)的途径仍然选择了相对简单的缓存解决方案来实施。尽管该解决方案也有些缺陷,但出现的可能性非常低且可以接受。

  现在我们知道,在没有鸡蛋的知识点之后,让我们看看为什么呼叫超时时间的方法不是问题。

  java.util.concurrent.completablefuture#get(long,java.util.concurrent.timeunit)

  首先,您可以看到内部调用方法是不同的:

  有一个超时get()方法,在内部调用TimeDget方法,并且参数为超时。

  单击计时方法以了解为什么呼叫胆管时间的get()方法不是问题:没有问题:

  答案是在代码代码中写给您的:我们故意在此处旋转(例如watchget),因为上面的纳米段()呼叫就像旋转。

  可以看出,在此方法中,没有呼吁运行时。可利用的处理器,因此没有相应的问题。

  现在,我们回到开始:

  所以您说,如果我们更改下面的效果?

  它必须有所不同。

  再一次:Dubbo作为开源中间件可能会以各种不同的JDK版本运行,此方法是其主要链接上的核心代码。对于特定的JDK版本,这种优化确实是为了表现性能改进非常有用。

  因此,编写中间件仍然有些有趣。

  最后,我将向您发送机会提交Dubbo的源代码。

  在下面的课程中:

  org.apache.dubbo.rpc.asyncrpcresult

  仍然有两种方法:

  但是上面的get()方法仅由测试类调用:

  您可以将它们完全更改为调用(长时间,TimeUnit单元),然后直接删除GET()方法。

  我认为这一定是合并的。

  如果您想为开源项目做出贡献并熟悉该过程,那么这是一个很好的机会。