O Que É RMI?

A ferramente da linguagem Java para chamadas distribuídas

Introdução a Remote Method Invocation

Apesar de RMI ser uma ferramenta nativamente de Java, a noção de chamadas distribuídas a objetos foi originalmente pensada no middleware CORBA, uma arquitetura dos primórdios dos anos 1990. A ideia principal, que perdurou ao longo das décadas, é poder invocar métodos cujas implementações se encontram em outra máquina, efetivamente terceirizando o processamento e compartilhando os recursos disponíveis. Há diversos cuidados que devem ser levados em conta, como, por exemplo, o modo de enviar os parâmetros à máquina de destino. O processo de empacotar tais dados ganhou o nome de marshalling, sendo bastante importante na troca de informações.

Interfaces em Java

Java é uma linguagem fortemente tipada orientada a objetos com uma sintaxe inspirada em C, possuindo uma interessante separação entre classes e interfaces, algo não oferecido pela maioria das linguagens de programação. Em Java, uma interface descreve um conjunto de métodos para um objeto, mas não fornece nenhuma implementação. Uma classe, por outro lado, pode descrever e implementar métodos, além de nos permitir incluir campos para guardar dados, uma funcionalidade não oferecida por interfaces.

Este distanciamento da noção de classes e interfaces nos permite usufruir de um nível de abstração dificilmente observado em outros ambientes, sendo um convite para a implementação de invocações distribuídas. Já é possível perceber que, para este leque de aplicações no qual estamos interessados, uma interface nos possibilitará descrever de forma bastante abstrata métodos para objetos numa máquina A, enquanto as implementações ficarão a cargo de uma outra máquina B responsável por fornecer seus serviços a A.

De forma ainda mais generalista, uma interface pode ser pensada como um cartão de visitas que a máquina B (o servidor) distribuirá para todas as outras máquinas (os clientes) interessadas em utilisar seus serviços que, ao invocados, serão inteiramente realizados por B mediante o envio dos parâmetros para o trabalho desejado. Ao final, B responderá com seu resultado e o serviço terá sido terceirizado com sucesso.

O exemplo abaixo ilustra o caso de um Media Player, que pode ser pensado como uma interface para gerenciar diversos serviços oferecidos pelas possíveis mídias suportadas pela aplicação. Apertar "Play" em uma Media Player não irá reproduzir uma música ou um vídeo do próprio Media Player, mas sim de algum dispositivo previamente conectado a ele, que implementará esta função, nos permitindo ser capazes de trocar a implementação da função "Play" dependendo da mídia a qual tivermos de vontade de assistir em um dado momento:


O Modelo de Objetos Distribuídos

No artigo que deu origem ao RMI (traduzido abaixo), entitulado "A Distributed Object Model for the Java System", os autores Ann Wollrath, Roger Riggs e Jim Waldo descrevem com precisão os fundamentos do modelo sendo proposto:

"Em nosso modelo, um objeto remoto é aquele cujos métodos podem ser acessados de um outro espaço de endereçamento e, potencialmente, de uma outra máquina. Um objeto deste tipo é descrito por uma interface remota, que nada mais é do que uma interface (em Java) que declara os métodos de um objeto remoto. Portanto, Remote Method Invocation (ou RMI) é a ação de invocar um método (de uma interface remota) em um objeto remoto. Mais importante ainda, uma invocação de método em um objeto remoto possui a mesma sintaxe que uma invocação em um objeto local."

Com esta definição em mãos, podemos começar a pensar, ainda utilizando um nível de abstração elevado, em como os parâmetros dos métodos serão passados às invocações remotas. Tendo em vista que os espaços de endereçamento de cada processo (cliente e servidor) são fundamentalmente distintos, todos os parâmetros serão enviados e recebidos por cópia, e não referência. Assim, se enviarmos um outro objeto como parâmetro ao servidor, existirão duas cópias deste objeto em dois locais distintos. Uma operação realizada a este parâmetro no lado do servidor não irá alterar o objeto original no lado do cliente. Caso haja interesse, será necessário enviar a versão alterada de volta ao cliente para este decidir o que fazer com esta cópia.

Semântica

No contexto de chamadas distribuídas, é comum que queiramos uma certa consistência em nossos sistemas. Para isso, é necessário adotar um modelo com uma dada semântica. Uma ideia muito básica, por exemplo, é a semântica "talvez": se implementarmos algo do tipo, nosso sistema não se preocupará com possíveis falhas e, consequentemente, nossa chamada terá sido talvez executada no servidor. O importante de ser entendido é que, em consequência do modelo que adotamos, não há garantias sobre o sucesso de nossas chamadas e nem sobre quantas vezes ela terá sido executada no destino.

O Java RMI, por sua vez, implementa uma semântica chamada "no-máximo-uma-vez". Isto quer dizer que, quando uma invocação de um método remoto é feito, o invocador acaba com uma das seguintes respostas:

  • um resultado, o que garantirá que o método foi executado exatamente uma vez no servidor;
  • ou uma exceção, informando que nenhum resultado foi recebido. Neste caso, o método terá sido executado no servidor ou uma vez ou nenhuma vez.

O segundo caso, em que uma falha ocorre, não nos dá nenhuma informação sobre o que de fato aconteceu ou sobre quando exatamente o erro foi encontrado. É graças à garantia da semântica "no-máximo-uma-vez" que podemos, por exemplo, ter a certeza de que nossa invocação jamais terá sido executada duas vezes por acidente no destino. Modelos como esse ajudam a contornar possíveis erros, evitando que o sistema como um todo acabe tendo que parar.