Java web应用程序的真实世界用户活动日志记录策略

Java web应用程序的真实世界用户活动日志记录策略,java,multithreading,tomcat,Java,Multithreading,Tomcat,我有一个web应用程序,我的客户请求他知道。 我想出了两个主意。 我想验证我的想法是否正确。 这些是客户的要求。 谁:访问者是谁(如remoteAddress IP) 何时:访问者何时在web应用程序上执行操作 内容:访问者采取了什么具体行动,例如“按下打印按钮” 其中:哪一页,例如访问者访问的URL 我的想法1. 只需发出一个SQL查询来记录访问网站的每个人的用户活动。 如果访问者单击指向页面的链接,web应用程序会将用户活动写入数据库并呈现请求的页面。 我认为这将对用户体验产生不良影响。页

我有一个web应用程序,我的客户请求他知道。

我想出了两个主意。
我想验证我的想法是否正确。


这些是客户的要求。

谁:访问者是谁(如remoteAddress IP)
何时:访问者何时在web应用程序上执行操作
内容:访问者采取了什么具体行动,例如“按下打印按钮”
其中:哪一页,例如访问者访问的URL

我的想法1.
只需发出一个SQL查询来记录访问网站的每个人的用户活动。 如果访问者单击指向页面的链接,web应用程序会将用户活动写入数据库并呈现请求的页面。
我认为这将对用户体验产生不良影响。页面呈现可能会变慢,因为它必须做额外的工作。这种方法最终会发出太多SQL查询并最终成为一个坏主意吗?

我的想法2.
为每个用户或每个用户活动(如单击“打印”按钮)启动一个新线程。 页面渲染以自己的速度进行,日志记录将在线程中单独完成。
我认为这可能会创建太多线程。这是一个好主意还是会占用太多资源


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

想法1很好。创建一个实用程序方法,将条目记录到数据库中。所以每次你想记录一些事件,只要调用这个方法。不应该占用太多服务器资源,也不会显著降低响应时间。

想法1很好。创建一个实用程序方法,将条目记录到数据库中。所以每次你想记录一些事件,只要调用这个方法。不应占用太多服务器资源,也不会显著降低响应时间。

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

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

我的怀疑是,如果发现性能问题,就会发现瓶颈是数据库。但这只是一种预感——你必须对自己的设置进行分析,以找出优势和劣势


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

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

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

我的怀疑是,如果发现性能问题,就会发现瓶颈是数据库。但这只是一种预感——你必须对自己的设置进行分析,以找出优势和劣势


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

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

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

有很多方法可以在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(...));
  }
}
公共类请求记录器{
专用终螺纹;
private final BlockingQueue logRequestQueue=新建LinkedBlockingQueue();
公共请求记录器(){
this.thread=新线程(new Runnable()){
@重写公共无效运行(){
试一试{
而(!Thread.currentThread().isInterrupted()){
final LogRequest req=logRequestQueue.take();
//…以某种方式将其记录到数据库中。。。
}
}捕获(例外)