public static int getCloseTimeout(URL url) {
        String configuredCloseTimeout = System.getProperty(Constants.CLOSE_TIMEOUT_CONFIG_KEY);
        int defaultCloseTimeout = -1;
        if (StringUtils.isNotEmpty(configuredCloseTimeout)) {
            try {
                defaultCloseTimeout = Integer.parseInt(configuredCloseTimeout);
            } catch (NumberFormatException e) {
                // use default heartbeat
            }
        }
        if (defaultCloseTimeout < 0) {
            defaultCloseTimeout = getIdleTimeout(url);
        }
        int closeTimeout = url.getParameter(Constants.CLOSE_TIMEOUT_KEY, defaultCloseTimeout);
        int heartbeat = getHeartbeat(url);
        if (closeTimeout < heartbeat * 2) {
            throw new IllegalStateException("closeTimeout < heartbeatInterval * 2");
        }
        return closeTimeout;
    }
@Test
    void testGetCloseTimeout() {
        URL url1 = URL.valueOf("dubbo://127.0.0.1:12345?heartbeat=10000");
        URL url2 = URL.valueOf("dubbo://127.0.0.1:12345?heartbeat=10000&heartbeat.timeout=50000");
        URL url3 = URL.valueOf("dubbo://127.0.0.1:12345?heartbeat=10000&heartbeat.timeout=10000");
        URL url4 = URL.valueOf("dubbo://127.0.0.1:12345?close.timeout=30000&heartbeat=10000&heartbeat.timeout=10000");
        URL url5 = URL.valueOf("dubbo://127.0.0.1:12345?close.timeout=40000&heartbeat=10000&heartbeat.timeout=50000");
        URL url6 = URL.valueOf("dubbo://127.0.0.1:12345?close.timeout=10000&heartbeat=10000&heartbeat.timeout=10000");
        Assertions.assertEquals(30000, UrlUtils.getCloseTimeout(url1));
        Assertions.assertEquals(50000, UrlUtils.getCloseTimeout(url2));
        Assertions.assertThrows(RuntimeException.class, () -> UrlUtils.getCloseTimeout(url3));
        Assertions.assertThrows(RuntimeException.class, () -> UrlUtils.getCloseTimeout(url4));
        Assertions.assertEquals(40000, UrlUtils.getCloseTimeout(url5));
        Assertions.assertThrows(RuntimeException.class, () -> UrlUtils.getCloseTimeout(url6));
    }