Files
KLALB/src/org/kne/cloud/network/congestion/MpscMessageBatcher.java
T

208 lines
5.6 KiB
Java

package org.kne.cloud.network.congestion;
import org.jctools.queues.MessagePassingQueue;
import org.jctools.queues.varhandle.MpscVarHandleArrayQueue;
import org.kne.cloud.network.ThreadTool;
import org.kne.opencl64.Releaser;
import java.lang.ref.Cleaner;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.LockSupport;
import java.util.function.Consumer;
/**
* 基于 MPSC 队列的高性能消息批量发送器
*/
public class MpscMessageBatcher<T> implements MessageBatcher<T> {
private static final Cleaner clr = Cleaner.create();
private final MpscMessageBatcher0<T> impl;
private final MpscMessageBatcherReleaser<T> releaser;
public MpscMessageBatcher(int batchSize, long maxDelayNanos) {
this.impl = new MpscMessageBatcher0<>(batchSize, maxDelayNanos, 65536);
this.releaser = new MpscMessageBatcherReleaser<>(impl);
clr.register(this, releaser);
}
@Override
public int getBatchSize() { return impl.getBatchSize(); }
@Override
public long getMaxDelay() { return impl.getMaxDelay(); }
@Override
public void setConsumer(Consumer<List<T>> consumer) { impl.setConsumer(consumer); }
@Override
public void putMessage(T message) { impl.putMessage(message); }
@Override
public void putMessages(List<T> messages) { impl.putMessages(messages); }
@Override
public void flush() { impl.flush(); }
@Override
public int getQueueSize() { return impl.getQueueSize(); }
@Override
public boolean isClosed() { return impl.isClosed(); }
@Override
public void close() { impl.close(); }
public static void main(String[] args) throws InterruptedException {
MpscMessageBatcher<Integer> batcher =
new MpscMessageBatcher<>(10, 1_000_000L);
batcher.setConsumer(System.out::println);
int n = 0;
for (;;) {
batcher.putMessage(n++);
batcher.putMessage(n++);
batcher.putMessage(n++);
batcher.putMessage(n++);
Thread.sleep(1);
}
}
}
class MpscMessageBatcherReleaser<T> extends Releaser<MpscMessageBatcher0<T>> {
public MpscMessageBatcherReleaser(MpscMessageBatcher0<T> resource) {
super(resource);
}
@Override
protected void release(MpscMessageBatcher0<T> resource) {
resource.close();
}
}
class MpscMessageBatcher0<T> implements Runnable, MessageBatcher<T> {
private final MessagePassingQueue<T> queue;
private final int batchSize;
private final long maxDelay;
private volatile Consumer<List<T>> consumer;
private final Thread batchThread;
private final AtomicBoolean closed = new AtomicBoolean(false);
private long firstTime;
public MpscMessageBatcher0(int batchSize, long maxDelay, int queueCapacity) {
this.batchSize = batchSize;
this.maxDelay = maxDelay;
this.queue = new MpscVarHandleArrayQueue<>(queueCapacity);
this.batchThread = ThreadTool.makeVDaemonThread("MpscMessageBatcher", this);
this.batchThread.start();
}
@Override
public void setConsumer(Consumer<List<T>> consumer) {
this.consumer = consumer;
}
@Override
public void putMessage(T message) {
if (message == null || closed.get()) return;
// 无锁入队
while (!queue.offer(message)) {
// 队列满时,主动尝试 check 一次(自旋等待)
Thread.yield();
}
check(false);
}
@Override
public void putMessages(List<T> messages) {
for (T msg : messages) {
putMessage(msg);
}
}
@Override
public void run() {
while(!closed.get()) {
check(false);
// 更精确的等待策略
long sleepTimeNanos = calculateSleepTime();
if (sleepTimeNanos > 0) {
LockSupport.parkNanos(sleepTimeNanos);
} else {
// 避免忙等待
LockSupport.parkNanos(1_000_000L); // 1ms
}
}
}
private void check(boolean force) {
long currTime=System.nanoTime();
if(queue.size()>=batchSize||(currTime-firstTime)>maxDelay||force) {
if(consumer!=null) {
ArrayList<T>batch=new ArrayList<>(batchSize);
queue.drain(batch::add, batchSize);
if(!batch.isEmpty())
try {
consumer.accept(batch);
}catch(Exception e) {
e.printStackTrace();
}
firstTime=currTime;
}
}
}
/**
* 计算需要等待的时间
* @return 等待时间(纳秒)
*/
private long calculateSleepTime() {
long elapsed = System.nanoTime() - firstTime;
long remaining = maxDelay - elapsed;
if (remaining <= 0) {
return 0; // 立即处理
}
// 返回剩余时间或10ms中的较小值
return Math.min(remaining, 10_000_000L);
}
@Override
public void flush() {
check(true);
}
@Override
public int getQueueSize() {
return queue.size();
}
@Override
public boolean isClosed() {
return closed.get();
}
@Override
public void close() {
closed.set(true);
LockSupport.unpark(batchThread);
flush(); // 确保剩余消息被处理
}
@Override
public int getBatchSize() {
return batchSize;
}
@Override
public long getMaxDelay() {
return maxDelay;
}
}