LipeRMI - uma alternativa ao RMI para a Internet

Olá a todos :o)

Esta é a minha primeira mensagem neste fórum, e eu gostaria de divulgar um projeto que eu desenvolvi para substituir o RMI nativo do Java - o projeto teve o código-fonte aberto hoje e foi licenciando sob LGPL.

Estou divulgando neste fórum pois acredito que projetos abertos só crescem com a participação de todos e com o contato com a comunidade.

Acredito não estar fazendo mal ao fórum. Mas, em todo caso, desculpem-me pois jamais tive esta intenção.

Vamos ao que interessa: a proposta é re-escrever o RMI para resolver todos os problemas que a implementação original do RMI traz.

Um deles - que torna o RMI tão impopular - é o uso na Internet, e esse é o principal foco do projeto.

O problema do RMI

Quando você faz chamadas via RMI não é possível garantir de que maneira ela vai ser transportada na camada de comunicação. E quando perdemos controle sobre isso, talvez nosso software não funcione corretamente em todos os ambientes em que gostariamos.

A situação otimizada quando o usuário da aplicação está se conectando através da Internet é que a latência da comunicação seja mínima e que nosso cliente não precise alterar nada na topologia da sua rede para poder se conectar.

Com o RMI perdemos nestes dois pontos: a comunicação via RMI é custosa (utilizando muita banda - as vezes causando uma latência indesejada) e requer que o usuário final tenha um IP válido com algumas portas abertas (e para isto talvez ele tenha que alterar a topologia da sua rede).

Destes dois problemas, uso excessivo de banda não é o pior, mas pode ser otimizado.
O maior problema é quando o usuário precisa reconfigurar sua rede por causa do seu aplicativo que utiliza RMI - e a consequência disso pode ser óbvia: a não utilização do aplicativo.

O problema na camada de comunicação do RMI é que praticamente para uma invocação/retorno, um novo socket é criado; os sockets não seguem uma direção única e tanto o cliente como o servidor podem tentar conectar um no outro.

Listeners remotos (callbacks) também criam um problemão; já que sempre são chamados na direção servidor->cliente; fazendo, portanto, com que o servidor tente conectar no cliente, obrigando que o cliente tenha um IP válido e que a porta esteja disponível para conexão - o que não é o ideal na Internet, já que os clientes muitas vezes estão em redes locais, atrás de NATs/roteadores ou firewalls.

Quer dizer, se você tem uma aplicação voltada para o usuário final que use callbacks, sempre que um novo usuário quiser utilizar a aplicação ele vai ter que pedir pro administrador da rede criar uma regra no NAT apontando uma porta do IP válido para o IP interno dele. Inviável para uma aplicação desktop, não acha?

Tudo isso acontece por um único motivo - bem conhecido, na verdade - o RMI originalmente não foi feito para ser utilizado na Internet.

Solução “Sun”: Sobreescrever as Factorys

O RMI oferece meios para trocar as Factorys dos sockets. Parece ideal para resolver todo o problema, mas não é. O RMI permite, através destas Factorys, que nós manipulemos os dados trafegados pelas streams dos sockets e as propriedades internas dos novos sockets.
Porém, quem continua decidindo até quando manter um socket ativo ou quando criar novos ainda é o RMI.

Minha proposta

A minha proposta é bastante clara: re-implementar o RMI de uma maneira que possibilite que as chamadas sejam feitas através da Internet e minimize o uso de banda.
Esta nova implementação deve oferecer uma API muito parecida com o RMI nativo, exigindo a troca de apenas poucas linhas de código para uma re-adaptação de qualquer aplicativo.
As aplicações comerciais mais populares utilizam um conceito muito simples para se comunicar pela Internet: sempre que um cliente quer se comunicar com o servidor, o cliente é o responsável por fazer a conexão, e é através desta mesma conexão que o servidor envia dados para o cliente.

O servidor apenas deve estar ouvindo em alguma porta específica pré-determinada. E utilizando poucas portas fixas no servidor podemos criar regras para que o firewall permita a entrada de novas conexões para atender os clientes.

Depois que o cliente já estiver conectado, esta conexão deve permanecer aberta durante toda sessão de troca de dados entre o cliente e o servidor (ou até que a aplicação permita que a conexão seja fechada). Desta forma o servidor nunca irá precisar abrir uma conexão no sentido servidor->cliente, pois é possível utilizar a conexão já existente. E não precisando abrir novas conexões, o cliente não precisa se preocupar com firewalls - resolvendo o maior problema do RMI.

Melhorias

A minha idéia não é seguir a risca a implementação padrão da Sun. A proposta do projeto é melhorar tudo que for possível, inclusive a API.

Por exemplo, minha implementação permite que a aplicação tenha acesso a alguns eventos de conexão - quando uma conexão inicia ou acaba. Isto é muito útil para resolver antigos problemas, onde o servidor tinha que ficar sempre invocando algum método remoto para saber se o cliente continua respondendo - e que custa alguma banda preciosa.

Experiência

O projeto já vinha sendo testado e utilizado há alguns meses em projetos pessoais - tal como este jogo http://lipe.advant.com.br/dalmuti que utiliza Java WebStart + LipeRMI + SWT.

O resultado do LipeRMI hoje é um projeto maduro e suficientemente bom para ser utilizado em aplicações robustas.

Porém certamente ainda pode ser muito melhorado e eu estou procurando pessoas pra ajudar a divulgar o projeto, pois acredito que testando, utilizando, enviando sugestões e reportando bugs é a melhor maneira para se manter um projeto open-source vivo e com qualidade.

O link para a página oficial do LipeRMI é http://lipermi.sourceforge.net

Qualquer contribuição será muito bem vinda :o)


[size=7]E desculpem, novamente, por utilizar o fórum para divulgação. Meu objetivo não é utilizar o fórum como meio de propaganda para um projeto pessoal, e sim melhorar a qualidade de projetos open source com a participação da comunidade - que no caso de vocês do GUJ é a maior de todas :oP[/size]

Olá

Parabéns pela API e por tê-la disponibilizado Open Source. Espero que a minha contribuição sirva para abrir a cabeça de mais gente que lerá esta notícia.

Minhas considerações que valem para todos:

  1. Leiam o livro do Gregor Hohpe http://www.enterpriseintegrationpatterns.com/

  2. Por mais que a maioria por aí faça e fale em web services usando RPC, a base que precisamos são mensagens assíncronas. Todas as tecnologias baseadas em mensagens síncronas tenderão a esgotar a largura de banda mais cedo ou mais tarde.

  3. Quando eu digo que a base precisa ser via mensagens assíncronas não quero dizer que não se possa conceber um sistema onde para o cliente a percepção seja síncrona. Em outras palavras: se poderia imaginar um sistema em que para uma consulta ao saldo no banco, o cliente espere pelo saldo ou mensagem de erro. Porém, a comunicação por debaixo dos panos pode ser assíncrona e os sistemas (bite e bytes) envolvidos não precisam ficar bloqueados esperando respostas.

  4. Desculpe escrever tudo isto em cima da sua API mas é porque eu, como todo mundo que desenvolve sistemas, fui acostumado a raciocinar com sistemas síncronos e a gente precisa meter na cabeça que no mundo desacoplado que nos espera, todas as trocas de mensagens deverão ser assíncronas.

[]s
Luca

[quote=Luca]
2. Por mais que a maioria por aí faça e fale em web services usando RPC, a base que precisamos são mensagens assíncronas. Todas as tecnologias baseadas em mensagens síncronas tenderão a esgotar a largura de banda mais cedo ou mais tarde.[/quote]

keep-alive do http foi um super avanço e facilitou a vida de todo mundo… inclusive ajudar a salvar banda.

generalizar nunca dá certo. E, além disso, algumas coisas simplesmente não funcionam assincronamente, principalmente transferencia de stream onde o decode depende completamente dos dados passados.

Em tempos de Java EE 5 , alguem usa RMI puro ainda ? Eu sei q EJB usa RMI/IIOP , mas a proposta pelo que vi eh utilizar ele nativamente…

a pergutna é:

LipeRMI usa IIOP tmb ?

lipe, parece muito legal! voce ja chegou a ver o jboss remoting para conhecer as similaridades?

Luca,

Muito legal suas considerações.

Eu estive envolvido no desenvolvimento em um sistema de grande porte, que serviu como middleware de uma grande empresa de telecom de nível nacional.

Esta plataforma tinha como propósito expor para parceiros desenvolvedores de aplicações as funcionalidades da operadora (como tarifação de usuário, consulta de status de assinante, envio de SMS, envio de MMS, etc.) de uma forma transparente (através claro de XML) e segura (onde um parceiro só pode usar as features específicas do seu perfil).

Pois bem, no nosso design expusemos várias formas de conexão e, dentre elas, a assíncrona. Tratava-se de um protocolo onde o cliente mandava a requisição, recebia um ACK e, tão logo uma condição final se desse nessa requisição, era feito um callback para uma URL pré-acordada com o parceiro.

Sabe quantos parceiros utilizaram esta forma, em meu ver extremamente mais eficiente? Um parceiro, e este parceiro era uma aplicação interna desenvolvida pela mesma equipe que desenvolveu a plataforma.

Será que é tão difícil entender o TAMANHO DESPERDÍCIO de utilizarmos requisições síncronas e, pior ainda, busy waits que ficam polando infinitamente até que tenhamos o resultado?

Abraços!

Olá

JMP, Meu post se refere a interligação entre sistemas onde o keep-alive do HTTP não ajuda tanto assim de forma isolada.

Meu intuito não foi generalizar e sim enfatizar. Há lá fora todo um mundo puramente síncrono baseado em DCOM, COM, RPC, etc. E um mundo que está crescendo. Vou listar 3 coisas que acho:

  • Para mim estamos em meio a uma mudança na arquitetura dos sistemas onde o único lado visível à camada de apresentação é o uso do AJAX mas a grande revolução pode estar lá na retaguarda.

  • Eu acho que o conceito de serviços agora tem mais chances de pegar do que nos tempos da arquitetura DNA da Microsoft.

  • E acho que a maioria está usando web services no modo RPC.

Eu gostaria que todo mundo estivesse escrevendo web services trocando mensagens assíncronas para o bem do futuro dos programadores que estão iniciando que vão herdar este legado.

Aproveitei a API dele, que é boa e tem lá a sua utilidade, para abordar este tema porque apesar de saber que há implementações assíncronas de RMI, em geral RMI é síncrono. E o mundo síncrono pode caminhar por outros meios mais padronizados do que RMI como XML-RPC, SOAP ou REST.

Deu para entender que meu intuito foi alertar a todos que ao interligar sistemas tenham sempre em mente que devem trocar mensagens assíncronas?

Só mais um lembrete: JMS é uma das melhores e menos usadas APIs do Java.

[]s
Luca

Olá

Lipe

Aproveite e dê uma olhada em
Implementing Asynchronous Remote Method Invocation in Java

É muito antigo mas pode ser útil.

[]s
Luca

Luca, mas se eu tenho chamadas assíncronas, não caio no mesmo problema citado pelo lipeandrade?

Obrigado :o)

[quote]2. Por mais que a maioria por aí faça e fale em web services usando RPC, a base que precisamos são mensagens assíncronas. Todas as tecnologias baseadas em mensagens síncronas tenderão a esgotar a largura de banda mais cedo ou mais tarde.

  1. Quando eu digo que a base precisa ser via mensagens assíncronas não quero dizer que não se possa conceber um sistema onde para o cliente a percepção seja síncrona. Em outras palavras: se poderia imaginar um sistema em que para uma consulta ao saldo no banco, o cliente espere pelo saldo ou mensagem de erro. Porém, a comunicação por debaixo dos panos pode ser assíncrona e os sistemas (bite e bytes) envolvidos não precisam ficar bloqueados esperando respostas.

  2. Desculpe escrever tudo isto em cima da sua API mas é porque eu, como todo mundo que desenvolve sistemas, fui acostumado a raciocinar com sistemas síncronos e a gente precisa meter na cabeça que no mundo desacoplado que nos espera, todas as trocas de mensagens deverão ser assíncronas.[/quote]

Acredito que todos esses pontos dependem muito da aplicação.

Webservices, por exemplo, trazem uma solução para paradigmas diferentes de RMI e os líderes técnicos de projeto devem conhecer o máximo delas quanto possível. Cada uma atende a uma necessidade específica.

Webservices são ótimos para utilizar na Internet pois suas requisições trafegam via HTTP de uma forma complemente transparente. Porém, perde-se também em performance pois exige que as requisições passem antes por um servidor HTTP. Outro ponto fraco é que não são capazes de fazer callbacks, exigindo que os clientes fiquem fazendo pooling sempre necessitem receber algum dado assíncrono do servidor - e dependendo do número de clientes isto pode ser muito custoso para a
performance do servidor.

A minha implementação RMI não é tão flexível e transparente quanto requisições HTTP que os webservices utilizam.
Mas por outro lado é mais rápido pois utiliza uma compactação mais eficiente nos dados trafegados (e portanto, menos banda). Além disso, caso callbacks sejam necessários, são feitos em tempo real pois a conexão já está estabelecida. Com isso, pode-se suportar um número maior de clientes conectados simultaneamente pois não há necessidade de pooling - aumentando a performance da rede e do servidor.

Pense, por exemplo, nestas duas problemáticas:

  • Um cliente POP3. Como deve ficar verificando por novos e-mails? Deve ficar conectado (keep-alive) o tempo todo esperando callbacks de avisos de novos e-mails ou deve ficar fazendo pooling a cada, digamos, 15 minutos?
  • E um sistema de mensagens instantaneas à-la MSN, que as mensagens devem ser entregues com o mínimo de atraso?

Para resolver o primeiro problema, Webservices se adequaria perfeitamente (um encapsulamento POP3 em Webservices).

Já para o segundo, eu optaria sem dúvida nenhuma por RMI.

[quote]Em tempos de Java EE 5 , alguem usa RMI puro ainda ? Eu sei q EJB usa RMI/IIOP , mas a proposta pelo que vi eh utilizar ele nativamente…

a pergutna é:

LipeRMI usa IIOP tmb ?[/quote]

As pessoas têm uma visão muito “enterprise” de Java e poucas pessoas se preocupam em oferecer Java para desktop. Pouquíssimas pessoas procuram aprender mais sobre isso também.

Não se pode pensar em Application Servers, EJB e coisas pesadíssimas quando estamos falando de aplicações para utilizar em casa.

O LipeRMI tenta aproximar as aplicações Java do desktop e do usuário final. Se você der uma olhada no jogo que eu fiz, verá o quão familiar pode ser uma aplicação desktop escrita em Java.

LipeRMI não utiliza nada além de reflection e sockets. É uma abordagem voltada para a Internet e deve ser rápida, poderosa, simples e sem dependencias, utilizando apenas JSE.

Uma das grandes vantagens quando pensamos desta maneira é a possibilidade de rodarmos nossa aplicação em qualquer ambiente, inclusive em dispositivos móveis. :o)

[ ]s
Felipe

Olá

[quote=danieldestro]Luca, mas se eu tenho chamadas assíncronas, não caio no mesmo problema citado pelo lipeandrade?

Não, por vários motivos:

  1. Se você usa SOAP por HTTP passa por todos os FW sem problemas (ao contrário de RMI sem tunel por HTTP)

  2. Se você está interligando 2 ou mais aplicações diretamente na raça há 2 modos de fazer com que o provider se comunique com o consumer:
    a) De tempos em tempos o consumer lança uma mensagem "sonda"
    b) O sistema consumer também atua como provider

  3. Mas se você usa mensagens assíncronas não precisa nada disto porque provavelmente está usando algum JMS provider que se encarrega de enviar a resposta para o consumer que tem algum OnMessage escutando. Acho que os ESBs como o ServiceMix e o Mule trabalham com mensagens assíncronas.

[]s
Luca

Olá

Certíssimo! Como eu disse, eu só aproveitei o lançamento da sua API para chamar a atenção da importância de pensar em comunicação assíncrona. Aliás, até peço desculpas por ter me aproveitado da oportunidade.

Eu sou velho fã de aplicações desktop com acesso à Internet via protocolo HTTP pela porta 80. Já trabalhei em muitas aplicações assim, inclusive uma com mais de 10 mil clientes simultâneos.

Na minha experiência é muito difícil convencer o departamento de redes de alguma empresa a abrir qualquer outra porta e qualquer outro protocolo. Mesmo HTTP na porta 80, as empresas estão sempre escaneando e já vi questionamentos sobre o que está trafegando. Este é uma das dificuldades que sua API precisa superar caso a comunicação precise atravessar a DMZ.

Mas internamente pode ter utilidade e por isto eu a elogiei.

[]s
Luca

No caso de apps “desktop”, acho esses motivos não se aplicam muito.

Olá

Só mais um adendo:

Este é um ambiente ideal para mensagens assíncronas. O fato de ser assíncrono não significa atraso. O exemplo que dei do banco é real. O cliente que está no msn pode até pensar que o sistema é síncrono e ele até pode ficar sentado esperando a resposta. Mas na prática é assíncrono e quando volta a mensagem a gente é interrompido com a janelinha que sobe ou com o “HaHã”.

Fórum é um dos exemplos mais claros de sistemas assíncronos para o cliente. Enquanto escrevo esta mensagem, épossível que você esteja aí envolvido em um monte de outras coisas. Até que seu email apita que tem resposta nova no GUJ e você vem aqui ler.

Porém eu não estou preocupado com o cliente ser humano que escreve no msn ou em fóruns e sim o cliente máquina que se comunica com outra máquina em alguma outra parte do mundo. É aqui que as comunicações costumam ser síncronas usando COM, DCOM, RPC, etc. e deveriam ser assíncronas.

[]s
Luca

Olá

Sim, concordo desde que além de desktop também sejam confinados em uma rede local. Mas se são aplicações que usam serviços externos é bom que sejam assíncronas.

Imagine uma aplicação desktop que solicite serviços de cotações de vários fornecedores. Ora, os web services deles responderão em momentos diferentes e pode ser até que usem fusos horários diferentes. Já imaginou deixar uma thread aberta esperando a resposta? Mensagens assíncronas economizam recursos e largura de banda.

[]s
Luca

Exato, RMI originalmente não oferece meios para você “esconder” o cliente da aplicação atrás de um firewall, rede local ou roteador. Caso você precise disso, terá que utilizar SOAP, XML-RPC ou qualquer outra coisa do tipo.

Este é um problema que o projeto tenta resolver.

É interessante ter a possibilidade de encapsular as chamadas de maneiras diferentes, dependendo do meio onde a invocação remota é trafegada sem trocar a API.

Caso a única opção seja encapsulamento através de uma requisição HTTP pois há um proxy impedindo a conexão no meio do caminho, o LipeRMI deve ser capaz de otimizar o uso de banda, encapsular a invocação e gerenciar isso da melhor maneira possível, enfileirando chamadas/respostas.

Assim, quando o cliente fizer uma nova chamada (ou quando um certo delay for atingido) as respostas de suas chamadas-já-processadas são obtidas pelo cliente e novas invocações podem ser feitas neste mesmo request.

Esta é uma idéia para versões futuras do LipeRMI.

[quote]2. Se você está interligando 2 ou mais aplicações diretamente na raça há 2 modos de fazer com que o provider se comunique com o consumer:
a) De tempos em tempos o consumer lança uma mensagem "sonda"
b) O sistema consumer também atua como provider

  1. Mas se você usa mensagens assíncronas não precisa nada disto porque provavelmente está usando algum JMS provider que se encarrega de enviar a resposta para o consumer que tem algum OnMessage escutando. Acho que os ESBs como o ServiceMix e o Mule trabalham com mensagens assíncronas.[/quote]

Com RMI você já tem todas estas opções, utilizando listeners remotos (assíncronos) ou aguardando simplemente o retorno de um método (síncrono).

No caso do RMI você pode implementar uma interface no cliente que será responsável por receber as mensagens assíncronas e processar no cliente disparando eventos locais de um jeito muito parecido com JMS.

Tudo isso feito de forma completamente transparente e abstrata, como se um método no cliente fosse invocado por alguma thread local.

A idéia inicial do LipeRMI é preencher uma lacuna que não existe: um framework para comunicação que seja leve, possa ser utilizado livremente, seja inteligente para que clientes atrás de redes locais, roteadores, firewalls (e no futuro http proxys) possam utilizar de forma transparente e que não tenha dependencias, possibilidando a utilização em dispositivos mais simples.

Pense assim: nas ocasiões em que você provavelmente utilizaria Sockets (afim de deixar a aplicação mais leve, sem dependencias, mais rápida e que possibilite um controle da comunicação) você poderá utilizar LipeRMI sem maiores problemas, aumentando a abstração utilizando invocações remotas de uma forma que você não perca o controle da comunicação.

Olá

No meu caso pessoal, a maioria das vezes que usei sockets foi para comunicar com sistemas desenvolvidos em outras linguagens ou para abrir servidores em que eu não poderia controlar quem enviaria as mensagens.

Mas como eu já desenvolvi há muitos anos atrás um sistema de comunicação serial que usava compactação de dados por dicionário (como o ZIP), sei que sua API pode ter muita utilidade.

Ainda acho RMI bem diferente de JMS. E JMS também tem seus problemas. A vantagem é que a galera que desenvolve os providers dentro dos ESBs tenta resolvê-los enquanto com algo baseado em RMI a batata esquenta na nossa mão.

Mas repito, a API é boa e tem utilidade. (além de ter permitido dar este meu recado sobre a importância de mensagens assíncronas).

[]s
Luca

nao sei se é do conhecimento de todos, ou se estou falando de uma tecnologia totalmente defasada mas vcs ja ouviram falar em CORBA? no meu trabalho de monografia eu criei uma api para criação e manutenção de grids usando RMI para objetos RMI agora queria portar ele para oferecer objetos CORBA. Será que corba caiu em desuso?

Olá,

Parabens pelo projeto. Por muito tempo eu procurava algo nesse sentido!!.

Mas depois de todas essas informações, ficou a duvida: O Lipe faz chamadas assincronas ou não. Como?

Ah!! Ele precisa também que cada classe remota impemente uma interface.

Caso não precisasse teria um polimorfismo entre objetos locais e remotos, tal como no middleware ProActive, que usa o RMI como base.

Aliás acho o ProActive, um middleware muito bom. Alguém já vc já viu ou tem alguma opnião sobre esse middleware. o link e http://www.objectweb.org/ProActive

RMI, teoricamente, é síncrono. Fazer RMI assíncrono é complicar sem necessidade.