分布式播放框架应用程序

时间:2015-12-15 23:24:47

标签: playframework playframework-2.0 akka akka-remote-actor

我试图找出构建应用程序的最佳方法,这样我就可以以多余的方式从我的播放框架应用程序发送推送通知。

我想实施一个"休息时间"在用户修改数据30秒后向移动设备发送推送通知。如果用户在该30秒范围内进行了另一次修改,则需要取消原始通知并替换为应在最新修改后30秒发送的新通知,依此类推。

问题是我的API后端需要相互通信,以确保他们不会在30秒内发送多个通知,因为它们是负载平衡的。例如:

  1. User1进行修改,发送到API Server1。触发通知将在30秒内发生。
  2. User1对同一记录进行第二次修改,5秒后,最终被路由到API Server2。触发另一个通知在30秒内发送,因为它不知道Server1收到的信息。
  3. 这是不正确的行为 - User1应该只接收一个通知,因为修改发生时数据不是"静止"持续30秒。

    由于我对阿卡不是特别熟悉,这似乎是一个很好的学习机会。看起来我可以通过Akka远程处理来解决这个问题。

    这是我能想到的最简单的架构:

    • 在每个API实例中使用路由器创建一个akka系统(" notifications"),以便向每个API实例发送消息,每个API实例都有一个Akka actor(" notificationActor")

    我的application.conf看起来像这样:

    akka {
    
      actor {
        provider = "akka.remote.RemoteActorRefProvider"
    
        deployment {
          /router {
            router = round-robin-group
            routees.paths = [
              "akka.tcp://notifications@server1:2552/user/notificationActor",
              "akka.tcp://notifications@server2:2552/user/notificationActor"]
          }
        }
      }
      remote {
        enabled-transports = ["akka.remote.netty.tcp"]
        netty.tcp {
          hostname = "server1" // set to server1 or server 2 upon deployment
          port = 2552
        }
      }
    }
    

    我正在设置系统,演员和路由器,如下所示:

    // the system, instantiated once per server
    private val system = ActorSystem("notifications")
    
    // the local actor, instantiated once per server
    private val notificationActor = system.actorOf(Props[NotificationActor], name = "notificationActor")
    
    // the router, instantiated once per server
    private val router = system.actorOf(FromConfig.props(Props[NotificationActor]), name = "router")
    

    当我需要发送通知时,我会告诉演员安排通知。这样每个系统都可以保持键/值对中的Cancellable实例,如果数据在不同的服务器上更新,则取消通知:

    Client.scala(近似,可能有拼写错误)

    def updateData(data: DbData) = {
       // update data in db
       NotificationController.sendMessage(Notification(data.key))
    }
    

    NotificationController.scala(近似,可能有拼写错误)

    def sendMessage(pushNotification: Notification) = {
    
       // cancel all notifications related to this data on all servers
       router ! Broadcast(CancelNotification(pushNotification))
    
       // schedule the new notification on the next available actor
       router ! ScheduleNotification(pushNotification)
    }
    

    CancelNotification.scala(近似,可能有拼写错误)

    case class CancelNotification(pushNotification: Notification)
    

    ScheduleNotification.scala(近似,可能有拼写错误)

    case class ScheduleNotification(pushNotification: Notification)
    

    NotificationActor.scala(近似,可能有拼写错误)

    val cancellableMap: Map[String, Cancellable] = // (new concurrent hash map)
    def receive: Receive = {
      case ScheduleNotification(pushNotification) => //uses - this.context.system.scheduler.scheduleOnce and stores the key/Cancellable pair in cancellableMap
      case CancelNotification(pushNotification) => //use stored Cancellable to cancel notification, if present
      case Notification(key) => //actually send the push notification
    }
    

    这在本地工作正常,但是一旦我将它部署到我的测试环境(有多台机器),其他所有消息似乎都会丢失。我认为这是因为它试图将这些消息发送到Server2,但我没有在任何应用程序的日志文件中看到任何错误。我试图在我的akka​​配置中添加更多日志记录,但是我没有在logs / application.log(默认播放框架日志)中看到任何额外的输出:

    akka {
    
      loglevel = "DEBUG"
      log-config-on-start = on
    
      actor {
        provider = "akka.remote.RemoteActorRefProvider"
    
        deployment {
          /router {
            router = round-robin-group
            routees.paths = [
              "akka.tcp://notifications@server1:2552/user/notificationActor",
              "akka.tcp://notifications@server2:2552/user/notificationActor"]
          }
        }
    
        debug {
          unhandled = on
        }
      }
      remote {
        log-sent-messages = on
        log-received-messages = on
        enabled-transports = ["akka.remote.netty.tcp"]
        netty.tcp {
          hostname = "server1" // set to server1 or server 2 upon deployment
          port = 2552
        }
      }
    }
    

    为什么Server2不接收消息?我是否可以在每个实例上使用来自所有服务器的actor实例化一个actor系统?他们应该能够交流吗?

    另外,如果我过于复杂,我会对其他解决方案持开放态度。如果我能让它发挥作用,这似乎是最简单的方法。

1 个答案:

答案 0 :(得分:0)

我想我明白了。这个体系结构似乎有效,但是我通过从本地计算机运行它并将server1和server2添加到我的配置中解决了两个问题。

  1. 当应用程序启动时我没有实例化我的actor系统 - 我使用了懒惰的vals,这意味着演员没有准备好,因为它没有收到导致它的请求要被创造。换句话说,Server2上的notificationActor实例没有被监听,因为它尚未初始化。

    为了解决这个问题,我将所有akka相关组件(系统,actor和路由器)的初始化移动到我的GlobalSettings类的onStart方法,以便在播放应用程序启动时立即开始。这是主要问题。

  2. 我的邮件无法序列化。

相关问题