Mastering Apollo’s Distributed Config: How Http Long Polling Keeps Services in Sync

This article explains Apollo’s distributed configuration center, its overall architecture, the use of HTTP long polling with Spring’s DeferredResult, code examples for listening and publishing config changes, and strategies to ensure message reliability across multiple nodes.

Su San Talks Tech
Su San Talks Tech
Su San Talks Tech
Mastering Apollo’s Distributed Config: How Http Long Polling Keeps Services in Sync

Preface

The topic is Apollo, a distributed configuration center. For those unfamiliar with config centers, common options include Apollo (Ctrip open‑source with permission and workflow features), Nacos (Alibaba open‑source with DNS and RPC discovery), and Spring Cloud Config (seamlessly integrated with the Spring Cloud ecosystem).

Apollo enjoys high popularity in China’s developer community, with over 100k stars on GitHub and many production cases, while Nacos is also a strong contender.

Overall Architecture

Most configuration centers share a similar design. The typical workflow is:

User modifies and publishes configuration in the config center.

The config center notifies Apollo clients of the update.

Clients pull the latest configuration, update locally, and notify the application.

Http Long Polling

Apollo uses HTTP long polling to notify applications of configuration changes. The client sends an HTTP request that the server holds until new data arrives or a timeout occurs, effectively allowing the server to control response timing.

Although WebSocket offers more immediate push, long polling is lighter weight, easier to implement, and sufficient for infrequent, small‑size configuration updates.

Implementation Details

Spring’s DeferredResult is used on the server side to implement long polling. The following example shows a simple listener and publisher:

private final Multimap<String, DeferredResult<String>> deferredResultMap = HashMultimap.create();
private long DEFAULT_LONG_POLLING_TIMEOUT = 10 * 1000;

@GetMapping("/listen")
public DeferredResult<String> pollNotification(@RequestParam("namespace") String namespace) {
    DeferredResult<String> result = new DeferredResult<>(DEFAULT_LONG_POLLING_TIMEOUT, "timeout");
    result.onTimeout(() -> log.info("timeout"));
    result.onCompletion(() -> {
        log.info("completion");
        deferredResultMap.remove(namespace, result);
    });
    deferredResultMap.put(namespace, result);
    return result;
}

@GetMapping("/publish")
public Object publishConfig(@RequestParam("namespace") String namespace, @RequestParam("context") String context) {
    if (deferredResultMap.containsKey(namespace)) {
        Collection<DeferredResult<String>> deferredResults = deferredResultMap.get(namespace);
        for (DeferredResult<String> deferredResult : deferredResults) {
            deferredResult.setResult(context);
        }
    }
    return "success";
}

The server maintains a global deferredResultMap keyed by namespace. Clients call /listen to wait for changes; when /publish is invoked, the stored DeferredResult objects are completed, waking the waiting threads.

Timeout Considerations

Be aware of gateway or rate‑limiting timeouts (e.g., Nginx defaults to 60 seconds). If the server’s long‑poll timeout exceeds the gateway limit, the connection may be closed.

Ensuring No Message Loss

If a client node experiences a network glitch during long polling, it may miss a configuration change, leading to inconsistency. Apollo stores every change in a database with an auto‑increment notificationId. Each long‑poll request includes the last received notificationId, and the server returns the next change, effectively a paginated query.

The core method doLongPollingRefresh builds the request URL with the previous notificationId and processes the response:

private void doLongPollingRefresh(String appId, String cluster, String dataCenter, String secret) {
    final Random random = new Random();
    ServiceDTO lastServiceDto = null;
    while (!m_longPollingStopped.get() && !Thread.currentThread().isInterrupted()) {
        // ... omitted for brevity ...
        if (lastServiceDto == null) {
            List<ServiceDTO> configServices = getConfigServices();
            lastServiceDto = configServices.get(random.nextInt(configServices.size()));
        }
        url = assembleLongPollRefreshUrl(lastServiceDto.getHomepageUrl(), appId, cluster, dataCenter, m_notifications);
        HttpRequest request = new HttpRequest(url);
        request.setReadTimeout(LONG_POLLING_READ_TIMEOUT);
        if (!StringUtils.isBlank(secret)) {
            Map<String, String> headers = Signature.buildHttpHeaders(url, appId, secret);
            request.setHeaders(headers);
        }
        HttpResponse<List<ApolloConfigNotification>> response = m_httpClient.doGet(request, m_responseType);
        if (response.getStatusCode() == 200 && response.getBody() != null) {
            updateNotifications(response.getBody());
            updateRemoteNotifications(response.getBody());
            notify(lastServiceDto, response.getBody());
        }
        // ... omitted ...
    }
}

The method assembleLongPollRefreshUrl appends the previous notificationId to the request, allowing the server to return only newer notifications.

Conclusion

The article highlighted Apollo’s core technique—HTTP long polling—for delivering configuration updates, discussed its implementation with Spring’s DeferredResult, and presented strategies to guarantee reliable message delivery across multiple nodes, even under network failures.

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.

springApolloDistributed ConfigurationJava backendDeferredResult
Su San Talks Tech
Written by

Su San Talks Tech

Su San, former staff at several leading tech companies, is a top creator on Juejin and a premium creator on CSDN, and runs the free coding practice site www.susan.net.cn.

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.