You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java调用MAC地址查询API的GET请求结果不稳定问题求助

MAC地址厂商查询API调用不稳定问题排查与解决

我开发了一款本地网络设备分析程序,核心功能之一是通过MAC地址调用macvendors.com的API获取设备厂商信息。读取MAC地址的逻辑正常,但批量循环调用API时结果不稳定,有时能返回正确厂商信息,有时返回“VENDOR NOT FOUND”。延长超时时间、添加请求参数都没用,但调试模式下每次都能成功。

相关代码

API调用方法

public static String getVendor(String macAddress) {
        String dataUrl = "https://api.macvendors.com/" + macAddress;
        HttpURLConnection connection = null;
        try {
            URL url = new URL(dataUrl);
            connection = (HttpURLConnection) url.openConnection();
            connection.setRequestMethod("GET");
            connection.setConnectTimeout(10000);
            connection.setReadTimeout(10000);

            if (connection.getResponseCode() == HttpURLConnection.HTTP_OK) {
                BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream()));
                String inputLine;
                StringBuilder response = new StringBuilder();

                while ((inputLine = in.readLine()) != null) {
                    response.append(inputLine);
                }
                in.close();

                return response.toString();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        finally {
            if (connection != null) {
                connection.disconnect();
            }
        }

        return "VENDOR NOT FOUND";
    }

循环调用逻辑

public static void scanIP(InetAddress ip, NetworkInterface ni) {
        ConcurrentSkipListSet networkIps = scan(ip.toString().substring(1), 254);
        System.out.println("Devices connected to the network:");
        networkIps.forEach(deviceIP -> {
            try {
                System.out.println(deviceIP);
                String MACaddress = getMacAddrHost(InetAddress.getByName((String)deviceIP));
                System.out.println(MACaddress);
                System.out.println(getVendor(MACaddress));
                System.out.println();
            } catch (IOException | InterruptedException e) {
                throw new RuntimeException(e);
            }
        });
    }

问题根源

调试模式正常但批量运行失败,核心原因是macvendors.com的API存在请求频率限制:

  • 调试时单步执行,每个API请求间隔足够长,不会触发限流机制;
  • 批量循环调用时,请求发送过于密集,API服务器返回非200状态码(比如429 Too Many Requests),代码直接返回“VENDOR NOT FOUND”,未处理这类限流响应。

解决方案

1. 添加请求间隔

在循环调用getVendor后添加短暂休眠,避免请求过于密集:

networkIps.forEach(deviceIP -> {
    try {
        System.out.println(deviceIP);
        String MACaddress = getMacAddrHost(InetAddress.getByName((String)deviceIP));
        System.out.println(MACaddress);
        System.out.println(getVendor(MACaddress));
        System.out.println();
        // 添加1秒间隔,可根据API限制调整时长
        Thread.sleep(1000);
    } catch (IOException | InterruptedException e) {
        throw new RuntimeException(e);
    }
});

2. 实现重试机制

遇到非200响应时,重试几次再返回失败,处理临时限流或网络波动:

public static String getVendor(String macAddress) {
    int retryCount = 3; // 重试次数
    long retryDelay = 1000; // 初始重试间隔(毫秒)
    String dataUrl = "https://api.macvendors.com/" + macAddress;
    
    for (int i = 0; i < retryCount; i++) {
        HttpURLConnection connection = null;
        try {
            URL url = new URL(dataUrl);
            connection = (HttpURLConnection) url.openConnection();
            connection.setRequestMethod("GET");
            connection.setConnectTimeout(10000);
            connection.setReadTimeout(10000);

            int responseCode = connection.getResponseCode();
            if (responseCode == HttpURLConnection.HTTP_OK) {
                BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream()));
                String inputLine;
                StringBuilder response = new StringBuilder();

                while ((inputLine = in.readLine()) != null) {
                    response.append(inputLine);
                }
                in.close();
                return response.toString();
            } else if (responseCode == 429) {
                // 触发限流,指数退避等待后重试
                Thread.sleep(retryDelay);
                retryDelay *= 2;
            } else {
                // 其他非200状态,等待后重试
                Thread.sleep(retryDelay);
            }
        } catch (Exception e) {
            e.printStackTrace();
            try {
                Thread.sleep(retryDelay);
            } catch (InterruptedException ie) {
                Thread.currentThread().interrupt();
            }
        } finally {
            if (connection != null) {
                connection.disconnect();
            }
        }
    }
    return "VENDOR NOT FOUND";
}

3. 缓存查询结果

同一个MAC地址无需重复查询,用ConcurrentHashMap缓存结果,减少API调用次数:

private static final Map<String, String> MAC_VENDOR_CACHE = new ConcurrentHashMap<>();

public static String getVendor(String macAddress) {
    // 先从缓存获取,避免重复调用API
    if (MAC_VENDOR_CACHE.containsKey(macAddress)) {
        return MAC_VENDOR_CACHE.get(macAddress);
    }
    
    int retryCount = 3;
    long retryDelay = 1000;
    String dataUrl = "https://api.macvendors.com/" + macAddress;
    
    for (int i = 0; i < retryCount; i++) {
        HttpURLConnection connection = null;
        try {
            URL url = new URL(dataUrl);
            connection = (HttpURLConnection) url.openConnection();
            connection.setRequestMethod("GET");
            connection.setConnectTimeout(10000);
            connection.setReadTimeout(10000);

            int responseCode = connection.getResponseCode();
            if (responseCode == HttpURLConnection.HTTP_OK) {
                BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream()));
                String inputLine;
                StringBuilder response = new StringBuilder();

                while ((inputLine = in.readLine()) != null) {
                    response.append(inputLine);
                }
                in.close();
                String vendor = response.toString();
                MAC_VENDOR_CACHE.put(macAddress, vendor); // 存入缓存
                return vendor;
            } else if (responseCode == 429) {
                Thread.sleep(retryDelay);
                retryDelay *= 2;
            } else {
                Thread.sleep(retryDelay);
            }
        } catch (Exception e) {
            e.printStackTrace();
            try {
                Thread.sleep(retryDelay);
            } catch (InterruptedException ie) {
                Thread.currentThread().interrupt();
            }
        } finally {
            if (connection != null) {
                connection.disconnect();
            }
        }
    }
    MAC_VENDOR_CACHE.put(macAddress, "VENDOR NOT FOUND");
    return "VENDOR NOT FOUND";
}

4. 复用HTTP连接池

HttpURLConnection默认支持连接复用,手动调用disconnect()会关闭连接,可去掉该语句让连接池复用连接;也可以替换为OkHttp等更高效的HTTP客户端,自动管理连接池。

内容的提问来源于stack exchange,提问作者Alexei Bobrik

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.29 11:05:13