John Squirrels
Nível 41
San Francisco

Java RMI

Publicado no grupo Random-PT
Oi! Hoje vamos considerar um tópico bastante interessante: Java RMI. Isso significa Invocação de Método Remoto. Você pode usar o RMI para permitir que dois programas se comuniquem, mesmo que estejam em computadores diferentes. Isso soa legal? :) E não é tão difícil de fazer! Na lição de hoje, analisaremos os elementos da interação RMI e descobriremos como configurá-la. A primeira coisa que precisamos é de um cliente e um servidor. Não precisamos realmente mergulhar fundo na terminologia do computador. Quando se trata de RMI, esses são apenas dois programas. Um deles incluirá um objeto e o outro chamará métodos nesse objeto. Chamar métodos de um objeto que existe em um programa diferente — isso é algo que ainda não fizemos! É hora de tentar! :) Para evitar ficar atolado, vamos' Vamos manter nosso programa simples. Em geral, um servidor realiza alguns cálculos solicitados por um cliente. E assim será conosco. Nosso servidor será um simples programa de calculadora. Terá apenas um método:multiplique() . Ele multiplicará dois números enviados a ele pelo programa cliente e retornará o resultado. Primeiro de tudo, precisamos de uma interface:

import java.rmi.Remote;
import java.rmi.RemoteException;

public interface Calculator extends Remote {

   int multiply(int x, int y) throws RemoteException;
}
Por que precisamos de uma interface? Porque o RMI depende da criação de proxies, que você estudou nas lições anteriores . Como você provavelmente se lembra, trabalhamos com proxies por meio de interfaces, não de classes. Existem 2 requisitos importantes para nossa interface!
  1. Deve estender a interface remota.
  2. Todos os seus métodos devem gerar uma RemoteException (o IDE não fará isso automaticamente — você precisa adicionar isso manualmente!).
Agora precisamos criar uma classe de servidor que implemente nossa interface Calculadora . RMI na prática - 2Aqui também tudo é bem simples:

import java.rmi.RemoteException;

public class RemoteCalculationServer implements Calculator {

   @Override
   public int multiply(int x, int y) throws RemoteException {
       return x*y;
   }

}
Não há realmente nada para comentar aqui :) Agora precisamos escrever um programa de servidor que irá configurar e executar nosso objeto calculadora. Isso parecerá assim:

import java.rmi.AlreadyBoundException;
import java.rmi.Remote;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject;

public class ServerMain {

   public static final String UNIQUE_BINDING_NAME = "server.calculator";

   public static void main(String[] args) throws RemoteException, AlreadyBoundException, InterruptedException {

       final RemoteCalculationServer server = new RemoteCalculationServer();

       final Registry registry = LocateRegistry.createRegistry(2732);

       Remote stub = UnicastRemoteObject.exportObject(server, 0);
       registry.bind(UNIQUE_BINDING_NAME, stub);

       Thread.sleep(Integer.MAX_VALUE);

   }
}
Vamos descobrir isso :) Na primeira linha, declaramos alguma variável String:

public static final String UNIQUE_BINDING_NAME = "server.calculator";
Essa string é o nome exclusivo do objeto remoto. Nosso programa cliente usa esse nome para encontrar nosso servidor: você verá isso mais tarde. Em seguida, criamos nosso objeto calculadora:

final RemoteCalculationServer server = new RemoteCalculationServer();
Tudo está claro aqui. O que vem a seguir é mais interessante:

final Registry registry = LocateRegistry.createRegistry(2732);
Este objeto de Registro é um registro de objetos remotos. São objetos que outros programas podem acessar remotamente :) Passamos o número 2732 para o método LocateRegistry.createRegistry() . Este é o número da porta — um número exclusivo que outros programas usarão para localizar nosso registro de objeto (novamente, você verá isso abaixo). Seguindo em frente... Vamos ver o que acontece na próxima linha:

Remote stub = UnicastRemoteObject.exportObject(server, 0);
Criamos um esboço nesta linha. Um stub encapsula toda a chamada remota. Você pode considerar este o elemento mais importante do RMI. O que isso faz?
  1. Ele recebe todas as informações sobre uma chamada remota de algum método.
  2. Se o método tiver parâmetros, o stub irá desserializá-los. Preste atenção a este ponto! Os argumentos que você passar para os métodos chamados remotamente devem ser serializáveis ​​(afinal, eles serão transmitidos pela rede). Isso não é problema para nós — estamos apenas transmitindo números. Mas se você estiver transmitindo objetos, não se esqueça desse requisito!
  3. Depois disso, o stub chama o método desejado.
Passamos nosso objeto de servidor calculadora para o método UnicastRemoteObject.exportObject() . É assim que tornamos possível chamar remotamente seus métodos. Resta apenas uma coisa a fazer:

registry.bind(UNIQUE_BINDING_NAME, stub);
Nós "registramos" nosso stub no registro de objeto remoto sob o nome que criamos no início. Agora o cliente poderá encontrá-lo! Talvez você tenha notado que colocamos o thread principal do programa para dormir no final:

Thread.sleep(Integer.MAX_VALUE);
Só precisamos que o servidor funcione por um longo tempo. No IDE, lançaremos simultaneamente dois métodos main() : primeiro, o método main() do servidor (na classe ServerMain , que já escrevemos) e, segundo, o método main() do cliente (na classe ClientMain , que escreveremos abaixo). É importante que o programa do servidor não seja encerrado enquanto iniciamos o cliente, então apenas o colocamos para dormir por um longo tempo. De qualquer forma, ele continuará rodando :) Agora podemos rodar o método main() do nosso servidor . Deixe-o rodar e espere o programa cliente chamar algum método :) Agora vamos escrever o programa cliente! Ele enviará números ao nosso servidor para multiplicação.

import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;

public class ClientMain {

   public static final String UNIQUE_BINDING_NAME = "server.calculator";

   public static void main(String[] args) throws RemoteException, NotBoundException {

       final Registry registry = LocateRegistry.getRegistry(2732);

       Calculator calculator = (Calculator) registry.lookup(UNIQUE_BINDING_NAME);

       int multiplyResult = calculator.multiply(20, 30);

       System.out.println(multiplyResult);
   }
}
Parece simples. Mas o que está acontecendo aqui? Primeiro, o cliente deve saber o nome exclusivo do objeto cujos métodos chamará remotamente. Da mesma forma, no programa cliente, criamos a string pública estática final UNIQUE_BINDING_NAME = "server.calculator"; variável. Em seguida, no método main() , temos acesso ao registro de objetos remotos. Para fazer isso, precisamos chamar o método LocateRegistry.getRegistry() e passar o número da porta usada para criar nosso registro no programa ServerMain (porta 2732; esse número é apenas um exemplo — você pode tentar usar um número diferente):

final Registry registry = LocateRegistry.getRegistry(2732);
Agora só precisamos obter o objeto desejado do registro! Isso é fácil, porque conhecemos seu nome único!

Calculator calculator = (Calculator) registry.lookup(UNIQUE_BINDING_NAME);
Preste atenção ao tipo de casting. Lançamos o objeto recebido para a interface Calculator , não para a classe RemoteCalculationServer . Como dissemos no início da lição, o RMI depende de um proxy, portanto, as chamadas remotas estão disponíveis apenas para os métodos de uma interface, não para os métodos de uma classe. Por fim, chamamos remotamente o método multiple() em nosso objeto e enviamos o resultado para o console.

int multiplyResult = calculator.multiply(20, 30);
System.out.println(multiplyResult);
O método main() da classe ServerMain já está em execução há muito tempo. Agora é hora de executar o método main() no programa cliente ( ClientMain )! Saída do console:

600
É isso! Nosso programa (dois programas, na verdade!) fez o que deveria fazer :) Se você tiver tempo e vontade, pode incrementar um pouco. Por exemplo, faça a calculadora suportar as quatro operações aritméticas padrão e passe números não como tipos primitivos, mas como objetos CalculationInstance(int x, int y) . Até a próxima aula! :)
Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION