How Nacos Implements Long Polling with AsyncContext – A Deep Dive

This article walks through Nacos's LongPollingService source code, showing how it extracts headers, calculates timeouts, detects configuration changes, enforces connection limits, and uses Servlet 3.0 AsyncContext to handle long‑polling without blocking threads.

JavaEdge
JavaEdge
JavaEdge
How Nacos Implements Long Polling with AsyncContext – A Deep Dive

Nacos implements configuration update notifications via a long‑polling mechanism built on the Servlet 3.0 AsyncContext API. The following outlines the key steps of LongPollingService and the associated code.

1. Retrieve Header and Delay Information

The service extracts two custom request headers that control long‑polling behavior and obtains a configurable delay value.

public static final String LONG_POLLING_HEADER = "Long-Pulling-Timeout";
public static final String LONG_POLLING_NO_HANG_UP_HEADER = "Long-Pulling-Timeout-No-Hangup";

String timeoutHeader = req.getHeader(LongPollingService.LONG_POLLING_HEADER);
String noHangUpFlag = req.getHeader(LongPollingService.LONG_POLLING_NO_HANG_UP_HEADER);
int delayTime = SwitchService.getSwitchInteger(SwitchService.FIXED_DELAY_TIME, 500);

2. Compute Timeout

If a fixed polling interval is enabled, the timeout is the larger of 10 000 ms and the configured interval. Otherwise the timeout is derived from the header value minus the delay, with a lower bound of 10 000 ms.

long timeout = -1L;
if (isFixedPolling()) {
    timeout = Math.max(10000, getFixedPollingInterval());
} else {
    timeout = Math.max(10000, Long.parseLong(timeoutHeader) - delayTime);
}

3. Detect Configuration Changes

The server compares the MD5 hash of the client’s configuration with the current server version using MD5Util.compareMd5. If differences are found, a response is generated immediately; otherwise, if the "no‑hang‑up" flag is set, a log entry is recorded and the request returns without waiting.

long start = System.currentTimeMillis();
List<String> changedGroups = MD5Util.compareMd5(req, rsp, clientMd5Map);
if (changedGroups.size() > 0) {
    generateResponse(req, rsp, changedGroups);
    LogUtil.CLIENT_LOG.info("{}|{}|{}|{}|{}|{}|{}",
        System.currentTimeMillis() - start, "instant",
        RequestUtil.getRemoteIp(req), "polling",
        clientMd5Map.size(), probeRequestSize, changedGroups.size());
    return;
} else if (noHangUpFlag != null && noHangUpFlag.equalsIgnoreCase(TRUE_STR)) {
    LogUtil.CLIENT_LOG.info("{}|{}|{}|{}|{}|{}|{}",
        System.currentTimeMillis() - start, "nohangup",
        RequestUtil.getRemoteIp(req), "polling",
        clientMd5Map.size(), probeRequestSize, changedGroups.size());
    return;
}

4. Connection‑Limit Check

The request IP is examined against rate‑limiting rules. If the limit is exceeded, a 503 Service Unavailable response is sent and processing stops.

String ip = RequestUtil.getRemoteIp(req);
ConnectionCheckResponse limitResult = checkLimit(req);
if (!limitResult.isSuccess()) {
    generate503Response(req, rsp, limitResult.getMessage());
    return;
}

5. Start Asynchronous Context

To avoid blocking the servlet thread, the request is switched to asynchronous mode. The async timeout is set to 0 (no automatic timeout) because the long‑polling logic controls timing.

final AsyncContext asyncContext = req.startAsync();
asyncContext.setTimeout(0L);

6. Create and Submit Long‑Polling Task

A ClientLongPolling object is instantiated with all required parameters and handed to the dedicated thread pool via ConfigExecutor.executeLongPolling.

String appName = req.getHeader(RequestUtil.CLIENT_APPNAME_HEADER);
String tag = req.getHeader("Vipserver-Tag");
ConfigExecutor.executeLongPolling(
    new ClientLongPolling(asyncContext, clientMd5Map, ip,
        probeRequestSize, timeout, appName, tag));

7. Outcome

Using AsyncContext allows Nacos to keep the HTTP connection open while waiting for configuration changes. When a change is detected, the server replies instantly; otherwise the connection remains open until the calculated timeout expires. This non‑blocking design enables the handling of thousands of concurrent long‑polling requests without exhausting servlet threads, improving scalability and response latency.

Immediate response on configuration change.

Connection stays open until timeout when no change occurs.

Nacos Long Polling Diagram
Nacos Long Polling Diagram
Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaBackend DevelopmentNacoslong pollingAsyncContext
JavaEdge
Written by

JavaEdge

First‑line development experience at multiple leading tech firms; now a software architect at a Shanghai state‑owned enterprise and founder of Programming Yanxuan. Nearly 300k followers online; expertise in distributed system design, AIGC application development, and quantitative finance investing.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.