6.824 2004 Lecture 4: RPC Topic: Remote Procedure Call (RPC) Tools to help us divide up programs onto multiple machines Big goal: transparency! Suppose you start with a simple program Example 1 Two modules: application, and database (reserve() and change()) OK if only one ticket agent in the whole world... Now you want to split this up: A single server running the database. Lots of client hosts talking to it over the network. Could just change the s/w: Get rid of function calls. Read and write network connections directly. Example 2. Network I/O is awkward! Nowhere near as convenient as function calls. Compiler automates function call: Type checking, placement of args on stack. Can we extend automation to cross-network inter-module interfaces? Remote Procedure Call: The Idea Look at Example 3. Two pieces: "client stubs", "server stubs". Client stubs pretend to be the original server functions. Server stubs pretend to be the client functions. Thus Example 1 code can be used un-changed! Most of the work: "marshalling" and "un-marshalling." Note that request message format includes "procedure number". And that server stub code "dispatches" on proc num. Now the programmer can make the same client reserve() call, and implement reserve() on the server the same way, and the RPC machinery takes care of the communication. What are the potential benefits of RPC? Transparent distributed computing Existing programs don't need to be modified Can write s/w that's location-independent Enforces well-defined interfaces Allows portable interfaces Plug together separately written programs at RPC boundaries e.g. NFS and X clients and servers How can we improve on the manually-written stub RPC scheme? Generate marshaling/unmarshaling code automatically from types. Could have support for more complex types (arrays, var-length strings). Convert to standard byte order (little-endian vs big-endian). Unmarshalling code needs to validate format. Match up reply to request with some kind of ID. Message length field (perhaps in UDP packet). Client needs to specify which server. Extra argument to reserve() stub? What does an RPC system consist of? Will base explanation on SUN RPC, which NFS paper mentions. 1. Standards for wire format of RPC msgs and data types. XDR and RPC. 2. Library of routines to marshal / unmarshal data. 3. Stub generator, or RPC compiler, to produce "stubs". For client: marshal arguments, call, wait, unmarshal reply. For server: unmarshal arguments, call real fn, marshal reply. 4. Server framework: Dispatch each call message to correct server stub. 5. Client framework: Give each reply to correct waiting thread / callback. 6. Binding: how does client find the right server? What does a Sun RPC request contain? all 32 bits. this is wire format. UDP header... xid call/reply rpc version program # program version procedure # auth stuff arguments Of main interest: xid, prog#, proc# Server dispatch uses prog#, proc# Client reply dispatch uses xid Client remembers the xid of each outstanding call Authentication fields An attempt to do cryptographic security at RPC level Transparent to application code Turns out not to work well What "security" means is too app-dependent Authenticate user? host? data? Typically just holds your numeric UNIX user id, not verification at all Marshaling arguments "Linearize" data scattered in memory into byte stream "Externalize" data representation so it is portable Formats defined by XDR standard Easy for e.g. int -- same representation, though portable byte order... Arrays and strings? Prepend a length. Pointers? Follow them? How much data does a char * point to? May be unclear how to efficiently linearize e.g. hash table. What if circular pointers? I.e. representing a graph structure? Need programmer or language support for this. What needs to be in an RPC reply? xid call/reply accepted? (vs bad rpc version, or auth failure) auth stuff success? (vs bad prog/proc #) results How does the stub generator work? You give it a description of the procedure calls and arg/res data types. Sun defines a C-like standard. See 2nd page of handout. It produces: Routines to marshall / unmarshall. Routines to read/write call on the wire. Maybe client / server stubs. What does the client framework do? Keeps track of outstanding requests. For each, xid and caller's thread / callback. Matches replies to caller. Might be multiple callers using one socket. NFS client in kernel. Usually handles timeing out and retransmitting. What does the server framework do? Need a context in which to execute the procedure. In a threaded system: Create a new thread per request. Master thread reads socket[s]. Or a fixed pool of threads, and a queue if too many requests. NFS srvrs. Or just one thread, serial execution. Simplifies concurrency. X srvr. Key feature: support for concurrent RPCs If one RPC takes multiple blocking steps to compute, Can I serve another one in the meantime? For example, DNS. Service routine is an RPC client. May also avoid deadlock if I send RPC to ... to myself In an async programming system: Callback registered per prog#/proc#. (Rather than per socket. fdcb() calls un-marshaling function). What about binding? Client needs to be able to talk to the correct server It needs an IP address Use DNS. Client knows RPC prog #, needs to know server's TCP/UDP port # Could use a well-known port: NFS uses port 2049 Could use a "port mapper" per server server programs register prog#/port with port mapper clients can ask for port, given prog# avoids dedicating scarce port #s to services Conclusion Automatic marshaling has been a big success Mimicing procedure call interface is not that useful Attempt at full transparency has been mostly a failure But you can push this pretty hard: Network Objects, Java RMI