2017-01-02 2 views
9

В моем встроенном драйвере Selenium/PhantomJSDriver кажется, что ресурсы не очищаются. Запуск клиента синхронно вызывает миллионы открытых файлов и, в конце концов, вызывает исключение типа «Слишком много файлов».Слишком много открытых файлов (Selenium + PhantomJSDriver)

Вот некоторые выходы я собрал из lsof в то время как программа работает в течение ~ 1 мин

$ lsof | awk '{ print $2; }' | uniq -c | sort -rn | head 
    1221966 12180 
     34790 29773 
     31260 12138 
     20955 8414 
     17940 10343 
     16665 32332 
     9512 27713 
     7275 19226 
     5496 7153 
     5040 14065 

$ lsof -p 12180 | awk '{ print $2; }' | uniq -c | sort -rn | head 
    2859 12180 
     1 PID 

$ lsof -p 12180 -Fn | sort -rn | uniq -c | sort -rn | head 
    1124 npipe 
    536 nanon_inode 
     4 nsocket 
     3 n/opt/jdk/jdk1.8.0_60/jre/lib/jce.jar 
     3 n/opt/jdk/jdk1.8.0_60/jre/lib/charsets.jar 
     3 n/dev/urandom 
     3 n/dev/random 
     3 n/dev/pts/20 
     2 n/usr/share/sbt-launcher-packaging/bin/sbt-launch.jar 
     2 n/usr/share/java/jayatana.jar 

Я не понимаю, почему использование -p флага lsof имеет меньший набор результатов. Но, похоже, большая часть записей pipe и anon_inode.

Клиент очень прост в ~ 100 строках, а по окончании использования звонки driver.close() и driver.quit(). Я экспериментировал с кэшированием и повторным использованием клиентов, но это не ослабило открытые файлы

case class HeadlessClient(
          country: String, 
          userAgent: String, 
          inheritSessionId: Option[Int] = None 
         ) { 
    protected var numberOfRequests: Int = 0 
    protected val proxySessionId: Int = inheritSessionId.getOrElse(new Random().nextInt(Integer.MAX_VALUE)) 
    protected val address = InetAddress.getByName("proxy.domain.com") 
    protected val host = address.getHostAddress 
    protected val login: String = HeadlessClient.username + proxySessionId 
    protected val windowSize = new org.openqa.selenium.Dimension(375, 667) 

    protected val (mobProxy, seleniumProxy) = { 

    val proxy = new BrowserMobProxyServer() 
    proxy.setTrustAllServers(true) 
    proxy.setChainedProxy(new InetSocketAddress(host, HeadlessClient.port)) 
    proxy.chainedProxyAuthorization(login, HeadlessClient.password, AuthType.BASIC) 
    proxy.addLastHttpFilterFactory(new HttpFiltersSourceAdapter() { 
     override def filterRequest(originalRequest: HttpRequest): HttpFilters = { 
     new HttpFiltersAdapter(originalRequest) { 
      override def proxyToServerRequest(httpObject: HttpObject): io.netty.handler.codec.http.HttpResponse = { 
      httpObject match { 
       case req: HttpRequest => req.headers().remove(HttpHeaders.Names.VIA) 
       case _ => 
      } 
      null 
      } 
     } 
     } 
    }) 
    proxy.enableHarCaptureTypes(CaptureType.REQUEST_CONTENT, CaptureType.RESPONSE_CONTENT) 
    proxy.start(0) 
    val seleniumProxy = ClientUtil.createSeleniumProxy(proxy) 
    (proxy, seleniumProxy) 
    } 

    protected val driver: PhantomJSDriver = { 
    val capabilities: DesiredCapabilities = DesiredCapabilities.chrome() 
    val cliArgsCap = new util.ArrayList[String] 
    cliArgsCap.add("--webdriver-loglevel=NONE") 
    cliArgsCap.add("--ignore-ssl-errors=yes") 
    cliArgsCap.add("--load-images=no") 

    capabilities.setCapability(CapabilityType.PROXY, seleniumProxy) 
    capabilities.setCapability("phantomjs.page.customHeaders.Referer", "") 
    capabilities.setCapability("phantomjs.page.settings.userAgent", userAgent) 
    capabilities.setCapability(PhantomJSDriverService.PHANTOMJS_CLI_ARGS, cliArgsCap) 

    new PhantomJSDriver(capabilities) 
    } 

    driver.executePhantomJS(
    """ 
     |var navigation = []; 
     | 
     |this.onNavigationRequested = function(url, type, willNavigate, main) { 
     | navigation.push(url) 
     | console.log('Trying to navigate to: ' + url); 
     |} 
     | 
     |this.onResourceRequested = function(request, net) { 
     | console.log("Requesting " + request.url); 
     | if (! (navigation.indexOf(request.url) > -1)) { 
     |  console.log("Aborting " + request.url) 
     |  net.abort(); 
     | } 
     |}; 
    """.stripMargin 
) 

    driver.manage().window().setSize(windowSize) 

    def follow(url: String)(implicit ec: ExecutionContext): List[HarEntry] = { 
    try{ 
     Await.result(Future{ 
     mobProxy.newHar(url) 
     driver.get(url) 
     val entries = mobProxy.getHar.getLog.getEntries.asScala.toList 
     shutdown() 
     entries 
     }, 45.seconds) 
    } catch { 
     case e: Exception => 
     try { 
      shutdown() 
     } catch { 
      case shutdown: Exception => 
      throw new Exception(s"Error ${shutdown.getMessage} cleaning up after Exception: ${e.getMessage}") 
     } 

     throw e 
    } 
    } 

    def shutdown() = { 
    driver.close() 
    driver.quit() 
    } 
} 

Я попробовал несколько версий Selenium в случае Были исправлены ошибки. Build.sbt:

libraryDependencies += "org.seleniumhq.selenium" % "selenium-java" % "3.0.1" 
libraryDependencies += "net.lightbody.bmp" % "browsermob-core" % "2.1.2"   

Кроме того, я попытался PhantomJS 2.0.1 и 2.1.1:

$ phantomjs --version 
    2.0.1-development 

$ phantomjs --version 
    2.1.1 

Является ли это PhantomJS или проблема Селен? Мой клиент неправильно использует API?

+0

Я не награждаю награду, потому что не было дано правильных ответов. Бони предлагал быстрые идеи, но не отвечал на комментарии или не взаимодействовал дальше. Это справедливо? – nkconnor

ответ

3

Использование ресурсов вызвано BrowserMob. Чтобы закрыть прокси-сервер и очистить его ресурсы, необходимо позвонить stop().

Для этого клиента, что означает, изменяющие shutdown методу

def shutdown() = { 
    mobProxy.stop() 
    driver.close() 
    driver.quit() 
} 

Другой метод, abort, предлагает немедленное прекращение действия прокси-сервера и не ждать, пока трафик прекратить.

0

По-моему, проблема PhantomJS. Вы можете попробовать следующие варианты:

  1. Использовать phantomjs 2.5.0-beta. Он был недавно выпущен. Я не уверен, что это обновление решает вашу проблему, но, по крайней мере, стоит попробовать. По словам журнала изменений, новые возможности этой версии:

    • Upgrade QtWebKit к QtWebKitNG
    • Модернизированный Qt для 5.7.1
  2. Очистите phantomjs процессы после закрытия WebDriver. Вы можете реализовать свой собственный очиститель, чтобы заставить phantomjs фактически закрыться после driver.close() (ссылаясь на killall -9 phantomjs или тому подобное).

+0

после уничтожения процессов PhantomJS открытые файлы все еще существуют – nkconnor