美文网首页
完全掌控logging的生与死(三)异步缓冲AsyncAppen

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

作者: 牧羊人刘俏 | 来源:发表于2020-12-29 16:14 被阅读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是一个有锁的数据结构,性能上面还是有提升的空间的,后面我们会实现一个无锁的队列来实现日志的异步刷盘。

    相关文章

      网友评论

          本文标题:完全掌控logging的生与死(三)异步缓冲AsyncAppen

          本文链接:https://www.haomeiwen.com/subject/qezfoktx.html