BlockingQueue实现高性能的日志服务

BlockingQueue实现高性能的日志服务

在大多数服务器应用中都会用到日志,例如,在代码中插入println语句就是一种简单的日志。像PrintWriter这样的字符流类是线程安全的,因此这种简单的方法不需要显示同步。然而,这种内联日志功能会给一些高容量的应用程序带来一定的性能开销。另外一种替代方法就是通过调用log方法将日志消息放入某个队列中,并由其他线程进行处理

通过将IO操作从处理请求的线程中分离出来,可以缩短处理请求的平均服务时间。调用log方法的线程将不会在因为等待输出流的锁或者IO完成而被阻塞,他们只需要将消息放入队列,然后就返回到各自的任务中。另一方面,虽然在消息队列上可能发生竞争,但put操作相对于记录日志的IO操作是一种更为轻量级的操作,因此在实际使用中发生阻塞的概率更小(只要队列没有填满)。由于发出日志请求的线程现在被阻塞的概率降低,因此该线程在处理请求时被交换出去的概率也会降低。

从某种意义上讲,我们只是将工作分散开来,并将IO操作移动到了另一个用户感知不到开销的线程上。

通过把所有记录日志的IO转移到一个线程,还消除了流上的竞争,因此又去掉一个竞争资源。这将提高整体的吞吐量,因为在调度中消耗的资源更少,上下文切换的次数更少,并且锁的管理也更简单。


看代码:

package container;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.PrintWriter;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * Created with IntelliJ IDEA.
 * User: ASUS
 * Date: 14-8-7
 * Time: 下午6:12
 * To change this template use File | Settings | File Templates.
 */
public class LogService {

    private final BlockingQueue<String> queue;
    private final LogThread logThread = new LogThread();
    private final PrintWriter printWriter;

    private boolean isShutdown = false;
    private int reservations;

    public LogService(BlockingQueue<String> queue, PrintWriter printWriter) {
        this.queue = queue;
        this.printWriter = printWriter;
    }

    public void start() {
        logThread.start();
    }

    public void stop() {
        synchronized (this) {
            isShutdown = true;
        }
        //Interrupts this thread.
        logThread.interrupt();
    }

    public void log(String msg) throws InterruptedException {
        synchronized (this) {
            if (isShutdown) {
                throw new IllegalStateException("log service stop!!");
            }
            ++reservations;
        }
        queue.put(msg);
    }


    /**
     * 该线程类是做打印日志用的
     */
    private class LogThread extends Thread {
        public void run() {
            try {
                while (true) {
                    synchronized (LogService.this) {
                        if (isShutdown && reservations == 0) {
                            break;
                        }
                    }

                    try {
                        String msg = queue.take();
                        synchronized (LogService.this) {
                            --reservations;
                        }
                        printWriter.println(msg);
                        printWriter.flush();
                    } catch (InterruptedException e) {
                        //retry
                    }
                }
            } finally {
                printWriter.close();
            }
        }
    }


    /**
     * 测试日期服务
     * 该日志服务使用生产者和消费者模式
     * 并且可以可靠的取消操作
     *
     * @param args
     * @throws FileNotFoundException
     */
    public static void main(String args[]) throws FileNotFoundException {

        //使用阻塞队列
        BlockingQueue<String> queue = new ArrayBlockingQueue<String>(10);


        //把日志写入指定文件
        File file = new File("C:\\test.log");
        PrintWriter p = new PrintWriter(file);

        final LogService logService = new LogService(queue, p);

        //开启日志服务
        logService.start();

        ExecutorService service = Executors.newCachedThreadPool();

        //使用多线程调用日志服务
        for (int n = 0; n < 100; n++) {
            final int finalN = n;
            service.submit(new Runnable() {
                @Override
                public void run() {
                    try {
                        logService.log("thread-" + finalN + " use log");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
        }

        //logService.stop();//使用stop方法停止日志服务
    }
}

(该程序不会退出,只有调用stop停止日志服务)

在我C盘下有一日志文件:test.log

thread-1 use log

.................

thread-90 use log

thread-92 use log

thread-26 use log

.................

thread-79 use log

thread-80 use log

共有100条记录

====END====

你可能感兴趣的:(BlockingQueue实现高性能的日志服务)