【问题标题】:Ways to buffer REST response缓冲 REST 响应的方法
【发布时间】:2018-04-02 15:46:19
【问题描述】:

有一个 REST 端点,它为我的应用程序提供大量(数十 GB)数据。
应用程序按照自己的节奏处理数据,随着传入数据量的增长,我开始遇到 REST 端点超时。
意思是,处理速度低于网络直通输出。
不幸的是,没有办法足够提高处理速度,因为没有“足够”——传入的数据量可能会无限增长。

我正在考虑一种在处理之前在本地存储传入数据的方法,以便在超时发生之前释放 REST 端点连接。

到目前为止,我想出的是将传入数据下载到临时文件并使用 OutputStream/InputStream 同时读取(处理)所述文件。
缓冲排序,使用文件。

这带来了它自己的问题:

  • 如果处理速度变得比下载速度更快 一些时间,我得到EOF?
  • 文件解析器使用 ObjectInputStream 并且在空文件/ EOF的情况下表现得很奇怪
  • 等等

有没有传统的方法来做这样的事情?
有替代解决方案吗?
请提供一些指导。

更新:

我想指出:http 服务器是我无法控制的。
将其视为供应商数据提供者。他们有很多消费者,并且拒绝只为一个而改变任何东西。
看起来我们是唯一使用他们所有数据的人,因为我们的客户端应用程序处理速度远远高于他们的示例客户端性能指标。尽管如此,我们仍然无法将我们的应用程序性能与网络直通输出相匹配。

服务器不支持 http 范围请求或分页。
没有办法将数据分成块来加载,因为没有过滤属性来保证每个块都足够小。

简而言之:我们可以在超时发生之前的给定时间内下载所有数据,但无法处理它。
在 inputstream 和 outpustream 之间有一个适配器,作为一个阻塞队列,将有很大帮助。

【问题讨论】:

  • 听起来像back pressure 的情况。您是否考虑过像ReactorRxJava 这样的reactive streams 方法来解决这个问题?这些库具有良好的背压支持。
  • 我对响应式流有点熟悉,但是如何对他无法控制的服务施加背压呢? (这里是http服务器)
  • 在您的问题中,您不控制其余端点并不明显。
  • 为什么您的客户端超时?你不是以这样的方式控制超时,只要你需要处理所有数据就可以让它等待吗?我可能误解了你的问题,但你提出的方式似乎表明问题完全与超时有关,所以我很想知道这个特定问题是否不能简单地在服务连接的配置中解决。
  • 能否分享一个代码示例,如何读取远程文件?

标签: java performance rest buffer


【解决方案1】:

您正在使用 new ObjectInputStream(new FileInputStream(..._) 之类的东西,而 EOF 的解决方案可能是首先将 FileInputStream 包装在 WriterAwareStream 中,只要作者正在写作,当遇到 EOF 时就会阻塞。

无论如何,如果延迟无关紧要,我不会在下载完成之前开始处理。通常,如果对象列表不完整,您无能为力。

也许像Chronicle-Queue 这样的基于内存映射文件的队列可能会对您有所帮助。它比直接处理文件要快,而且使用起来可能更简单。


您还可以使用队列在内部实现HugeBufferingInputStream,该队列从其输入流中读取数据,如果它有大量数据,它会将它们吐出到磁盘。这可能是一个很好的抽象,完全隐藏了缓冲。

Guava 中还有FileBackedOutputStream,变大时会自动从使用内存切换到使用文件,但恐怕它针对小尺寸进行了优化(预计有数十GB,尝试使用内存没有意义) )。

【讨论】:

  • HugeBufferingInputStream 是迄今为止最好的主意。这几乎是我在提出主题问题时希望找到的。我确信在流行的实用程序库中一定有某种我不知道的东西,所以我不必处理繁琐的 InputStream api。
【解决方案2】:

是否有替代解决方案?

如果您的消费者(http 客户端)无法跟上数据流,您可能需要查看客户端管理自己正在进行的工作的设计,按需从服务器提取数据。

RFC 7233 描述范围请求

本地存储有限的设备可能会受益于只能请求较大表示的子集,例如非常大的文档的单个页面,或嵌入图像的尺寸

MDN Web Docs 站点上的HTTP Range requests 可能是更平易近人的介绍。

【讨论】:

  • 这确实是个好主意。我已经考虑过了,忘了提。不幸的是,服务不支持范围请求或分页。
【解决方案3】:

排队服务器就是为此而生的。 RabbitMQ、Kafka、Kinesis 等等。也许KStream 会起作用。使用从 HTTP 服务器获得的所有内容(考虑到不能将其分解为工作单元的约束),您可以将其划分为一些合理大小的字节块,可能为 1024kB。您的应用程序会将这些记录/消息推送/发布到主题/队列。它们都将共享一些共同的系列 ID,以便您知道哪些块匹配,并且每个块都需要携带一个序数,以便它们可以按正确的顺序重新组合在一起;使用单个 Kafka 分区,您可能可以依赖 offsets。您可能会发布该系列的最终记录,并带有“完成”标志,该标志将充当任何正在使用它的 EOF。当然,您会在所有数据排队后立即发送 HTTP 响应,尽管它可能还不一定会被处理。

【讨论】:

    【解决方案4】:

    不确定这对您的情况是否有帮助,因为您没有提到数据以什么结构和格式提供给您,但是,我将假设一个精美的标准化、深度嵌套的分层 xml(即几乎流媒体的最坏情况,对吧?... pega bix?)

    我提出了一个部分解决方案,可以让您避开无法控制客户端如何与 http 数据服务器交互的限制 -

    1. 部署您自己的网络服务器,采用您喜欢的任何现代技术(您可以控制) - 您的本地服务器将位于您本地缓存的数据副本前面

    2. 使用内置的 http 查询库、命令行实用程序(例如 aria2ccurlwget等)定期下载 Web 服务的输出。 al,一个 etl(或任何你喜欢的东西)直接到本地设备支持的 .xml 文件中 - 这种情况经常发生

    3. 将您的 REST 客户端指向您自己托管的 127.0.0.1/modern_gigabyte_large/get...“智能”服务器,而不是旧的 api.vendor.com/last_tested_on_megabytes/get... 服务器

    一些想法:

      1234563 /p> 1234563如果提供数据结构的样本,我可以更多地讨论这个问题
    • 所有这些工作都可以与您现有的应用程序并行运行,在您成功处理的“旧数据”的上一个版本上继续运行,直到下一个版本的“新数据”可用为止


    ^ 在交易中,您现在需要管理数据文件的“滑动窗口”,其中每个“结果”都是您的应用程序的特定实例,下载网络服务数据并将其存储在磁盘上,然后成功地将其摄取到您的模型中:

    1. 最后(两个?)压缩的好结果(根据我的经验,千兆字节的 xml 压缩了很多)

    2. 在流式传输到光盘/进行完整性检查/摄取数据时下一个待处理/临时结果 - (这将成为当前的“良好”结果,最后一个“良好”结果成为“先前良好”结果)

    3. 如果我们假设您正在摄取到关系数据库中,则当前(也可能是以前的)tables 将 Web 服务数据加载到您的应用中,以及下一个待处理的 table

    4. 切换这些成为元数据操作,但现在您的数据库必须至少存储 x2 的 web 服务数据(或 x3 - 任何适合您的限制)

    5. ...是的,你不需要这样做,但在出现问题后你会希望你这样做 :)

    看起来我们是唯一使用他们所有数据的人

    • 这意味着您可以通过某种方式对 Web 服务源进行分区或限制 - 其他客户端如何进行区分以免收到全部金额?

    【讨论】:

      【解决方案5】:

      您可以使用内存缓存技术,也可以使用 Java 8 流。请参阅以下链接了解更多信息: https://www.conductor.com/nightlight/using-java-8-streams-to-process-large-amounts-of-data/

      【讨论】:

        【解决方案6】:

        Camel 或许可以帮助您调节 REST 生产者和生产者之间的网络负载?

        例如,您可以在真正的 REST 端点之前引入一个充当代理的 Camel 端点,应用一些限制策略,然后再转发到真实的端点:

        来自("http://localhost:8081/mywebserviceproxy") 。风门(...) .to("http://myserver.com:8080/myrealwebservice);

        http://camel.apache.org/throttler.html http://camel.apache.org/route-throttling-example.html

        我的 2 美分,

        伯纳德。

        【讨论】:

          【解决方案7】:

          如果你有足够的内存,也许你可以使用像 Redis 这样的内存数据存储。

          • 当您从 Rest 端点获取数据时,您可以将数据保存到 Redis 列表(或任何其他适合您的数据结构)中。

          • 您的消费者将使用列表中的数据。

          【讨论】:

          • 不幸的是,我没有足够的内存。我们很少有数十甚至数百 GB 可用。不过,我正在考虑使用带有磁盘交换的嵌入式 MQ 提供程序。
          • 您可以更改应用服务器的超时值。不可能吗?或者是否可以更改块大小(也许使块更小)?我们在谈论什么样的数据?
          • 不要对所有事情都使用 redis :-) 这不是 redis 用例
          猜你喜欢
          • 1970-01-01
          • 2016-12-27
          • 2018-10-10
          • 2019-12-03
          • 1970-01-01
          • 1970-01-01
          • 2020-12-03
          • 2019-02-19
          • 1970-01-01
          相关资源
          最近更新 更多