在@MessageDriven bean中使用amazon sqs - 池/并行处理

时间:2016-12-15 19:28:16

标签: java-ee ejb jms amazon-sqs message-driven-bean

我们需要在Java EE应用程序中使用队列,因为它是一个云基础应用程序(部署在OpenShift Online上),我们喜欢使用amazon sqs。

如果我正确理解JMS / Java EE接收部分的理论,那么@MessageDriven bean由Java EE容器管理,以便并行创建许多bean实例(根据最大池大小) ,如果传入消息的数量很高。这当然是处理高负荷的一大好处。

但是,我不知道如何在Java EE应用程序中以这种方式集成aws sqs。我知道来自http://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-java-message-service-jms-client.html的异步接收器示例:

class MyListener implements MessageListener {

    @Override
    public void onMessage(Message message) {
        try {
            // Cast the received message as TextMessage and print the text to screen.
            if (message != null) {
                System.out.println("Received: " + ((TextMessage) message).getText());
            }
        } catch (JMSException e) {
            e.printStackTrace();
        }
    }
}

然后:

// Create a consumer for the 'TestQueue'.
MessageConsumer consumer = session.createConsumer(queue);

// Instantiate and set the message listener for the consumer.
consumer.setMessageListener(new MyListener());

// Start receiving incoming messages.
connection.start();

这是官方的异步接收器示例 - 它不是@MessageDriven bean。显而易见,我们需要在某处验证凭据(通过创建SQSConnectionFactory,然后创建连接,然后是会话 - 在示例中也有详细描述)。
但我强烈认为这个示例不会并行处理消息 - 即只有一个bean实例正在处理队列,这对于可扩展的高负载应用程序来说不是一个好的解决方案。

a)我们如何使用Amazon SQS实现真正的Java EE方式? 我只是找到了一些Spring的例子。但它必须是Java EE 7.

b)我们使用Wildfly(目前为8.2.1)。是否也可以让Wildfly在内部管理与AWS和应用程序的连接,我们可以像使用应用程序服务器管理队列那样使用队列(与数据源进行数据源访问相同的方法)?

stdunbar 获得答案后的结论:
似乎不可能以一种正确的方式,我喜欢做什么。所以我该怎么做?实施ManagedExecutorService作为 stdunbar 描述为' wrap'队列? - 但是这意味着也有一个本地队列,对于一个应该可扩展的应用程序来说这不是一个好的情况?! 什么是替代品?我们正在OpenShift Online上运行该应用程序。用例如实例化自己的装备可能更好。 ApacheMQ Cartridge ......当然有很多不满,比如成本,我们负责基础设施'。

老实说,在这种情况下我对AWS非常失望......

4 个答案:

答案 0 :(得分:3)

根据一些较早的docs I found

  

容器允许消息驱动的bean类的许多实例同时运行,从而允许并发处理消息流。

通过使用Amazon JMS集成,再加上声明性 MDB,您应该很高兴。我不会使用setMessageListener接口。您可以使用JMS的声明版本,因为您使用的是Wildfly 8.x / EE7:

@MessageDriven(activationConfig = { /* your config - i.e. queue name, etc */ })
public class MyListener implements MessageListener {
    @Override
    public void onMessage(Message message) {
    }
}

这允许容器根据需要创建任意数量的实例。请注意,Wildfly可能需要对JMS参数进行一些调整。

作为旁注,让亚马逊图书馆负责读取SQS队列。我开始推销我自己的读者,以为我可以解决它。但我发现,您不能将AWS Java库与从队列中读取的多个线程一起使用,因为您几乎每次都会获得重复读取。我有4个线程读取SQS队列,并将得到4个相同的消息。我最终改为使用一个读取器将消息放入LinkedBlockingDeque中,以供其他一些线程使用。

我展示的一切都是纯Java / EE。

修改
在使用Amazon SQS / JMS集成后,我觉得如果您使用它,就会浪费时间。它仅适用于JMS 1.1,因此它也使用旧的JMS语法和JNDI。此外,它仅适用于队列,而不适用于主题。

我强烈建议您创建自己的实现。 ManagedExecutorService,运行具有短SQS读取超时的队列读取器线程。每个循环将从SQS队列中读取并将消息放入JMS队列或主题。

很抱歉让您对此抱有希望 - 亚马逊并没有保持足够的价值。

答案 1 :(得分:3)

我不认为我的解决方案是正确的JAVA EE,但在我的情况下它可行。

配置:

@Singleton
public class SqsMessageManager
{
    private Integer numberOfReceivers = 3;

    public static SQSConnection connection = null;
    public static Queue queue = null;

    @Inject
    SqsMessageReceiver sqsMessageReceiver;

    public void init()
    {
        try
        {
            SQSConnectionFactory connectionFactory =
                    SQSConnectionFactory.builder()
                            .withRegion(Region.getRegion(Regions.EU_WEST_1))
                            .withAWSCredentialsProvider(new EnvironmentVariableCredentialsProvider())
                            .build();

            connection = connectionFactory.createConnection();

            queue = connection.createSession(false, Session.AUTO_ACKNOWLEDGE).createQueue("myQueue");

            for (int i = 0; i < numberOfReceivers; i++)
                connection.createSession(false, Session.AUTO_ACKNOWLEDGE).createConsumer(queue).setMessageListener(sqsMessageReceiver);

            connection.start();
        }
        catch (JMSException e)
        {
            e.getStackTrace();
        }
    }
}

然后发件人:

@Dependent
public class SqsMessageSender
{
    MessageProducer producer = null;
    Session senderSession = null;

    @PostConstruct
    public void createProducer(){
        try
        {
            // open new session and message producer
            senderSession = SqsMessageManager.connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
            producer = senderSession.createProducer(SqsMessageManager.queue);
        }catch(JMSException | NullPointerException e){
            ;
        }
    }

    @PreDestroy
    public void destroy(){
        try
        {
            // close session
            producer.close();
            senderSession.close();
        }catch(JMSException e){

        }
    }

    // sends a message to aws sqs queue
    public void sendMessage(String txt)
    {
        try
        {
            TextMessage textMessage = senderSession.createTextMessage(txt);
            producer.send(textMessage);
        }
        catch (JMSException e)
        {
            e.getStackTrace();
        }
    }
}

接收者:

@Dependent
public class SqsMessageReceiver implements MessageListener
{
    public void onMessage(Message inMessage) {
        ...
    }
}

答案 2 :(得分:0)

Payara Cloud Connectors似乎很新,但看起来很有希望。不知道这是否适用于其他容器。据我所知,它基于JCA适配器。

答案 3 :(得分:0)

通常,要使MDB“连接”到远程JMS Q,您需要资源适配器(RA)。从理论上讲,基于JMS规范实现的RA应该适用于任何符合规范的JMS提供程序,因此理论上您应该能够重用this implementation

然而,即使是上述项目的README,您也应该使用由特定JMS提供程序提供的RA而不是通用提供程序。不幸的是,亚马逊没有提供:(

最近,some awesome guy创建了unofficial open-source one。我只是在评估它,并会根据我以后的经验更新这个答案。 (非常欢迎来自本RA的其他用户的评论)