340 likes | 562 Views
CS6223: Distributed Systems. Advanced RPC. Challenges for RPC implementation. Locating server process Incompatible data representation Failure of remote procedure call Security ……. Locating services. A bind-server maintains a DB of locally provided services
E N D
CS6223: Distributed Systems Advanced RPC
Challenges for RPC implementation • Locating server process • Incompatible data representation • Failure of remote procedure call • Security • ……
Locating services • A bind-server maintains a DB of locally provided services • Each service is identified by its program no. and version no. • Each service is associated with its transport address • Service registration • When an RPC server is started, it registers itself to the bind-server with the transport address at which it is listening • Service query • When a client makes an RPC call to a server, it first inquires the bind-server of the transport address of the server
rpcbind in SUN OS • “rpcbind” should be started before any other RPC service. • can only be started by the super-user • When rpcbind is started, it checks if the name-to-address translation functions correctly. • If it fails, the network configuration databases may be corrupted. The rpcbind reports the error and terminates.
Report RPC information: rpcinfo • rpcinfo [host] • lists all registered RPC services with rpcbind on host in format of: program# ver# netid service owner • If host is not specified, the local host is the default. • Other options: rpcinfo [-T transport] [host] [prognum] [versnum]
RPC failures • Local procedure calls do not fail • If there is a core dump, the entire process dies • More error-prone with RPC: • Server failure • Network failure • Client failure (while server is still executing code for it) • Transparency breaks here • Applications should be prepared to deal with RPC failures
RPC semantics • Local procedure call: exactly once • Exactly once is difficult to achieve with RPC. A remote procedure may be executed: • 0 time: server crashed or RPC request is lost • 1 time: everything worked well • 1 or more: client retransmits the request after timeout while the server already executed the previous request
RPC semantics • Most RPC systems offer either • at least oncesemantics, or • at most oncesemantics • Try to design RPC functions idempotent: • idempotent functions: executing multiple times produces the same result as executing once • non-idempotent functions: side-effects
More issues Performance • RPC is slower … a lot slower Security • Messages visible over network • Authenticate client • Authenticate server
RPC protocol compiler: rpcgen • Generate stub routines from the interface definition • rpcgen –a name.x • name.h: header • name_xdr.c: XDR conversion routines • name_svc.c: server skeleton • name_server.c: service routines (user program) • name_clnt.c: client stub • name_client.c: client program
Other options to rpcgen • –Tproto_tbl.i: RPC dispatch table • –Scproto_client.c: sample client file • –Ssproto_server.c: sample server file • –Smmakefile.proto: sample makefile • –a all above files • –C: ANSI C code • –A: code supporting Auto MT (multi-thread) mode • –N: procedure can have multiple arguments
RPC Function Names and Parameters • Naming: lowercase, suffixed with an “_” and version no. • Returning parameter: pointer to the defined result • Two arguments in RPC calls: • a pointer to the defined argument • a pointer to a client handle • Two arguments in RPC service routines: • a pointer to the defined argument • a pointer to a server request (struct svc_req)
List of msg_client.c // see on-line listing main(argc, argv) int argc; char *argv[]; { host = argv[1]; msgprog_3(host); } void msgprog_3(host) { CLIENT *clnt; int *result_1; char * savemsg_3_arg, * *result_2; clnt = clnt_create(host, MSGPROG, MSGVER, "netpath"); if (clnt == (CLIENT *) NULL) {exit(1);} result_1 = savemsg_3(&savemsg_3_arg, clnt); if (result_1 == (int *) NULL) {clnt_perror(clnt, "call failed");} clnt_destroy(clnt); }
Create client handles CLIENT *clnt_create ( const char *host, /* name of the server host*/ const rpcprog_t prognum, /* service program number */ const rpcvers_t versnum, /* service version number */ const char *nettype /* transport selection */ ); • Create and return a client handle that first matches prog #, ver # and nettype. • Contact rpcbind on the server host to get the transport address of the specified service. • early binding – done once, not per procedure call
struct CLIENT typedef struct { AUTH *cl_auth; /* authenticator */ struct clnt_ops *cl_ops; caddr_t cl_private; /* private stuff */ char *cl_netid; /* network identifier */ char *cl_tp; /* device name */ } CLIENT; struct clnt_ops { enum clnt_stat (*cl_call)(); /* call remote procedure */ void (*cl_abort)(); /* abort a call */ void (*cl_geterr)(); /* get specific error code */ bool_t (*cl_freeres)(); /* frees results */ void (*cl_destroy)(); /* destroy this structure */ bool_t (*cl_control)(); /* the ioctl() of rpc */ int (*cl_settimers)(); /* set rpc level timers */ } ;
Destroy client handle void clnt_destroy(CLIENT *clnt); • Destroy the client's RPC handle and reclaim private data structures, including clnt itself. • Use of clnt is undefined after calling clnt_destroy().
List of msg_clnt.c // see on-line listing int * savemsg_3(argp, clnt) char **argp; CLIENT *clnt; { static int clnt_res; memset((char *)&clnt_res, 0, sizeof (clnt_res)); if (clnt_call(clnt, SAVEMSG, (xdrproc_t) xdr_wrapstring, (caddr_t) argp, (xdrproc_t) xdr_int, (caddr_t) &clnt_res, TIMEOUT) != RPC_SUCCESS) { return (NULL); } return (&clnt_res); }
Send RPC request to server enum clnt_stat clnt_call( CLIENT *clnt, /* pointer to the client handle*/ const rpcproc_t procnum, /* server procedure number */ const xdrproc_t inproc, /* XDR filter to encode arg */ const caddr_t arg, /* address of the argument*/ const xdrproc_t outproc, /* XDR filter to decode result*/ caddr_t res, /* address to store result*/ const struct timeval tout /* time-out value for trying the call*/ ); • If the remote call succeeds, the status returned is RPC_SUCCESS. • Otherwise, an appropriate error status is returned.
“xdr_” routines • For each user-defined data type in the interface, there is an “xdr_XXX” routine in _xdr.c file that converts data between the local representation and the standard XDR format. • There are library routines for converting standard data types in XDR, e.g., xdr_string, xdr_int, xdr_double, etc. They are not defined in _xdr.c file. • In client’s “clnt_call”, xdr routines encode the input parameter to XDR and decode the return result to the local format. • In server’s dispatch routine, “svc_getargs” decodes arguments from XDR to local format and “sendreply” encodes the result to XDR format.
const MAXSUBJ = 64; typedef char id[8]; typedef char code[8]; struct registargs { code subj_code; id stud_id; }; struct status { code subj_code; int result; }; struct regist_status { int total_regist; struct status subjs_status<MAXSUBJ>; }; program REGISTPROG { version REGISTVER { int REGIST(registargs) = 1; int DEREGIST(registargs) = 2; regist_status VIEW_STATUS(id) = 3; } = 3; } = 100023; regist.x (an example)
List of regist_xdr.c #include "regist.h" bool_t xdr_id(register XDR *xdrs, id objp) { if (!xdr_vector(xdrs, (char *)objp, 8, sizeof (char), (xdrproc_t) xdr_char)) return (FALSE); return (TRUE); } bool_t xdr_code(register XDR *xdrs, code objp) { if (!xdr_vector(xdrs, (char *)objp, 8, sizeof (char), (xdrproc_t) xdr_char)) return (FALSE); return (TRUE); } bool_t xdr_registargs(register XDR *xdrs, registargs *objp) { if (!xdr_code(xdrs, objp->subj_code)) return (FALSE); if (!xdr_id(xdrs, objp->stud_id)) return (FALSE); return (TRUE); } ……..
XDR encode/decode routine bool_t xdr_proc( XDR * xdrs, /* a XDR handle to or from which the data type is to be converted */ <type> * argresp /* pointer to the structure to be converted */ ) • convert between built-in C data types and an external data representation • simple data structures xdr_bool, xdr_char, xdr_double, xdr_enum, xdr_float, xdr_free, xdr_hyper, xdr_int, xdr_long, xdr_longlong_t, xdr_quadruple, xdr_short, xdr_u_char, xdr_u_hyper, xdr_u_int, xdr_u_long, xdr_u_longlong_t, xdr_u_short, xdr_void • complex data structures xdr_array, xdr_bytes, xdr_opaque, xdr_pointer, xdr_reference, xdr_string, xdr_union, xdr_vector, xdr_wrapstring • encode/decode routines for each user-defined type
struct XDR typedef struct XDR { enum xdr_op x_op; /* operation; fast additional param */ struct xdr_ops *x_ops; caddr_t x_public; /* users' data */ caddr_t x_private; /* pointer to private data */ caddr_t x_base; /* private used for position info */ int x_handy; /* extra private word */ } XDR; enum xdr_op { XDR_ENCODE = 0, /*encode the type into the stream */ XDR_DECODE = 1, /*decode the type into the stream */ XDR_FREE = 2 /* release the space allocated by an XDR_DECODE request */ };
List msg_server.c int * savemsg_3(argp, rqstp) char **argp; struct svc_req *rqstp; { static int result; FILE *fp; fp = fopen("msg.dat", "w+"); fprintf(fp, "%s\n", *argp); fclose (fp); result++; return (&result); }
struct svc_req struct svc_req { rpcprog_t rq_prog; /* service program number */ rpcvers_t rq_vers; /* service protocol version */ rpcproc_t rq_proc; /* the desired procedure */ struct opaque_auth rq_cred; /* raw creds from the wire */ caddr_t rq_clntcred; /* read only cooked cred */ SVCXPRT *rq_xprt; /* associated transport */ };
Server Skeleton • Create server handles • Create socket connection • Register the program dispatcher, with program #, version #, etc. • Start to listen and wait to accept requests for services • When an RPC request arrives, the server calls the dispatcher • The dispatcher calls the corresponding service routine for the requested service, and sends a reply back to client
List of msg_svc.c main() { if (!svc_create(msgprog_3, MSGPROG, MSGVER, "netpath")) { _msgout("unable to create (MSGPROG, MSGVER) for netpath."); exit(1); } svc_run(); /* NOTREACHED */ }
Create server handles int svc_create ( /* transport-independent create routine*/ const void *dispatch, /* dispatching program*/ const rpcprog_t prognum, /* service program number */ const rpcvers_t versnum, /* service version number */ const char *nettype /* transport selection */ ); • Create server handles for all the transports belonging to the class nettype, and return the number of server handles it created • Register itself to the rpcbind with the dispatch routine and the service interface specified by prog # and ver # • The dispatch routine will be called whenever there is an RPC for the given prog # and vers # (done by svc_run()).
nettype Define a class of transports which can be used for a particular application: • netpath (default nettype) • Choose from the transports indicated by their token names in the NETPATH environment variable. If NETPATH is unset or NULL, it defaults to visible. • visible • Choose the transports with the visible flag (v) set in the /etc/netconfig file. • circuit_v • same as visible except that it chooses only the connection oriented transports • datagram_v • same as visible except that it chooses only the connectionless datagram transports • circuit_n • same as netpath except that it chooses only the connection oriented transports • datagram_n • same as netpath except that it chooses only the connectionless datagram transports • udp: Internet UDP • tcp: Internet TCP
svc_run() Server starts running: • Must be called after all the services are registered • After initialization, the server waits for client requests • Upon a request arrives, the dispatch routine matching the pair of requested prog # and ver # is invoked.
Dispatch routine (in msg_svc.c) msgprog_3 (rqstp, transp) struct svc_req *rqstp; register SVCXPRT *transp; { switch (rqstp->rq_proc) { case NULLPROC: (void) svc_sendreply(transp, xdr_void, (char *)NULL); _rpcsvcstate = _SERVED; return; case SAVEMSG: local = (char *(*)()) savemsg_3; break; case READMSG: local = (char *(*)()) readmsg_3; break; default: svcerr_noproc(transp); _rpcsvcstate = _SERVED; return; } if (!svc_getargs(transp, _xdr_argument, (caddr_t) &argument)) { svcerr_decode(transp); return; } result = (*local)(&argument, rqstp); if (_xdr_result && result != NULL && !svc_sendreply(transp, _xdr_result, result)) { svcerr_systemerr(transp); } return; }
Get the arguments of request bool_t svc_getargs( const SVCXPRT *xprt, /* service transport handle associated with the request */ const xdrproc_t inproc, /* XDR decode for the arguments */ caddr_t in /* address where the arguments will be placed*/ ); • decode the arguments of an RPC request • return TRUE if decoding succeeds, and FALSE otherwise
Send back the reply bool_t svc_sendreply ( const SVCXPRT *xprt, /* service transport handle associated with the request */ const xdrproc_t outproc, /* xdr encode of output parameter*/ caddr_t out /* address of the results*/ ); • encode the RPC result and send it back to the client • return TRUE if send succeeds, and FALSE otherwise
MT (Multi-Thread) modes “rpcgen –A” option to turn on the Automatic MT mode • The generated dispatch program contains the code of creating a new thread to serve each new request, and the necessary code for mutual exclusion to protect shared variables • Turn on “–M” option implicitly, which generates MT safe stubs for passing arguments & results