终于找到JAVA解决chromeDriver长时间运行内存溢出导致程序崩溃的方法了

我的问题

我是在java中使用selenium+ChromeDriver采集数据时,需要扫码授权为了避免频繁扫码,这样就要保持浏览器一直开着,不能每次采集完之后不能使用driver.quit()退出方法。ChromeDriver长时间运行采集内存不断增加,慢慢导致程序崩溃。在网上搜了好多解决ChromeDriver内存溢出的方法都无效。被逼无奈转换思路。

我的思路

既然没办法解决ChromeDriver内存溢出,那能不能通过端口来监听ChromeDriver进程内存,如果内存达到阈值,那就直接关闭这个ChromeDriver进程,在创建一个ChromeDriver控制原来的浏览器?实现继续采集,通过实践是可以的。

解决方式

通过ChromeDriver端口获取使用的内存,如果达到阈值则进行关闭原来的ChromeDriver并用原端口创建一个新的ChromeDriver指定控制的浏览器端口。

下面直接给出代码自己参考修改,里面通过端口监听内存的方法可以看我另外一篇文章
链接: JAVA在windows和linux系统通过端口获取进程、内存、关闭进程方法

/**
     * 检查driver内存使用情况,超出阈值则替换
     */
    public static boolean checkDriverUsage(ChromeDriverContainer driverContainer) {
        // 获取driver使用端口
        log.info("检查driver内存使用情况开始...");
        Integer port = driverContainer.getManageParam().getPort();
        Integer browserPort = driverContainer.getManageParam().getBrowserPort();
        if (port == null || browserPort == null) {
            log.info("检查driver异常---->driverContainer端口或浏览器端口为空:{},{}", driverContainer, browserPort);
            return false;
        }
        // 根据端口获取内存使用情况
        String osName = System.getProperty("os.name");
        log.info("操作系统名称:{}", osName);
        String pid = null;
        long memoryUsageByPid = 0;
        try {
            if (osName.toLowerCase().contains("windows")) {
                pid = WindowsProcessMemoryUtil.findProcessIdUsingPort(port);
                if (StringUtils.isEmpty(pid)) {
                    log.info("检查driver异常---->通过端口获取进程PID为空:{}", port);
                    return false;
                }
                // 获取内存
                memoryUsageByPid = WindowsProcessMemoryUtil.getMemoryUsageByPid(pid);
            } else if (osName.toLowerCase().contains("linux")) {
                pid = LinuxProcessMemoryUtil.findProcessIdUsingPort(port);
                if (StringUtils.isEmpty(pid)) {
                    log.info("检查driver异常---->通过端口获取进程PID为空:{}", port);
                    return false;
                }
                // 获取内存
                memoryUsageByPid = LinuxProcessMemoryUtil.getMemoryUsageByPid(pid);
            }
            log.info("获取端口port:{}, 进程PID:{},已使用内存KB:{}", port, pid, memoryUsageByPid);
            if (memoryUsageByPid > 300 * 1024) { // 300mb
//            if (true) {
                // 超出监控重新创建
                log.info("超出阈值进行替换...");

                // 关闭进程
                if (osName.toLowerCase().contains("windows")) {
                    WindowsPortKillerUtil.killProcess(pid);
                } else if (osName.toLowerCase().contains("linux")) {
                    LinuxPortKillerUtil.killProcess(pid);
                }

                //增加延迟防止关闭进程慢导致创建driver失败
                ThreadUtil.sleep(3000);
                // 重新创建chromeDriver
                ChromeDriver driver = ChromeDriverUtils.getChromeDriverContainer(ChromeDriverParam.builder()
                        .headless(false)
                        .port(driverContainer.getManageParam().getPort())// 指定原端口
                        .functionOptions(options -> {
                            options.setExperimentalOption("debuggerAddress", "localhost:" + browserPort);// 指定原浏览器端口
                            BrowserMobProxyServer proxy = driverContainer.getProxy();

                            if (proxy != null) {
                                Integer proxyPort = proxy.getPort();
                                proxy.stop();
                                BrowserMobProxyServer browserMobProxy = new BrowserMobProxyServer();
                                browserMobProxy.start(proxyPort);
                                browserMobProxy.enableHarCaptureTypes(CaptureType.REQUEST_CONTENT, CaptureType.RESPONSE_CONTENT);
                                browserMobProxy.setHarCaptureTypes(CaptureType.RESPONSE_CONTENT);
                                browserMobProxy.newHar("kk:" + proxyPort);
                                Proxy seleniumProxy = ClientUtil.createSeleniumProxy(browserMobProxy);

                                // 实例化Selenium
                                options.setProxy(seleniumProxy);
                                options.setAcceptInsecureCerts(true);
                                driverContainer.setProxy(browserMobProxy);
                            }

                            return options;
                        })
                        .build()).getDriver();
                driverContainer.setDriver(driver);

            }
        } catch (Exception e) {
            log.error("检查driver异常---->", e);
            return false;
        }
        log.info("检查driver内存使用情况结束...");
        return true;
    }

我上面创建driver的时候使用了代理,所以也要停掉之前的代理用原来的代理端口创建一个新的代理。在创建新driver控制原来浏览器的时候我这遇到了一个问题:通过addArguments(“debuggerAddress”, “127.0.0.1:54223”)这种方式是可以控制原浏览器的,但是每次创建的时候又新打开了一个页面,如果再用这种方式创建driver控制浏览器就控制不了,在网上找到这个setExperimentalOption这个方法控制原浏览器就解决了。这我没找到问题原因有知道的大佬希望可以告知下。谢谢

我获取原driver控制浏览器端口是这么获取的一并放出来自己参考完善。

 ChromeDriver driver = getChromeDriver(chromeDriverService, options);
        Object o = driver.getCapabilities().asMap().get("goog:chromeOptions");
        //转成map
        Map<String, Object> map = (Map<String, Object>) o;
        if (map != null) {
            for (Map.Entry<String, Object> entry : map.entrySet()) {
                if (entry.getKey().equals("debuggerAddress")) {
                    String value = entry.getValue().toString();
                    String[] split = value.split(":");
                    if (split.length == 2) {
                        String ip = split[0];
                        String browserPort = split[1];
                        manageParam.setBrowserPort(Integer.parseInt(browserPort));
                    }
                }
            }
        }

我代码里面的创建driver方法都是包装了的,文章代码里面获取创建driver方式改成你们自己的即可。如果觉得有用麻烦点点赞,谢谢!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值