Как да получите асинхронен SNMPv2 в SCALA с snmp4j

Запитвам SNMP и искам да изпълня обратно извикване, когато извличането на данни приключи. За целта използвам следния код:

 def treeWalk(rootOid: String)(callback: (Seq[Tuple2[OID, Variable]]) => Unit) {//{{{
    val transport = new DefaultUdpTransportMapping
    val snmp = new Snmp(transport)
    transport.listen

    val target = new CommunityTarget
    target.setCommunity(new OctetString(community))
    target.setAddress(GenericAddress.parse("udp:" + address + "/161"))
    target.setRetries(retries)
    target.setTimeout(timeout)
    target.setVersion(SnmpConstants.version2c)

    val treeUtil = new TreeUtils(snmp, new DefaultPDUFactory)
    var variables: Seq[VariableBinding] = List()
    var _isFinished = false

    val listener = new TreeListener {
        def next(event: TreeEvent) = {
            println("NEXT")
            val vars = event.getVariableBindings
            if(vars != null)
               variables ++= vars
            true
        }
        def isFinished() = _isFinished
        def finished(event: TreeEvent) {
            val vars = event.getVariableBindings
            println("DONE")
            if(vars != null)
              variables ++= vars
            _isFinished = true
            snmp.close
            callback(variables.map(v => (v.getOid, v.getVariable)))
        }
  }
  treeUtil.walk(target, Array(new OID(rootOid)), null, listener)
}

Не получавам грешки или изключения, но никога не виждам редовете за печат в next() и finished() и обратното извикване не се изпълнява. Какво правя погрешно, как трябва да закача слушателя на събитието към него?

PS: Едноредовата синхронна версия връща данни добре...

Използвам Scala 2.11.6 и SNMP4J 2.3


person Todor Markov    schedule 18.03.2015    source източник
comment
Не съм запознат със SNMP, но ако е като повечето обратни извиквания, които съм виждал, тогава къде чакате да дадете време на повикването да завърши, за да може обратното извикване да бъде изпълнено?   -  person childofsoong    schedule 18.03.2015
comment
Извиквам метода, който виждате тук с println като функция за обратно извикване. Според моето разбиране обратното извикване трябва да бъде следващият запис в стека за повиквания след метода. Така че това би означавало, че няма включено чакане, методът ще го извика след завършване. Правилно?   -  person Todor Markov    schedule 18.03.2015
comment
В ситуация на блокиране, да. Въпреки това, когато имате асинхронно повикване, предоставено с обратно извикване, програмата може да прекрати, преди асинхронната операция да може да завърши. Можете да направите това или като я накарате да заспи, или като преструктурирате програмата си по начина, показан в stackoverflow.com/questions/16256279/wait-for-several-futures, така че да чака методът да се върне.   -  person childofsoong    schedule 18.03.2015
comment
По принцип основната нишка стартира асинхронната процедура и след това изчерпва инструкциите, така че тя прекратява и това причинява прекратяването на всички нишки на вашата програма, включително всичко, което се изпълнява от страната, която обработва асинхронното повикване.   -  person childofsoong    schedule 18.03.2015
comment
Всъщност вие сте прав, най-бързото решение беше Thread.sleep. В бъдеще ще го прилагам по-нататък.   -  person Todor Markov    schedule 19.03.2015
comment
Добре, отговорих въз основа на това - радвам се да знам, че това е всичко!   -  person childofsoong    schedule 19.03.2015


Отговори (1)


Когато работите с обратни извиквания, трябва да вземете под внимание, че основната нишка може лесно да завърши преди вашето асинхронно извикване, което води до прекратяване на основната нишка (и като разширение на останалата част от програмата), преди асинхронната задача да бъде завършена и обратното извикване да бъде достигнато. Лесният начин да направите това е да извикате Thread.sleep() и да поставите основната си нишка на пауза за определен период от време, за да изчакате. Можете обаче да подобрите това. Една от възможностите е да имате фрагмент от код като този:

var callbackComplete = false
someAsyncMethodWithCallback(callback)
while (!callbackComplete){
    Thread.sleep(100)
}

След това всичко, което трябва да направите, е вашето обратно извикване да включва ред, казващ callbackComplete = true и това ще го накара да прекрати.

Това обаче далеч не е идеално (но добре, ако просто се нуждаете от бързо временно решение). В идеалния случай трябва да прочетете страницата Фючърси и обещания в документацията на Scala . Той подробно описва как да се справят правилно с фючърси, както и как да се справят с неуспехи, и трябва да се счита за правилния ресурс за справяне с асинхронни повиквания.

person childofsoong    schedule 19.03.2015
comment
Благодаря, използвах дълъг режим на заспиване, за да го тествам, но би трябвало да премина към фючърси и обещания, тъй като е твърде критично за производителността, за да има фиксирано количество за заспиване. - person Todor Markov; 20.03.2015