У меня есть следующие реализации для классаScala stackoverflowexception - циклическая зависимость Possibe?
class User(identifier : Int, actor : ActorRef) extends Serializable {
var userName : String = Random.alphanumeric.take(4 + Random.nextInt(12)).mkString
var msgRate : Int = 0
var followers : MutableList[User] = new MutableList[User]()
var messageQueue = new LinkedBlockingQueue[String](Messages.maxBufferSize)
override def equals(o : Any) = o match {
case that : User => that.userName.equals(this.userName)
case _ => false
}
override def hashCode = identifier.hashCode
def getRecentMessages(n : Int) : List[String] = {
var msgList : List[String] = List.empty[String]
msgList = messageQueue.toArray().toList.asInstanceOf[List[String]]
return msgList
}
def isFollowing(user : User) : Boolean = {
user.getFollowers().contains(this)
}
def isFollowed(user : User) : Boolean = {
followers.contains(user)
}
def getFollowers() : MutableList[User] = {
return followers
}
def addFollower(follower : User) {
followers += follower
}
}
Когда я бегу за меньший набор актеров, добавив толкатель не вызывает никаких проблем, и код работает нормально. Однако для большого числа участников возникает вопрос:
java.lang.StackOverflowError
at akka.actor.SerializedActorRef$.apply(ActorRef.scala:420)
at akka.actor.LocalActorRef.writeReplace(ActorRef.scala:389)
at sun.reflect.GeneratedMethodAccessor1.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at java.io.ObjectStreamClass.invokeWriteReplace(ObjectStreamClass.java:1075)
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1134)
at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1547)
at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1508)
at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1431)
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1177)
at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1547)
at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1508)
at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1431)
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1177)
at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1547)
at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1508)
at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1431)
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1177)
Является ли это случай использования одного экземпляра пользователя в классе в виде последователей? Есть ли разрешение этой проблемы?
EDIT: Включая больше кода в соответствии с запросом. Извините за очень большую базу кода. Позвольте мне кратко рассказать об этом. Я работаю над симулятором сообщений, похожим на твиттер, где мне приходится управлять сообщениями на основе пользовательских тегов. Клиент случайным образом генерирует пользовательскую базу, отправляет сообщения со скоростью, а отправитель управляет данными.
Первоначальное рукопожатие включает отправку всей информации о клиенте из Interactor(). Сервер подтверждает каждый клиент, а затем это запланировано регулярно. Проблема возникает при отправке всей информации о клиенте, которая приводит к исключению stackoverflow. В коде проблема возникает в конце Init в Interactor()
Вот код:
object ClientApp extends App {
val system = ActorSystem("TwitterClientActor", ConfigFactory.load("applicationClient.conf"))
val sActor = system.actorFor("akka.tcp://[email protected]" + ipAddr + "/user/Server")
val serverVector = Vector.fill(Messages.nServers)(sActor)
val serverActor = system.actorOf(Props.empty.withRouter(RoundRobinRouter(routees = serverVector)), "serverRouter")
val interActor = system.actorOf(Props(new Interactor()))
var nRequests : Int = 0
val startTime = java.lang.System.currentTimeMillis()
interActor ! Init
}
// Intermediate manager system
class Interactor() extends Actor {
// Member definitions
import actorSys.dispatcher
// User list
for (i <- 0 to clientList.length - 1)
clientList(i) = new User(i, context.actorOf(Props(new Client(i : Int))))
readFollowersStats(clientList.length)
def receive = {
// Send request to users
case Init =>
for (curUser <- clientList)
serverActor ! RegisterClients(curUser)
// ISSUE IMMEDIATELY AFTER THIS
// Schedule after request
case ScheduleClient(identifier) =>
if (!limitReached) {
val curUser = clientList(identifier)
val cancellable = actorSys.scheduler.schedule(0.milliseconds, curUser.getMsgRate.milliseconds)(sendMsg(curUser))
cancelMap += (curUser -> cancellable)
}
case ClientCompleted =>
nCompleted += 1
if (nCompleted == clientList.length) {
serverActor ! Broadcast(PoisonPill)
context.system.shutdown()
}
}
def sendMsg(curUser : User) = {
nMessages.incrementAndGet()
if (nMessages.get() == Messages.msgLimit) {
println("Limit reached!")
limitReached = true
for (cancellable <- cancelMap.values)
cancellable.cancel()
}
else if (nMessages.get() < Messages.msgLimit) {
println(nMessages)
val curSec = java.lang.System.currentTimeMillis()
val curTime = ((curSec - ClientApp.startTime).toDouble)/1000
if (curTime >= Messages.peakStart && curTime < Messages.peakEnd) {
for (i <- 0 to Messages.peakScale) {
var rndTweet = randomTweet(curUser)
curUser.getReference() ! Tweet(rndTweet)
}
nMessages.addAndGet(Messages.peakScale - 1)
}
else {
var rndTweet = randomTweet(curUser)
//println(curUser + " ---> " + rndTweet)
curUser.getReference() ! Tweet(rndTweet)
}
}
}
def randomTweet(curUser : User) : String = {
// Return some random string
}
def readFollowersStats(usersCount : Int) {
// Read the file stats of the format, min-max percentage
while(file is not empty)
FollowersGeneration(usersCount, minFollowers.toInt, maxFollowers.toInt, percentage.toDouble)
}
}
def FollowersGeneration(usersCount : Int, minFollowers : Int, maxFollowers : Int, followersPercentage : Double) {
var noOfFollowers : Int = 0
var users : Double = (followersPercentage/100) * usersCount
var temp : Int = users.toInt
for (i <- 0 until temp) {
if (minFollowers < usersCount) {
// Random follower assignment...
// CODE ACCESSES FOLLOWERS HERE!!!
if (!user.isFollowed(clientList(id)))
user.addFollower(clientList(id))
}
}
}
}
class Client(identifier : Int) extends Actor {
var serverActor = ClientApp.serverActor
def receive = {
case "ACK" =>
println("Client " + identifier + " activated")
ClientApp.interActor ! ScheduleClient(identifier)
case Tweet(tweet) =>
serverActor ! Tweet(tweet)
// Other functions
}
}
EDIT2: Объяснение о прецеденте
Это является клиент-сервер модель. Клиент: Объект-инициатор создает класс брокера. Класс брокера создает список участников пользователя (в этом случае пользователь класса) и устанавливает отношения между разными пользователями, то есть назначает случайных последователей, скорость и другие свойства. Теперь весь список пользователей отправляется на сервер, где сервер активирует отдельные клиенты для начала обмена сообщениями. Клиент теперь отправляет случайные сообщения на сервер, для которого сервер обрабатывает их.
Первоначальный подход состоял в том, чтобы использовать класс User, как указано выше, а затем сохранить actorRef как член в классе и отправить его пользователю. Это была проблема, и я изменил список пользователей как класс актера. Я должен генерировать последователей и отправлять их на сервер. Я передал класс брокера, чтобы добавить последователей, используя сообщения. Теперь проблема возникает из программной перспективы, когда на стороне сервера мне приходится обращаться к подписчикам Актера пользователя. Я могу либо отправить сообщение с запросом пользователя '!' или использовать '?' чтобы получить фьючерсы. Это проблема, когда это замедлит возможности обработки сервера. Существуют ли более элегантные подходы, когда я могу получить доступ к члену actorRef или гораздо лучшему решению, где я могу назвать функцию?
Непонятно, как именно вы его вызываете, можете ли вы предоставить код? Проблема может быть в другом месте, потому что, по-видимому, происходит сериализация. –
Думаю, вам нужно пересмотреть свой дизайн. Ваш пользовательский класс НЕ является актером, но содержит «изменчивое» состояние, и вы передаете ему ActorRef. Я не пережил остаток вашего кода, но если вы можете объяснить, почему вы не являетесь Актером. –
Я согласен с @SoumyaSimanta - дизайн мне не очень понятен. Я думаю, вы можете упростить его, используя существующие функции Akka. Однако начните с фиксации этой строки: 'context.actorOf (Props (new Client (i: Int))). Вы не должны создавать участников с такими конструкторами. См. Этот документ: http://doc.akka.io/docs/akka/snapshot/scala/actors.html («Опасные варианты»). Возможно, это вызывает проблему. Во всяком случае, попытайтесь подтолкнуть изменчивое состояние к актеру, а не к сообщению. Также неясно, где обрабатывается «RegisterClients». –