周末的咖啡馆有点奇怪, 一群人围着几个老头儿在聊天。
“快说说,你们那个时候没有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看起来不错啊,要不我们试试?”
“试试去!不行的话就找这个罗伊算账!”
【本文为51CTO专栏作者“刘欣”的原创稿件,转载请通过作者微信公众号coderising获取授权】