【问题标题】:Akka and Spring integrationAkka 和 Spring 集成
【发布时间】:2015-01-11 06:17:00
【问题描述】:

我正在尝试让 akka 使用 spring 应用程序。这是一款非常适合 akka 模型的搜索应用程序。大多数关于此集成的在线示例和关于类型安全的示例都在谈论使用 akka 扩展来注入 spring 应用程序上下文。然而,他们都使用ActorSystem.actorOf() 方法来创建演员,这是众所周知的昂贵操作。

ActorSystem system = ctx.getBean(ActorSystem.class);
system.actorOf(.....)

见 - https://github.com/typesafehub/activator-akka-java-spring/blob/master/src/main/java/sample/Main.java

我想使用参与者来处理每个通过身份验证的 Web 请求。使用上面的代码,我最终会为每个不理想的请求创建根actor。

任何指针将不胜感激。

【问题讨论】:

    标签: java spring akka


    【解决方案1】:

    以下内容没有回答最初的问题,即当请求进入网络应用程序时,通常需要减少需要创建的新参与者的数量。

    要使用 Akka 路由器来做到这一点,它可以像以下代码行一样简单:

    getContext().actorOf(SpringExtProvider.get(getContext().system()).props("AnotherActor.").withRouter(new RoundRobinPool(100)), "another");
    

    Akka 文档提供了有关配置的更多详细信息,但值得一看 http://doc.akka.io/docs/akka/snapshot/java/routing.html。最好通过 Akka 配置文件定义它们的行为,而不是硬编码到应用程序中。你可以这样称呼它:

    getContext().actorOf(SpringExtProvider.get(getContext().system()).props("AnotherActor.").withRouter(new FromConfig()), "another");
    

    ..并在您的 application.conf 文件中定义路由器的类型和行为。

    如果您还没有考虑过,那么看看如何使您的网络重新请求异步也是值得的。一种方法是使用 Spring 的 DeferredResult 并将其实例传递给您的参与者,并在搜索请求完成时设置结果。

    --2011 年更新--

    我认为为什么演员选择对你不起作用是因为你试图使用 bean 名称,而不是演员名称作为演员选择的 pacth。创建路由器时,您没有指定演员名称,因此 Akka 会在内部为其命名,例如“$a”。

    为了说明,如果你创建你的演员:

    actorSystem.actorOf(this.get(actorSystem).props(applicationContext, "bean_name"), "actorName");
    

    那么您应该能够执行演员选择:

    actorSystem.actorSelection("actorName");
    

    或者创建一次上述路由器actor,然后在对 Spring MVC Web 服务发出的每个请求中重新使用它,您可以在 Spring @Configuration 类中创建它并将其 ActorRef 作为 bean 公开,这样您可以注入你的 Spring @Controller 类。以下是我创建 frmo 内存的一个简单示例,因此请确保它已经过测试/编译等。

    @Configuration
    public class Config {
    
       @Autowired
       private ActorSystem actorSystem;
    
       @Bean(name = "routerActorRef")
       public ActorRef routerActorRef() { 
          getContext().actorOf(SpringExtProvider.get(actorSystem).props("AnotherActor").withRouter(new RoundRobinPool(100)), "another");
       }
    
    }
    

    然后您可以通过编写如下代码将其注入另一个 Spring bean:

    @Autowired
    @Qualifier("routerActorRef")
    private ActorRef routerActorRef;
    

    需要注意的是,这只有在顶级actors中才真正可行,对于低级actors也可以,但要有效管理会变得相当棘手。

    -- 原始答案--

    您链接到的 Main 方法中的示例显示了如何创建一个初始顶级 Actor,该 Actor 将由基于系统的“用户”Actor 监督。这是一个非常简单的示例,演示了创建一个名为 CountingActor 的 Spring 托管 Actor,并在其中注入了一个名为 CountingService 的 Spring 托管 bean。

    为了进一步了解这个示例,您可以定义另一个类似于 CountingActor 的演员,例如

    @Named("AnotherActor")
    @Scope("prototype")
    class AnotherActor extends UntypedActor {
    
      // the service that will be automatically injected
      final AnotherService anotherService;
    
      @Inject
      public AnotherActor(@Named("AnotherService") AnotherService anotherService) {
        this.anotherService = anotherService;
      }    
    
      @Override
      public void onReceive(Object message) throws Exception {
        if (message == "doSomething") {
          anotherService.doSomething();
        } else {
          unhandled(message);
        }
      }
    }
    

    我假设有另一个名为 AnotherService 的 Spring 服务 bean,它有一个方法 doSomething(),该方法将在创建 AnotherActor 时注入。

    然后在 CountingActor 中你可以像这样创建 AnotherActor:

    @Named("CountingActor")
    @Scope("prototype")
    class CountingActor extends UntypedActor {
    
      public static class Count {}
      public static class Get {}
    
      // the service that will be automatically injected
      final CountingService countingService;
    
      @Inject
      public CountingActor(@Named("CountingService") CountingService countingService) {
        this.countingService = countingService;
      }
    
      private int count = 0;
    
      @Override
      public void onReceive(Object message) throws Exception {
        if (message instanceof Count) {
          count = countingService.increment(count);
          // Create AnotherActor here as a child of CountingActor by using the CountingActor's context
          ActorRef anotherActor = getContext().actorOf(SpringExtProvider.get(system).props("AnotherActor"), "another");
          anotherActor.tell("doSomething", getSelf());
        } else if (message instanceof Get) {
          getSender().tell(count, getSelf());
        } else {
          unhandled(message);
        }
      }
    }
    

    这里的actor创建和一个主要的区别是使用getContext().actorOf(...)而不是system.actorOf(...)。使用 getContext() 会导致将新 Actor 创建为 CounterActor 的子代,而不是顶级“用户” Actor。

    Akka 官方文档中对此行为有很好的描述:http://doc.akka.io/docs/akka/snapshot/java/untyped-actors.html#creating-actors-with-props

    【讨论】:

    • 谢谢,但这并不能解决我的主要问题——“我想使用参与者来处理每个通过身份验证的 Web 请求。使用上面的代码,我最终将为每个单独的参与者创建根参与者不理想的要求。”此外,以下行每次都会创建一个演员 - 你如何在此处添加路由器? ActorRef anotherActor = getContext().actorOf(SpringExtProvider.get(system).props("AnotherActor"), "another");
    • 我已经更新了上面的答案,对造成的误解深表歉意。我认为您要实现的目标应该相当简单。
    • 谢谢。但是,这不适用于根演员,因为我不能为使用弹簧上下文创建的演员执行 actorSystem.actorSelection("bean_name") -> actorSystem.actorOf(this.get(actorSystem).props(applicationContext, " bean_name"))。因此,它结束了为每个 Web 请求创建 Actor 及其子代!
    • 要解决这个问题,您可以在 Spring @Configuration 类中将路由器 Actor 的 ActorRef 公开为 Spring bean。我将在上面的答案中添加一个简短的、未经测试的代码 sn-p 来说明我的意思。
    • 我认为这些弹簧工厂可能会对您有所帮助 - github.com/sabomichal/akka-java-springfactory。检查单元测试的使用情况。但它与使用上面提到的程序化spring配置基本相同。
    猜你喜欢
    • 1970-01-01
    • 2013-05-17
    • 1970-01-01
    • 1970-01-01
    • 2019-04-16
    • 1970-01-01
    • 2014-10-22
    • 2023-01-30
    • 2019-03-08
    相关资源
    最近更新 更多