go-rod / rod

A Chrome DevTools Protocol driver for web automation and scraping.
https://go-rod.github.io
MIT License
5.01k stars 328 forks source link

NewUserMode最多启动2个浏览器,在尝试启动2个以上浏览器后卡住 #996

Closed Ecalose closed 6 months ago

Ecalose commented 6 months ago

Rod Version: v0.114.5 Leakless Version: v0.8.0

The code to demonstrate your question

package main

import (
    "fmt"
    "github.com/go-rod/rod"
    "github.com/go-rod/rod/lib/launcher"
    "github.com/google/uuid"
    "log"
    "os"
)

func main() {
    err := os.RemoveAll("tmp")
    if err != nil {
        fmt.Println(err)
    }
    for i := 0; i < 4; i++ {
        u, err := launcher.NewUserMode().
            UserDataDir("tmp/" + uuid.New().String()).Leakless(true).Headless(false).Set("--no-sandbox").Set("disable-gpu").Launch()
        if err != nil {
            log.Fatalln("launch:", err)
        }
        err = rod.New().ControlURL(u).Connect()
        if err != nil {
            log.Fatalln(err)
        }
        fmt.Println("browser:", i, "started")
    }
}

What you got

browser: 0 started
browser: 1 started

此后将会一直卡住

What you expect to see

browser: 0 started
browser: 1 started
browser: 2 started

What have you tried to solve the question

切换为New模式或不启用leakless后没有问题

ysmood commented 6 months ago

Does this work?

for i := 0; i < 4; i++ {
    time.Sleep(time.Second)
Ryan-gsq commented 6 months ago

This is useful for me, can you help me explain the reason for this?

Does this work?

for i := 0; i < 4; i++ {
    time.Sleep(time.Second)
Ecalose commented 6 months ago

Does this work?

for i := 0; i < 4; i++ {
    time.Sleep(time.Second)

不行,我这边依旧会卡住,测试平台是win11,浏览器是google chrome ,版本号120.0.6099.130,强制停止时我看到了3个leakless的cmd窗口

ysmood commented 6 months ago

确实,你单独试试 leakless 呢?不用 rod,这样很容易定位问题在哪。用 leakless 启动一个空服务。看看 leakless 的文档。 有可能是 Windows 的某种兼容问题。

Ecalose commented 6 months ago

linux环境相同结果,排除win的问题

Ryan-gsq commented 6 months ago

mac 也是这样的 应该和环境无关

Ryan-gsq commented 6 months ago
package main

import (
    "fmt"
    "github.com/go-rod/rod"
    "github.com/go-rod/rod/lib/launcher"
    "github.com/go-rod/rod/lib/proto"
    "github.com/samber/lo"
    "log"
    "os"
    "strconv"
    "time"
)

func main() {
    err := os.RemoveAll("tmp")
    if err != nil {
        fmt.Println(err)
    }

    launcher.NewManager()
    for i := 0; i < 4; i++ {
        userDir := fmt.Sprintf("tmp/%s", lo.RandomString(12, lo.LettersCharset))
        u, err := launcher.NewUserMode().Bin("/Applications/Chromium.app/Contents/MacOS/Chromium").
            UserDataDir(userDir).
            Headless(false).
            Leakless(true).
            Set("remote-debugging-port", strconv.Itoa(9222+i)). // 使用不同的端口
            Set("no-default-browser-check").
            Set("no-sandbox").
            Set("disable-gpu").
            Set("start-maximized").
            Launch()

        if err != nil {
            log.Fatalln("launch:", err)
        }
        print(u, "\t", userDir, "\n")
        b := rod.New().ControlURL(u).MustConnect()
        b.Page(proto.TargetCreateTarget{URL: "https://www.baidu.com"})
        fmt.Println("browser:", i, "started")
    }
}

似乎和启动参数有关系, 如果指定不同的"remote-debugging-port" 端口 也将正常启动. 用同一个端口时我发现, 指定不同的UserDataDir 页面会在同一个浏览器进程里面打开, 而且他们的cookie 是共用的

Ecalose commented 6 months ago
package main

import (
  "fmt"
  "github.com/go-rod/rod"
  "github.com/go-rod/rod/lib/launcher"
  "github.com/go-rod/rod/lib/proto"
  "github.com/samber/lo"
  "log"
  "os"
  "strconv"
  "time"
)

func main() {
  err := os.RemoveAll("tmp")
  if err != nil {
      fmt.Println(err)
  }

  launcher.NewManager()
  for i := 0; i < 4; i++ {
      userDir := fmt.Sprintf("tmp/%s", lo.RandomString(12, lo.LettersCharset))
      u, err := launcher.NewUserMode().Bin("/Applications/Chromium.app/Contents/MacOS/Chromium").
          UserDataDir(userDir).
          Headless(false).
          Leakless(true).
          Set("remote-debugging-port", strconv.Itoa(9222+i)). // 使用不同的端口
          Set("no-default-browser-check").
          Set("no-sandbox").
          Set("disable-gpu").
          Set("start-maximized").
          Launch()

      if err != nil {
          log.Fatalln("launch:", err)
      }
      print(u, "\t", userDir, "\n")
      b := rod.New().ControlURL(u).MustConnect()
      b.Page(proto.TargetCreateTarget{URL: "https://www.baidu.com"})
      fmt.Println("browser:", i, "started")
  }
}

似乎和启动参数有关系, 如果指定不同的"remote-debugging-port" 端口 也将正常启动. 用同一个端口时我发现, 指定不同的UserDataDir 页面会在同一个浏览器进程里面打开, 而且他们的cookie 是共用的

确实,如果随机端口号的同时随机userdir,则一个浏览器也启动不了

Ecalose commented 6 months ago

命令行同时启动3个不同dir不同端口headless的chrome没有问题

ysmood commented 6 months ago

那可能是 chrome 在竞争某个端口导致的,我 mac 跑 rod 的单元测试,同时启动 12 个浏览器都没有问题。你可以看看 rod 的单元测试的 setup_test.go

因为 UserMode 会使用同一个端口:

https://github.com/go-rod/rod/blob/ced12b56a2e1e3378e163a84ea07d7882fa34e37/lib/launcher/launcher.go#L135

而默认的模式使用的是随机端口:

https://github.com/go-rod/rod/blob/c945d86bb720cd97f6b09b87a971b149accbaee3/lib/defaults/defaults.go#L74

Ecalose commented 6 months ago

那可能是 chrome 在竞争某个端口导致的,我 mac 跑 rod 的单元测试,同时启动 12 个浏览器都没有问题。你可以看看 rod 的单元测试的 setup_test.go

因为 UserMode 会使用同一个端口:

https://github.com/go-rod/rod/blob/ced12b56a2e1e3378e163a84ea07d7882fa34e37/lib/launcher/launcher.go#L135

原来如此,感谢您的解答,设置参数RemoteDebuggingPort后没有问题