使用MDB滚动升级

时间:2012-06-15 15:25:45

标签: java java-ee glassfish

我们在Glassfish 3.1.2集群上部署了一个Java EE应用程序,该集群使用JAX-RS提供REST API。我们通过将EAR部署到重复的集群实例来定期部署新版本的应用程序,然后更新HTTP负载均衡器以将流量发送到更新的实例而不是旧实例。

这使我们可以在不损失可用性的情况下进行升级,如下所述:http://docs.oracle.com/cd/E18930_01/html/821-2426/abdio.html#abdip。我们经常对应用程序进行重大更改,这使得新版本“不兼容”(这就是我们使用两个集群的原因)。

我们现在必须为应用程序提供消息队列接口,以实现某些高吞吐量内部消息传递(来自C ++生产者)。但是,使用Message Driven Beans我看不出如何在没有任何服务中断的情况下升级应用程序?

我调查的选项是:

单个远程JMS队列(openMQ)

生产者将消息发送到单个消息队列,消息由MDB处理。当我们启动第二个集群实例时,应该将消息负载平衡到升级后的集群,但是当我们停止“旧”集群时,未完成的事务将会丢失。

我考虑过使用JMX在升级过程中禁用生产者/消费者到该消息队列,但这只会暂停消息传递。当我们禁用旧群集时,Oustanding消息仍会丢失(我认为?)。

我还考虑过抛弃@MessageDriven注释并手动创建MessageConsumer。这看起来确实有效,但MessageConsumer无法使用EJB注释访问其他EJB(据我所知):

// Singleton bean with start()/stop() functions that 
// enable/disable message consumption

@Singleton
@Startup
public class ServerControl {

private boolean running=false;

@Resource(lookup = "jms/TopicConnectionFactory")
private TopicConnectionFactory topicConnectionFactory;

@Resource(lookup = "jms/MyTopic")
private Topic topic;
private Connection connection;
private Session session;
private MessageConsumer consumer;    

public ServerControl()
{
    this.running = false;         
}

public void start() throws JMSException {
    if( this.running ) return;

    connection = topicConnectionFactory.createConnection();
    session = dbUpdatesConnection.createSession(false, Session.DUPS_OK_ACKNOWLEDGE);
    consumer = dbUpdatesSession.createConsumer(topic);
    consumer.setMessageListener(new MessageHandler());    


    // Start the message queue handlers
    connection.start();

    this.running = true;
}

public void stop() throws JMSException {
    if( this.running == false ) return;

    // Stop the message queue handlers

    consumer.close();

    this.running = false;
}
}

// MessageListener has to invoke functions defined in other EJB's

@Stateless
public class MessageHandler implements MessageListener {

@EJB
SomeEjb someEjb; // This is null

public MessageHandler() {
}

@Override
public void onMessage(Message message) {
    // This works but someEjb is null unless I 
    // use the @MessageDriven annotation, but then I 
    // can't gracefully disconnect from the queue
}

}

每个群集的本地/嵌入式JMS队列

  • 客户端必须连接到两个不同的消息队列代理(每个群集一个)。
  • 必须通知客户端集群实例正在关闭并停止向该代理上的队列发送消息。
  • 通常比现有的http解决方案更不方便和整洁。

备用邮件队列提供程序

  • 将Glassfish连接到不同类型的消息队列或不同的供应商(例如Apache OpenMQ),或许其中一个能够平衡远离特定消费者的流量?

我假设只是禁用应用程序只会“杀死”任何未完成的交易。如果禁用应用程序允许现有事务完成,那么我可以在启动第二个集群后执行此操作。

任何帮助将不胜感激!提前谢谢。

2 个答案:

答案 0 :(得分:0)

如果使用高可用性,则群集的所有消息都将存储在单个数据存储中,而不是每个实例上的本地数据存储。然后,您可以将两个群集配置为使用同一个存储。然后,当关闭旧的并启动新的时,您可以访问所有消息。

这是一个很好的video,有助于解释glassfish的高可用性jms。

答案 1 :(得分:0)

我不明白你假设当我们停止“旧”群集时,未完成的交易将会丢失。在应用程序停止之前,MDB将被允许完成其消息处理,并且任何未确认的消息将由“新”集群处理。

如果旧版本和新版本之间的负载均衡存在问题,我会将MDB放入单独的.ear并在新MDB联机时立即停止旧MDB,或者甚至在此之前,如果您的用例允许延迟在消息处理中,直到部署新版本。