Asynchronous RPC

AsyncRPC is a non-blocking and not a wholly asynchronous RPC library. It provides abstractions over the Sockets layer for non-blocking transmission of RPC calls and reception of RPC replies. It provides notification of RPC replies through callbacks. Callbacks can be registered at the time the RPC call is transmitted through an interface function along with some private data that could be required during the callback.

It is used as the RPC library for libnfsclient, a userland NFS client operations library, which in turn is used by a tool called nfsreplay.

The NFS benchmarking project page is here: NFSBenchmarking

I can be reached at <shehjart AT gelato DOT NO SPAM unsw DOT edu GREEBLIES DOT au>

News

Main features

Interface

The interface is very similar to the RPC library in the glibc, with the addition of callbacks and non-blocking socket IO.

Creating Client Handle

#include <clnt_tcp_nb.h>

CLIENT *clnttcp_nb_create(struct sockaddr_in *raddr, u_long prog, 
 u_long vers, int *sockp, u_int sbufsz, u_int rbufsz);


CLIENT *clnttcp_b_create(struct sockaddr_in *raddr, u_long prog, 
 u_long vers, int *sockp, u_int sbufsz, u_int rbufsz);

Use clnttcp_nb_create to initiate a connection to a remote server using a non-blocking socket. The 'clnt_b_create does the same using a blocking socket. The parameters are:

The function returns a handle which is used to identify this particular connection.

User callbacks

User callbacks are of the type

#include <clnt_tcp_nb.h>

typedef void (*user_cb)(void *msg_buf, int bufsz, void *priv);

Calling Remote Procedures

#include <clnt_tcp_nb.h>

extern enum clnt_stat clnttcp_nb_call(CLIENT *handle, u_long proc,
                xdrproc_t inproc, caddr_t inargs, user_cb callback, void * usercb_priv);

clnttcp_nb_call is the function used call remote procedures asynchronously.

Executing callbacks

Closing a connection

#include <clnt_tcp_nb.h>

void clnttcp_nb_destroy (CLIENT *h);

Simply call clnttcp_nb_destroy to the state related to this connection.

Retreiving amount of data transferred

#include <clnt_tcp_nb.h>

unsigned long clnttcp_datatx (CLIENT *h);

Returns the count of bytes transferred over this CLIENT handle.

Internals

Some aspects that need focus are presented here.

Plugability into glibc

Since glibc's RPC implementation has some degree of extensibility I've been able to use quite a bit of underlying infrastructure. It allows for new pluggable transport protocol handlers, pluggable XDR translation libraries and pluggable functions that actually do the reading and writing from sockets.

Some aspects of glibc RPC code structure are shown in the two pages here, which are basically pictures of diagrams I drew on a whiteboard to understand it myself. See glibcRPCDesign.

Record Stream Management

The RPC Record Marking Standard is used for serializing RPC messages over byte-stream transports like TCP. Since we have two different paths for sending(using clnttcp_nb_call) and receiving RPC messages(through callbacks), the XDR translation takes place differently in both cases.

Callbacks

Callbacks are called only with complete RPC messages. The buffers passed to the callbacks are in XDR format and need to be translated before being useful. Use the glibc XDRMEM routines to do this. For examples of use with NFS messages, see the XDR translation routines in libnfsclient callbacks. The source for libnfsclient is packaged as part of nfsreplay.

Callbacks are saved internally in a hashtable by using the RPC XID as the key. As each call produces a unique XID, each message needs a callback to be registered while sending that message. Registering a callback is optional and the library discards a reply which does not have a registered callback.

Callbacks might need some state information while processing each reply. This state can be provided as the user_cb_priv argument to clnttcp_nb_call. This reference is passed to the callback eventually as the priv argument of the callback functions, which are of the type user_cb. This approach provides for per-XID callback and private info, i.e. the callback and the private data passed to it can be different for each request.

The message buffers passed to the callbacks are freed after the callbacks return. Copy it, if persistence is needed.

Response Notification

Response notification happens through callbacks. Within the library, response processing is attempted right after the RPC call is made by clnttcp_nb_call. The attempt to read a response blocks if the socket was created using clnttcp_b_create. If the read results in a full RPC record being read, the callback is executed and clnttcp_nb_call returns. In case the socket was non-blocking and the read returns EAGAIN, clnttcp_nb_call returns without calling any callbacks. In such cases, the user application might need to explicitly initiate the callbacks using a completion function. The clnttcp_nb_receive function is used for this purpose. Again, clnttcp_nb_receive allows the user application to explicitly specify whether this invocation of clnttcp_nb_receive should block. In case there are buffers that can read without blocking, they are read in. The callbacks are called only if these buffers collate to form a complete RPC record. See the description of clnttcp_nb_receive to understand under what conditions it will block till atleast one callback is executed.

Code

libnfsclient and AsyncRPC are part of the nfsreplay source package. See nfsreplay page for instructions on checking out these two components.

Usage

Usage and building with the library involves including the header file and building the library user's C files with the clnt_tcp_nb.c source file.

The header clnt_tcp_nb.h is needed by all the files that use the interface functions above.

Support

Use nfsreplay lists for support and discussion.

IA64wiki: AsyncRPC (last edited 2008-03-13 01:13:11 by ShehjarTikoo)

Gelato@UNSW is sponsored by
the University of New South Wales National ICT Australia The Gelato Federation Hewlett-Packard Company Australian Research Council
Please contact us with any questions or comments.