使用rpcgen构建分布式程序的例子
- 格式:doc
- 大小:208.50 KB
- 文档页数:20
分布式系统的重要不言而喻, 下面是使用rpcgen工具构建一个分布式程序的例子, 其实主要就是<TCP/IP进行网际互联>卷3上面的那个例子, 就是下面这本书.另外还参考了<分布式系统原理与范例>这本书, 即Andrew S. Tanenbaum写的这本, 这本书是学习分布式的必读书之一. 相当不错.整个文章分为三部分, 第一部分是对RPC(远程过程调用)的一个介绍, 第二部分是对使用rpcgen构建分布式程序的一个介绍, 第三部分是使用rpcgen构建分布式程序的详细步骤.可以先按照第三部分的步骤自己先试试, 如果有兴趣再回过头来看它的介绍. 另外, 我写的这个只是一个入门, 如果以后有机会再详细地讨论一下内部的机理. 事情总是一步一步来的.一. RPC概念1.1 介绍在中间件的实现中,引入了远程过程调用RPC(Remote Procedure Call)的概念。
同时,许多分布式系统是基于进程间的显式消息交换的,然而消息的发送和接收过程无法隐藏通信的存在,而通信的隐藏对于在分布式系统中实现访问透明性是极为重要的。
因此这个问题在很长一段时间内都没有找到合适的解决办法,后来Birrel和Nelson在1984年的一篇论文中引入了一套与传统方法截然不同的通信处理手段。
他们认为应该允许程序调用位于其它机器上的进程。
当机器A上的进程调用B上的进程时,A上的调用进程被挂起,而B上的被调用进程开始执行。
调用方可以通过使用参数将信息传送给被调用方,然后可以通过传回的结果得到信息。
编程人员看不到任何消息传递过程。
这种方法就称为远程过程调用RPC。
目前,RPC作为一种广泛使用的技术,已成为许多分布式系统的基础。
1.2 构建分布式程序的两种模式在设计分布式应用时,程序员可以使用下列两种方法之一:面向通信的设计由通信协议开始。
设计报文格式和语法,指明对每个传入报文将如何反应以及如何产生每个外发报文,以此来设计客户和服务器各构件。
面向应用的设计由应用开始。
设计常规的应用程序来解决问题。
构建并测试可在单台机器上运行的常规程序的工作版本。
将这个程序划分成两个或多个程序片,加入通信协议以允许每片程序在单独的计算机上执行。
远程过程调用模型使用面向应用的方法,它强调的是所要解决的问题而不是所需要的通信。
利用远程过程调用,程序员首先设计一个解决问题的常规程序,接着将其划分成若赶干片,这些程序片运行在两台或更多的计算机上。
程序员可遵循良好的设计原则,以便使代码模块化并且可维护。
在理想的情况下,远程过程调用提供的不只是抽象上的概念。
它允许程序员在将一个程序划分成若干片之前,先构建,编译和测试一个解决该问题的常规程序的版本,以便确保能够正确解决问题。
不但如此,因为RPC以方法调用为边界划分程序,所以将程序划分为本地部分和远程部分并不会引起程序结构的很大变化。
实际上,将某些过程从一个程序转移到远程机器上时,有可能不需要改变。
1.3 常规过程调用的概念性模型如下图所示,为常规的程序调用。
本地过程调用.jpg1.4 远程过程调用模型远程过程调用模型使用了和常规程序一样的过程抽象,但是它允许一个过程的边界跨越两台计算机。
如下图所示。
远程过程调用.jpg1.5 常规过程调用的执行和返回程序从一个主程序开始执行,并一直继续下去,直到遇到一个过程调用。
这个调用使程序的执行转入到某个指定的代码处继续执行。
常规过程调用的执行流程如下图所示:常规过程调用.jpg1.6 分布式系统的过程模型在分布式系统中,其中的某个过程有可能在另外的机器上,因此,其调用过程模型如下图如示:分布式中的过程调用.jpg1.7 客户-服务器和RPC之间的对比远程过程调用允许程序员以一种他所熟悉的环境来思考客户和服务器的交互,如同常规的过程调用,远程过程调用把控制权传递给被调用的进程。
也像常规过程调用一样,在调用进程中,系统把调用过程的执行挂起。
而只允许被调用过程执行。
当远程程序发出响应时,这对应于在常规过程调用中执行return。
控制权返回给调用者,被调用过程停止执行。
嵌套的过程调用的想法也可应用到远程过程调用。
远程过程调用也许要调用另一个远程过程。
如上图所示。
二. 分布式程序的生成原理RPC的实现包括一个工具,它自动地生成实现分布式程序所需要的大多数代码。
这个工具叫做rpcgen,它读取一个规约文件作为输入,生成C的源文件作为输出。
规范文件包含常量,全局数据类型,全局数据以及远程过程(包括过程参数和结果类型)的声明。
rpcgen产生的代码包含了实现客户和服务器程序所需要的大部分源代码。
具体地说,rpcgen包括参数整理,发送RPC报文,把传入调用分派到正确的过程,发送应答,在参数和结构的外部表示和本地数据表示之间进行转换。
rpcgen的输出与应用程序和程序员编写的少数文件相结合后,便产生出了完整的客户和服务器程序。
rpcgen读取输入文件,该文件包含有对远程过程的说明。
它产生四个输出文件,每个文件都包含有源代码,如果输入的文件(即规约文件)具有名字q.x,则输出的文件如下所示:q.h:常量和类型的声明q_xdr.c XDR过程调用q_clnt.c 客户端的通信接口q_svc.c 服务器端的通信接口具体步骤在第三部分。
三. 分布式程序的生成步骤在这里,举的例子就是Douglas er书上的那个例子。
这个例子重点在于解释rpcgen 如何工作的。
3.1 查找字典在该例子中,考虑实现一个简单的数据库功能的程序。
该数据库提供四个基本的操作:初始化,插入一个新的条目,删除一个条目,查找一个条目。
假设每个条目都是一个单词。
因此该数据库的功能就可以看作是一个字典。
应用程序插入一组单词,接着使用数据库来检查新单词,以便知道这个单词是否在该字典中。
3.2 构建程序的八个步骤(1)构建解决该问题的常规应用程序要构建这个字典应用例子的分布式版本,第一步要求程序员构造解决该问题的常规程序。
文件dict.c包含了该常规程序:CODE:/* dict.c */#include<stdio.h>#include<stdlib.h>#include<ctype.h>#include<string.h>#define MAXWORD 50 /* 每个单词的长度限制*/#define DICTSIZ 100 /* 字典一共可以存放多少个单词 */char dict[DICTSIZ][MAXWORD+1];int nwords = 0;/** nextin(): 读入命令与单词*/int nextin(char *cmd, char *word){int i, j;char buf[100] = {0};if (fgets(buf, sizeof(buf)-1, stdin) == NULL) return -1;for (i = 0; i < 100; i++)if (buf[i] != ' ')break;if (i == 100)return -1;*cmd = buf[i];while (1){if (i == 100)return -1;i++;if (buf[i] == ' ')continue;else if (buf[i] == '\n')return 0;elsebreak;}j = 0;while (buf[i] != '\n'){*word++ = buf[i];i++;j++;}return j;}/** initw(): 初始化这个字典*/int initw(){nwords = 0;return 1;}/** insertw(const char *word): 向这个字典中加入单词 */int insertw(const char *word){strcpy(dict[nwords], word);nwords++;return nwords;}/** deletew(const char *word): 从这个字典中删除单词 */int deletew(const char *word){int i;for (i = 0; i < nwords; i++){if (strcmp(word, dict[i]) == 0){nwords--;strcpy(dict[i], dict[nwords]);return 1;}}return 0;}/** lookupw(const char *word): 在字典中查找某个单词 */int lookupw(const char *word){int i;for (i = 0; i < nwords; i++){if (strcmp(word, dict[i]) == 0)return 1;}return 0;}int main(int argc, char **argv){char word[MAXWORD+1];char cmd;int wrdlen;while (1){wrdlen = nextin(&cmd, word);if (wrdlen < 0)exit(0);word[wrdlen] = '\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.\n", word);break;case 'l':if (lookupw(word))printf("%s is found.\n", word);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;}该dict.c程序使用了一个二维数组来存储单词。