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

node-opcua v2.14.0客户端重连服务器过慢问题求助

How to Speed Up Reconnection in node-opcua 2.14.0 Client

I’ve worked with node-opcua extensively, and the slow reconnection you’re seeing stems mostly from suboptimal connection strategy settings and overly lenient default timeouts. Let’s break down the fixes step by step:

Key Issues in Your Current Setup

  • Limited Retry Attempts: Your connectionStrategy sets maxRetry: 1, meaning the client only tries to reconnect once after the first failure. After that, it waits for an internal 5-minute timeout before attempting to reconnect again.
  • Slow Failure Detection: Default TCP keepalive and request timeout settings take too long to detect a dead connection after a hard network cut (like turning off WiFi).
  • Subscription Settings Don’t Accelerate Detection: Your subscription’s lifetime and keepalive counts are configured in a way that delays the client’s awareness of server unresponsiveness.

Step-by-Step Fixes

1. Optimize the Connection Strategy

Update your connection strategy to enable infinite retries with reasonable backoff times. This ensures the client keeps trying to reconnect as soon as the network comes back up:

const connectionStrategy = {
  initialDelay: 2000,       // Wait 2s before first retry
  maxDelay: 10000,          // Cap retry delay at 10s to avoid excessive waiting
  maxRetry: 0,              // 0 = infinite retries (critical for quick reconnection)
  retryOnFailure: true      // Ensure retries trigger on connection failures
};
  • maxRetry: 0 is the biggest fix here—your original setting of 1 only allowed one retry, which caused the 5-minute delay before the next reconnection attempt.
  • The backoff range prevents overwhelming the server when it comes back online.

2. Reduce Request Timeout to Speed Up Failure Detection

Add a requestTimeout to your client options. This tells the client how long to wait for a response before marking a request as timed out, triggering reconnection attempts sooner:

const options: OPCUAClientOptions = {
  applicationName: 'AppName',
  connectionStrategy: connectionStrategy,
  securityMode: MessageSecurityMode.None,
  securityPolicy: SecurityPolicy.None,
  endpoint_must_exist: false,
  requestTimeout: 10000     // Timeout requests after 10s instead of the default 50s
};

Your original request timeout hint was 50s, which is why you saw the timed_out_request event only after ~60s. Lowering this makes the client detect unresponsiveness much faster.

3. Adjust Subscription Parameters for Faster Termination

Tweak your subscription settings to make the server terminate the subscription sooner if it loses connection, helping the client realize the issue faster:

const subscription = ClientSubscription.create(session, {
  requestedPublishingInterval: 10,
  requestedLifetimeCount: 5,    // Terminate if no keepalive for 5 intervals (50ms)
  requestedMaxKeepAliveCount: 1,// Send keepalive every 1 interval (10ms)
  maxNotificationsPerPublish: 10,
  publishingEnabled: true,
  priority: 10,
});
  • requestedMaxKeepAliveCount: 1 ensures the server sends frequent keepalives, so the client gets regular confirmation the connection is alive.
  • requestedLifetimeCount: 5 triggers subscription termination quickly if keepalives stop, alerting the client to the disconnect.

4. Reinitialize Subscriptions After Reconnection

Your current code only creates subscriptions once during initial connection. You need to recreate them when the connection is restored. Update your event listeners to handle this:

function attachEventListeners(client) {
  client.on("connection_reestablished", async () => {
    console.log(`> Connection reestablished !`);
    try {
      currentSession = await client.createSession();
      console.log(`> Recreated session !`);
      initSubscription(client, currentSession);
    } catch (e) {
      console.log(`Failed to recreate session: ${e}`);
    }
  })
  // Keep your other event listeners here...
}

Add a currentSession variable at the top of your code to track the active session across reconnections.

Why the close Event Isn’t Triggered

When you force a network cut, the TCP connection enters a "half-open" state—your client doesn’t receive a proper FIN packet from the server, so the socket doesn’t trigger the close event immediately. It takes time for the TCP stack to detect the dead connection via keepalive probes (which have long default timeouts). The timed_out_request event is the client’s way of detecting this unresponsiveness since it doesn’t get a response to its requests.

Full Modified Code

Here’s the complete adjusted code incorporating all fixes:

import { OPCUAClient, MessageSecurityMode, SecurityPolicy, OPCUAClientOptions, ClientSubscription, DataValue, ClientMonitoredItem, ReadValueIdLike, MonitoringParametersOptions, AttributeIds, TimestampsToReturn } from 'node-opcua';

// Server connection
const connectionStrategy = {
  initialDelay: 2000,
  maxDelay: 10000,
  maxRetry: 0,
  retryOnFailure: true
};

const options: OPCUAClientOptions = {
  applicationName: 'AppName',
  connectionStrategy: connectionStrategy,
  securityMode: MessageSecurityMode.None,
  securityPolicy: SecurityPolicy.None,
  endpoint_must_exist: false,
  requestTimeout: 10000
};

const url = 'opc.tcp://opcuaserver.com:48010';
let currentSession: any = null;

async function main() {
  console.log('> Connecting...');
  const client = OPCUAClient.create(options);
  await client.connect(url);
  console.log('> Connected !');
  try {
    console.log('> Creating session...');
    currentSession = await client.createSession();
    console.log('> Session created !');
    attachEventListeners(client);
    initSubscription(client, currentSession);
  } catch (e) {
    console.log(`couldn't create opcua session. error: ${e}`);
  }
}

main().catch(err => console.log('Main error:', err)).then(() => console.log('process finished'));

function attachEventListeners(client) {
  client.on("start_reconnection", () => {
    console.log(`> Starting reconnection...`);
  })
  .on("connection_reestablished", async () => {
    console.log(`> Connection reestablished !`);
    try {
      currentSession = await client.createSession();
      console.log(`> Recreated session !`);
      initSubscription(client, currentSession);
    } catch (e) {
      console.log(`Failed to recreate session: ${e}`);
    }
  })
  .on("connection_failed", (e) => {
    console.log(`> Connection failed !`, e);
  })
  .on("reconnection_attempt_has_failed", (e) => {
    console.log(`> Re-connection attempt has failed !`, e);
  })
  .on("close", () => {
    console.log(`> Connection closed !`);
  })
  .on("backoff", () => {
    console.log(`> Connection backoff !`);
  })
  .on("after_reconnection", (err) => {
    console.log("> Reconnection process has been completed: ", err);
  })
  .on("timed_out_request", (request) => {
    console.log(`> Request has timed out without receiving a response:`);
    console.log(`  Request handle: ${request.requestHandle}, Subscription ID: ${request.subscriptionAcknowledgements?.[0]?.subscriptionId}`);
  });
}

function initSubscription(client, session) {
  const subscription = ClientSubscription.create(session, {
    requestedPublishingInterval: 10,
    requestedLifetimeCount: 5,
    requestedMaxKeepAliveCount: 1,
    maxNotificationsPerPublish: 10,
    publishingEnabled: true,
    priority: 10,
  });

  subscription.on("keepalive", (x) => {
    console.log("> Subscription > Keepalive", x);
  })
  .on("terminated", () => {
    console.log("> Subscription > Terminated");
  });

  // Subscribe to "Air Conditioner Humidity" sensor
  const itemToMonitor: ReadValueIdLike = {
    nodeId: "ns=3;s=AirConditioner_6.Humidity",
    attributeId: AttributeIds.Value
  };
  const parameters: MonitoringParametersOptions = {
    samplingInterval: 10,
    discardOldest: true,
    queueSize: 10
  };

  const monitoredItem = ClientMonitoredItem.create(
    subscription,
    itemToMonitor,
    parameters,
    TimestampsToReturn.Both
  );
  monitoredItem.on("err", (err) => {
    console.log(`subscribed resource error: ${err}`);
  });
  monitoredItem.on("changed", (value: DataValue) => {
    console.log(`subscribed resource changed value: ${value.value.value}`);
  });
}

With these changes, your client will detect connection failures within 10 seconds, start reconnection attempts immediately, and restore subscriptions as soon as the network is back up—no more 5-minute waits.

内容的提问来源于stack exchange,提问作者Eduardo G.R.

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.09 06:22:48