java akka 远程_远程演员–发现Akka

本文介绍了在Java Akka中处理远程Actor的需求,当应用需要向外扩展时,选择构建集群。文章探讨了如何避免多线程访问冲突,以及如何通过配置只在特定节点创建远程Actor的代理,实现透明的远程调用。文中还提到了防止并发HTTP连接的问题,通过在Actor内部实现请求队列来解决。最后,强调了保持Actor状态封装的重要性。
摘要由CSDN通过智能技术生成

java akka 远程

假设我们的测试应用程序取得了巨大的成功,并且一台服务器逐渐无法处理不断增长的流量。 我们面临两种选择:用更好的服务器替换服务器(向上扩展)或购买第二台服务器并构建集群(向外扩展)。 我们选择构建集群是因为将来更容易扩展。 但是,我们很快发现我们的应用程序不再满足第一个要求:

客户端应用程序最多应从一个线程调用URL […] –禁止使用多个HTTP连接同时获取随机数。
显然,集群中的每个节点都是独立的,具有自己的独立Akka实例,因此是RandomOrgClient actor的独立副本。 为了解决此问题,我们有几种选择:

  • 具有全局(群集范围!)锁(分布式监视器,信号灯等),以保护多线程访问。 普通synchronized是不够的。
  • …或在集群中创建一个专用节点以与random.org通信,所有其他节点通过某个API使用该节点
  • …或仅在一个节点上仅创建RandomOrgClient一个实例,并通过某些API(RMI,JMS…)将其公开给远程客户端

您还记得花了多少时间来描述ActorActorRef之间的区别吗? 现在,这种区别将变得显而易见。 事实证明,我们的解决方案将基于最后的建议,但是我们不必担心API,序列化,通信或传输层。 更好的是,Akka中没有此类API可以处理远程角色。 在配置文件中足以说明这一点:假定仅在此节点上创建此特定参与者。 所有其他节点(而不是在本地创建相同的actor)将返回一个特殊的代理,该代理从外部看起来像是正常的actor,而实际上它将所有消息转发到其他节点上的远程actor。

再说一遍:我们无需更改代码中的任何内容,这足以在配置文件中进行一些调整:

akka {
  actor {
  provider = "akka.remote.RemoteActorRefProvider"
  deployment {
      /buffer/client {
        remote = "akka://RandomOrgSystem@127.0.0.1:2552"
      }
    }
  }
  remote {
    transport = "akka.remote.netty.NettyRemoteTransport"
    log-sent-messages = on
    netty {
      hostname = "127.0.0.1"
    }
  }
}

而已! 集群中的每个节点都由服务器地址和端口标识。 配置的关键部分是声明/buffer/client仅创建127.0.0.1:2552 。 每隔一个实例(在不同的服务器或端口上工作),而不是创建actor的新副本,将构建一个特殊的透明代理,以调用远程服务器。

如果您不记得我们解决方案的体系结构,则下图演示了消息流。 如您所见,每个节点都有自己的RandomOrgBuffer副本(否则,对缓冲区的每次访问都将导致远程调用,这完全破坏了缓冲区的目的)。 但是,每个节点(中间的节点除外)都有对RandomOrgClient actor的远程引用(中间的节点在本地访问此actor):

中间的机器(JVM 1)在端口2552上执行,它是唯一与random.org通信的random.org 。 其他所有服务器(分别在2553和2554上运行的JVM 2和3)都通过JVM 1与该服务器间接通信。顺便说一句,我们可以使用配置文件或-akka.remote.netty.port=2553更改每个节点使用的TCP / IP端口。 -akka.remote.netty.port=2553 Java属性。

在再次宣布过早的成功之前,我们面临另一个问题。 或者实际上,我们还没有真正克服最初的障碍。 由于RandomOrgClient现在由多个访问RandomBuffer参与者(跨集群分布),但它仍然可以启动到random.org多个并发的HTTP连接,代表在集群中的每个节点的。 容易想到这样的情况,其中多个RandomOrgBuffer实例同时发送FetchFromRandomOrg消息,开始多个并发HTTP连接。 为了避免这种情况,如果一个请求尚未完成,我们在actor中实现了一种对请求进行排队的技术

case class FetchFromRandomOrg(batchSize: Int)

case class RandomOrgServerResponse(randomNumbers: List[Int])

class RandomOrgClient extends Actor {

  val client = new AsyncHttpClient()
  val waitingForReply = new mutable.Queue[(ActorRef, Int)]

  override def postStop() {
    client.close()
  }

  def receive = LoggingReceive {
    case FetchFromRandomOrg(batchSize) =>
      waitingForReply += (sender -> batchSize)
      if (waitingForReply.tail.isEmpty) {
        sendHttpRequest(batchSize)
      }
    case response: RandomOrgServerResponse =>
      waitingForReply.dequeue()._1 ! response
      if (!waitingForReply.isEmpty) {
        sendHttpRequest(waitingForReply.front._2)
      }
  }

  private def sendHttpRequest(batchSize: Int) {
    val url = "https://www.random.org/integers/?num=" + batchSize + "&min=0&max=65535&col=1&base=10&format=plain&rnd=new"
    client.prepareGet(url).execute(new RandomOrgResponseHandler(self))
  }
}

private class RandomOrgResponseHandler(notifyActor: ActorRef) extends AsyncCompletionHandler[Unit]() {
  def onCompleted(response: Response) {
    val numbers = response.getResponseBody.lines.map(_.toInt).toList
    notifyActor ! RandomOrgServerResponse(numbers)
  }
}

这次要注意waitingForReply队列。 当请求从远程Web服务获取随机数时,要么我们发起新的连接(如果队列中除了我们之外不包含任何人)。 如果有其他的演员等候,我们必须有礼貌地把自己放在队列,记住谁要求多少个号码( waitingForReply + =( sender -> batchSize ))。 收到答复后,我们将从队列中的第一个演员(等待时间最长的演员)中取出,并代表他发起另一个请求。

毫不奇怪,没有multithreading或同步代码。 但是,重要的是不要通过在receive方法之外访问其状态来破坏封装。 我通过读取onCompleted()方法内的waitingForReply队列犯了这个错误。 因为此方法是由HTTP客户端工作线程异步调用的,所以潜在地我们可以同时从两个线程访问actor状态(如果它正在同时处理接收中的某些消息)。 这就是为什么我决定将HTTP回复回调提取到一个单独的类中,而不具有对actor的隐式访问的原因。 这更加安全,因为通过编译器本身可以保护对参与者状态的访问。

这是“发现Akka”系列的最后一部分。 请记住,完整的源代码可在GitHub找到

参考: 远程演员–Java和邻里博客中,从我们的JCG合作伙伴 Tomasz Nurkiewicz 发现Akka

翻译自: https://www.javacodegeeks.com/2012/11/remote-actors-discovering-akka.html

java akka 远程

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值