Actors locais ou remotos ? Tanto faz !

Quem já trabalhou com RMI em Java sabe que hoje em dia é um processo altamente burocrático. Muitos desistem quando seguem um tutorial passo-a-passo e no final não funciona. Eu já havia feito programas que usavam RMI nos primórdios do Java e já achava, por um lado difícil de implementar, por outro lado, mais simples que a alternativa da época que era Corba. Quando precisei usar novamente, há uns dois anos atrás, descobri que estava ainda mais complicado. Quem quiser se aventurar, pode ler o Tutorial sobre RMI da Sun.

Dentre as burocracias, temos que rodar o rmiregistry para ser o registro de objetos distribuídos ou criar um programaticamente. Depois, temos que criar um stub dinamicamente, usar este stub para fazer o bind no registro, criar um SecurityManager, configurar um arquivo policy, criar uma interface e implementá-la, fazer lookup no cliente, parâmetros na execução, etc…

Seguindo a descoberta de Scala, passei pela seção de Actors e achei muito interessante, porém, a implementação que vi era local. Pensei então: Se eu fosse projetar em Scala uma solução de comunicação remota, iria usar o modelo de Actors para tal. Será que não pensaram assim ? Sim ! Exatamente ! E a burocracia ? Menor que essa, só adivinhando !

Para transformar a comunicação local dos Actors em remota, basta apenas colocar a porta TCP a ser usada pelo servidor, registrar-se e no cliente, fazer o lookup. Pronto ! Vejamos um hello world remoto:

Servidor:

package scalademo

import scala.actors.Actor._
import scala.actors.remote.RemoteActor._

object ServerActor {
  def main(args: Array[String]) {
    actor {
      alive(5000)
      register('Server, self)
      println("Server running..." )
      loop {
        react {
          case text => sender ! "Message |"
                          + text + "| was received ok"
        }
      }
    }
  }
}

Cliente:

package scalademo

import scala.actors.Actor._
import scala.actors.remote._
import scala.actors.remote.RemoteActor._

object ClientActor {
  def main(args: Array[String]) {
    actor {
      val server = select(Node("localhost", 5000), 'Server)
      println("Sending hello..." )
      server ! "Hello"
      receive {
        case text => println(text)
      }
    }
  }
}

Caso queiram ver um exemplo mais completo, recomendo este artigo. Para simplificar, usei objetos da classe String como transporte. Em uma aplicação mais completa, é conveniente usarmos case classes.

Assim como em Java, evito usar um repositório público de classes via HTTP para o classloader. A complicação adicional, que está presente em ambas linguagens, só se justifica quando o lado servidor pode receber objetos de classes não previstas originalmente. Normalmente as aplicações são fechadas, isto é, todas as classes já são conhecidas. Assim, é somente uma questão de empacotar as classes necessárias nos jars do servidor e do cliente.

Abaixo a burocracia ! Escalabilidade é fazer o simples fácil, e o complicado possível.

Alexei Barbosa de Aguiar