首页 > 解决方案 > Python Web 应用程序中的 H20 内存泄漏

问题描述

我的决策引擎建立在带有 uWSGI 和 Nginx 的 python-flask 框架上。作为通过 HTTP 请求评估用户的一部分,我使用 h2o==3.20.0.7 运行记分卡以生成分数以对用户做出决定。下面给出了我如何在我的应用程序中使用 h2o 的一些说明

h2o.init()  # initialize 

predictions = h2o.mojo_predict_pandas(features_df, MODEL_MOJO_ZIP_FILE_PATH, MODEL_GENMODEL_JAR_PATH)  # generate score
# features_df -> pandas DF

应用程序开始时的 H2o 详细信息

--------------------------  ----------------------------------------
H2O cluster uptime:         01 secs
H2O cluster timezone:       Etc/UTC
H2O data parsing timezone:  UTC
H2O cluster version:        3.20.0.7
H2O cluster version age:    1 year, 7 months and 10 days !!!
H2O cluster name:           H2O_from_python_unknownUser_t8cqu9
H2O cluster total nodes:    1
H2O cluster free memory:    1.656 Gb
H2O cluster total cores:    4
H2O cluster allowed cores:  4
H2O cluster status:         accepting new members, healthy
H2O connection url:         http://localhost:54321
H2O connection proxy:
H2O internal security:      False
H2O API Extensions:         XGBoost, Algos, AutoML, Core V3, Core V4
--------------------------  ----------------------------------------

H2o(作为单独的服务运行)和烧瓶应用程序都在同一台服务器上运行(负载均衡器下有 3-8 台服务器)。

有时内存使用量稳步增加并抛出Cannot allocate memory

在计算记分卡时。然后它有时会自动自行解决。记分卡在 HTTP 请求下与其他规则(顺序运行)一起运行,但仅在计算记分卡时才报告错误。假设它需要更多内存,因为它涉及 h2o。在这个周期中,流量看起来是相同的。所以我希望这不是由于高流量。

根据我的调查,一些内存挂在某处并且没有释放。

我做了以下变通方法来释放挂起的内存并减少影响

来自 python 的 h2o 中的1 个GC

https://aichamp.wordpress.com/2016/11/10/calling-h2o-garbage-collect-from-python

Python H2O 内存管理

2计划服务重启 - 用新服务器优雅地替换旧服务器。

我想了解内部发生的事情并介绍适当的修复而不是解决方法。帮助将不胜感激。

供您参考,

我没试过

1由于当前版本太旧(1年7个月11天)将H2o集群更新到新版本 - 同意最好使用最新版本但不保证不会再次发生同样的事情并且需要付出努力在验证分数、结果等方面也更多

2我没有限制 H2o 的内存使用,min_mem_size因为我不希望记分卡评估失败。

我打算

1添加内存分析器以轻松了解与我的应用程序相关的每个部分/进程的内存利用率

编辑

2从烧瓶应用程序中分离出 h2o 并将其托管在不同的服务器中,以便轻松扩展。- 不过,同样的问题是可能的。

谢谢

标签: pythonpandash2o

解决方案


您描述的方法与我推荐的方法不同。

为简单起见(忽略多个服务器和负载平衡),我将绘制您的设置的架构图,如下所示:

[Client HTTP program] -> [python flask app] -> [java scoring backend]

这种高级架构很好,但是您已经以我将说的最困难的方式而不是预期的方式来实现 java 评分层部分。

预期的方法是仅使用 MOJO 和轻量级 MOJO 运行时。一种直接的方法是将 MOJO 包装在一个非常简单的最小 Web 服务中。

以下是 MOJO 的 javadoc 的链接:

以及一个 github 存储库,演示了如何在一个简单的 Java servlet 容器中使用 MOJO:

此外,这里是一个较旧的 github 存储库,您可能会发现它使用 POJO 而不是 MOJO 很有用。MOJO更好。使用 MOJO 而不是 POJO,但您可能会发现阅读此 repo 中的文档很有帮助:

请注意,如果您这样做,您仍然可以根据需要单独扩展/负载平衡 [python flask app] 和 [java scores backend] 服务,尽管我的期望是 java 将比 python 快得多,所以它可能只需将 python 和 java 以两个为一组进行缩放,并让 python 向本地 java 发出请求,就更容易了。


好的,既然我已经谈到了最佳实践方式,让我指出一些我可以在你现在正在做的事情中发现的问题(困难的方式)。

  1. 你没有提到你是一次打分还是批量打分。使用完整的 H2O-3 服务器本身进行评分更适合批量评分,并且一次对一行评分效率极低。解析过程是重量级的,评分过程是重量级的,每次一行。这将影响延迟。

  2. 虽然您可以将 MOJO 对象本身读入完整的 H2O-3 服务器进程并将其用于批量评分,但在实时 HTTP 工作流中执行此操作绝不是本意。(有趣的是,在 H2O-3 存在的前 5 年里,甚至不可能支持这样做。)

  3. 如果您自己不清理,肯定会有内存泄漏。

    不建议将 H2O-3 服务器进程作为长时间运行的服务运行以进行评分。但是,如果您真的想这样做,请执行以下步骤:

    • 需要清除内存中的对象。您可以使用 h2o.ls() 找到它们,并使用 R/python 客户端 API 中的 h2o.rm() 调用删除它们。数据集和分数都需要清理。不过,您可能不想删除模型本身。

    • 我不希望您需要在 Java 进程中手动触发垃圾回收,但如果您愿意,您可以这样做。就个人而言,我只在打开 Java 标志(如 -XX:+PrintGCDetails -XX:+PrintGCTimeStamps)时才这样做,这样我就可以看到压缩对 Full GC 后剩余多少可用堆内存的影响。我这样做是为了查看对象是否真的被保留,因此我可以确认它们正在被清除。我喜欢将这些日志提供给http://gceasy.io并可视化它们。

    • 监视日志以查看 Full GC 后剩余的空闲堆。

    • 即使您在清理内存方面做得正确,也要为 H2O-3 服务器进程提供大量内存。我什至不会在 -Xmx 小于 5G 的笔记本电脑上运行它。因此,我将原始发布者的 Java 堆描述为严重不足(H2O cluster free memory: 1.656 Gb)。

    • 如果您在 Full GC 爬升后看到剩余的空闲堆,请重新启动 Java 进程,因为这不是标准用例,也不是经过彻底测试的东西。开发团队认为 H2O-3 集群更像是中短寿命服务(小时/天),而不是长期运行的服务(数月以上,例如 nginx/apache)。


希望有帮助!


推荐阅读