繁体   English   中英

分布式播放框架应用程序中的远程actor系统之间的交叉通信

[英]Cross communicate between remote actor systems in distributed play framework application

我正在尝试找出构建应用程序的最佳方法,以便可以以冗余方式从播放框架应用程序发送推送通知。

我想实现“休息期”,以便在用户修改数据30秒后将推送通知发送到移动设备。 如果用户在30秒钟内进行了另一个修改,则需要取消原始通知,并用新的通知替换,该通知应在最近的修改后30秒发送,依此类推。

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

  1. User1进行修改,然后发送到API Server1。 触发通知将在30秒内发生。
  2. User1在5秒后对同一记录进行了第二次修改,最终被路由到API Server2。 因为不知道Server1收到的信息,另一个通知将在30秒内发送出去。

这是不正确的行为-User1应该仅收到一个通知,因为发生了修改,而数据在30秒钟内没有“静止”。

由于我对Akka并不是特别熟悉,因此这似乎是一个很好的学习机会。 看来我可以使用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")

当我需要发送通知时,我告诉演员安排时间。 这样,每个系统都可以保留键/值对中的Cancelableable实例,并在数据在其他服务器上更新时取消通知:

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系统吗? 他们应该能够交流吗?

另外,如果我对此过于复杂,则可以接受其他解决方案。 如果我可以使用它,这似乎是最简单的方法。

我想我知道了。 该体系结构似乎可以工作,但是我有两个问题可以解决,方法是从本地计算机上运行它,并将server1和server2添加到配置中。

  1. 应用程序启动时,我没有实例化actor系统-我使用的是惰性val,这意味着actor尚未准备就绪,因为它没有收到导致创建该actor的请求。 换句话说,由于尚未初始化,所以Server2上的notificationActor实例未在监听。

    为了解决这个问题,我将所有与Akka相关的组件(系统,角色和路由器)的初始化都移到了GlobalSettings类的onStart方法中,以便在播放应用程序启动后就可以开始使用。 这是主要问题。

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM