Uber Engineering在Android上搅拌rxjava背压

0.
Uber Engineering在Android上搅拌rxjava背压

关于App Activity-e.g的信息快速有效地传输 - 例如,请求游乐设施,下订单或接近拾取位置 - 对于Uber产品的无缝用户体验至关重要。

在优步,我们使用rxjava.在我们的Android应用程序中大量才能在观察者之间清楚地传达事件并表达复杂异步变换。优步的骑手和驱动程序应用程序使用rxjava具有许多异步状态可见,比如向司机发送新的调度信息,或向正在出行的乘客发送新的uberPOOL匹配信息。尽管RxJava对我们非常有用,RxJava反压力迫使我们创造性地思考我们如何使用这个库。

在本文中,我们提供了一个rxjava backressure的示例,并分享我们通过rxjava backressure运算符否定它的最佳实践,更宽容rxjava 1.x配置以及rxjava 2.x。

在压力下:与rxjava 1.x的背压案例

当Observable释放事件的速度快于操作符消化事件的速度时,RxJava就会产生反压力;这会导致延迟、崩溃和其他问题。在许多情况下RxJava回压是常见的,特别是在处理多线程或异步事件。使用rxjava 1.x,无论是否需要支持,Backure都是有问题的;因此,遗漏背心xception.错误和相应的崩溃通常会发生很少的上下文或洞察事件导致它们的洞察力。

2015年,我们开始远离迁移奥托(具有Android支持的增强的事件总线)并将越来越多的rxjava实现到我们的骑手和驱动程序应用程序中。这导致了一个上升遗漏背心xception.崩溃。由于崩溃的根本原因难以识别,我们的移动平台团队需要更深入地了解背压在Rxjava环境中的工作原理。我们还需要开发解决方案,以防止它放慢我们的应用程序。

在实践中,导致一个事件序列遗漏背心xception.很复杂。要保持简单的事情,我们将使用一个小的rxjava 1.x片段来展示这种无法消耗事件的情况导致a遗漏背心xception.

如上所述,rxjava 1.x的rx.ring-buffer.size.属性存储任何尺寸内存环形缓冲区这是RxJava在Observable无法跟上事件发射速率时使用的。环形缓冲区的大小被显式设置为一个较低的数字(8),因为不同的平台对此值有不同的默认值。而Java虚拟机(JVM)默认为每个环形缓冲区128项,Android具有更低的16个限制。

在我们的示例中,我们创建一个主题并在主线程上发出它,但尝试在新的线程调度程序上使用事件。为了保持确定性和可重复的,我们使用单个线程调度程序,使用睡眠呼叫启动线程,以确保在从主线程中发出时无法消耗事件。

接下来,我们创建一个publishsubject.在我们的单线程调度程序上有一个观察者打印它收到的值。最后,我们通过迭代主线程和呼叫的值来发出项目0到11的项目publishsubject#onnext()

如果您运行示例段,则会在抛出此异常后崩溃:

由于代码在主线程上发出了12个项目(在我们的8个项目限额4)上,我们的观察员背景调度程序无法处理它们,因为线程当前处于睡眠状态。排放超过8个项目的限制也会导致遗漏背心xception.,强迫我们的代码崩溃。

在下一节中,我们讨论如何缓解这种背压-不需要阿司匹林。

缓解压力:缓冲策略

我们可以实施一些缓冲战略来解决遗漏背心xception.上面的例子和其他人喜欢它。最简单的方法是使用onbackpressurebuffer()操作员从反弹库,它有效地默认情况下rxjava 2.x默认情况下:

主题

onbackpressureBuffer.()

观察龙调度程序)

地图((整数整数- >细绳格式“%d”整数)))

订阅系统出去::打印);

操作员无缝缓冲所有值,直到下游消费者摄取它们。命令输出是其相应值的所有值,如下所示:

另一种策略是使用使用的事件onbackpressuredrop()操作符。这将忽略队列满时的新事件,如下所示:

最后一个选项是使用onbackpressurelatest()操作符。这确保了最新的值未删除并将丢弃在队列已满一旦填写后丢弃其他值,如下所示:

除了上面的例子之外,这些策略也有自定义行为的参数化版本。例如,如果您想使用onbackpressurebuffer()但只缓冲了一个固定数量的项目,你可以应用onbackpressurebuffer()操作员,它采用容量参数。

在优步工程中,我们采用这些解决方案的混合来处理背压,具体取决于用例。如今,大部分超级Android代码库使用rxjava 2.x(图书馆的下一次迭代),允许我们选择退出BackFress。对于我们剩下的rxjava 1.x代码,我们会突破我们的环形缓冲区大小,以实现与rxjava 2.x类似的行为。(我们稍后会更详细地覆盖这一点。)

心灵背压操作员副作用

如果没有策略来缓解背压,则在高记忆压力下的应用可以容易地填充内环缓冲器并使用a崩溃遗漏背心xception.。一个常见的情景导致一个遗漏背心xception.当应用程序没有响应时,在主线程上观察值。如果队列在消耗这些事件之前填写,则该应用程序将崩溃遗漏背心xception.。自从此以来默认队列大小在Android仅为16项(与JVM上的128件),这可能成为一个主要问题。

虽然使用缓冲策略似乎是一个显而易见的决定,这些反压力解毒剂仍然有副作用。例如,您必须确信在内存中进行缓冲不会导致应用程序耗尽内存。特别是在Android开发中,保持低内存配置是非常重要的;缓冲普通的旧java对象(POJOS)不太可能导致崩溃。但是,工程师应该在缓冲具有大文件大小等比特图或其他内存密集资源的情况下进行两次进行两次。

事实上,对于丢弃物体的策略onbackpressuredrop()onbackpressurelatest(),您需要评估丢弃事件是否会导致不良反应。如果事件是有状态(例如,发送以启动和停止某个应用程序功能的事件),则丢弃事件可能会导致难以在运行时找到问题。

一旦你对背压的基本了解,rxjava文件变得更加有用。消耗事件的每个操作员都有自己的背压部分,详细描述了它将如何表现。

浏览可用的运营商,我们可以通过使用的原始示例完全避免崩溃范围操作员,注意下游反压力和传输值的要求。

使用rxjava 2.x改进

现在我们已经将原因和解决方法彻底解剖到Rxjava 1.x背压,值得注意的是Rxjava 2.x包含新类型使图书馆能够更好地处理背压。

可观察到的输入RxJava 2。X没有反压力的概念。实现可观察到的有效地与使用相同onbackpressurebuffer()默认。UI事件,一次性网络请求和状态更改应全部使用此方法。这合适的也许, 和类型也可以决定这种行为。

如果您需要支持BackFressure,RxJava 2.x的新课程,可流动,是背压意识可观察到的在rxjava 1.x.但是,更新的库现在需要明确选择背压策略以防止惊喜缺失。

实现RxJava 2。RxJava 1.x中的行为

我们目前使用PERECACY RXJAVA 1.x代码和UBER Android应用程序的rxjava 2.x代码,并从后者型号中引发了所有新代码。将大多数代码迁移到rxjava 2.x为我们的骑手应用程序后,我们仍然有一条长长的遥远的rxjava 1.x代码崩溃崩溃。将环形缓冲属性撞到128(匹配JVM上的默认值)后,完全消除了背压崩溃,而无需任何内存应变。h

但是,不应轻描淡写此属性。它将全局应用于应用程序中的每个rxjava 1.x流,包括使用rxjava的第三方库。此外,工程师应该有意识任何rxjava 1.x可观察到正在处理背压。尽管有这些调整,这种情况可能导致崩溃和其他负面影响。

更清楚地了解背压如何工作,Uber Engineering.能够以增强的稳定性重新建立我们的骑手和驱动程序应用程序,为更具无缝的用户体验。我们希望这些知识对您的rxjava努力有用。

Tony Cosentini是Uber移动平台团队的软件工程师。

图片标题:“水动力流动曼达雷背鳍“由Conor Myhrvold,Raja Ampat,印度尼西亚。

评论