您的第一个讯息–发现Akka

Akka是受Erlang启发的平台(框架?),它承诺可轻松开发可伸缩,多线程和安全的应用程序。 虽然在大多数流行语言中,并发是基于多个线程之间共享的内存,并由各种同步方法来保护,但是Akka提供了基于参与者的并发模型。 Actor是一个轻量级的对象,您可以通过向其发送消息来与之交互。 每个参与者一次最多只能处理一条消息,并且显然可以将消息发送给其他参与者。 在一个Java虚拟机中,可以同时存在数百万个参与者,从而建立一个层次化的父级( 主管 )–子级结构,其中父级监视子级的行为。 如果这还不够,我们可以轻松地在集群中的多个节点之间划分参与者,而无需修改任何代码。 每个参与者可以具有内部状态(字段/变量集),但是通信只能通过消息传递进行,而不能通过共享数据结构(计数器,队列)进行。

上面这些功能的组合带来了更安全,更稳定和可扩展的代码-代价是并发编程模型发生了根本的范式转换。 如此众多的流行语和承诺,让我们继续举例。 这不会成为“ Hello,world”的例子,但我们将尝试构建一个小而完整的解决方案。 在接下来的几篇文章中,我们将实现与random.org API的集成。 该Web服务使我们能够基于大气噪声(无论意味着什么)来获取真正的随机数(与伪随机生成器相对)。 API并没有那么复杂,请访问以下网站并刷新几次:

https://www.random.org/integers/?num=20&min=1000&max=10000&col=1&base=10&format=plain

那么困难在哪里? 阅读自动客户指南,我们了解到:

  1. 客户端应用程序最多应从一个线程调用上面的URL –禁止使用多个HTTP连接同时获取随机数。
  2. 我们应该分批加载随机数,而不是每次请求都一一加载。 上面的请求在一个呼叫中使用num = 20个数字。
  3. 警告我们延迟,甚至在一分钟后响应仍可能到达
  4. 客户端应定期检查随机数配额(每天最多免费提供给定数量的随机位)

所有这些要求使得与random.org集成random.org不平凡。 在本系列中,我才刚刚开始,我们将逐步改进我们的应用程序,逐步学习Akka的新功能。 一旦了解了平台的基本概念,我们很快就会意识到,陡峭的学习曲线是有回报的。 所以,让我们编码!

今天,我们将尝试处理前两个要求,即在任何给定时间点不超过一个连接,并分批加载数量。 对于这一步,我们实际上并不需要Akka,简单的同步和一个缓冲区就足够了:

val buffer = new Queue[Int]

def nextRandom(): Int = {
  this.synchronized {
    if(buffer.isEmpty) {
      buffer ++= fetchRandomNumbers(50)
    }
    buffer.dequeue()
  }
}

def fetchRandomNumbers(count: Int) = {
  val url = new URL("https://www.random.org/integers/?num=" + count + "&min=0&max=65535&col=1&base=10&format=plain&rnd=new")
  val connection = url.openConnection()
  val stream = Source.fromInputStream(connection.getInputStream)
  val randomNumbers = stream.getLines().map(_.toInt).toList
  stream.close()
  randomNumbers
}

此代码有效,并且等效synchronized keyword in Javasynchronized keyword in JavanextRandom()工作方式应该很明显:如果缓冲区为空,则用从服务器获取的50个随机数填充它。 最后,从缓冲区中获取并返回第一个值。 此代码有很多缺点,首先是从synchronized块开始。 每次通话的同步费用都很高,这似乎是过大的选择。 而且我们还没有进入集群,在该集群中,每个集群都必须维护一个活动连接,而不仅仅是使用一个JVM!

我们将从执行一个演员开始。 Actor基本上是一个扩展Actor特性并实现receive方法的类。 此方法负责接收和处理一条消息。 让我们重申一下我们已经说过的话:每个参与者每次最多只能处理一条消息,因此永远不会并发调用receive方法。 如果参与者已经在处理某些消息,则其余消息将保留在每个参与者专用的队列中。 由于有了这个严格的规则,我们可以避免在actor内部进行任何同步,而这始终是线程安全的。

case object RandomRequest

class RandomOrgBuffer extends Actor {

  val buffer = new Queue[Int]

  def receive = {
    case RandomRequest =>
      if(buffer.isEmpty) {
        buffer ++= fetchRandomNumbers(50)
      }
      println(buffer.dequeue())
  }
}

fetchRandomNumbers()方法保持不变。 由于actor一次只能处理一条消息,因此可以免费获得对random.org的单线程访问。 说到消息,在这种情况下, RandomRequest是我们的消息–空对象不传递任何信息,但其类型除外。 在Akka中,消息几乎总是使用case类或其他不可变类型实现的。 因此,如果我们想支持获取任意数量的随机数,则必须将其包括在消息中:

case class RandomRequest(howMany: Int)

class RandomOrgBuffer extends Actor with ActorLogging {

  val buffer = new Queue[Int]

  def receive = {
    case RandomRequest(howMany) =>
      if(buffer.isEmpty) {
        buffer ++= fetchRandomNumbers(50)
      }
      for(_ <- 1 to (howMany min 50)) {
        println(buffer.dequeue())
      }
  }

现在,我们应该尝试向新演员发送一些信息。 显然,我们不能只调用传递消息作为参数的receive方法。 首先,我们必须启动Akka平台,并要求演员参考。 以后,此参考首先用于使用有点违反直觉的方式发送消息! 方法,可以追溯到Erlang的日子:

object Bootstrap extends App {
  val system = ActorSystem("RandomOrgSystem")
  val randomOrgBuffer = system.actorOf(Props[RandomOrgBuffer], "buffer")

  randomOrgBuffer ! RandomRequest(10)  //sending a message

  system.shutdown()
}

运行该程序后,我们应该在控制台上看到10个随机数。 对这个简单的应用程序进行一些实验(完整的源代码可以在GitHub上找到request-response标签 )。 特别要注意的是,发送消息是非阻塞的,并且消息本身是在不同的线程中处理的(与JMS类似)。 尝试发送其他类型的消息并修复receive方法,以便它可以处理多种类型的消息。

到目前为止,我们的应用程序不是很有用。 我们想以某种方式访问​​我们的随机数,而不是将它们(异步!)打印到标准输出中。 您可能会猜到,由于只能通过异步消息传递来建立与参与者的通信(参与者无法“返回”结果,也不能将其放置在任何全局共享内存中)。 因此,演员将通过直接发送给我们(发给发送者)的回复消息将结果发送回去。 但这将是下一篇文章的一部分。

这是我最初发表在scala.net.pl上的文章“ Poznajemy Akka:pierwszy komunikat ”的翻译

参考: 您的第一条信息–Java和社区博客上从我们的JCG合作伙伴 Tomasz Nurkiewicz 发现Akka

翻译自: https://www.javacodegeeks.com/2012/10/your-first-message-discovering-akka.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值