【问题标题】:Preventing race conditions for message processing防止消息处理的竞争条件
【发布时间】:2011-10-04 11:48:46
【问题描述】:

我有一个通过 Web 服务接收消息(事件)的 J2EE 应用程序。消息具有不同的类型(根据类型需要不同的处理)并按特定顺序发送。它发现了一个问题,即某些消息类型的处理时间比其他消息类型长。结果是在序列中第二个接收到的消息可能在序列中的第一个之前被处理。我试图通过在处理消息的方法周围放置一个同步块来解决这个问题。这似乎可行,但我不确定这是“正确”的方法吗?是否有可能更合适的替代方案或者这是“可接受的”?我已经包含了一小段代码来尝试更清楚地解释。 ....任何建议/指导表示赞赏。

public class EventServiceImpl implements EventService {
  public String submit (String msg) {

    if (msg == null)
        return ("NAK");

            EventQueue.getInstance().submit(msg);

    return "ACK";
  }
}


public class EventQueue {
    private static EventQueue instance = null;
    private static int QUEUE_LENGTH = 10000;
    protected boolean done = false;
    BlockingQueue<String> myQueue = new LinkedBlockingQueue<String>(QUEUE_LENGTH);

protected EventQueue() {
    new Thread(new Consumer(myQueue)).start();
}

public static EventQueue getInstance() {
      if(instance == null) {
         instance = new EventQueue();
      }
      return instance;
}

public void submit(String event) {
    try {
        myQueue.put(event);
    } catch (InterruptedException ex) {
    }
}

class Consumer implements Runnable {
    protected BlockingQueue<String> queue;

    Consumer(BlockingQueue<String> theQueue) { this.queue = theQueue; }

    public void run() {
      try {
        while (true) {
          Object obj = queue.take();
          process(obj);
          if (done) {
            return;
          }
        }
      } catch (InterruptedException ex) {
      }
    }

    void process(Object obj) {
        Event event = new Event( (String) obj);
        EventHandler handler = EventHandlerFactory.getInstance(event);
        handler.execute();
    }
}

// Close queue gracefully
public void close() {
    this.done = true;
}

【问题讨论】:

    标签: java jakarta-ee


    【解决方案1】:

    我不确定您正在使用的框架 (EJB(MDB)/JMS) 是什么。通常应避免在托管环境中使用同步,如 EJB/JMS(这不是一个好习惯)。一种解决方法是

    • 客户端在发送下一条消息之前应该等待来自服务器的确认。
    • 这样,您的客户端本身将控制事件的顺序。

    请注意,如果有多个客户端提交消息,这将不起作用。

    编辑:

    您有一种情况,Web 服务的客户端在不考虑消息处理时间的情况下按顺序发送消息。它只是一个接一个地转储消息。这是基于Queue ( First In First Out ) 的解决方案的好案例。我建议以下两种方法来完成此操作

    1. 使用 JMS 。这将增加添加JMS providers 和编写一些管道代码的额外开销。

    2. 使用诸如Producer-Consumer 之类的多标题模式,其中您的Web 服务处理程序会将传入消息转储到队列中,而单线程 消费者将一次使用一条消息。使用 java.util.concurrent 包查看this example

    3. 使用数据库。将传入消息转储到数据库中。使用不同的基于调度程序的程序扫描数据库(基于序列号)并相应地处理消息。

      第一种和第三种解决方案对于这类问题非常标准。第二种方法会很快,并且在您的代码中不需要任何额外的库。

    【讨论】:

    • 在这种情况下,我对发送消息的系统没有任何访问/控制权 - 尽管每条消息都包含一个序列号。我已经使用 cxf 构建了接收消息的 Web 服务。
    • 你有多个客户端在他们自己的消息序列中发送?
    • 该服务只有一个客户端。实际上,FIFO 的想法已经闪过我的脑海,虽然我不太确定如何实现它(我将通读参考资料)——我认为 JMS(对我而言)可能增加了太多的复杂性。
    • 我已经修改了上面的代码示例以包含一个非常接近于您引用的 using java.util.concurrent 包的示例的 calss。它确实似乎运行正常。
    • 看起来不错:)。由于您已经声明了没有任何容量的队列,它将以Integer.MAX_VALUE 作为容量,这将避免在提交对象进行处理时出现任何阻塞(我相信您要处理的对象数量远远少于Integer.MAX_VALUE)跨度>
    【解决方案2】:

    如果要按特定顺序处理事件,那么为什么不尝试在消息中添加“eventID”和“orderID”字段呢?这样,您的 EventServiceImpl 类可以排序、排序,然后以正确的顺序执行(无论它们被创建和/或交付给处理程序的顺序如何)。

    同步handler.execute() 块不会得到预期的结果,我预计。 synchronized 关键字所做的只是防止多个线程同时执行该块。它在正确排序下一个线程的领域中没有任何作用。

    如果synchronized 块似乎确实使事情正常进行,那么我断言您非常幸运,因为消息正在以正确的顺序创建、传递和执行。在多线程环境下,这个是不放心的!我会采取措施确保你控制住了这一点,而不是依靠好运。

    例子:

    1. 消息的创建顺序为“client01-A”、“client01-C”、 'client01-B'、'client01-D'
    2. 消息以“client01-D”的顺序到达处理程序, 'client01-B'、'client01-A'、'client01-C'
    3. EventHandler 可以区分消息从一个客户端到另一个客户端并开始缓存“client01”的消息。
    4. EventHandler recv 的“client01-A”消息,并且知道它可以处理并这样做。
    5. EventHandler 在缓存中查找消息“client01-B”,找到并处理它。
    6. EventHandler 找不到“client01-C”,因为它还没有到达。
    7. EventHandler recv 的“client01-C”并对其进行处理。
    8. EventHandler 在缓存中查找“client01-D”,找到并处理它,并认为“client01”交互完成。

    这些方面的东西将确保正确处理并促进多线程的良好使用。

    【讨论】:

    • 感谢您的 cmets。我无法控制发送系统,但它旨在以正确的顺序发送消息(尽管我怀疑无法保证)。其中一个问题是,在我的代码中,每次收到消息时都会调用处理程序。假设每 2 毫秒接收一次消息,第一条消息(仅)需要 30 毫秒来处理。然后处理程序的第二个实例将处理消息 2,同时处理第一条消息(损坏数据)。在您描述的方法中,处理程序是否必须是单例才能按顺序处理?
    • 单例可能是实现多线程可以轻松访问的数据结构的最简洁方式,是的。问题:由于您无法控制消息的创建,因此每条消息是否属于不同类型,或者在每组消息中是否有 >1 条相同类型的消息?
    • 消息有多种类型。工厂返回接收到的消息类型的处理程序。然后通过调用处理程序上的执行方法来处理消息 - 本质上是命令模式。在我上面的示例中,第一条消息可能包含第二条消息操作的数据 - 所以第一个消息类型处理程序会持久保存数据 - 第二个操作它。
    • 这就是我对同步块的想法的来源——试图确保一个执行方法在下一个开始之前完成。但是正如您所指出的,这里没有任何东西可以管理消息的“顺序”。
    • 那么有没有办法根据消息类型对消息进行“排序”?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-03-21
    • 1970-01-01
    • 2017-05-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多