3

我有一个处理来自 3rd 方站点的回调的 servlet S。

回调调用以特定顺序发生。因此,我需要将它们排队。

我建议使用内存队列,如

java.util.ConcurrentLinkedQueue

所以逻辑看起来像这样:

  • Servlet S 接收回调并将接收到的项目排队到队列 Q 中。
  • 此时,承载 servlet S 实例的线程将终止。
  • 一个消费者线程从 Q 中读取并依次处理每一个。

据我了解,Servlet S 的每个实例都在其自己的线程中执行。

如何为将服务于 Queue 的整个 webapp(战争)创建单个消费者线程?基本上我需要单例实例:

  1. 线程池
  2. 并发链接队列
4

5 回答 5

5

这不是 servlet 容器的用途。如果您打算使用基于标准的方法,那么您确实需要一个更成熟的 J2EE 应用程序服务器。否则,您将拥有的是 hack,但它们可能足以完成您的任务。

我可能会尝试创建一个 DaemonServlet。这只是一个没有映射到 URL 的普通 servlet(可能除了用于监视目的的盲 URL,尽管对于这种事情更喜欢 JMX)。init()加载 servlet 时调用该方法。您可以在其中启动一个线程。可以说,您可能需要创建两个:一个可以完成工作。另一个确保第一个正在运行并destroy()在调用时优雅地终止它。

或者,如果您正在使用Spring(并且,让我们面对现实,什么样的 whacko 不使用 Spring?),您可以简单地在应用程序上下文中创建一个 bean,它做很多相同的事情,除了 Spring 生命周期事件(例如,在 InitializingBean 上的 afterPropertiesSet())。

其实我有一个更好的建议。使用异步消息消费者,这将更清洁和更具可扩展性,但这是基于基于JMS的解决方案,而不仅仅是一个LinkedBlockingQueue(无论如何 JMS 可能是一个更好的主意)。但是,根据您的限制,您可能没有可用的 JMS 作为选项。

于 2008-12-26T02:01:44.567 回答
2

请记住,虽然您的 servlet 位于单独的线程中,但它们位于同一个 VM 实例中,因此它们存在于共享内存空间中。如果您创建一个 Singleton 实例,它将自动由所有 servlet 共享。您还可以创建一个单独的 servlet 来充当您的共享数据,这样做的好处是您可以根据需要使用容器服务来持久化它。

OReilly 的关于 servlet 编程的书中对执行此操作的方法进行了彻底的检查——技术上称为“servlet 协作”,可在此处在线获得。

于 2008-12-26T01:58:05.680 回答
1

哦,另一个想法:您不想做的一件事尝试在 servlet 容器中管理自己的线程池;它可以比你做得更好,除非你把它弄乱了。

于 2008-12-26T01:59:37.877 回答
0

我支持说管理自己的线程池是一个坏主意的评论。

Servlet 用于处理 HTTP 请求。HTTP 是一种同步请求/响应协议。如何处理它们的逻辑属于其他地方。该处理程序可能是同步的或异步的,但这应该取决于处理程序的实现。servlet 应该决定对于给定的响应要推迟到哪个处理程序,仅此而已。

即使您使用 Tomcat 或 servlet/JSP 引擎,您仍然可以通过将 ActiveMQ 添加到 Tomcat 实现来使用 Spring、JMS 和 MDP。

于 2008-12-26T02:26:17.807 回答
0

使用监听器,我创建了一个线程池,我可以使用 ServletContext 与旧版 Tomcat 应用程序集成以存储线程池并让监听器管理生命周期,这样它就不会逗留。

@WebListener
public class MyThreadPool implements ServletContextListener {

    private static final String CONTEXT_ATTRIBUTE = "MyThreadPool";
    private ExecutorService myThreadPool;

    @Override
    public void contextInitialized(ServletContextEvent sce) {
        myThreadPool = Executors.newFixedThreadPool(10);
        sce.getServletContext().setAttribute(CONTEXT_ATTRIBUTE, myThreadPool);
    }

    public static ExecutorService getPool(Servlet servlet) {
        return (ExecutorService) servlet.getServletConfig()
               .getServletContext().getAttribute(CONTEXT_ATTRIBUTE);
    }

    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        myThreadPool.shutdown();
        sce.getServletContext().removeAttribute(CONTEXT_ATTRIBUTE);
    }
}
于 2019-04-22T06:34:56.550 回答