Linux编程之从零开始搭建RPC分布式系统

我一毕业进公司就接触到了RPC,主要是使用前辈们搭建好的RPC框架以及封装好的RPC函数进行业务开发,虽说使用RPC框架开发已经近半年了,但一直想知道如何从零开始搭建起这么一个好用的分布式通信系统框架,近日心血来潮,虽说没人教怎么搭建,但自己在网上查阅了大量资料后,开始自己一手一脚从零搭建这么一个RPC框架,所以就有了以下这篇文章,以记录我的搭建过程。
 
首先对RPC做一个简要介绍。
 
RPC的全称是Remote Procedure Call,它能够在本地以函数调用的形式来实现网络操作,让程序员集中关注于业务逻辑,不用关心底层的数据通信。
 
网络通信的应用程序大多数是使用显式网络编程(explicit network programming)的方式编写的,比如我们所熟悉的socket编程。客户端调用socket、connect、read和write,服务器则调用socket、bind、listen等函数。我们熟悉的大多数应用程序(Web浏览器、Web服务器、Telnet客户、Telnet服务器等程序)就是以这种方式编写的。
 
编写分布式应用程序的另一种方法就是使用隐式网络编程(implicit network programming)。远程过程调用(RPC)提供了这么一个工具。使用隐式网络编程的好处就是,程序员不需要把把大量精力放在网络通信程序的编写上,因为这一块工作已经有RPC框架帮你实现了,所以程序员可以把更多精力放在业务逻辑的开发上去。
 
这里就不对RPC做进一步详细的理论性解析,这篇文章主要讲述RPC的实践,我们将一步一步搭建起一个基于RPC的完整的分布式通信系统框架。本文分为两个部分,第一部分讲述如何利用rpcgen工具搭建起来RPC通用骨架,第二部分我们就使用该骨架进行进一步完善,增加相应的处理函数,把血肉补充完全,做一个简单的分布式计算系统demo。

参考资料:

  1. 《UNIX网络编程.卷2:进程间通信(第2版)》[PDF] 下载见 http://www.linuxidc.com/Linux/2013-01/77936.htm
  2. 《Linux C高级程序员指南》 PDF下载见 http://www.linuxidc.com/Linux/2017-02/140414.htm
  3. 《rpcgen_mannual》

rpcgen_mannual PDF可以到Linux公社资源站下载:

------------------------------------------分割线------------------------------------------

具体下载目录在 /2017年资料/2月/9日/Linux编程之从零开始搭建RPC分布式系统/

------------------------------------------分割线------------------------------------------

cccccc

一、使用rpcgen工具生成RPC底层骨架
 
1.生成my.x文件,然后在该文件编写以下程序
首先创建文件夹rpc(mkdir rpc),以后的所有文件都放在这个文件夹下。
创建my.x文件(my为文件名,.x为后缀):vi my.x
在my.x填入下面代码:
#define MY_RPC_PROG_NUM         0x38000010   //程序号

struct my_io_data_s        //定义消息结构
{
    int mtype;
    int len;
    char data[1024];
};

typedef struct my_io_data_s my_io_data_t;

program MY_RPC_PROG { 

    version MY_RPC_VERS1 {
        int MY_RPCC(my_io_data_t) = 1;    /* 过程号 = 1 */
    } = 1;        /* Version number = 1 */

    version MY_RPC_VERS2 {
        my_io_data_t MY_RPCC(my_io_data_t) = 1;    /* 过称号 = 1 */
    } = 2;        /* Version number = 2 */

} = MY_RPC_PROG_NUM;    /* Program number */
这里我创建了两个版本,version1和version2,版本的数量是可以自己定制的,如果你需要一个的话定义一个即可。因为我打算定义一个版本用于SET的操作,一个用于GET操作,所以定义了两个版本。
 
上面使用了RPC语言,我对以上几个特殊名词做一下解释。
每个RPC过程由程序号、版本号和过程号来唯一确定。
RPC版本号:程序号标志一组相关的远程过程,程序号的是有范围的,我们需要在范围内填写程序号。
 
程序号范围
简述
0x00000000 - 0x1FFFFFFF
由Sun公司定义,提供特定服务
0x20000000 - 0x3FFFFFFF
由程序员自己定义,提供本地服务或用于调试
0x40000000 - 0x5FFFFFFF
用于短时间使用的程序,例如回调程序
0x60000000 - 0xFFFFFFFF
保留程序号
 
这里我们使用的范围当然是0x20000000 - 0x3FFFFFFF,我填的是0x38000010。
 
版本号:在version的大括号里我们定义两个我们将要使用的RPC调用函数的类型,比如:
version 1:我们定义了int MY_RPCC(my_io_data_t),这表明我们以后PRC框架使用的RPC调用函数的函数类型那个将会是:int * my_rpcc_1(my_io_data_t *argp, CLIENT *clnt)
 
version 2:my_io_data_t MY_RPCC(my_io_data_t) 则会变成 my_io_data_t * my_rpcc_2(my_io_data_t *argp, CLIENT *clnt)
 
所以我们可以根据我们需要的类型模仿编写即可。
 
2.使用rpcgen指令生成以下几个文件
使用rpcgen my.x生成系列文件(可以加参数-C,表示使用ANSI C)
 
 
执行该指令后,文件夹下将出现以下几个文件。
my.h:
/*
 * Please do not edit this file.
 * It was generated using rpcgen.
 */

#ifndef _MY_H_RPCGEN
#define _MY_H_RPCGEN

#include <rpc/rpc.h>


#ifdef __cplusplus
extern "C" {
#endif


struct my_io_data_s {
    int mtype;
    int len;
    char data[1024];
};
typedef struct my_io_data_s my_io_data_s;

typedef my_io_data_s my_io_data_t;

#define MY_RPC_PROG 666
#define MY_RPC_VERS1 1

#if defined(__STDC__) || defined(__cplusplus)
#define MY_RPCC 1
extern  int * my_rpcc_1(my_io_data_t *, CLIENT *);
extern  int * my_rpcc_1_svc(my_io_data_t *, struct svc_req *);
extern int my_rpc_prog_1_freeresult (SVCXPRT *, xdrproc_t, caddr_t);

#else /* K&R C */
#define MY_RPCC 1
extern  int * my_rpcc_1();
extern  int * my_rpcc_1_svc();
extern int my_rpc_prog_1_freeresult ();
#endif /* K&R C */
#define MY_RPC_VERS2 2

#if defined(__STDC__) || defined(__cplusplus)
extern  my_io_data_t * my_rpcc_2(my_io_data_t *, CLIENT *);
extern  my_io_data_t * my_rpcc_2_svc(my_io_data_t *, struct svc_req *);
extern int my_rpc_prog_2_freeresult (SVCXPRT *, xdrproc_t, caddr_t);

#else /* K&R C */
extern  my_io_data_t * my_rpcc_2();
extern  my_io_data_t * my_rpcc_2_svc();
extern int my_rpc_prog_2_freeresult ();
#endif /* K&R C */

/* the xdr functions */

#if defined(__STDC__) || defined(__cplusplus)
extern  bool_t xdr_my_io_data_s (XDR *, my_io_data_s*);
extern  bool_t xdr_my_io_data_t (XDR *, my_io_data_t*);

#else /* K&R C */
extern bool_t xdr_my_io_data_s ();
extern bool_t xdr_my_io_data_t ();

#endif /* K&R C */

#ifdef __cplusplus
}
#endif

#endif /* !_MY_H_RPCGEN */

my_clnt.c:

/*
 * Please do not edit this file.
 * It was generated using rpcgen.
 */

#include <memory.h> /* for memset */
#include "my.h"

/* Default timeout can be changed using clnt_control() */
static struct timeval TIMEOUT = { 25, 0 };

int *
my_rpcc_1(my_io_data_t *argp, CLIENT *clnt)
{
    static int clnt_res;

    memset((char *)&clnt_res, 0, sizeof(clnt_res));
    if (clnt_call (clnt, MY_RPCC,
        (xdrproc_t) xdr_my_io_data_t, (caddr_t) argp,
        (xdrproc_t) xdr_int, (caddr_t) &clnt_res,
        TIMEOUT) != RPC_SUCCESS) {
        return (NULL);
    }
    return (&clnt_res);
}

my_io_data_t *
my_rpcc_2(my_io_data_t *argp, CLIENT *clnt)
{
    static my_io_data_t clnt_res;

    memset((char *)&clnt_res, 0, sizeof(clnt_res));
    if (clnt_call (clnt, MY_RPCC,
        (xdrproc_t) xdr_my_io_data_t, (caddr_t) argp,
        (xdrproc_t) xdr_my_io_data_t, (caddr_t) &clnt_res,
        TIMEOUT) != RPC_SUCCESS) {
        return (NULL);
    }
    return (&clnt_res);
}

my_svc.c

/*
 * Please do not edit this file.
 * It was generated using rpcgen.
 */

#include "my.h"
#include <stdio.h>
#include <stdlib.h>
#include <rpc/pmap_clnt.h>
#include <string.h>
#include <memory.h>
#include <sys/socket.h>
#include <netinet/in.h>

#ifndef SIG_PF
#define SIG_PF void(*)(int)
#endif

static void
my_rpc_prog_1(struct svc_req *rqstp, register SVCXPRT *transp)
{
    union {
        my_io_data_t my_rpcc_1_arg;
    } argument;
    char *result;
    xdrproc_t _xdr_argument, _xdr_result;
    char *(*local)(char *, struct svc_req *);

    switch (rqstp->rq_proc) {
    case NULLPROC:
        (void) svc_sendreply (transp, (xdrproc_t) xdr_void, (char *)NULL);
        return;

    case MY_RPCC:
        _xdr_argument = (xdrproc_t) xdr_my_io_data_t;
        _xdr_result = (xdrproc_t) xdr_int;
        local = (char *(*)(char *, struct svc_req *)) my_rpcc_1_svc;
        break;

    default:
        svcerr_noproc (transp);
        return;
    }
    memset ((char *)&argument, 0, sizeof (argument));
    if (!svc_getargs (transp, (xdrproc_t) _xdr_argument, (caddr_t) &argument)) {
        svcerr_decode (transp);
        return;
    }
    result = (*local)((char *)&argument, rqstp);
    if (result != NULL && !svc_sendreply(transp, (xdrproc_t) _xdr_result, result)) {
        svcerr_systemerr (transp);
    }
    if (!svc_freeargs (transp, (xdrproc_t) _xdr_argument, (caddr_t) &argument)) {
        fprintf (stderr, "%s", "unable to free arguments");
        exit (1);
    }
    return;
}

static void
my_rpc_prog_2(struct svc_req *rqstp, register SVCXPRT *transp)
{
    union {
        my_io_data_t my_rpcc_2_arg;
    } argument;
    char *result;
    xdrproc_t _xdr_argument, _xdr_result;
    char *(*local)(char *, struct svc_req *);

    switch (rqstp->rq_proc) {
    case NULLPROC:
        (void) svc_sendreply (transp, (xdrproc_t) xdr_void, (char *)NULL);
        return;

    case MY_RPCC:
        _xdr_argument = (xdrproc_t) xdr_my_io_data_t;
        _xdr_result = (xdrproc_t) xdr_my_io_data_t;
        local = (char *(*)(char *, struct svc_req *)) my_rpcc_2_svc;
        break;

    default:
        svcerr_noproc (transp);
        return;
    }
    memset ((char *)&argument, 0, sizeof (argument));
    if (!svc_getargs (transp, (xdrproc_t) _xdr_argument, (caddr_t) &argument)) {
        svcerr_decode (transp);
        return;
    }
    result = (*local)((char *)&argument, rqstp);
    if (result != NULL && !svc_sendreply(transp, (xdrproc_t) _xdr_result, result)) {
        svcerr_systemerr (transp);
    }
    if (!svc_freeargs (transp, (xdrproc_t) _xdr_argument, (caddr_t) &argument)) {
        fprintf (stderr, "%s", "unable to free arguments");
        exit (1);
    }
    return;
}

int
main (int argc, char **argv)
{
    register SVCXPRT *transp;

    pmap_unset (MY_RPC_PROG, MY_RPC_VERS1);
    pmap_unset (MY_RPC_PROG, MY_RPC_VERS2);

    transp = svcudp_create(RPC_ANYSOCK);
    if (transp == NULL) {
        fprintf (stderr, "%s", "cannot create udp service.");
        exit(1);
    }
    if (!svc_register(transp, MY_RPC_PROG, MY_RPC_VERS1, my_rpc_prog_1, IPPROTO_UDP)) {
        fprintf (stderr, "%s", "unable to register (MY_RPC_PROG, MY_RPC_VERS1, udp).");
        exit(1);
    }
    if (!svc_register(transp, MY_RPC_PROG, MY_RPC_VERS2, my_rpc_prog_2, IPPROTO_UDP)) {
        fprintf (stderr, "%s", "unable to register (MY_RPC_PROG, MY_RPC_VERS2, udp).");
        exit(1);
    }

    transp = svctcp_create(RPC_ANYSOCK, 0, 0);
    if (transp == NULL) {
        fprintf (stderr, "%s", "cannot create tcp service.");
        exit(1);
    }
    if (!svc_register(transp, MY_RPC_PROG, MY_RPC_VERS1, my_rpc_prog_1, IPPROTO_TCP)) {
        fprintf (stderr, "%s", "unable to register (MY_RPC_PROG, MY_RPC_VERS1, tcp).");
        exit(1);
    }
    if (!svc_register(transp, MY_RPC_PROG, MY_RPC_VERS2, my_rpc_prog_2, IPPROTO_TCP)) {
        fprintf (stderr, "%s", "unable to register (MY_RPC_PROG, MY_RPC_VERS2, tcp).");
        exit(1);
    }

    svc_run ();
    fprintf (stderr, "%s", "svc_run returned");
    exit (1);
    /* NOTREACHED */
}

my_xdr.c

/*
 * Please do not edit this file.
 * It was generated using rpcgen.
 */

#include "my.h"

bool_t
xdr_my_io_data_s (XDR *xdrs, my_io_data_s *objp)
{
    register int32_t *buf;

    int i;
     if (!xdr_int (xdrs, &objp->mtype))
         return FALSE;
     if (!xdr_int (xdrs, &objp->len))
         return FALSE;
     if (!xdr_vector (xdrs, (char *)objp->data, 1024,
        sizeof (char), (xdrproc_t) xdr_char))
         return FALSE;
    return TRUE;
}

bool_t
xdr_my_io_data_t (XDR *xdrs, my_io_data_t *objp)
{
    register int32_t *buf;

     if (!xdr_my_io_data_s (xdrs, objp))
         return FALSE;
    return TRUE;
}

猜你喜欢

转载自www.linuxidc.com/Linux/2017-02/140415.htm