Chronicle 具有编年史队列内存的应用程序不断增长

Chronicle 具有编年史队列内存的应用程序不断增长,chronicle,chronicle-queue,Chronicle,Chronicle Queue,我已经实现了一个简单的Spring启动应用程序,它接收一条网络消息,使用appender.writeText(str)将其排入SingleChronicleQueue,另一个线程使用tailer.readText()轮询消息。经过一些处理后,已处理的消息被放入另一个要发送的队列中。 我在应用程序中有三个队列 应用程序每晚旋转文件,第一件奇怪的事情是文件大小(每个Q)相同(每个Q不同)。 最大的cq4文件约为每天220MB 我面临的问题是,从开始到现在的三天内,内存从480MB增长到1.6GB,这

我已经实现了一个简单的Spring启动应用程序,它接收一条网络消息,使用appender.writeText(str)将其排入SingleChronicleQueue,另一个线程使用tailer.readText()轮询消息。经过一些处理后,已处理的消息被放入另一个要发送的队列中。 我在应用程序中有三个队列

应用程序每晚旋转文件,第一件奇怪的事情是文件大小(每个Q)相同(每个Q不同)。 最大的cq4文件约为每天220MB

我面临的问题是,从开始到现在的三天内,内存从480MB增长到1.6GB,这是不合理的

我认为我在配置中缺少了一些东西,或者我的实现太幼稚/糟糕了。(我不应该在每次使用后都关闭appender和tailer)

下面是一个简单的例子,也许有人可以解释一下

@Service
public class QueuesService {
    private static Logger LOG = LoggerFactory.getLogger(QueuesService.class);

    @Autowired
    AppConfiguration conf;

    private SingleChronicleQueue Q = null;
    private ExcerptAppender QAppender = null;
    private ExcerptTailer QTailer = null;

    public QueuesService() {
    }

    @PostConstruct
    private void init() {

        Q = SingleChronicleQueueBuilder.binary(conf.getQueuePath()).indexSpacing(1).build();
        QAppender = Q.acquireAppender();
        QTailer = Q.createTailer();
    }

    public ExcerptAppender getQAppender() {
        return QAppender;
    }

    public ExcerptTailer getQTailer() {
        return QTailer;
    }
}


@Service
public class ProcessingService {
    private static Logger LOG = LoggerFactory.getLogger(ProcessingService.class);

    @Autowired
    AppConfiguration conf;

    @Autowired
    private TaskExecutor taskExecutor;

    @Autowired
    private QueuesService queueService;

    private QueueProcessor processor = null;

    public ProcessingService() {
    }

    @PostConstruct
    private void init() {
        processor = new QueueProcessor();
        processor.start();
    }

    @Override
    public Message processMessage(Message msg, Map<String, Object> metadata) throws SomeException {

        String strMsg = msg.getMessage().toString();

        if (LOG.isInfoEnabled()) {
            LOG.info("\n" + strMsg);
        }

        try {
            queueService.getQAppender().writeText(strMsg);

            if (LOG.isInfoEnabled()) {
                LOG.info("Added new message to queue. index: " + queueService.getQAppender().lastIndexAppended());
            }
        }
        catch(Exception e) {
            LOG.error("Unkbown error. reason: " + e.getMessage(), e);
        }
    }

    class QueueProcessor extends Thread {

        public void run() {
            while (!interrupted()) {
                try {
                    String msg = queueService.getEpicQTailer().readText();

                    if (msg != null) {
                        long index = queueService.getEpicQTailer().index();
                        // process
                    }
                    else {
                        Thread.sleep(10);
                    }
                }
                catch (InterruptedException e) {
                    LOG.warn(e);
                    this.interrupt();
                    break;
                }
            }

            ThreadPoolTaskExecutor tp = (ThreadPoolTaskExecutor) taskExecutor;
            tp.shutdown();
        }
    }
}
@服务
公共类队列服务{
私有静态记录器LOG=LoggerFactory.getLogger(QueuesService.class);
@自动连线
appconf配置;
私有队列Q=null;
私有摘录附加器QAppender=null;
私有摘录器QTailer=null;
公共队列服务(){
}
@施工后
私有void init(){
Q=SingleChronicleQueueBuilder.binary(conf.getQueuePath()).indexspace(1.build();
QAppender=Q.acquireAppender();
QTailer=Q.createTailer();
}
公开摘录附录getQAppender(){
返回卡盘;
}
公共摘录tailer getQTailer(){
返回QTailer;
}
}
@服务
公共类处理服务{
私有静态记录器LOG=LoggerFactory.getLogger(ProcessingService.class);
@自动连线
appconf配置;
@自动连线
私有任务执行器任务执行器;
@自动连线
专用排队服务;
专用队列处理器=空;
公共处理服务(){
}
@施工后
私有void init(){
处理器=新的队列处理器();
processor.start();
}
@凌驾
公共消息processMessage(消息消息、映射元数据)引发某些异常{
字符串strMsg=msg.getMessage().toString();
if(LOG.isInfoEnabled()){
日志信息(“\n”+strMsg);
}
试一试{
queueService.getQAppender().writeText(strMsg);
if(LOG.isInfoEnabled()){
LOG.info(“将新消息添加到queue.index:+queueService.getQAppender().lastIndexAppended());
}
}
捕获(例外e){
LOG.error(“Unkbown error.reason:”+e.getMessage(),e);
}
}
类队列处理器扩展线程{
公开募捐{
而(!interrupted()){
试一试{
字符串msg=queueService.getEpicQTailer().readText();
如果(msg!=null){
long index=queueService.getEpicQTailer().index();
//过程
}
否则{
睡眠(10);
}
}
捕捉(中断异常e){
LOG.warn(e);
这个。中断();
打破
}
}
ThreadPoolTaskExecutor tp=(ThreadPoolTaskExecutor)taskExecutor;
tp.shutdown();
}
}
}

编年史队列旨在使用虚拟内存,虚拟内存可以比主内存(或堆)大得多,而不会对系统造成重大影响。这允许您快速随机访问数据

下面是一个进程在3小时内写入1 TB的示例

这显示了随着队列的增长,它会变慢多少

即使在一台128 GB的机器上,它的大小为1 TB,它也会在2秒钟内完成1 GB的写入


虽然这不会引起技术问题,但我们知道这确实会影响到那些也觉得“奇怪”的人,我们计划采用一种减少虚拟内存使用的模式(即使在某些使用情况下稍微慢一点)

嗨,帕特,非常感谢你的回答。我只是想澄清一下,队列是否在每个周期后清除?@GalNitzan它应该在一个周期后清除(即当前并持续一些时间)。如果没有,那就是一个bug。有些人使用每小时的周期而不是每天的周期。我更喜欢每天的,但我能理解人们想要每小时一次的。事实上,我所想看到的是,在一个循环之后,一些内存在午夜被释放,而我没有看到,我所看到的是内存消耗在不断增长。我想我需要调试:)谢谢你的回答。