Selenium Headless Chrome-Java堆问题

时间:2018-12-21 00:06:02

标签: selenium google-chrome selenium-webdriver web-scraping selenium-chromedriver

我正在使用在EC2的Ubuntu上安装的Headless Chrome(Selenium Chrome Web驱动程序)进行网络抓取。对于少量请求,它可以正常工作..但是,当同时发出大量请求(数百个)时,它们不断崩溃,我总是不得不重新启动服务器。

有人用它来支持大负载吗?我正在使用t2.medium ec2服务器。

在日志中,我看到与浏览器通信时出错:

2018-12-20 19:18:04.565 ERROR 1292 --- [io-8080-exec-79] o.s.boot.context.web.ErrorPageFilter     : Forwarding to error page from request [/v1.0/search] due to exception [Error communicating with the remote browser. It may have died.
Build info: version: '3.11.0', revision: 'e59cfb3', time: '2018-03-11T20:26:55.152Z'
System info: host: 'ip-172-31-17-81', ip: '172.31.17.81', os.name: 'Linux', os.arch: 'amd64', os.version: '4.4.0-1072-aws', java.version: '1.8.0_191'
Driver info: driver.version: RemoteWebDriver
Capabilities {acceptInsecureCerts: false, acceptSslCerts: false, applicationCacheEnabled: false, browserConnectionEnabled: false, browserName: chrome, chrome: {chromedriverVersion: 2.37.544315 (730aa6a5fdba15..., userDataDir: /tmp/.org.ch
romium.Chromium...}, cssSelectorsEnabled: true, databaseEnabled: false, handlesAlerts: true, hasTouchScreen: false, javascriptEnabled: true, locationContextEnabled: true, mobileEmulationEnabled: false, nativeEvents: true, networkConnectio
nEnabled: false, pageLoadStrategy: normal, platform: LINUX, platformName: LINUX, rotatable: false, setWindowRect: true, takesHeapSnapshot: true, takesScreenshot: true, unexpectedAlertBehaviour: , unhandledPromptBehavior: , version: 69.0.3
497.100, webStorageEnabled: true}
Session ID: e47c3c443164cbd2a3586ee6321d26f8]

org.openqa.selenium.remote.UnreachableBrowserException: Error communicating with the remote browser. It may have died.
Build info: version: '3.11.0', revision: 'e59cfb3', time: '2018-03-11T20:26:55.152Z'
System info: host: 'ip-172-31-17-81', ip: '172.31.17.81', os.name: 'Linux', os.arch: 'amd64', os.version: '4.4.0-1072-aws', java.version: '1.8.0_191'
Driver info: driver.version: RemoteWebDriver
Capabilities {acceptInsecureCerts: false, acceptSslCerts: false, applicationCacheEnabled: false, browserConnectionEnabled: false, browserName: chrome, chrome: {chromedriverVersion: 2.37.544315 (730aa6a5fdba15..., userDataDir: /tmp/.org.ch
romium.Chromium...}, cssSelectorsEnabled: true, databaseEnabled: false, handlesAlerts: true, hasTouchScreen: false, javascriptEnabled: true, locationContextEnabled: true, mobileEmulationEnabled: false, nativeEvents: true, networkConnectio
nEnabled: false, pageLoadStrategy: normal, platform: LINUX, platformName: LINUX, rotatable: false, setWindowRect: true, takesHeapSnapshot: true, takesScreenshot: true, unexpectedAlertBehaviour: , unhandledPromptBehavior: , version: 69.0.3
497.100, webStorageEnabled: true}
Session ID: e47c3c443164cbd2a3586ee6321d26f8
        at org.openqa.selenium.remote.RemoteWebDriver.execute(RemoteWebDriver.java:566)
        at org.openqa.selenium.remote.RemoteWebDriver.execute(RemoteWebDriver.java:602)
        at org.openqa.selenium.remote.RemoteWebDriver.quit(RemoteWebDriver.java:445)
        at mt.service.KlookServiceImpl.searchTrips(KlookServiceImpl.java:138)
        at mt.controller.TripSearchController.getTripResults(TripSearchController.java:120)
        at sun.reflect.GeneratedMethodAccessor163.invoke(Unknown Source)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:498)
        at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:221)
        at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:137)
        at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:110)
        at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandleMethod(RequestMappingHandlerAdapter.java:776)
        at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:705)
        at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85)
        at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:959)
        at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:893)
        at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:967)
...
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:505)
        at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:169)
        at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:103)
        at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:956)
        at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:116)
        at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:436)
        at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1078)
        at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:625)
        at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:316)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
        at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
        at java.lang.Thread.run(Thread.java:748)
Caused by: java.lang.RuntimeException: java.util.concurrent.TimeoutException
        at org.openqa.selenium.net.UrlChecker.waitUntilUnavailable(UrlChecker.java:145)
        at org.openqa.selenium.remote.service.DriverService.stop(DriverService.java:214)
        at org.openqa.selenium.remote.service.DriverCommandExecutor.execute(DriverCommandExecutor.java:95)
        at org.openqa.selenium.remote.RemoteWebDriver.execute(RemoteWebDriver.java:545)
        ... 58 common frames omitted
Caused by: java.util.concurrent.TimeoutException: null
        at java.util.concurrent.FutureTask.get(FutureTask.java:205)
        at com.google.common.util.concurrent.SimpleTimeLimiter.callWithTimeout(SimpleTimeLimiter.java:156)
        at org.openqa.selenium.net.UrlChecker.waitUntilUnavailable(UrlChecker.java:115)
        ... 61 common frames omitted

还有Java堆空间问题:

2018-12-20 19:18:20.294 ERROR 1292 --- [io-8080-exec-84] o.s.boot.context.web.ErrorPageFilter     : Forwarding to error page from request [/v1.0/search] due to exception [Java heap space]

java.lang.OutOfMemoryError: Java heap space


Exception: java.lang.OutOfMemoryError thrown from the UncaughtExceptionHandler in thread "http-bio-8080-exec-96"

发生此错误时,我需要重新启动服务器。然后,少量运行即可使一切恢复正常,但是如果负载较大,问题就会重演。

1 个答案:

答案 0 :(得分:0)

这些错误消息...

ERROR 1292 --- [io-8080-exec-79] o.s.boot.context.web.ErrorPageFilter     : Forwarding to error page from request [/v1.0/search] due to exception [Error communicating with the remote browser. It may have died.
.
org.openqa.selenium.remote.UnreachableBrowserException: Error communicating with the remote browser. It may have died.
.
2018-12-20 19:18:20.294 ERROR 1292 --- [io-8080-exec-84] o.s.boot.context.web.ErrorPageFilter     : Forwarding to error page from request [/v1.0/search] due to exception [Java heap space]
.
java.lang.OutOfMemoryError: Java heap space
.
Exception: java.lang.OutOfMemoryError thrown from the UncaughtExceptionHandler in thread "http-bio-8080-exec-96"    

...表示在将请求转发到错误页面时引发了 java.lang.OutOfMemoryError


OutOfMemoryError异常

OutOfMemoryError Exception是内存泄漏的常见指示。当 Java堆中没有足够的空间来分配对象时,将引发此错误。当垃圾收集器无法为容纳新对象提供空间,并且无法进一步扩展堆时,会发生这种情况。当本机内存不足以支持Java类的加载时,也可能引发此错误。在极少数情况下,如果花费大量时间进行垃圾回收并且释放了少量内存,就会抛出java.lang.OutOfMemoryError

>

java.lang.OutOfMemoryError:Java堆空间

详细消息 Java heap space 表示无法在Java堆中分配对象。此错误不一定表示内存泄漏。问题可能很简单,就像配置问题一样,在该问题中,指定的堆大小(或默认大小,如果未指定)对于应用程序来说是不够的。

在某些情况下,对于特定的长期使用的应用程序,该消息可能表明该应用程序无意间持有了对对象的引用,这防止了对象被垃圾回收。这是内存泄漏的Java语言。

注意:应用程序调用的API也可能无意中包含对象引用。

关于Chrome Headless会话无法预测的CPU和内存消耗的讨论很多。根据讨论Building headless for minimum cpu+mem usage,可以通过以下方式优化CPU +内存使用率:

  • 使用自定义代理或C ++ ProtocolHandlers,您可以返回存根1x1像素的图像,甚至完全阻止它们。
  • Chromium小组正在努力在制作框架时添加programmatic control。目前无头Chrome仍在尝试以 60 fps 进行渲染,这非常浪费。许多页面的确需要几帧(也许 10-20 fps )才能正确呈现(由于使用了requestAnimationFrameanimation triggers),但是我们期望有很多CPU可以节省的钱。
  • MemoryInfra应该可以帮助您确定哪个组件是设置中最大的内存使用者。
  • 用法可以是:

    $ headless_shell --remote-debugging-port=9222 --trace-startup=*,disabled-by-default-memory-infra http://www.chromium.org
    
  • 铬总是会使用尽可能多的资源。如果要有效限制其利用率,则应考虑使用cgroups

您可以在Limit chrome headless CPU and memory usage

中找到详细的讨论

其他方法

从@BenChilds的answer中抽出一片叶子,无论使用哪种平台,都始终为使用配置了有限的最大堆内存量。 Java选择减小默认值。

您可以采用几种方法来确定程序所需的内存量或减少程序使用的内存量。垃圾收集语言(例如 Java C#)的一个常见问题是保留对不再使用的对象的引用,或者在可以重用它们时分配许多对象。只要对象引用了它们,它们将继续使用堆空间,因为垃圾收集器不会删除它们。

在这种情况下,您可以使用Java内存探查器来确定程序中哪些方法正在分配大量对象,然后确定是否有办法确保不再引用它们,或者不将其分配给对象。第一名。除上述 MemoryInfra 以外,还有另一个选项JMP

如果确定出于某种原因分配这些对象并且需要保留引用,则在启动程序时只需增加最大堆大小。如果您不能保证您的程序将在有限的内存中运行,那么您总是会遇到此问题。仅在耗尽所有这些之后,您才需要研究将对象缓存到磁盘等中。

作为解决方案,您总是可以提及 ...我需要Xgb的内存... ,并且无法通过改进算法或内存分配模式来解决它。通常,只有在大型数据集(例如数据库或某些科学分析程序)上运行的算法才会出现这种情况,然后诸如缓存和内存映射IO之类的技术就变得有用。

一种更简单的方法是使用命令行选项运行 Java

  • -Xms1g 设置1 GB的堆大小。
  • -Xmx2048m ,它设置了2048 MB的堆大小。
  • -Xmx2g ,它设置了2 GB的堆大小。
  • -Xmx 设置堆的最大大小。

要在 Eclipse 中设置此选项,您需要转到:

  

运行->运行配置...->单击 VM参数文本框中的(x)= Arguments 选项卡-> -Xms1g -Xmx2048m -Xmx

快照:

VM arguments

但是,增加堆大小不是理想的解决方案,按照最佳做法,它是:

  • 使用正确的对象类型,例如:StringStringBufferStringBuilder
  • 区分staticnon static变量。
  • 正确使用multithreading