使用Wildfly进行批处理/批量消息(JMS)处理

使用Wildfly进行批处理/批量消息(JMS)处理,jms,wildfly,bulkinsert,Jms,Wildfly,Bulkinsert,我有一个JMS队列,其中填充了一些时间序列数据。为了防止数千个单事务SQL插入,我希望以一种庞大的方式处理它们,而不是以MessageListener onMessage“每条消息”的方式处理它们 我想到的唯一解决方案是安排从队列中获取大量消息并定期保存它们 @Stateless public class SensorDataReceiver { private static final int THRESHOLD_IN_SECONDS = 10; private stati

我有一个JMS队列,其中填充了一些时间序列数据。为了防止数千个单事务SQL插入,我希望以一种庞大的方式处理它们,而不是以MessageListener onMessage“每条消息”的方式处理它们

我想到的唯一解决方案是安排从队列中获取大量消息并定期保存它们

@Stateless
public class SensorDataReceiver {

    private static final int THRESHOLD_IN_SECONDS = 10;

    private static final int QUEUE_TIMEOUT_IN_MILLIS = 1000;

    @Resource(mappedName = "java:jboss/jms/queue/sensorData")
    private Queue queue;

    @Inject
    private JMSContext context;

    @Inject
    private SensorDataDAO sensorDataDAO;

    @SneakyThrows
    @Schedule(hour = "*", minute = "*", second = "*/15", persistent = false)
    public void scheduled() {
        LocalDateTime statUpPlusThreshold = now().plusSeconds(THRESHOLD_IN_SECONDS);
        JMSConsumer consumer = context.createConsumer(queue);

        List<SensorData> sensorDataToInsert = new ArrayList<>();
        do {
            ObjectMessage message = (ObjectMessage) consumer.receive(QUEUE_TIMEOUT_IN_MILLIS);

            if (message == null) {
                break;
            }

            sensorDataToInsert.add((sensorData) message.getObject());
        } while (now().isBefore(statUpPlusThreshold) && sensorDataToInsert.size() < 10_000);

        logger.info(format("Got \"%d\" SensorData to persist.", sensorDataToInsert.size()));
        sensorDataDAO.batchSaveOrUpdate(sensorDataToInsert);
        logger.info(format("Persisted \"%d\" SensorData.", sensorDataToInsert.size()));
    }
}
@无状态
公共类传感器数据接收器{
专用静态最终整数阈值(单位:秒)=10;
私有静态最终整数队列超时,单位为毫秒=1000;
@资源(mappedName=“java:jboss/jms/queue/sensorData”)
专用队列;
@注入
私有JMSContext上下文;
@注入
私有SensorDataDAO SensorDataDAO;
@鬼鬼祟祟
@时间表(hour=“*”,minute=“*”,second=“*/15”,persistent=false)
公众假期{
LocalDateTime statUpPlusThreshold=now().plusSeconds(阈值以秒为单位);
JMSConsumer consumer=context.createConsumer(队列);
List sensorDataToInsert=new ArrayList();
做{
ObjectMessage message=(ObjectMessage)consumer.receive(队列超时,单位:毫秒);
如果(消息==null){
打破
}
sensorDataToInsert.add((sensorData)message.getObject());
}while(now().isBefore(statUpPlusThreshold)和&sensorDataToInsert.size()<10_000);
logger.info(格式(“获取\%d\”传感器数据以保存。”,sensorDataToInsert.size());
sensorDataDAO.batchSaveOrUpdate(sensorDataToInsert);
logger.info(格式(“持久化\%d\“SensorData.”,sensorDataToInsert.size());
}
}

但我不认为这是最聪明的方法,因此,当计划执行速度快于配置的时间间隔(我可以在测试系统上大约2-3秒内插入10k行)时,我每分钟会浪费时间处理更多消息,另一方面,此代码容易产生“重叠的计划执行”

我建议拥有一个无状态bean池,这些无状态bean始终处于活动状态(即,它们不被调度),它们使用一定数量的消息(即,直到队列为空,这将是任意数量的消息),然后在单个数据库操作中插入这些消息中的数据

池中的所有bean可以同时处于活动状态,并且可以尽快使用和插入它们的批。这将确保消息以及时的方式被使用,这将有望避免队列中的任何消息堆积

您可以在
receive
上有一个超时,这样,如果您在达到批处理大小之前到达队列末尾,数据仍将及时插入


为了在应用程序服务器启动时启动它,您可以使用
@Startup
@Singleton
注释bean,然后使用
@PostConstruct
注释方法,该方法循环足够多的时间来填充“池”并在您的
@Stateless
bean上调用该方法,该bean将接收并处理成批的消息。

使用无状态bean池可能是一个不错的方法,但我如何告诉它们使用消息队列(而不是每个消息的MessageListener.onMessage()中)?与您在问题中的
@Stateless
bean中所做的相同-通过注入
JMSContext
并调用
receive
。但是如何触发调用
receive
“自动”的方法?