Web应用程序的真实用户活动日志记录策略

时间:2013-04-03 05:28:03

标签: java multithreading tomcat

我有一个网络应用程序,我的客户要求他想知道。

我提出了两个想法 如果他们是正确的,我想验证我的想法。


这些是客户要求。

谁:访客是谁(例如remoteAddress-IP)
什么时候:访问者什么时候在网络应用程序上做了什么 什么:访客采取了什么确切的行动,例如“按下打印按钮”
其中:哪个页面,例如访问者访问的网址

我的想法1.
只需发出SQL查询即可记录访问该网站的每个人的用户活动。 如果访问者单击了指向页面的链接,则Web应用程序会将用户活动写入数据库并呈现所请求的页面。
我认为这会对用户体验产生不良影响。页面渲染可能会变慢,因为它必须做额外的工作。这种方法最终会发出太多的SQL查询并最终成为一个坏主意吗?
我的想法2.
为每个用户或每个用户活动(例如单击打印按钮)启动新线程。 页面渲染以自己的速度继续进行,并且将在线程中单独完成日志记录。
我认为这可能会创建太多线程。这是一个好主意还是最终会占用太多资源?

我想知道这些是否是一个好的,现实世界的做法。如果有更好的方法,请分享。 :)

5 个答案:

答案 0 :(得分:1)

建议1:使用和不使用数据库日志记录系统来配置应用程序。您可能会发现数据库日志记录可以满足您的性能需求,而无需进行重大架构更改或性能调整。

如果您发现自己能够维持吞吐量要求,但偶尔会在高峰时段进行备份,则可以将数据库日志记录移动到单个线程中,并使用java.util.concurrent中的并发队列传递日志消息。这比为每个日志事件分配一个单独的线程要高效得多。

我怀疑如果你发现性能问题,你会发现成为数据库的瓶颈。但这只是预感 - 你必须描述自己的设置,以找到优点和缺点。

注意:要比较启用和不启用日志记录的性能,您可以在启动时将开/关布尔值或特定日志级别配置为最终静态变量。如果这些条件保证为假,JIT将优化if (loggingEnabled)if (logLevel > 3)块中包含的任何代码。这使得您可以使用和不使用日志记录运行相同的代码,而无需在分析当前(非日志记录)方法时为日志记录代码支付性能损失。

答案 1 :(得分:0)

创意1很好。创建一个实用程序方法以将条目记录到数据库中。因此,每次要记录某些事件时,只需调用此方法即可。不应该占用太多的服务器资源,也不会大大减慢响应时间。

答案 2 :(得分:0)

您的问题描述似乎符合生产者/消费者模式。

您有许多线程处理用户请求,这些请求将生成将信息写入数据库的请求。您将有一个(或可能多个)线程消耗这些请求并将数据写入数据库。

有许多方法可以在Java中实现它。你可以:

  • 每次要将一条信息记录到数据库中时启动一个新线程
  • 使用具有有限数量线程的共享ExecutorService,这将被重用并避免多种开销
  • 使用共享BlockingQueue,您将在其中放入来自servlet的日志请求,并在循环中拥有一个(或多个)线程take()元素,并根据数据库将数据写入数据库你带的元素

在任何情况下,我都会将此逻辑封装在RequestLogger类中,例如:

public class RequestLogger {
  private final Thread thread;
  private final BlockingQueue<LogRequest> logRequestQueue = new LinkedBlockingQueue<>();
  public RequestLogger() {
    this.thread = new Thread(new Runnable() {
      @Override public void run() {
        try {
          while (!Thread.currentThread().isInterrupted()) {
            final LogRequest req = logRequestQueue.take();
            // ... log it to the database somehow ...
          }
        } catch (Exception e) { ... do something ... }
      }
    });
  }

  public void start() { thread.start(); }
  public void stop() { thread.interrupt(); }
  public void log(...) {
    logRequestQueue.offer(new LogRequest(...));
  }
}

您实例化此类一次,调用.start(),您就可以记录所需的所有内容。

如果您需要(例如,因为您发现日志请求累积太快),您可以更改while(...)循环中的逻辑以一次接收更多请求(如果可用),并且您的代码向数据库发出一次写入,或者在单个事务中写入,或类似的事情。

这实际上取决于您的特定情况,但如果您遇到性能问题,则瓶颈将成为数据库。

答案 3 :(得分:0)

出于性能原因,最好将日志处理与页面呈现分开,这最好使用JMS完成。

当用户执行某些操作时,您需要做的就是创建日志对象并使用消息源发送JMS消息(JMS队列应该是合适的)。

在另一端(消息接收器),您需要设置一个消息接收器来接收消息并记录它们(在您的情况下为DB)。

这种解耦确保了对用户活动的最小影响以及对使用操作的可靠记录。

答案 4 :(得分:0)

现代日志框架支持ASYNCHRONOUS登录的想法。例如,有log appender in LogBack支持异步日志记录,响应时间非常长。

相关问题