Tenho uma classe que é um Ator:
class Client(server: Server, systemActor: ActorRef) extends Actor {
...
}
Tenho uma lista que gerencia clientes conectados. O ator que gera os atores clientes observa uma lista de "servidores" no banco de dados e gera uma conexão de cliente para cada um, assim:
private val clientList = mutable.ArrayBuffer.empty[Client]
class RegistrationWatcherActor(val systemActor: ActorSystem) extends Actor with Timers {
implicit val system: ActorSystem = context.system
timers.startSingleTimer(TickKey, FirstTick, FiniteDuration(1, TimeUnit.SECONDS)) // wait 10 seconds to give database time to initiate
private def checkRegistrations(): Unit = {
val database = DatabaseUtil.getInstance
val serversOutgoing: Seq[Server] = database.getAll[Server](classOf[Server])
for (server <- serversOutgoing) {
val client = clientList.find{ client => client.server == server }
if (client.isEmpty) { // client for this server was not found, so create one
}
}
}
def receive: Receive = {
case FirstTick =>
timers.startTimerWithFixedDelay(TickKey, Tick, FiniteDuration(1, TimeUnit.SECONDS))
case Tick =>
checkRegistrations()
}
}
O Problema
Na verificação, if (client.isEmpty)
gostaria de criar uma instância de Client
, gerá-la como um ator e colocar a referência na lista para que ela não seja criada novamente. Como você pode ver, meu método de identificar se o cliente foi criado ou não é verificar se ele está serverID
armazenado no banco de dados.
O ideal é fazer:
val client = new Client(server, systemActor)
systemActor.actorOf(Props(client), server.serverID)
clientList.append(client)
Alguma sugestão, por favor?
Alternativa
Percebi que posso fazer clientList
uma lista de String
e armazenar o serverID
aqui. No entanto, eventualmente gostaria que o RegistrationWatcher
pudesse ter controle sobre aspectos do cliente, então uma referência ao Client
objeto seria preferível.
Como princípio geral, parte do ponto do modelo de ator é que os atores são autônomos. Querer
RegistrationWatcherActor
"ter controle" sobre aspectos doClient
comportamento/estado do é, portanto, um sinal de que a decomposição de responsabilidades para os atores está errada.Se houver algum estado que você realmente precise compartilhar entre atores (ou entre um ator e o mundo externo), observe que a ilusão de thread único dentro dos atores está sendo quebrada e você está basicamente de volta ao mundo da simultaneidade multithread (por exemplo, precisando usar
synchronized
blocos,volatile
s, coleções simultâneas ou atomics): mesmo se você tivesse uma referência aoClient
, não há garantia real de que chamar métodos noClient
doRegistrationWatcherActor
seria visível até a próxima vez que oRegistrationWatcherActor
enviasse uma mensagem para oClient
e essa mensagem estivesse prestes a ser processada. Provavelmente é melhor nessa situação ter o estado compartilhado encapsulado em umAtomicReference
que é passado para oClient
e salvar oAtomicReference
noRegistrationWatcherActor
.