2014-11-28 2 views
1

У меня есть следующие реализации для класса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 или гораздо лучшему решению, где я могу назвать функцию?

+0

Непонятно, как именно вы его вызываете, можете ли вы предоставить код? Проблема может быть в другом месте, потому что, по-видимому, происходит сериализация. –

+0

Думаю, вам нужно пересмотреть свой дизайн. Ваш пользовательский класс НЕ является актером, но содержит «изменчивое» состояние, и вы передаете ему ActorRef. Я не пережил остаток вашего кода, но если вы можете объяснить, почему вы не являетесь Актером. –

+0

Я согласен с @SoumyaSimanta - дизайн мне не очень понятен. Я думаю, вы можете упростить его, используя существующие функции Akka. Однако начните с фиксации этой строки: 'context.actorOf (Props (new Client (i: Int))). Вы не должны создавать участников с такими конструкторами. См. Этот документ: http://doc.akka.io/docs/akka/snapshot/scala/actors.html («Опасные варианты»). Возможно, это вызывает проблему. Во всяком случае, попытайтесь подтолкнуть изменчивое состояние к актеру, а не к сообщению. Также неясно, где обрабатывается «RegisterClients». –

ответ

0

Вышеприведенный код является примером плохой практики для того, чтобы обернуть актера внутри класса. Убедитесь, что модель актера используется вместо того, чтобы приближаться к ней с объектно-ориентированной точки зрения.

В приведенном выше сценарии, структура будет определена в:

Broker -> UserActor -> User 

Брокер имеет карту, и это может быть использовано для решения любых зависимостей с ним. Модель гарантирует, что модель актера отделена от логики объекта. Reference

Другой альтернативой является использование Futures. This link - отличное место для начала.

Смежные вопросы