使用Ask模式导致为每个请求创建default-akka.actor.default-dispatcher线程

时间:2016-06-21 20:18:10

标签: scala akka

信息:您可能希望在介绍后直接跳到编辑4。

最近我写了一个简单的scala服务器应用程序。该应用程序主要将传入的数据写入数据库或检索它。这是我的第一个scala-akka应用程序。 当我将应用程序部署到我的服务器后,它在大约一天后失败了。我从digitalocean提供的简单统计数据中了解到,如果我从服务器请求数据,那么CPU的使用率会以线性方式上升。如果我没有从服务器请求任何东西,那么CPU使用率几乎是恒定的,但并没有从之前的状态下降。 我将应用程序连接到visualvm并看到如果我不对app(I)做任何事情,线程数是常数,或者如果我以类似锯子的方式将内容发送到服务器(II),则增长。 enter image description here 在线程数和CPU使用率之间有一个明显的颜色,这使得sens。 当我检查线程选项卡时,我看到大多数线程都是default-akka.actor.default-dispatcher个线程 enter image description here 他们似乎也做不了多少。

什么可能导致这类问题?我该如何解决? 广告。编辑4:我认为我找到了问题的根源,但我仍然不明白为什么会发生这种情况,以及我应该如何解决它。

PS:我必须承认屏幕截图不是来自失败的应用程序。我没有原来的失败。然而,这个程序和失败的程序之间的唯一区别是application.conf我添加了:

actor {
    default-dispatcher {
      fork-join-executor {
        # Settings this to 1 instead of 3 seems to improve performance.
        parallelism-factor = 2.0

        parallelism-max = 24

        task-peeking-mode = FIFO
      }
    }
  }

这似乎减缓了线程数量上升的速度,但没有解决问题。

编辑:WriterActor用法的片段(RestApi

trait RestApi extends CassandraCluster {
  import models._
  import cassandraDB.{WriterActor, ReaderActor}
  implicit def system: ActorSystem
  implicit def materializer: ActorMaterializer
  implicit def ec: ExecutionContext

  implicit val timeout = Timeout(20 seconds)

  val cassandraWriterWorker = system.actorOf(Props(new WriterActor(cluster)), "cassandra-writer-actor")
  val cassandraReaderWorker = system.actorOf(Props(new ReaderActor(cluster)), "cassandra-reader-actor")
  ...
  def cassandraReaderCall(message: Any): ToResponseMarshallable = message match {
    //...
    case message: GetActiveContactsByPhoneNumber => (cassandraReaderWorker ? message)(2 seconds).mapTo[Vector[String]].map(result => Json.obj("active_contacts" -> result))
    case _ => StatusCodes.BadRequest
  }

  def confirmedWriterCall(message: Any) = {
    (cassandraWriterWorker ? message).mapTo[Boolean].map(result => result)
  }

  val apiKeyStringV1 = "test123"

  val route =
    ...
        path("contacts") {
          parameter('apikey ! apiKeyStringV1) {
            post {
              entity(as[Contacts]){ contact: Contacts =>
                cassandraWriterWorker ! contact
                complete(StatusCodes.OK)
              }
            } ~
            get {
              parameter('phonenumber) { phoneNumber: String =>
                complete(cassandraReaderCall(GetActiveContactsByPhoneNumber(phoneNumber)))
              }
            }
          }
        } ~
        } ~
        path("log"/ "gps") {
          parameter('apikey ! apiKeyStringV1) {
            (post & entity(as[GpsLog])) { gpsLog =>
              cassandraWriterWorker ! gpsLog
              complete(StatusCodes.OK)
            }
          }
        }
      }
    }
}

编辑2:Writer Worker相关代码。 我没有发布所有方法,因为它们基本相同。但是here你可以找到整个文件

import java.util.UUID

import akka.actor.Actor
import com.datastax.driver.core.Cluster
import models._

class WriterActor(cluster: Cluster) extends Actor{
  import scala.collection.JavaConversions._

  val session = cluster.connect(Keyspaces.akkaCassandra)

  // ... other inserts
  val insertGpsLog = session.prepare("INSERT INTO gps_logs(id, phone_number, lat, long, time) VALUES (?,?,?,?,?);")

  // ...
  def insertGpsLog(phoneNumber: String, locWithTime: LocationWithTime): Unit =
    session.executeAsync(insertGpsLog.bind(UUID.randomUUID().toString, phoneNumber, new java.lang.Double(locWithTime.location.lat),
      new java.lang.Double(locWithTime.location.long), new java.lang.Long(locWithTime.time)))

  def receive: Receive = {
    // ...
    case gpsLog: GpsLog => gpsLog.locationWithTimeLog.foreach(locWithTime => insertGpsLog(gpsLog.phoneNumber, locWithTime))
  }
}

编辑3.错过过多线程使用的诊断。

我害怕错过诊断问题的根源。接下来,我在写完后添加了数据请求并忘了它。当我删除它时,线程数量停止增长。所以这是犯错的最可能的地方。我更新了使用ReaderActor的特征,并在下面添加了ReaderActor的相关代码。

object ReaderActor {
  // ...
  case class GetActiveContactsByPhoneNumber(phoneNumber: String)
}

class ReaderActor(cluster: Cluster) extends Actor {
  import models._
  import ReaderActor._
  import akka.pattern.pipe
  import scala.collection.JavaConversions._
  import cassandra.resultset._
  import context.dispatcher

  val session = cluster.connect(Keyspaces.akkaCassandra)

  def buildActiveContactByPhoneNumberResponse(r: Row): String = {
    val phoneNumber = r.getString(ContactsKeys.phoneNumber)
    return phoneNumber
  }

  def buildSubSelectContactsList(r: Row): java.util.List[String] = {
    val phoneNumber = r.getSet(ContactsKeys.contacts, classOf[String])
    return phoneNumber.toList
  }

  def receive: Receive = {
    //...
    case GetActiveContactsByPhoneNumber(phoneNumber: String) =>
      val subQuery = QueryBuilder.select(ContactsKeys.contacts).
        from(Keyspaces.akkaCassandra, ColumnFamilies.contact).
        where(QueryBuilder.eq(ContactsKeys.phoneNumber, phoneNumber))
      def queryActiveUsers(phoneNumbers: java.util.List[String]) = QueryBuilder.select(ContactsKeys.phoneNumber).
        from(Keyspaces.akkaCassandra, ColumnFamilies.contact).
        where(QueryBuilder.in(ContactsKeys.phoneNumber, phoneNumbers))
      session.execute(subQuery) map((row: Row) => session.executeAsync(queryActiveUsers(buildSubSelectContactsList(row))) map(_.all().map(buildActiveContactByPhoneNumberResponse).toVector) pipeTo sender)
    //...
  }
}

编辑4 我在本地运行代码,控制所有请求。当没有请求时,正在运行的线程的数量在一定数量附近交替,但是没有上升或下降的趋势。 我提出了各种要求,看看会发生什么变化。 下面张贴的图片显示了几个州。

  • 我 - 还没有请求。线程数44-45
  • II - 向ReaderActor发出请求后。线程数46-47
  • III - 向ReaderActor发出请求后。线程数48-49
  • IV - 向ReaderActor发出请求后。线程数50-51
  • V - 向WrtierActor发出请求后。线程数51-52(但没问题,请注意启动了守护程序线程)
  • VI - 向WriterActor发出请求后。线程数51-52(常数)
  • VII - 在向ReaderActor发出请求之后(但是前三个是不同的资源)。线程数53-54

local_test_num_of_threads

所以每次我们从数据库中读取时(无论使用多少次executeAsync调用)都会发生2个额外的线程。读取和写入调用之间的唯一区别是,一个使用ask模式而另一个不使用。我通过改变路线来检查它:

            get {
                  parameter('phonenumber) { phoneNumber: String =>
                    complete(cassandraReaderCall(GetActiveContactsByPhoneNumber(phoneNumber)))
                  }
                }

        get {
              parameter('phonenumber) { phoneNumber: String =>
                cassandraReaderWorker ! GetActiveContactsByPhoneNumber(phoneNumber)
                complete(StatusCodes.OK)
              }
            }

现在显然没有得到任何结果,但也没有产生这些线程。 所以答案似乎在于问题模式。

我希望有人可以提供一个答案,说明为什么会发生这种情况以及如何解决这个问题?

0 个答案:

没有答案