Chamada Remota de Procedimento (RPC)
padrão cliente-servidor A (cliente) B (servidor) send (B, pedido) receive(b, &resp) receive (A, ) processa pedido send(a, ) repetição de padrão de comunicação encapsulação em abstração de mais alto nível transparência
chamada remota A (cliente) B (servidor) send (B, pedido) receive(b, &resp) receive (A, ) processa pedido send(a, ) transformação em chamada de procedimento transparência para comunicação e suporte a passagem de parâmetros
chamadas locais main proc1 proc2 proc3 proc4 proc5 proc6 proc7 proc8 programa convencional organizado e entendido em como uma sequência de chamadas a procedimentos
chamada remota Computador 1 Computador 2 main proc1 proc2 proc3 proc4 proc5 proc6 proc7 proc8 protocolo permite chamada de procedimento remoto chamada remota pode ficar mais ou menos ênfase em estruturação do programa bastante associado ao modelo cliente-servidor
chamadas remotas main proc1 proc2 proc3 proc4 proc5 proc6 proc7 proc8 possibilidade de estruturas mais complexas mais comum em sistemas de objetos distribuídos
RPC: modelo de execução processo cliente permanece bloqueado durante execução de chamada remota A (cliente) B (servidor) resp = foo(a1, a2, ) function foo (arg1, arg2, ) return resposta end
RPC: modelo de execução stubs são responsáveis por intermediar a comunicação entre quem faz a chamada (caller) e quem é chamado (callee) cliente servidor stub cliente stub servidor
RPC - papel dos stubs processo chamador processo chamado cham local stub cliente stub servidor runtime RPC runtime RPC
camadas envolvidas supondo uso de TCP protocolo sobre TCP conversão de dados conversão de chamada remota em protocolo TCP prg cliente ferramenta stubs cli e srv conversão entre formatos de repres. protocolo tcp
ferramentas/ geração de stubs linguagens que incorporam conceito de RPC compilador gera diferentes partes do programa e stubs compilador cliente servidor programa fonte stub cliente stub servidor bibliotecas + ferramentas possivelmente com interoperabilidade entre linguagens descrição interface gerador de stubs stub cliente stub servidor
conversão de dados marshalling/unmarshalling empacotamento e desempacotamento problemas representações diferentes para inteiros alinhamento de dados estruturas com ponteiros
soluções representação em XML (web services) XDR (external data representation) rotinas de conversão escritas pelo programador
RPC Birrell e Nelson: 1984 PARC implementação em linguagens como CCLU Sun RPC objetos distribuídos: CORBA, RMI, J2EE, SOAP
exemplo SR linguagem criada especificamente para aprender/usar distribuição e concorrência aplicação é um mundo fechado: não há como processos lançados indepentemente se comunicarem ver exemplo
exemplo SOAP RPC SOAP padrão de comunicação em serviços web messaging ou RPC mensagens SOAP baseadas em XML bibliotecas (Java, C++,...) oferecem facilidades de manipulação dessas
requisição SOAP <SOAP-ENV:Envelope xmlns:soap-env= http://schemas.xmlsoap.org/soap/envelope/ SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/ > <SOAP-ENV:Header> <t:transid xmlns:t= http://a.com/trans >1234</t:transId> </SOAP-ENV:Header> <SOAP-ENV:Body> <m:add xmlns:m= http://a.com/calculator > <a xsi:type= integer >3</a> <b xsi:type= integer >4</b> </m:add> </SOAP-ENV:Body> </SOAP-ENV:Envelope> Copyright 2000 Kenn Scribner and Mark C. Stiver
Resposta SOAP <SOAP-ENV:Envelope xmlns:soap-env= http://schemas.xmlsoap.org/soap/envelope/ SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/ > <SOAP-ENV:Header> <t:transid xmlns:t= http://a.com/trans >1234</t:transId> </SOAP-ENV:Header> <SOAP-ENV:Body> <m:addresponse xmlns:m= http://a.com/calculator > <c xsi:type= integer >7</c> </m:addresponse> </SOAP-ENV:Body> </SOAP-ENV:Envelope> Copyright 2000 Kenn Scribner and Mark C. Stiver
ver exemplo exemplo de uso em Java
Um exemplo histórico de RPC: Sun-RPC sistema originalmente criado para máquinas Sun. oferecido depois por diversos sistemas operacionais! arquitetura definida inclui: uma linguagem para definição das interfaces (cabeçalhos de procedimentos, etc); a ferramenta RPCGEN, que gera os stubs cliente e servidor automaticamente; uma biblioteca RPC, que pode ser usada diretamente na construção de programas que não usem o RPCGEN; o protocolo de comunicação entre os stubs. utiliza TCP ou UDP
Sun-RPC - Tradução de dados tradução entre formatos de dados: utilização de uma representação padrão, XDR (external Data Representation Standard). formato origem formato padrão formato destino conversão é especificada para um conjunto pré-definido de tipos de dados.
rpcgen - funcionamento procedimentos servidores programa servidor prog_proc.c stub servidor cc prog_svc prog_svc.c especificação RPC prog.x rpcgen prog.h biblioteca RPC cliente rprog.c prog_clnt.c stub cliente cc programa cliente rprog
Exemplo rpcgen
aplicação Cliente main Chamadas a Procedimentos Remotos inicializar Programa Remoto proxima insere remove busca Banco de Dados
criar uma especificação rpcgen /* rbd.x especificação rpc para um programa de banco de dados que oferece os procedimentos INSERE, REMOVE e BUSCA */ struct example { /* estrutura não usada, declarada para ilustrar como rpcgen */ int exfield1; /* constrói rotinas XDR para converter estruturas */ char exfield2; ; program RBDPROG{ /* nome do programa remoto */ version RDBVERS{ /* declaração da versão */ int INICIALIZAR(void) = 1; /* primeiro procedimento deste programa */ int INSERE(string) = 2; /* segundo procedimento deste programa */ int REMOVE(string) = 3; /* terceiro procedimento deste programa */ int BUSCA(string) = 4; /* quarto procedimento deste programa */ = 1; /* definição da versão do programa */ = 0x30090949; /* número do programa remoto (deve ser único) */
rodar o rpcgen rpcgen rbd.x rdb_svc.c rpcgen rdb.h rdb_clnt.c rdb_xdr.c
rpcgen arquivo.h /* rbd.h */ struct example { int exfield1; char exfield2; ; typedef sruct example example; bool_t xdr_example(); #define RBDPROG (u_long) 0x30090949) #define RDBVERS ((u_long) 1) #define INICIALIZAR ((u_long) 1) extern int *inicializar_1(); #define INSERE ((u_long) 2) extern int *insere_1(); #define REMOVE ((u_long) 3) extern int *remove_1(); #define BUSCA ((u_long) 4) extern int *busca_1();
rpcgen arquivo de conversão XDR /* rbd_xdr.c */ #include <rpc/rpc.h> #include rbd.h bool_t xdr_example(xdrs, objp) XDR *xdrs; example *objp; { if (!xdr_int(xdrs, &objp->exfield1)) { return(false); if (!xdr_char(xdrs, &objp->exfield2) { return(false); return(true);
/* rbd_clnt.c */ #include <rpc/rpc.h> #include rbd.h int * inicializar_1(argp, clnt) void *argp; CLIENT *clnt; { static int res; bzero((char *)&res, sizeof(res)); if (clnt_call(clnt, INICIALIZAR, xdr_void, argp, xdr_int, &res, TIMEOUT)!= RPC_SUCCESS) return (NULL); return (&res); int *insere_1(argp, clnt) char **argp; CLIENT *clnt; { static int res; bzero((char *)&res, sizeof(res)); if (clnt_call(clnt, INSERE, xdr_wrapstring, argp, xdr_int, &res, TIMEOUT)!= RPC_SUCCESS) return (NULL); return (&res); rpcgen stub do cliente int * remove_1(argp, clnt) char **argp; CLIENT *clnt; { static int res; bzero((char *)&res, sizeof(res)); if (clnt_call(clnt, REMOVE, xdr_wrapstring, argp, xdr_int, &res, TIMEOUT)!= RPC_SUCCESS) return (NULL); return (&res); int *busca_1(argp, clnt) char **argp; CLIENT *clnt; { static int res; bzero((char *)&res, sizeof(res)); if (clnt_call(clnt, BUSCA, xdr_wrapstring, argp, xdr_int, &res, TIMEOUT)!= RPC_SUCCESS) return (NULL); return (&res);
/* rbd_svc.c */ #include <rpc/rpc.h> #include rbd.h rpcgen stub do servidor svc_run(); (void)fprintf( SVC_RUn retornado \n ); exit(1); static void rbdprog_1(); main() { SVCXPRT *transp; (void)pmap_unset(rbdprog, RBDVERS); transp = svcudp_create(rpc_anysock); if (transp == NULL) { (void) fprintf( Não pode criar serviço udp\n ); exit(1); if (!svc_register(transp, RBCPROG, RBDVERS, rbdprog_1, IPPROTO_UDP)) { (void) fprintf( Não pode registrar tal prog.\n ); exit(1); transp = svctcp_create(rpc_anysock, 0, 0); if (transp == NULL) { (void) fprintf( Não pode criar serviço TCP\n ); exit(1); if (!svc_register(transp, RBCPROG, RBDVERS, rbdprog_1, IPPROTO_TCP)) { (void) fprintf( Não pode registrar tal prog.\n ); exit(1); static void rbdprog_1(rqstp, transp) struct svc_req *rqstp; SVCXPRT *transp; { union { char *insere_1_arg; char *remove_1_arg; char *busca_1_arg; argument; char *result; bool_t (*xdr_argument) (), (*xdr_result)(); char *(*local)(); switch (rqstp->rq_proc) { case NULLPROC: ( void)svc_sendreply(transp, xdr_void,(char *) NULL); return; case INICIALIZAR: xdr_argument = xdr_void; xdr_result = xdr_int; local = (char *(*)())inicializar_1; break; case INSERE: xdr_argument = xdr_wrapstring; xdr_result = xdr_int; local = (char *(*)()) insere_1; break;
rpcgen continuação stub do servidor case REMOVE: xdr_argument = xdr_wrapstring; xdr_result = xdr_int; local = (char *(*)())remove_1; break; case BUSCA: xdr_argument = xdr_wrapstring; xdr_result = xdr_int; local = (char *(*)()) busca_1; break; default: svcerr_noproc(transp); return; bzero((char*)&argument, sizeof(argument)); if (!svc_getargs(transp, xdr_argument, &argument)) { svcerr_decode(transp); return; result = (*local)(&argument, rqstp); if (result!= NULL &&!svc_sendreply(transp, xdr_result, result )) { svcerr_systemerr(transp); if (!svc_freeargs(transp, xdr_argument, &argument)) { (void)fprintf( Problema nos argumentos\n ); exit(1);
escrever procedimentos de interface com o stub Rotinas de Interface do Cliente /* rbd_cif.c - inicializar, insere, remove, busca */ #include <rpc/rpc.h> #include rbd.h extern CLIENT *handle; /* handle para procedimento remoto */ int inicializar() { return *inicializar_1(handle); int insere(item) char *item; { char **arg; arg = &item; return *insere_1(arg, handle); int remove(item) char *item; { char **arg; arg = &item; return *remove_1(arg, handle); int busca(item) char *item; { char **arg; arg = &item; return *busca_1(arg, handle);
rotinas de interface do servidor /* rbd_sif.c - inicializar_1, insere_1, remove_1, busca_1 */ #include <rpc/rpc.h> #include rbd.h static int retcode; int *inicializar_1() { retcode = inicializar(); return &retcode; int *insere_1(i) char **i; { retcode = insere(*i); return &retcode; int *remove_1(i) char **i; { retcode = remove(*i); return &retcode; int *busca_1(i) char **i; { retcode = busca(*i); return &retcode;
Programa Servidor /* rbd_srp.c - inicializar, insere, remove, busca*/ #include <rpc/rpc.h> #include rbd.h /* Procedimentos remotos do servidor e dados globais */ char bd[bdsize][maxword+1] /* armazena o dicionário de palavras */ int npalavras = 0; /* número de palavras no dicionário */ int inicializar() { npalavras = 0; return 1; int remove(palavra) char *palavra; { int i; for (i=0; i<npalavras; i++) if (strcmp(palavra, bd[i]) == 0) { npalavras--; return 0; strcpy(bd[i], bd[npalavras]); return 1; int insere(palavra) char *palavra; { strcpy(bd[npalavras], palavra); npalavras++; return npalavras; int busca(palavra) char *palavra; { int i; for (i=0; i<npalavras; i++) if (strcmp(palavra, bd[i]) == 0 ) return 1; return 0;
semântica de chamadas pelo menos uma vez no máximo uma vez exatamente uma vez relação com protocolo subjacente falhas e reinicializações de servidores funções idempotentes
binding amarração entre cliente e servidor reedita problema de localização de destinatário solução portmapper no Sun RPC em geral: chamada a servidor que detém: nome localização (IP, porta) estado e se nenhum servidor for localizado vários servidores forem localizados
críticas sincronismo modelo um a um dificuldades de tratamento de falhas dinamismo
sincronismo processo que faz a chamada permanece bloqueado falta de interatividade local desperdício de uso da CPU processamento outras operações de comunicação
RPC e multithreading (cliente) combinação de RPC com multithreading A A 1 2 B C sobreposição de computação e comunicação disparo de várias chamadas trocas de contexto preemptivas custo necessidade de sincronização
alternativas cliente pode disparar threads apenas no momento de fazer chamadas remotas uso de mecanismos como co-rotinas chamadas assíncronas
tratamento de chamadas concorrência no servidor A B S como tratar???
concorrência no servidor opções análogas às vistas para servidores em geral
RPC assíncrona controle retorna imediatamente registravalor(novoval); em alguns casos, função de callback pode ser chamada qdo chamada se completa function funcb(v) globalval = v end obtemnovoval(args, funcb);
programa como máquina de estado function collect(val) acc = acc + val repl = repl + 1 if (repl==expected) then print ("Current Value: ", acc/repl) end end function askvals (peers) repl = 0; expected = 0; acc = 0 for p in pairs (peers) do expected = expected + 1 p:currvalue{callback=collect end end
problema de perda de locais
exemplo luarpc function request(peers) local acc, repl = 0, 0 local expected = table.getn(peers) function avrg (val) repl = repl+1 acc = acc + val if (repl==expected) then print ("Current Value: ", acc/repl) end end CLOSURE for _,p in ipairs (peers) do rpc.async(p, "currvalue", avrg)() end end
variantes RPC assíncrona
comunicação um a um como pensar em canais ou mailboxes onde processos espalhados por várias máquinas podem recolher chamadas? no caso de tolerância a falhas: às vezes desejável que os diversos processos recebam a mesma chamada replicação comunicação em grupo
tratamento de falhas como retornar informações sobre falhas no modelo de chamada remota? callbacks exceções
dinamismo modelo apresentado exige conhecimento sobre interfaces antes da geração do programa cliente geração estática de stubs suficiente para maior parte das situações adaptação dinâmica servidores com diferentes otimizações cache otimização dependendo do estilo de uso do cliente chamada dentro de loop descoberta dinâmica de servidores relação com serviços de binding mais complexos