查看原文
其他

咖啡馆的故事:FTP, RMI , XML-RPC, SOAP, REST一网打尽

码农翻身刘欣 码农翻身 2021-04-27


周末的咖啡馆有点奇怪,  一群人围着几个老头儿在聊天。


 “快说说,你们那个时候没有HTTP, 没有JavaScript,到底是怎么让这些机器上的程序进行'交谈'的?” 


ftp老头儿满脸沧桑,喝了一口咖啡,说道:“简单得很,机器A通过我,就是ftp, 上传一个文件到机器B的指定路径,然后再让rexec 去调用机器B上的程序,这程序是程序员写的,可以读取FTP目录下的文件,执行业务逻辑就行了。” 



“什么是rexec ? ”  


“就是从一台机器上远程执行另外一个机器的命令嘛!” rexec老头儿略带怒气地说道,自己虽然没有ftp出名,但是不至于没人知道吧!

rexec [remote_host] [command] 


“切!骗谁呢,根本不可能,怎么会用这么笨的方式!”人群中传来了表示不屑的声音。  


ftp和rexec相视苦笑,这些程序员不会想到,早些年真有这么做的系统,是真实的故事。    


ftp招呼telnet一起喝咖啡,不再出声。 


RPC


门口传来一阵喧嚣,CORBA和Java RMI风风火火地走了进来。 


人群呼啦一下子就围了上去,抛弃了那三个老头儿。 


只听见Java RMI说道:“所有的程序本质上都是函数调用,函数调用在一个进程内是无比自然的事情。 如果是跨越机器、跨越进程呢? 如果一个机器上的函数,能调用另外一个机器上的函数,就像调用本地方法一样,会是什么样子? ”  


CORBA笑着说:“哈哈, 画面太美不敢想象。 ” 


人群中有人问道:“调用远程方法就像调用本地方法一样?怎么可能?” 


Java RMI说道:“在概念上其实极其简单,无非就是自动生成客户端代理和服务器端代理,这两个代理完成了大量的脏活和累活,比如:网络通信,参数序列化...... ” 


CORBA接着说道:“魔法都在这两个代理当中,我们称之为Stub(客户端代理)和Skeleton(服务器端代理)。这个Stub代理提供了和服务器一模一样的接口,客户端程序只要调用它,它就会把请求发到服务器端的Skeleton代理进行处理。 所以对于客户端程序来说,网络不可见,就像是调用本地的方法一样。” 



Java RMI说道:“对,我们把这种方式称为RPC。” 


人群中发出一片惊叹声:“这RPC可真好啊,Stub和Skeleton代码能自动生成,我们拿到以后,马上就可以动手编程了,底层什么都不用关心。 ” 


Java RMI说道:“底层可以采用二进制的协议,性能不要太好哦!”  


人群中又是一阵欢呼:“太好了!”


那边的ftp老头儿警告到:“大家要小心,要注意平台绑定,你想用Java RMI吗? 对不起,客户端和服务器都得用Java,都得安装Java虚拟机,  什么Python, C#, 没门儿, 连想都不要想。 ”  


telnet接着说:“更重要的是客户端和服务器紧密绑定,服务器端的变化,都必须得重新生成Stub和Skeleton 。 ” 

没想到这些老家伙们目光如炬。 


 “什么? 这也太无理了吧!” 人群呼啦一声又涌到了三个老头那里。 


只见ftp老头儿在纸上写到: 比如说有这么一个接口, 可以根据用户ID查找用户信息。


public interface UserService extends Remote{
    public User findUser(int  id) throws RemoteException;
}


利用Java RMI的工具,可以生成Stub和Skeleton, 客户端拿到Stub以后,可以开心地去编程了。 


至于UserService的具体实现代码,客户端不用操心。  


过了两天,某个客户端要求要给这个接口增加一个新的方法:按照名称来查找用户。 


public User findUser(String name) throws RemoteException;


那对不起了,需要重新生成新的Stub和Skeleton, 所有的客户端都会受到影响,即使你根本不需要新的方法。 


大家纷纷唉声叹气,这RPC实在是太烦人了! 


有没有一种办法,让服务器端独立变化,而不影响客户端,或者说尽量不影响客户端呢? 


XML-RPC


后面有个小伙字若有所思,他刚学会了XML, 他觉得既然XML的描述能力这么强,能不能用XML来描述一个方法调用和参数呢? 


比如服务器端有个接口是getUser,需要提供的参数是用户ID, 可以这么描述: 



然后通过HTTP Post把这个XML发送到服务器端,服务器端进行解析,获取方法名称和参数的值,调用真正的方法,把结果也以XML形式返回, 客户端收到以后再解析就可以得到结果了。  


想到此处,他大声叫道:“别生成什么Stub和Skeleton代理了,直接用HTTP和XML该多好啊。” 


人群被他的奇异想法所吸引,呼啦一下又围了过来。 


小伙子画了一张图, 展示了这个处理的过程: 



有人问道:“返回的数据格式可能很复杂, 怎么表示啊。” 


小伙子说:“这正是XML的强项啊,图中展示的是int型,还可以有double ,boolean ,string 等各种类型,甚至可以定义结构体。” 



对XML来说,这样的结构体就是小菜一碟。 


“这样客户端和服务器端就变成松耦合的了,如果服务器端想添加一个新的接口,客户端就不用做变化了。我打算把他叫做XML-RPC”  小伙子说道。  


“这种办法真好!” 人群中开始躁动起来,“我们都用XML-RPC吧!” 


SOAP


“小伙子,你叫什么名字?” 狂热的人群中有个人冷静地问道。 


“Dave Winer, 怎么了? ”  


“Winner? 嗯,你的名字真不错,天生赢家啊, 有没有兴趣和我们微软一起制定一个新的RPC标准?” 


“新标准? 我的XML-RPC已经很完善了,又简单又好用。”


“No,No, 还欠缺不少东西,最要命的就是客户端和服务器端没有正式的协议约定,都是口头约定,或者文档约定,对吧?” 


Dave Winer点点头。 


“你想想,如果我们把一个服务器对外提供的接口也用XML精确地描述下来,任何程序,只要读取这个XML文件,就知道接口的方法名,参数名,该有多好?” 


Dave Winer又点点头。 


“还有啊,你的XML-RPC只支持HTTP, 我们的新标准可以支持任意协议啊, HTTP, SMTP,TCP,UDP......都可以。” 


“我还是觉得HTTP最好!”  


“想想看,如果我们的新协议搞成了,所有的B2B的电子商务系统都可以用这一套协议来自动通信,多么完美的世界! 你仔细想想,你是想在这个破咖啡馆喝一辈子咖啡,还是想和我们微软一起改变世界?” 


一年以后,Dave Winer 新的协议问世了,不,这其实是一套协议:  


WSDL :用于描述一个服务的接口,参数...... 


UDDI : 实现服务的注册和发现 


SOAP : 和XML-RPC很像,但是更加规范,更加正式,更加复杂...... 


他们之间的关系如图所示: 



微软的.NET战略适时启动,Web Service的宣传铺天盖地:你只要用WSDL定义了接口,就可以选择任何语言来实现! Java , Python, 甚至C语言都可以,当然,我们的Visual Studo, C#和它结合得更好,欢迎使用。 


人们趋之若鹜。 


几年以后


Dave Winer又一次来到了咖啡馆,这一次他选择了一个角落坐下,要了一杯咖啡,静静地听大家聊天。 

“你们知道吗,微软太坑爹了,那个SOAP实在是太难用了!”


“没错没错,罗嗦,罗嗦,太罗嗦了。你看看,我每次发个SOAP请求得多麻烦:” 


<?xml version="1.0"?>
<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope" xmlns:m="http://www.example.org">
  <soap:Header>
  </soap:Header>
  <soap:Body>
    <m:GetUser>
      <m:UserID>1001</m:UserID>
    </m:GetUser>
  </soap:Body>
</soap:Envelope>


“这算什么,返回值也是同样罗嗦的XML,解析起来实在是累死了。”


“是啊,如果没有可视化工具的辅助,简直是无法使用。” 


Dave Winer一边喝咖啡一边想,没办法,这XML就是这样,不过我们的SOAP搞得是不是有点过分了?


“我觉得这就是那些大厂商们为了赚钱而搞出的东西,都是为了卖他们的软件,一点都不实用!” 


“我们还是回归最简单的HTTP调用吧!”  有人提议。 


比如想获取一个用户的信息,可以调用这样的API  http://xxx.com/getUser?id=1001 


“服务器端还要返回又臭又长的XML吗? ”


“不,我们可以用JSON这种数据格式,简洁紧凑,对JavaScript非常友好,处理起来非常方便。” 


大家都表示同意。 


“大家别激动,如果用这种方式,和原来的XML-RPC本质上是一样的,都是把服务器端看做是一堆函数的集合,然后客户端去调用他们。Java RMI是通过Stub/Skeleton代理的方式,XML-RPC是通过XML的方式。” 一个叫做罗伊的小伙提醒道。 



 “那可不咋地,服务器端不就是一堆函数吗?”  有人说道。  


“大家转换一下思路,别把他们当成函数,当成资源(Resource), 从动词转换成名词试试。”  


听到罗伊这新奇的想法,一群人又围了上来。


“名词? 资源? ”


“是啊,比如说用户,学生,订单等等。他们天然可以用uri来表示。” 


 




“有点意思, 那对这些资源怎么操作?” 


“HTTP的方法GET,POST, DELETE,PUT,HEAD...... 可以充当动词啊。”罗伊说道。  



“我的妈啊,你竟然把HTTP的方法当成增删改查了。” 


话虽这么说,可是大家都觉得这种方式挺简单的,充分利用了HTTP的特性,只要脑子里不要把服务器端看成函数,而是当作一堆名词资源就可以了。


“这种方式叫什么名字?” 


“RESTful API !”  


“这RESTful看起来不错啊,要不我们试试?”


“试试去!不行的话就找这个罗伊算账!”


(码农翻身注: RESTful 还有更多的内容,例如Stateless, Cacheable,..... 这篇文章已经够长了,不再展开了。)


往期精彩回顾

我是一个线程

我是一个Java Class

面向对象圣经

函数式编程圣经

TCP/IP之大明邮差

CPU阿甘

我是一个网卡

我是一个路由器

一个故事讲完HTTPs

编程语言的巅峰

Java:一个帝国的诞生

JavaScript:一个屌丝的逆袭

    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存