您可以从ItemListener中访问Hazelcast队列吗?

时间:2018-09-21 21:39:01

标签: hazelcast

我有一个用例,其中有一组项目DiagnosticRun已提交到集群。我想按顺序处理它们(以避免冲突)。我正在尝试使用受Queue保护的Hazelcast Lock,以确保一次处理一个项目。 Hazelcast在我的群集中以嵌入式模式运行。如果我在ItemListener中注册了Queue,从take()方法内部调用Queue上的itemAdded()是否安全?例如:

@Component      
public class DistributedQueueListener
{
    public static final String DIAGNOSTICS_RUN_QUEUE_NAME = "diagnosticRun";

    @Autowired
    private HazelcastInstance hazelcast;

    @Autowired
    private ProductVersioningService productVersioningService;

    private IQueue<DiagnosticRun> diagnosticRunQueue;
    private ILock diagnosticRunLock;
    private String diagnosticRunListenerId;

    @PostConstruct
    public void init()
    {
        diagnosticRunQueue = hazelcast.getQueue(DIAGNOSTICS_RUN_QUEUE_NAME);
        diagnosticRunLock = hazelcast.getLock("diagnosticRunLock");
        diagnosticRunListenerId = diagnosticRunQueue.addItemListener(new DiagnosticRunListener(), false);
    }

    @PreDestroy
    public void stop()
    {
        diagnosticRunQueue.removeItemListener(diagnosticRunListenerId);
    }

    public class DiagnosticRunListener implements ItemListener<DiagnosticRun>
    {
        @Override
        public void itemAdded(ItemEvent<diagnosticRun> item)
        {
            diagnosticRunLock.lock(5, TimeUnit.SECONDS);
            try
            {
                DiagnosticRun diagnosticRun = diagnosticRunQueue.poll();
                if(diagnosticRun != null)
                {
                    productVersioningService.updateProductDeviceTable(diagnosticRun);
                }
            }
            finally
            {
                diagnosticRunLock.unlock();
            }
        }

        @Override
        public void itemRemoved(ItemEvent<diagnosticRun> item)
        {
        }   
    }       
}

我不确定从该位置和线程在take()上调用Queue是否是线程安全的。

如果不允许这样做,我将不得不建立自己的长时间运行循环以poll()Queue。我不确定在Spring Boot应用程序中设置长时间运行的线程的最佳方法是什么。假设上面的方法不起作用,下面的代码是否是线程安全的?还是有更好的方法来做到这一点?

@Component      
public class DistributedQueueListener
{
    public static final String DIAGNOSTIC_RUN_QUEUE_NAME = "diagnosticRun";

    @Autowired
    private HazelcastInstance hazelcast;

    @Autowired
    private ProductVersioningService productVersioningService;

    private IQueue<diagnosticRun> diagnosticRunQueue;
    private ILock diagnosticRunLock;

    private ExecutorService executorService;

    @PostConstruct
    public void init()
    {
        diagnosticRunQueue = hazelcast.getQueue(DIAGNOSTIC_RUN_QUEUE_NAME);
        diagnosticRunLock = hazelcast.getLock("diagnosticRunLock");

        executorService = Executors.newFixedThreadPool(1);
        executorService.submit(() -> listenToDiagnosticRuns());
    }

    @PreDestroy
    public void stop()
    {
        executorService.shutdown();
    }

    private void listenToDiagnosticRuns()
    {
        while(!executorService.isShutdown())
        {
            diagnosticRunLock.lock(5, TimeUnit.SECONDS);
            try
            {
                DiagnosticRun diagnosticRun = diagnosticRunQueue.poll(1L, TimeUnit.SECONDS);
                productVersioningService.updateProductDeviceTable(diagnosticRun);
            }
            catch(InterruptedException e)
            {
                logger.error("Interrupted polling diagnosticRun queue", e);
            }
            finally
            {
                diagnosticRunLock.unlock();
            }
        }
    }
}

1 个答案:

答案 0 :(得分:0)

首先,我将确定我不是完全执行线程的专家,何时执行这些线程可能会引起分歧,但这是我的想法,因此请任何人加入,因为这似乎是一个有趣的案例。您的第一个解决方案将Hazelcast事件线程与操作线程混合在一起。实际上,由于单个事件,您正在触发要调用的三个操作。如果在对updateProcductDeviceTable的调用中添加了任意延迟,则最终会看到它会变慢,但在一段时间后会重新恢复。这将导致您的本地事件队列在调用操作时堆积。您可以将您正在做的所有事情放在一个单独的线程中,您可以在#itemAdded上“唤醒”,或者如果可以承受一点延迟,则可以在第二种解决方案上进行操作。但是,我会在

中进行一些更改

listenToDiagnosticsRuns()方法:

private void listenToDiagnosticRuns()
{
    while(!executorService.isShutdown())
    {
        if(diagnosticRunQueue.peek() != null)
       {
           diagnosticRunLock.lock(5, TimeUnit.SECONDS);
           try
           {
               DiagnosticRun diagnosticRun = diagnosticRunQueue.poll(1L, TimeUnit.SECONDS);
               if(diagnosticRun != null)
               {
                   productVersioningService.updateProductDeviceTable(diagnosticRun);
               }
           }
           catch(InterruptedException e)
           {
               logger.error("Interrupted polling diagnosticRun queue", e);
           }
           finally
           {
               diagnosticRunLock.unlock();
           }
        } // peek != null
        else
        {
            try
            {
                Thread.sleep(5000);
            }
            catch (InterruptedException e)
            {
                 //do nothing
            }
        }
    }
}