Mastering Request Merging: Hystrix Collapser, BatchCollapser & ConcurrentHashMultiset Explained

This article explores how merging similar requests upstream can dramatically reduce downstream load, detailing Hystrix Collapser usage and configuration, a custom BatchCollapser implementation with time‑ and count‑based triggers, and the thread‑safe ConcurrentHashMultiset for high‑duplicate statistical scenarios.

Top Architect
Top Architect
Top Architect
Mastering Request Merging: Hystrix Collapser, BatchCollapser & ConcurrentHashMultiset Explained

Introduction

In typical request‑response models each request occupies its own thread and resources, causing high I/O cost when many identical operations are performed. Merging similar requests upstream before sending them downstream can greatly reduce load and improve throughput.

Hystrix Collapser

Hystrix provides a collapser that merges requests using either a custom batch method or annotations. The @HystrixCollapser annotation marks the method to be collapsed, while the corresponding @HystrixCommand defines the batch method. Important points include:

Add @HystrixCollapser on the method to be collapsed and @HystrixCommand on the batch method.

The single method accepts a single argument; for multiple arguments wrap them in a custom class.

single returns java.util.concurrent.Future<T>, batch returns java.util.List<T>, and the result count must match the input count.

Example:

public class HystrixCollapserSample {
    @HystrixCollapser(batchMethod = "batch")
    public Future<Boolean> single(String input) {
        // never executed
        return null;
    }

    public List<Boolean> batch(List<String> inputs) {
        return inputs.stream()
                     .map(it -> Boolean.TRUE)
                     .collect(Collectors.toList());
    }
}

Configuration is done via the @HystrixCollapser annotation, specifying collapserKey, batchMethod, scope (REQUEST or GLOBAL), and collapserProperties such as maxRequestsInBatch, timerDelayInMilliseconds, and requestCache.enabled.

BatchCollapser (Custom Implementation)

When the result of individual requests is not needed, a simpler collapser can be built. It stores incoming requests in a thread‑safe container (e.g., LinkedBlockingDeque) and triggers batch execution either after a time interval or when a request count threshold is reached.

Key design points:

The container must preserve order and allow safe bulk removal without explicit locking.

A ScheduledExecutorService replaces a blocking Timer to schedule periodic batch processing.

Singleton factory ensures one collapser per handler class.

public class BatchCollapser<E> implements InitializingBean {
    private volatile LinkedBlockingDeque<E> batchContainer = new LinkedBlockingDeque<>();
    private Handler<List<E>, Boolean> cleaner;
    private long interval;
    private int threshHold;

    public void submit(E event) {
        batchContainer.add(event);
        if (batchContainer.size() >= threshHold) {
            clean();
        }
    }

    private void clean() {
        List<E> transferList = new ArrayList<>(threshHold);
        batchContainer.drainTo(transferList, 100);
        if (!transferList.isEmpty()) {
            cleaner.handle(transferList);
        }
    }

    @Override
    public void afterPropertiesSet() {
        SCHEDULE_EXECUTOR.scheduleAtFixedRate(() -> {
            try {
                clean();
            } catch (Exception e) {
                logger.error("clean container exception", e);
            }
        }, 0, interval, TimeUnit.MILLISECONDS);
    }
}

ConcurrentHashMultiset

For high‑frequency duplicate data, Guava’s ConcurrentHashMultiset stores each element together with a count, allowing lock‑free increments and decrements. It is ideal for scenarios such as real‑time statistics where the same key appears many times.

if (ConcurrentHashMultiset.isEmpty()) {
    return;
}
List<Request> transferList = new ArrayList<>();
ConcurrentHashMultiset.elementSet().forEach(request -> {
    int count = ConcurrentHashMultiset.count(request);
    if (count <= 0) return;
    transferList.add(count == 1 ? request : new Request(request.getIncrement() * count));
    ConcurrentHashMultiset.remove(request, count);
});

Summary

Use Hystrix collapser when each request’s result is required despite the extra latency. Use the custom BatchCollapser for fire‑and‑forget scenarios where batching can be triggered by time or count. Use ConcurrentHashMultiset for high‑duplicate statistical workloads. Combining BatchCollapser with ConcurrentHashMultiset can further reduce downstream pressure.

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.

Spring BootJava concurrencyHystrixrequest mergingBatchCollapserConcurrentHashMultiset
Top Architect
Written by

Top Architect

Top Architect focuses on sharing practical architecture knowledge, covering enterprise, system, website, large‑scale distributed, and high‑availability architectures, plus architecture adjustments using internet technologies. We welcome idea‑driven, sharing‑oriented architects to exchange and learn together.

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.