完全掌控logging的生与死(三)异步缓冲AsyncAppen

2020-12-29  本文已影响0人  牧羊人刘俏

在第二篇里面我们实现了一个非常轻量级的异步刷盘的AsyncFlushRollingFileAppender,但是功能非常有限,在logback里面已经提供了一个现成的异步缓冲刷盘的Appender AsyncAppender,这个Appender其实就是一个包装类,典型的装饰模式的应用。其继承的类图如下


image.png

核心的功能都在AsyncAppenderBase里面进行了实现。
在AsyncAppenderBase里面定义了一个 BlockingQueue 用于缓冲消息事件,在start方法里面默认是new了个

 blockingQueue = new ArrayBlockingQueue<E>(queueSize);

当然我们可以重新这个start方法,自定义一个无锁堵塞队列。比如很火的无锁框架 jctools包里面提供的数据结构。
定义了一个异步的线程 Worker,核心代码如下

    class Worker extends Thread {

        public void run() {
            AsyncAppenderBase<E> parent = AsyncAppenderBase.this;
            AppenderAttachableImpl<E> aai = parent.aai;

            // loop while the parent is started
            while (parent.isStarted()) {
                try {
                    E e = parent.blockingQueue.take();
                    aai.appendLoopOnAppenders(e);
                } catch (InterruptedException ie) {
                    break;
                }
            }

            addInfo("Worker thread will flush remaining events before exiting. ");

            for (E e : parent.blockingQueue) {
                aai.appendLoopOnAppenders(e);
                parent.blockingQueue.remove(e);
            }

            aai.detachAndStopAllAppenders();
        }
    }

核心代码就两句

 //通过阻塞方法去拿事件
 E e = parent.blockingQueue.take();
//拿到知道,调用装饰的Appender的doAppend方法,将事件写到outputStream里面去。
 aai.appendLoopOnAppenders(e);

在这个Appender里面有几个参数

queueSize 队列大小,默认是256
discardingThreshold 丢弃的阈值,默认到80%才考虑是否丢弃消息,需要子类重写如下的方法,比如实现发现log的日志等级小于INFO直接的丢弃

protected boolean isDiscardable(E eventObject) {
        return false;
    }

neverBlock 是否堵塞 默认不堵塞,意思就是堵塞队列满了,消息就丢了,你可以设置成true,这样当队列满了之后,一直堵塞到消息丢进去为止。

进行如下的配置

<configuration>
  <appender name="FILE" class="ch.qos.logback.core.FileAppender">
    <file>myapp.log</file>
    <encoder>
      <pattern>%logger{35} - %msg%n</pattern>
    </encoder>
  </appender>

  <appender name="ASYNC" class="ch.qos.logback.classic.AsyncAppender">
    <appender-ref ref="FILE" />
  </appender>

  <root level="DEBUG">
    <appender-ref ref="ASYNC" />
  </root>
</configuration>

这样写日志的时候会先写到ASYNC这个内存Appender,然后ASYNC再调用FILE这个Appender异步的写到outputStream了,上面的这个异步写日志貌似很完美了,但是由于我们使用的数据结构ArrayBlockingQueue是一个有锁的数据结构,性能上面还是有提升的空间的,后面我们会实现一个无锁的队列来实现日志的异步刷盘。

上一篇 下一篇

猜你喜欢

热点阅读