Search code examples
ejbejb-3.1jboss6.xmessage-driven-bean

How to do statistics collection in MDBs state of the art?


I have multiple MDBs (and plenty of mdb instances) as consumers for messages. I have to collect certain statistics inside these Beans and send them every X (currently 30) seconds to a JMS destination.

Is it ok to do this in the bean itself?

for example: the bean gathers the data localy and has a writeStatistic() method that uses the @Scheduled annotation.

Is it possible to do the statistic stuff centralised?

a central bean collects all statistics data from all bean instances and sends it of. If this is possible, how can it be done?

EDIT

I solved my task by writing a singleton session bean that looks like this:

@Singleton
@Startup
@ConcurrencyManagement(ConcurrencyManagementType.BEAN)
@Asynchronous
public class StatisticsCollector {

    private ConcurrentMap<Integer, ConcurrentMap<String, AtomicInteger>> statistics = new ConcurrentHashMap<Integer, ConcurrentMap<String, AtomicInteger>>();

    public void trackDatum(int projectId, String property, int increment) {
        LOG.debug("Tracking datum: project: " + projectId + ", property: " + property + ", increment: " + increment);

        // get statistics for project
        ConcurrentMap<String, AtomicInteger> productstats;
        if (!statistics.containsKey(projectId)) {
            synchronized (statistics) {
                if (!statistics.containsKey(projectId)) {
                    productstats = new ConcurrentHashMap<String, AtomicInteger>();
                    statistics.put(projectId, productstats);
                } else {
                    productstats = statistics.get(projectId);
                }
            }
        } else {
            productstats = statistics.get(projectId);
        }

        // get current counter for property
        AtomicInteger value;
        if (!productstats.containsKey(property)) {
            synchronized (productstats) {
                if (!productstats.containsKey(property)) {
                    value = new AtomicInteger();
                    productstats.put(property, value);
                } else {
                    value = productstats.get(property);
                }
            }
        } else {
            value = productstats.get(property);
        }

        // increment
        value.addAndGet(increment);
    }

    @Schedule(minute = "*", hour = "*", second = "*/30", persistent = false)
    public void sendStatistics() {
        // send statistics to remote consumer via JMS
    }
}

I did the concurrency management myself as i wanted to get as much performance out of the bean as i could.


Solution

  • Is it ok to do this in the bean itself?

    I suppose you mean this:

    @MessageDriven(...)
    public MDBean implements MessageListener {
         private Statistic statisticForThisBean;
         // ...
    
         @Timeout
         public void sendStatisticForThisBean() {
             // ...
         }
    }
    

    It's certainly not an option. There's no guarantee that the @Timeout-annotated method will run for all bean instances at all, not to mention your requirement of running every 30 seconds. The timeout method runs on one of free instances of an MDB, and the container chooses it.

    Is it possible to do the statistic stuff centralised?

    Personally, I'd do it this way: have all MDBs (all instances) send partial statistics to a queue, at the end of their processing logic. I'd write a separate component that would:

    • exist in one instance only,
    • collect all messages from the statistics queue for 30 seconds (in a loop), producing a summary, then sending it to the final destination, then restart.

    The "component" could be a timer task, or a separate stand-alone application.

    Another possible solution could be a single stateful bean, that MDBs would call in turn to update statistics. However, the following limitations apply:

    • it's likely to cause performance problems, unless SFSB methods calls are fast (i.e. when the statistics are not complicated),
    • it can't be run asynchronously, so you can't guarantee the 30 seconds span between calls to the statistic-gathering engine; if the JMS traffic stops completely, so the SFSB will stop responding, too.