Хиты больше, чем общее количество запросов - PullRequest
0 голосов
/ 01 октября 2019

Итак, я начал изучать актеров Scala и Akka, Akka-Http. Я попытался реализовать простой счетчик посещений, используя Akka Http, который подсчитывает каждое попадание на странице localhost. Я использовал инструмент 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
  }
}

Прошу прощения за мои незрелые / любительские навыки кодирования. Я все еще учусь, и я знаю, что это связано с параллелизмом. Но я пока не могу найти решение. Пожалуйста, помогите.

edit # 1: Я тоже попробовал AtomicInteger. Это не помогло. edit # 2: я пробовал полный способ akka-http с ask и await тоже. это тоже не помогло.

1 Ответ

0 голосов
/ 04 октября 2019

С вашим кодом мало проблем.

Вы определяете класс дела final case class Inc(), но отправляете объект-компаньон actor1 ! Inc. Тем не менее, вы все равно сопоставляете объект-компаньон case Inc =>, и ваш код работает. Но так не должно быть.

Другая проблема, мы храним, модифицируем и извлекаем var number: Int = 0 за пределами границ актера. И я думаю, именно поэтому у вас есть ошибки. Актер должен изменить только внутреннее состояние.

Я изменил ваш код, введя шаблон запроса , чтобы можно было извлечь значение из субъекта.

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
  }
}
...