All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.micrometer.statsd.internal.BufferingFlux Maven / Gradle / Ivy

There is a newer version: 1.14.1
Show newest version
/**
 * Copyright 2017 VMware, Inc.
 * 

* Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at *

* https://www.apache.org/licenses/LICENSE-2.0 *

* Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package io.micrometer.statsd.internal; import java.time.Duration; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; import java.util.stream.Collectors; import reactor.core.publisher.DirectProcessor; import reactor.core.publisher.Flux; public class BufferingFlux { private BufferingFlux() { } /** * Creates a Flux that implements Nagle's algorithm to buffer messages -- joined by a delimiter string -- to up a * maximum number of bytes, or a maximum duration of time. This avoids sending many small packets in favor of fewer * larger ones. * * @param source The input flux. * @param delimiter The delimiter to use to join messages * @param maxByteArraySize The buffered payload will contain no more than this number of bytes * @param maxMillisecondsBetweenEmits Buffered payloads will be emitted no less frequently than this. * @return A flux implementing Nagle's algorithm. * @see Nagle's algorithm */ public static Flux create(final Flux source, final String delimiter, final int maxByteArraySize, final long maxMillisecondsBetweenEmits) { return Flux.defer(() -> { final int delimiterSize = delimiter.getBytes().length; final AtomicInteger byteSize = new AtomicInteger(0); final AtomicLong lastTime = new AtomicLong(0); final DirectProcessor intervalEnd = DirectProcessor.create(); final Flux heartbeat = Flux.interval(Duration.ofMillis(maxMillisecondsBetweenEmits)) .map(l -> "") .takeUntilOther(intervalEnd); // Create a stream that emits at least once every $maxMillisecondsBetweenEmits, to avoid long pauses between // buffer flushes when the source doesn't emit for a while. final Flux sourceWithEmptyStringKeepAlive = source .doOnTerminate(intervalEnd::onComplete) .mergeWith(heartbeat); return sourceWithEmptyStringKeepAlive .bufferUntil(line -> { final int bytesLength = line.getBytes().length; final long now = System.currentTimeMillis(); // Update last time to now if this is the first time lastTime.compareAndSet(0, now); final long last = lastTime.get(); long diff; if (last != 0L) { diff = now - last; if (diff > maxMillisecondsBetweenEmits && byteSize.get() > 0) { // This creates a buffer, reset size byteSize.set(bytesLength); lastTime.compareAndSet(last, now); return true; } } int additionalBytes = bytesLength; if (additionalBytes > 0 && byteSize.get() > 0) { additionalBytes += delimiterSize; // Make up for the delimiter that's added when joining the strings } final int projectedBytes = byteSize.addAndGet(additionalBytes); if (projectedBytes > maxByteArraySize) { // This creates a buffer, reset size byteSize.set(bytesLength); lastTime.compareAndSet(last, now); return true; } return false; }, true) .map(lines -> lines.stream() .filter(line -> !line.isEmpty()) .collect(Collectors.joining(delimiter, "", delimiter))); }); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy