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

Spring Boot集成Jamod与UPS通信:首次请求成功后续定时请求失败

问题诊断与解决方案

核心问题分析

从报错和代码来看,问题出在连接生命周期管理不当:

  1. 定时任务每次循环都创建新的StatusProvider并调用connect(),导致重复创建TCP连接且未正确释放,UPS设备会主动断开闲置/过多的连接,后续请求使用失效连接就会抛出流中断、连接结束类错误。
  2. 首次请求成功后,后续定时请求复用的连接可能已被UPS端关闭,但代码未检查连接有效性,直接执行事务导致重试失败。

具体修复方案

1. 复用Modbus TCP连接,避免重复创建

将ModbusTCPConnection改为设备级实例,仅在连接失效时重建:

public class JamodStatusProvider implements StatusProvider {
    private ModbusTCPConnection connection;
    private final String deviceIp;
    private final int devicePort;

    public JamodStatusProvider(String deviceIp, int devicePort) throws UnknownHostException {
        this.deviceIp = deviceIp;
        this.devicePort = devicePort;
        this.connection = new ModbusTCPConnection(InetAddress.getByName(deviceIp), devicePort);
        // 配置连接参数
        connection.setTimeout(10000); // 10秒超时
        connection.setKeepAlive(true); // 启用TCP保活,维持长连接
    }

    @Override
    public void connect() throws Exception {
        // 仅当连接断开时重建
        if (!connection.isConnected()) {
            // 先关闭旧连接(如果存在)
            if (connection != null) {
                connection.close();
            }
            connection = new ModbusTCPConnection(InetAddress.getByName(deviceIp), devicePort);
            connection.setTimeout(10000);
            connection.setKeepAlive(true);
            connection.connect();
        }
    }

    // 应用关闭时释放连接
    public void destroy() {
        if (connection != null && connection.isConnected()) {
            connection.close();
        }
    }

    // 其他方法如getInputVoltage...
}

2. 缓存StatusProvider,避免重复实例化

在定时任务类中添加缓存,每个设备对应一个StatusProvider实例,避免每次循环都新建:

private final Map<DeviceEntity, StatusProvider> providerCache = new ConcurrentHashMap<>();

public void logDeviceDataPeriodically() {
    try {
        List<DeviceEntity> devices = deviceEntityRepository.findAll();
        for (DeviceEntity deviceEntity : devices) {
            String providerType = deviceEntity.getStatusProviderEntity().getProviderName();
            StatusProvider statusProvider = providerCache.computeIfAbsent(deviceEntity, key -> {
                try {
                    return providerFactory.createProvider(providerType);
                } catch (Exception e) {
                    e.printStackTrace();
                    return null;
                }
            });

            if (statusProvider != null) {
                try {
                    statusProvider.connect(); // 确保连接有效
                    // 读取并保存数据逻辑
                    DeviceData deviceData = new DeviceData();
                    deviceData.setInputVoltage(statusProvider.getInputVoltage());
                    deviceData.setOutputVoltage(statusProvider.getOutputVoltage());
                    deviceData.setLoadPercentage(statusProvider.getLoadPercentage());
                    deviceData.setTime(statusProvider.getTime());
                    deviceData.setTimestamp(LocalDateTime.now());
                    deviceData.setDeviceEntity(deviceEntity);
                    deviceDataRepository.save(deviceData);
                } catch (Exception e) {
                    // 连接失效,移除缓存,下次循环重建
                    providerCache.remove(deviceEntity);
                    e.printStackTrace();
                }
            } else {
                System.err.println("Unknown provider - " + providerType);
            }
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
}

3. 增强事务异常处理,失败时重试重建连接

修改readRegisters方法,捕获连接异常后主动重建连接并重试:

public Register[] readRegisters(int startingAddress, int quantity, int unitId) throws Exception {
    ReadMultipleRegistersRequest request = new ReadMultipleRegistersRequest(startingAddress, quantity);
    request.setUnitID(unitId);

    ModbusTCPTransaction transaction = new ModbusTCPTransaction(connection);
    transaction.setRequest(request);
    transaction.setRetries(2); // 自定义重试次数

    try {
        transaction.execute();
        ReadMultipleRegistersResponse response = (ReadMultipleRegistersResponse) transaction.getResponse();
        return response.getRegisters();
    } catch (ModbusException | IOException e) {
        // 连接失效,重建后重试一次
        connection.close();
        connection.connect();
        transaction.execute();
        ReadMultipleRegistersResponse response = (ReadMultipleRegistersResponse) transaction.getResponse();
        return response.getRegisters();
    }
}

4. 额外优化建议

  • 检查UPS设备的TCP连接闲置超时设置,如果允许,调大超时时间匹配你的定时周期(60秒)。
  • 若UPS不支持长连接,可改为每次请求前新建连接,请求完成后立即关闭(需在finally块中确保关闭):
    // 修改readRegisters为每次使用临时连接
    public Register[] readRegisters(int startingAddress, int quantity, int unitId) throws Exception {
        ModbusTCPConnection tempConn = new ModbusTCPConnection(InetAddress.getByName(deviceIp), devicePort);
        tempConn.setTimeout(10000);
        try {
            tempConn.connect();
            ReadMultipleRegistersRequest request = new ReadMultipleRegistersRequest(startingAddress, quantity);
            request.setUnitID(unitId);
            ModbusTCPTransaction transaction = new ModbusTCPTransaction(tempConn);
            transaction.setRequest(request);
            transaction.execute();
            ReadMultipleRegistersResponse response = (ReadMultipleRegistersResponse) transaction.getResponse();
            return response.getRegisters();
        } finally {
            if (tempConn.isConnected()) {
                tempConn.close();
            }
        }
    }
    

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 07:50:03