0

所以我开始学习 Scala 和 Akka actor,Akka-Http。我尝试使用 Akka Http 实现一个简单的点击计数器,它计算本地主机页面上的每次点击。我使用 wrk 工具运行 10 个线程和 100 个连接,之后计数和总请求数不匹配(见 wrk)。

这是我的代码:


object WebServer3 {

  var number: Int = 0


  final case class Inc()
  class ActorClass extends Actor with ActorLogging {

    def receive = {
      case Inc => number = number + 1
    }
  }


  def main(args: Array[String]) {
    implicit val system = ActorSystem("my-system")
    implicit val materializer = ActorMaterializer()
    implicit val executionContext = system.dispatcher

    val actor1 = system.actorOf(Props[ActorClass], "SimpleActor")
    val route =
      path("Counter") {

        get {
          actor1 ! Inc
         complete(HttpEntity(ContentTypes.`text/html(UTF-8)`, s"<h1>You visited $number times</h1>"))
        }
      }

    val bindingFuture = Http().bindAndHandle(route, "localhost", 8080)
    println(s"Server online at http://localhost:8080/\nPress RETURN to stop...")
    StdIn.readLine() // let it run until user presses return
    bindingFuture
      .flatMap(_.unbind()) // trigger unbinding from the port
      .onComplete(_ => system.terminate()) // and shutdown when done
  }
}

请原谅我不成熟/业余的编码技能。我还在学习,我知道这与并发有关。但我还找不到解决方案。请帮忙。

编辑#1:我也尝试过 AtomicInteger。那没有帮助。编辑#2:我也尝试了完整的 akka-http 方式,询问和等待。这也无济于事。

4

1 回答 1

1

您的代码几乎没有问题。

您正在定义一个案例类final case class Inc(),但您正在发送一个伴随对象actor1 ! Inc。但是,您仍然匹配伴随对象case Inc =>并且您的代码有效。但它不应该这样做。

其他问题,我们在var number: Int = 0actor边界之外存储、修改和检索。我认为这就是为什么你有错误的计数。Actor 只能更改内部状态。

我通过引入询问模式修改了您的代码,以便可以从参与者中检索值。

import akka.actor.{Actor, ActorLogging, ActorSystem, Props}
import akka.http.scaladsl.Http
import akka.http.scaladsl.model.{ContentTypes, HttpEntity}
import akka.http.scaladsl.server.Directives._
import akka.pattern.ask
import akka.stream.ActorMaterializer
import akka.util.Timeout

import scala.concurrent.duration._
import scala.io.StdIn

object WebServer3 {


  final case object IncAndGet //not a case class anymore

  class ActorClass extends Actor with ActorLogging {
    private var number: Int = 0 //inner state must be private and not accessible from outside of an actor

    def receive = {
      case IncAndGet =>
        number += 1
        context.sender() ! number // send current value back to sender
    }
  }

  def main(args: Array[String]) {
    implicit val system = ActorSystem("my-system")
    implicit val materializer = ActorMaterializer()
    implicit val executionContext = system.dispatcher
    implicit val timeout: Timeout = 2.seconds

    val actor1 = system.actorOf(Props[ActorClass], "SimpleActor")
    val route =
      path("counter") {
        get {
          onComplete((actor1 ? IncAndGet).mapTo[Int]) { number =>
            complete(
              HttpEntity(ContentTypes.`text/html(UTF-8)`,
                         s"<h1>You visited $number times</h1>"))
          }
        }
      }

    val bindingFuture = Http().bindAndHandle(route, "localhost", 8080)
    println(s"Server online at http://localhost:8080/\nPress RETURN to stop...")
    StdIn.readLine() // let it run until user presses return
    val _ = bindingFuture
      .flatMap(_.unbind()) // trigger unbinding from the port
  }
}
于 2019-10-04T14:34:44.830 回答