Multithreading 什么样的;“事件总线”;春天用?内置,反应堆,阿克卡?

Multithreading 什么样的;“事件总线”;春天用?内置,反应堆,阿克卡?,multithreading,spring,akka,event-driven-design,project-reactor,Multithreading,Spring,Akka,Event Driven Design,Project Reactor,几周后我们将开始一个新的Spring4应用程序。我们想使用一些事件驱动架构。今年,我到处读有关“反应堆”的书,在网上寻找时,我偶然发现了“阿克卡” 因此,目前我们有3种选择: Spring的ApplicationEvent: 反应堆: Akka: 我找不到真正的比较 现在我们只需要像这样的东西: X注册以侦听事件E Y注册以侦听事件E Z发送事件E 然后X和Y将接收并处理事件 我们很可能会以异步方式使用它,但肯定也会有一些同步场景。我们很可能总是发送一个类作为事件。(Reactor示例

几周后我们将开始一个新的Spring4应用程序。我们想使用一些事件驱动架构。今年,我到处读有关“反应堆”的书,在网上寻找时,我偶然发现了“阿克卡”

因此,目前我们有3种选择:

  • Spring的
    ApplicationEvent
  • 反应堆:
  • Akka
我找不到真正的比较


现在我们只需要像这样的东西:

  • X
    注册以侦听
    事件E
  • Y
    注册以侦听
    事件E
  • Z
    发送
    事件E
然后
X
Y
将接收并处理事件

我们很可能会以异步方式使用它,但肯定也会有一些同步场景。我们很可能总是发送一个类作为事件。(Reactor示例主要使用字符串和字符串模式,但也支持对象)


据我所知,
ApplicationEvent
默认情况下是同步工作的,
Reactor
以异步方式工作。而且
Reactor
还允许使用
await()
方法使其有点同步
Akka
提供与反应堆差不多的功能,但也支持远程处理

关于Reactor的
await()
方法:它能等待多个线程完成吗?或者甚至是其中的一部分?如果我们从上面举个例子:

  • X
    注册以侦听
    事件E
  • Y
    注册以侦听
    事件E
  • Z
    发送
    事件E
可以通过这样的方式使其同步:等待
X
Y
完成。是否可以让它只等待
X
,而不等待
Y


也许还有其他选择?比如JMS呢

很多问题,但希望你能提供一些答案

谢谢大家!


编辑:示例用例
  • 当一个特定事件被触发时,我想创建10000封电子邮件。每封电子邮件都必须包含特定于用户的内容。因此,我会创建许多线程(max=系统cpu内核),它们创建邮件并且不会阻止调用线程,因为这可能需要几分钟的时间

  • 当触发特定事件时,我希望从未知数量的服务收集信息。每次提取大约需要100毫秒。在这里,我可以想象使用Reactor的
    wait
    ,因为我需要这些信息来继续我在主线程中的工作

  • 当触发特定事件时,我希望根据应用程序配置执行一些操作。因此,应用程序必须能够动态(取消)注册消费者/事件处理程序。他们会为这件事做自己的事,我不在乎。因此,我将为每个处理程序创建一个线程,然后继续在主线程中执行我的工作

  • 简单的解耦:我基本上知道所有的接收器,但我只是不想调用代码中的每个接收器。这主要应该同步完成


  • 听起来我需要一个线程池或一个环形缓冲区。这些框架是否有动态的环形缓冲区,如果需要的话,它们的大小会增加?

    仔细定义您希望从框架中得到什么。如果一个框架的特性超过了您的需要,那么它并不总是好的。更多的特性意味着更多的bug,更多的代码需要学习,性能也会降低

    一些值得关注的功能包括:

    • 参与者的性质(线程或轻量级对象)
    • 在机群上工作的能力(Akka)
    • 持久消息队列(JMS)
    • 特定功能,如信号(没有信息的事件)、转换(将不同端口的消息组合成复杂事件的对象,请参见Petri网)等
    注意像wait这样的同步特性——它会阻塞整个线程,并且在线程池上执行actor时是危险的(线程饥饿)

    需要查看的更多框架:

    -在某些情况下,允许
    等待
    ,而不会导致线程不足

    -信号、转换

    附加组件:两种演员

    通常,并行工作系统可以表示为一个图,其中活动节点相互发送消息。在Java中,与大多数其他主流语言一样,活动节点(参与者)可以实现为线程或由线程池执行的任务(可运行或可调用)。通常,参与者的一部分是线程,另一部分是任务。这两种方法各有优缺点,因此为系统中的每个参与者选择最合适的实现是至关重要的。 简单地说,线程可以阻塞(并等待事件),但会为其堆栈消耗大量内存。任务不能阻塞,而是使用共享堆栈(池中的线程)

    若任务调用阻塞操作,它将从服务中排除池线程。如果许多任务阻塞,它们可能会排除所有线程,从而导致死锁-那些可以解除阻塞的任务无法运行。这种死锁称为线程饥饿。若为了防止线程不足,将线程池配置为无限,我们只需将任务转换为线程,就失去了任务的优势

    为了消除对任务中阻塞操作的调用,任务应该分为两个(或多个)-第一个任务调用阻塞操作并退出,其余的任务被格式化为异步任务,在阻塞操作完成时启动。当然,阻塞操作必须有一个可选的异步接口。因此,例如,应该使用NIO或NIO2库,而不是同步读取套接字

    不幸的是,标准java库缺少asynchro
    @Autowired
    Environment env;
    @Autowired
    SmtpClient client;
    
    // Using a ThreadPoolDispatcher
    Deferred<DomainObject, Stream<DomainObject>> input = Streams.defer(env, THREAD_POOL);
    
    input.compose()
      .map(new Function<DomainObject, EmailTemplate>() {
        public EmailTemplate apply(DomainObject in) {
          // generate the email
          return new EmailTemplate(in);
        }
      })
      .consume(new Consumer<EmailTemplate>() {
        public void accept(EmailTemplate email) {
          // send the email
          client.send(email);
        }
      });
    
    // Publish input into Deferred
    DomainObject obj = reader.readNext();
    if(null != obj) {
      input.accept(obj);
    }