zoukankan      html  css  js  c++  java
  • 利用java concurrent 包实现日志写数据库的并发处理

    一、概述

    在很多系统中,往往需要将各种操作写入数据库(比如客户端发起的操作)。

    最简单的做法是,封装一个公共的写日志的api,各个操作中调用该api完成自己操作日志的入库。但因为入数据库效率比较低,如果每个操作自己入库,则会影响响应速度。而且当操作并发度很高时,往往同时有多个线程在写数据库,也会对系统有影响。

    考虑的解决方案是,这个api并不实际完成入库,而是将每个操作日志信息写到一个公共的缓存中,然后应用系统起了一个独立的线程(一直运行)在后台进行入库。如果当前缓存中有记录,就写库,没有记录,就堵塞住。

    这里关键的是要有一个缓存日志的数据结构,我们这里使用java current包中的并发数据结构LinkedBlockingDeque类。

    二、具体代码

    import java.util.ArrayList;
    import java.util.List;
    import java.util.concurrent.LinkedBlockingDeque;
    
    public class LogManager implements Runnable {
        //定义缓存数据结构,支持并发操作
        private static LinkedBlockingDeque<ActionLog> list = new LinkedBlockingDeque<ActionLog>();
        static{
            //创建并启动线程,该线程实现日志的入库
            new Thread(new LogManager()).start();
        }
        public static void addActionLog(ActionLog log) {
            list.add(log);
        }
    
        @Override
        public void run() {
            List<ActionLog> items = new ArrayList<ActionLog>();
            while (true) {
                //从缓存中取出1条日志,但不从缓存中删除。用于检查队列中是否有数据
                ActionLog item = list.peek();
                if (item == null && items.size() > 0) {
                    //item为null说明缓存中没有日志了,这时如果临时队列items中有记录,就可以批量入库了
                    work(items);
                }
                //从缓存中取出并删除最前面的日志,如果无数据,在堵塞
                ActionLog result = deleteItem();
                //将取出的记录放到临时队列中
                items.add(result);
            }
        }
    
        private ActionLog deleteItem() {
            ActionLog result = null;
            try {
                //取出并删除最前面的一条数据,如果缓存中无数据,则堵塞住
                result = list.takeFirst();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return result;
        }
    
        private void work(List<ActionLog> items) {
            //TODO 完成入库。这里代码没有提供
            //入库后删除临时队列中记录
            items.clear();
        }
    
    }

    各种工作线程调用 addActionLog 方法将要入库的日志放到缓存中。由LogManager通过自己的线程控制入库。

    三、小结

    上述代码虽然简单,但很好的解决了前面提出的问题。不过这个解决方案存在一个问题。

    如果说系统产生日志的速度超过了单个线程入库的速度,上述代码就有问题。就会造成该线程不停地入库。

    这时就需要考虑增加额外的处理速度,如增加入库线程。

    所以上述代码,只适合并发日志量不是特别大的情况下的场景。

  • 相关阅读:
    NET开源框架(转载)
    数据行转列的应用(json数据源)
    防止通过URL下载文件
    jquery中的$.post()方法无法给变全局变量的问题
    页面乱码问题的解决方案
    在mvc中使用Ninject进行依赖注入
    在mvc4.0中使用json数据
    使用thinkphp3.2中的验证码功能
    ThinkPHP中邮件发送功能
    ASP.NET页面运行机制
  • 原文地址:https://www.cnblogs.com/51kata/p/5153498.html
Copyright © 2011-2022 走看看