使用rpcgen构建分布式程序的例子_第1页
使用rpcgen构建分布式程序的例子_第2页
使用rpcgen构建分布式程序的例子_第3页
使用rpcgen构建分布式程序的例子_第4页
使用rpcgen构建分布式程序的例子_第5页
免费预览已结束,剩余17页可下载查看

付费下载

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、!-分布式系统的重要不言而喻,下面是使用rpcgen工具构建一个分布式程序 的例子,其实主要就是VTCP/IP进行网际互联 卷3上面的那个例子,就是下面 这本书.另外还参考了 分布式系统 原理与范例 这本书,即Andrew S. Tanenbaum写的 这本,这本书是学习分布式的必读书之一.相当不错.整个文章分为三部分,第一部分是对RPC远程过程调用)的一个介绍,第二部 分是对使用rpcgen构建分布式程序的一个介绍,第三部分是使用rpcgen构建分 布式程序的详细步骤.可以先按照第三部分的步骤自己先试试,如果有兴趣再回过头来看它的介绍. 另外,我写的这个只是一个入门,如果以后有机会再详细地讨

2、论一下内部的机 理.事情总是一步一步来的. RPC概念1.1介绍在中间件的实现中,弓I入了远程过程调用RPC( Remote Procedure Call )的概念。同时,许多分布式系统是基于进程间的显式消息交换的,然而消息的发送和接收过程无法隐藏通信的存在,而通信的隐藏对于在分布式系统中实现访问透明性是极为重要的。因此这个问题在很长一段时间内都没有找到合适的解决办 法,后来Birrel和Nelson在1984年的一篇论文中引入了一套 与传统方法截然 不同的通信处理手段。他们认为应该允许程序调用位于其它机器上的进程。当机器A上的进程调用B上的进程时,A上的调用进程被挂起,而B上的被调用进 程开

3、始执行。调用方可以通过使用参数将信息传送给被调用方,然后可以通过传 回的结果得到信息。编程人员看不到任何消息传递过程。这种方法就称为远程过程调用RPC目前,RPC乍为一种广泛使用的技术,已成为许多分布式系统的 基础。1.2构建分布式程序的两种模式在设计分布式应用时,程序员可以使用下列两种方法之一:面向通信的设计由通信协议开始。设计报文格式和语法,指明对每个传入报文将如何反应以及 如何产生每个外发报文,以此来设计客户和服务器各构件。面向应用的设计由应用开始。设计常规的应用程序来解决问题。构建并测试可在单台机器上运 行的常规程序的工作版本。将这个程序划分成两个或多个程序片,加入通信协议 以允许每片

4、程序在单独的计算机上执行。远程过程调用模型使用面向应用的方法,它强调的是所要解决的问题而不是所 需要的通信。利用远程过程调用,程序员首先设计一个解决问题的常规程序, 接 着将其 划分成若赶干片,这些程序片运行在两台或更多的计算机上。程序员可 遵循良好的设计原则,以便使代码模块化并且可维护。在理想的情况下,远程过程调用提供的不只是抽象上的概念。它允许程序员在 将一个程序划分成若干片之前,先构建,编译和测试一个解决该问题的常规程序 的版本,以便确保能够正确解决问题。不但如此,因为RPC以方法调用为边界划分程序,所以将程序划分为本地部分和远程部分并不会引起程序结构的很大变 化。实际 上,将某些过程从

5、一个程序转移到远程机器上时,有可能不需要改变。1.3常规过程调用的概念性模型 如下图所示,为常规的程序调用。本地过程调用.jpg1.4远程过程调用模型远程过程调用模型使用了和常规程序一样的过程抽象, 边界跨越两台计算机。如下图所示。但是它允许一个过程的:| szmj远程过程调用.jpg1.5常规过程调用的执行和返回程序从一个主程序开始执行,并一直继续下去,直到遇到一个过程调用。这个 调用使程序的执行转入到某个指定的代码处继续执行。常规过程调用的执行流程 如下图所示:主軽序找码过理A代码常规过程调用.jpg1.6分布式系统的过程模型在分布式系统中,其中的某个过程有可能在另外的机器上, 模型如下图

6、如示:因此,其调用过程机器1 1側I:剖4黑)机需2上购进崔AO第器机曙:J匕邮】过程创1脛并簿)分布式中的过程调用.jpg1.7客户-服务器和RPC之间的对比远程过程调用允许程序员以一种他所熟悉的环境来思考客户和服务器的交互, 如同常规的过程调用,远程过程调用把控制权传递给被调用的进程。也像常规过程调用一样,在调用进程中,系统把调用过程的执行挂起。 而只允许被调用过程 执行。当远程程序发出响应时,这对应于在常规过程调用中执行return。控制权返回给调用者, 被调用过程停止执行。 嵌套的过程调用的想法也可应用到远程过程 调用。远程过程调用也许要调用另一个远程过程。如上图所示。二 . 分布式程

7、序的生成原理RPC 的实现包括一个工具,它自动地生成实现分布式程序所需要的大多数代 码。这个工具叫做rpcgen,它读取一个规约文件作为输入,生成 C的源文件作 为 输出。规范文件包含常量,全局数据类型,全局数据以及远程过程(包括过 程参数和结果类型)的声明。 rpcgen 产生的代码包含了实现客户和服务器程序 所需 要的大部分源代码。具体地说,rpcgen包括参数整理,发送RPC报文,把 传入调用分派到正确的过程, 发送应答, 在参数和结构的外部表示和本地数据表 示 之间进行转换。 rpcgen 的输出与应用程序和程序员编写的少数文件相结合 后,便产生出了完整的客户和服务器程序。rpcgen

8、 读取输入文件,该文件包含有对远程过程的说明。它产生四个输出文 件,每个文件都包含有源代码,如果输入的文件(即规约文件)具有名字 q.x , 则输出的文件如下所示:q.h :常量和类型的声明 q_xdr.c XDR 过程调用 q_clnt.c 客户端的通信接口 q_svc.c 服务器端的通信接口 具体步骤在第三部分。三. 分布式程序的生成步骤在这里,举的例子就是Douglas E.Comer书上的那个例子。这个例子重点在于 解释 rpcgen 如何工作的。3.1 查找字典在该例子中, 考虑实现一个简单的数据库功能的程序。 该数据库提供四个基本 的操作:初始化,插入一个新的条目,删除一个条目,查

9、找一个条目。假设每个 条目都 是一个单词。因此该数据库的功能就可以看作是一个字典。应用程序插 入一组单词,接着使用数据库来检查新单词, 以便知道这个单词是否在该字典中。 3.2 构建程序的八个步骤包含了该常规程序:(1)构建解决该问题的常规应用程序 要构建这个字典应用例子的分布式版本, 第一步要求程序员构造解决该问题的 常规程序。文件 dict.cCODE:/* dict.c */#include<stdio.h> #include<stdlib.h> #include<ctype.h> #include<string.h>#define MAX

10、WORD 50#define DICTSIZ 100/*/*每个单词的长度限制 */字典一共可以存放多少个单词 */char dictDICTSIZMAXWORD+1; int nwords = 0;/* nextin():读入命令与单词*/ int nextin(char *cmd, char *word)int i, j;char buf100 = 0;if (fgets(buf, sizeof(buf)-1, stdin) = NULL)return -1;for (i = 0; i < 100; i+)if (bufi != ' ')break;if (i = 1

11、00)return -1;*cmd = bufi;while (1)if (i = 100) return -1;i+;if (bufi = ' ')continue;else if (bufi = 'n')return 0;elsebreak;j = 0;while (bufi != 'n')*word+ = bufi;i+;j+;return j;!-/* initw():*/初始化这个字典int initw()nwords = 0;return 1;/* insertw(const char *word):向这个字典中加入单词*/int i

12、nsertw(const char *word)strcpy(dictnwords, word);nwords+;return nwords;/* deletew(const char *word): */从这个字典中删除单词int deletew(const char *word)int i;for (i = 0; i < nwords; i+)if (strcmp(word, dicti) = 0)nwords-;strcpy(dicti, dictnwords);return 1;return 0;/* lookupw(const char *word): */在字典中查找某个单词

13、int lookupw(const char *word)!-int i;for (i = 0; i < nwords; i+)if (strcmp(word, dicti) = 0)return 1;return 0;int main(int argc, char *argv)char wordMAXWORD+1;char cmd;int wrdlen;while (1)wrdlen = nextin(&cmd, word);if (wrdlen < 0)exit(0);wordwrdlen = '0'switch(cmd)case 'I'

14、:initw();printf("Dictionary initialized to empty.n");break;case 'i':insertw(word);printf("%s inserted.n", word);break;case 'd':if (deletew(word)printf("%s deleted.n", word);elseprintf("%s not found.n", word);break;case 'l':if (lookupw(w

15、ord)printf("%s is found.n", word);elseprintf("%s is not found.n", word); break;case 'q':printf("program quits.n");exit(O);default:p rintf("command %c invalid.'n", cmd);break;return 0;该dict.c程序使用了一个二维数组来存储单词。全局变量nwords记录了任何时刻字典中单词的数量。主程序包含一个循环,在每次循环

16、中读取并处 理输入文件中的一行。该程序就是普通的常规程序,编译并运行:gcc dict.c - o dict./dict可以如下来实验QUOTE:i hello i world l hello d hello l hello q(2将程序划分成两部分常规程序一旦构建完成并经过测试,就可以将它划分成本地构件和远程 构件了。下图为这个程序的过程调用情况:程序组织情况.jpg在考虑哪个过程可以转移到远程机器上时,程序员必须考虑每个过程所需要 的资源。例如nextin在每次被调用时要读取下一个输入行,并对它进行分析。 因为 它需要访问程序的标准输入,所以nextin必须放在主程序的机器中。简单 来说就

17、是执行I/O或者访问文件描述符的过程不能轻易地转移到远程机器中。同时还要考虑每个过程所要访问的数据所处的位置。例如,lookupw需要访问全部单词数据库,如果执行lookupw的机器不同于字典所处的机器,对lookupw 的RPC调用就必须将整个字典作为参数来传递。将巨大的数据结构作为参数传递 给远程过程的效率非常低。一般来说执行过程的机器应当与放置过程所要访问数据的机器是同一台,将巨大的数据结构传递给远程过程的效率是很低的。对于本应用来说,应当把过程 insertw ,deletew,initw ,lookupw和字典本 身放在同一台机器中。下图说明了这种划分方式:H算机戈k的近程樫徑il算

18、机划分程序.jpg相当于将dict.c划分成了两个文件dictl.c和dict2.c。这里的两个程序只是一个示例,不需要写出来,但是后面将要编写与这两个程序相似的 另外两个程序。文件dictl.c包含主程序和过程nextin :CODE:/* dictl.c */#includevstdio.h>#includevstdlib.h> #include<ct yp e.h> #include<string.h>!-#define MAXWORD 50 char dictDICTSIZMAXWORD+1;int nwords = 0;int nextin(cha

19、r *cmd, char *word)int i, j;char buf100 = 0;if (fgets(buf, sizeof(buf)-1, stdin) = NULL) return -1;for (i = 0; i < 100; i+)if (bufi != ' ')break;if (i = 100)return -1;*cmd = bufi;while (1)if (i = 100)return -1;i+;if (bufi = ' ')continue;else if (bufi = 'n')return 0;elsebre

20、ak;j = 0;while (bufi != 'n')*word+ = bufi;j+;return j;i+;!-int main(int argc, char *argv)char wordMAXWORD+1;char cmd;int wrdlen;while (1)wrdlen = nextin(&cmd, word);if (wrdlen < 0)exit(0);wordwrdlen = '0'switch(cmd)case 'I':initw();printf("Dictionary initialized t

21、o empty.n");break;case 'i':insertw(word);printf("%s inserted.n", word); break;case 'd':if (deletew(word)printf("%s deleted.n", word);elseprintf("%s not found.n", word);break;case 'l':if (lookupw(word)printf("%s is found.n", word);

22、elseprintf("%s is not found.n", word);break;case 'q':printf("program quits.n");exit(0);default:printf("command %c invalid.n", cmd);break;return 0;!- 文件 dict2.c 包含了来自最初的应用程序的一些函数。它们将成为远程 程序的一部分。另外,还包含对各个函数要共享的全局数据的声明。CODE:/* dict2.c */ #include<string.h> #d

23、efine MAXWORD 50 #define DICTSIZ 100 char dictDICTSIZMAXWORD+1;int nwords = 0;int initw()nwords = 0;return 1;int insertw(char *word)strcpy(dictnwords, word);nwords+;return nwords;int deletew(char *word)int i;for (i = 0; i < nwords; i+)if (strcmp(word, dicti) = 0)nwords-;strcpy(dicti, dictnwords);

24、return 1;return 0;int lookupw(char *word)int i;for (i = 0; i < nwords; i+)if (strcmp(word, dicti) = 0) return 1;return 0;注意,对符号常量MAXWORD定义在两个构件中都出现了,因为它们都 要声明用于存储字的变量, 然而,只有在文件 dict2.c 中才含有用于存储 字典的数据结构的声明,因为只有远程过程才包含字典的数据结构。此时gcc - c dictl.cgcc - c dict2.c 编译一下检查是否有语法错误。( 3)创建 rpcgen 规约 程序员一旦为某个分

25、布式程序选择了一种结构,就可以准备 rpc 规约 了。从本质上说, rpcgen 规约文件包含了对远程程序的声明以及它所使 用的数据结构。该规约文件包含常量, 类型定义,以及对客户和服务器程序的声明。 即: 声明在客户或服务器中所使用的常量RPC编程语言来给出。对于该例子的规约如下,声明所使用的数据类型 声明远程程序,每个程序中所包含的过程以及它们的参数类型 所有这些声明必须用rdict.x :CODE:/* rdict.x */const MAXWORD = 50;const DICTSIZ = 100;struct exampleint exfield1;char exfield2;pro

26、gram RDICTPROG/*远程程序的名称 */version RDICTVERS/*版本 */int INITW(void) = 1; int INSERTW(string)= 2;int DELETEW(string) = 3;int LOOKUPW(string)= 4;/*/*/*/*第一个函数 */第二个函数 */第三个函数 */第四个函数 */该程序的版本号 = 0x30090949;/*远程程序标识 , 必须唯一 */ ( 4)运行 rpcgen在完成了规约后,程序员运行 rpcgen 来检查语法错误,并且生成四个代码文件: rdict.h rdict_clnt.c rdic

27、t_svc.c和 rdict_xdr.c 。 = 1;/*/输入命令:rpcgen rdict.x为rpcgen产生的XDF转换文件,XDF即卩External Data 。是数据传输的一个规范。为rpcgen产生的客户端的代码。为rpcgen产生的服务器端的代码。rdict.h 为rpcgen产生的.h文件,即头文件。rdict_xdr.cRep rese ntati onrdict_cl nt.crdict_svc.c这些文件一旦生成,就可以被编译成目标代码的形式。-c rdict_cInt.c-c rdict_svc.c-c rdict_xdr.cgccgccgcc到了这一步,需要程序员

28、再编写接口过程。程序员需要编写四个程序, 分别为客户端程序与客户端的接口程序,服务器程序与服务器端的接口程 序。如下图所示,图中阴影部分为需要程序员自己编写的代码:稈序框架jpg(5)编写接口过程rpcgen 产生的文件并没有构成完整的程序,它还要求程序员必须编写 客户端和服务器端的接口。客户端接口 rdict_cif.c 程序如下所示:CODE:/* rdict_cif.c */ #include<rpc/rpc.h> #include<stdio.h> #define RPC_CLNT #include"rdict.h" extern CLIEN

29、T *handle;static int *ret;int initw()ret = initw_1(0, handle);return ret = NULL ? 0 : *ret;int insertw(char *word)char *arg;arg = &word;ret = insertw_1(arg, handle);return ret = NULL ? 0 : *ret;int deletew(char *word)char *arg;arg = &word;ret = deletew_1(arg, handle);return ret = NULL ? 0 :

30、*ret;int lookupw(char *word)char *arg;arg = &word;ret = lookupw_1(arg, handle);return ret = NULL ? 0 : *ret; 服务器端接口例程 rdict_sif.c 如下:CODE:/* rdict_sif.c */ #include<rpc/rpc.h> #define RPC_SVC #include"rdict.h" static int retcode;int initw(void), insertw(char *), deletew(char *),

31、lookupw(char *);int *insertw_1_svc(char *w, struct svc_req* rqstp)retcode = insertw(*(char*)w);return &retcode;int *initw_1_svc(void* w, struct svc_req* rqstp)retcode = initw();return &retcode;int *deletew_1_svc(char *w, struct svc_req* rqstp)retcode = deletew(*(char*)w);return &retcode;

32、int *lookupw_1_svc(char *w, struct svc_req *rqstp)retcode = lookupw(*(char*)w);return &retcode;(6)编译和链接客户程序首先gcc - c rdict_cif.c编译客户端的接口程序。再 gcc - c rdict_xdr.c 编译 XDR程序。RPC!信例程用该句柄和而客户端程序还需要声明并初始化一个句柄, 服务器通信。客户端程序 rdict.c 如下:CODE:/* rdict.c */ #include<rpc/rpc.h> #include<stdlib.h>

33、#include<stdio.h> #include<ctype.h> #include"rdict.h"服务器端 ip address */#define MAXWORD 50 #define RMACHINE "" /*CLIENT *handle;int nextin(char *cmd, char *word)int i, j;char buf100 = 0;if (fgets(buf, sizeof(buf)-1, stdin) = NULL)return -1;for (i = 0; i < 1

34、00; i+) if (bufi != ' ')break;if (i = 100) return -1;*cmd = bufi;while (1)if (i = 100)return -1;i+;if (bufi = ' ')continue;else if (bufi = 'n')return 0;elsebreak;j = 0;while (bufi != 'n')*word+ = bufi;!-j+;return j;int main(int argc, char *argv)char wordMAXWORD+1;char

35、 cmd;int wrdlen;handle = clnt_create(RMACHINE, RDICTPROG, RDICTVERS, "tcp");if (handle = NULL)printf("cound not contact remote program.n");exit(1);while (1)wrdlen = nextin(&cmd, word);if (wrdlen < 0)exit(0);wordwrdlen = '0'switch(cmd)case 'I':initw();printf("Dictionary initialized to empty.n");break;case 'i':insertw(word);printf("%s inserted.n", word);break;case 'd':if (deletew(word)printf("%s deleted.n", word); elseprintf("%s not found

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论