1

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

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


这些是客户的要求。

who:访问者是谁(例如 remoteAddress-IP)
when:访问者什么时候在 Web 应用程序上
做了什么什么:访问者采取了什么确切的操作,例如“按下打印按钮”
where:哪个页面,例如 URL访问者访问

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

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

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

4

5 回答 5

2

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

如果您发现您能够维持您的吞吐量要求,但偶尔会在高峰期备份,您可以将 DB 日志记录移动到单个线程中,并使用来自java.util.concurrent. 这将比为每个日志事件分叉一个单独的线程更有效。

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

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

于 2013-04-03T05:42:42.523 回答
2

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

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

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

这种解耦可确保对用户活动的影响最小,并为使用操作提供可靠的日志记录。

于 2016-02-29T13:34:21.427 回答
0

想法1很好。创建一个实用方法以将条目记录到数据库中。所以每次你想记录一些事件时,只需调用这个方法。不应占用过多的服务器资源,也不会显着减慢响应时间。

于 2013-04-03T05:30:43.160 回答
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(...)循环中的逻辑以一次接收更多请求(如果可用),并使您的代码发出一次写入数据库,或写入单个事务,或类似的东西。

这实际上取决于您的特定场景,但如果您遇到性能问题,那么瓶颈将是数据库。

于 2013-04-03T05:50:20.923 回答
0

现代日志框架支持异步登录的思想。例如,LogBack中有一个日志附加器,它支持异步日志记录,响应时间非常好。

于 2017-12-02T12:26:51.710 回答