joe老大关于rpc之争的文章

joe老大关于rpc之争的文章
原文:http://armstrongonsoftware.blogspot.com.nyud.net/2008/05/road-we-didnt-go-down.html

通过我的哪吒机器人,我收到joe老大blog更新的消息,以为joe老大的blogspot不会更新,甚是惊喜.
因为"功夫网"的原因,我们无法访问blogger(blogspot,我以前的blog就在这里,因为党的原因,我只好放弃),因此在我提及的原文的 url中包含.nyud.net的后缀,这个是coralcdn的url,coralcdn我非常感兴趣,我曾经在以前的blog中分析过一部分,其为一个基于P2P的开源的CDN网络.回到正题继续说joe老大的blog.

Erlang中的rpc
最近,在erlang的邮件列表中,我参与了一个非常有趣的讨论,Steve Vinoski和他的朋友们谈及RPC的一些"错误".
这个讨论开始与5月22日,FaceBook宣布部署采用erlang编写的chat server.
Steve发表了一个回复说:

引用
多年的CORBA经验告诉我,使用RPC是一件非常困难,非常错误的事情.而erlang风格的RPC却非常棒,因为erlang本身是分布式的.对于一般的语言,RPC带来的困难远远多于其解决的问题 ...--Steve Vinoski


后面出现更多的回复,要求Steve详尽的解释其观点.

随后,Steve给出了丰富且有说服力的论述,关于RPC的问题:
"如果你没有很多的时间和精力,那么我告诉你,RPC的目标是使分布式的调用和本地调用尽可能的相似,但是这恰恰是其最根本的错误.因为远程调用和本地调用具有很多的不同..."

不错,不错,不错.当我读到这里时,在我的脑海里闪现出一连串的好,好,好.谢谢Steve,更多Seeve的论述参看RPC under file

Erlang没有选择的道路

Steve过去曾深入研究RPC,并且感受RPC带给他问题,但是现在Steve站出来,告诉我们他曾经经历的一切.

对一个远程操作竭力的封装,使其看起来像本地操作,这是RPC主要错误所在,因为远程和本地调用完全不同,我再次重申这个观点.

在不是最坏的情况下,远程调用和本地调用对性能的影响也差别很大.一个本地调用可能仅仅需要几十微秒(microsecond),而通过RPC远程调用可能会消耗数十毫秒.

如果程序员,不知道本地调用和远程调用间的不同,那么他很难写出高效的代码.如果其在软件内部掺杂了很多RPC调用,那么很有可能他的软件性能会保收到毁灭性的打击.

我曾将亲眼目睹很多工程的失败,正是因为参与者对远程调用和本地调用没有清晰的认识.

尤其需要注意的是,这种认识上的模糊对大型的项目的开发影响更坏.因为在较小的开发团队中,每个参与者都了解其使用的调用为远程还是本地调用.

Erlang如何实现RPC
所有的erlang程序都是有很多并行的process组成, process可以创建其他的process,可以发送、接收消息。这些操作在erlang中都是非常轻量,高效的操作。

Process可以被连接起来(link),用来应对错误处理。如果Process A与Process B相连(通过调用link/1函数),当A发生错误时,B会接收到一个错误信号,反之B发生错误时,A也会收到信号。Process连接机制,内部使用 Process的消息发送/接收机制。

当我们进行分布式系统开发时,需要多种形式的RPC调用。如果使用RPC,那么对于各种问题,RPC具有各种各种的规范及形式

严格的形式要求以及对错误的处理方式使应用RPC成为一场灾难。

在Erlang的时间,通过send,receive和link,开发者非常轻松的构建具有自定义错误处理功能的“个人RPC”。

Erlang中没有“RPC存根生成器”,也完全没有必要拥有类似的生成器。

在很多程序中,可能仅仅需要一些简单的RPC调用,在Erlang中,我们可以这样实现:
(译者注:joe是不是写错了呢,rcp 应该为 rpc)

rpc(Pid, Request) ->
    Pid ! {self(), Request},
    receive
        {Pid, Response} ->
        Response
    end.


非常简单,这段代码首先发送一个请求,然后等待应答。

基于上面的代码,可以进行很多有用的扩展。简单的RPC在发送请求后,永远的等待应答,所以如果应答无法返回(比如远程主机crash),那么请求方会被永远的挂起。通过添加一个timeout可以轻松解决这个问题:

rpc(Pid, Request, Time) ->
     Pid ! {self(), Request},
     receive {Pid, Response} ->
         Response
     after Time ->
         {error, timeout}
     end.


现在我们有了更高的要求。如果我们想产生一个exception,当与我们通信的远程主机die的时候,那么代码如下:

rpc(Pid, Request) ->
    link(Pid),
    Pid ! {self(), Request},
    receive
        {Pid, Response} ->
            Response
    end.

通过link/1函数,我们将自身process与Pid连接起来,确保远程主机出错,die时,本地Process也终止。

新任务,现在我们想“并行”执行两个RPC:

rpc(Pid1, Pid2, Request) ->
    Pid1 ! Pid2 ! {self(), Request}
         receive {Pid1, Response} ->
             receive {Pid2, Response2} ->
                {Response1, Response2}
end end.


(请不要担心这段代码是否工作,Response1和Response2返回的顺序对代码没有影响)

通过上面的几个小例子,我想要说明的是:在Erlang中RPC的形式和规模以及错误处理程序员可以进行各种精确,详尽的控制。

同时上面的例子也说明,如果可以方便的修改我们的RPC,仅仅通过Process和消息。

“标准”的RPC基于一个假设-所有的应答都应返回给client。

在RPC框架中(比如SOAP),可能会有这样的处理:让X去做Y,最后把结果发送给Z。在Erlang也很容易:

rpc(tell, X, toDo, Y, reply Z) ->
     X ! {Z, Y}.


(在这里,我们固定发送的tuple消息中,第一个元素为希望得到应答的Process,第二个参数为要执行的动作。在文章开始的例子中,调用者等待应答,那么Z可以设置为self())。

现在,我们要给我们的程序加上版本控制功能,也很容易:

rpc(Pid, Request, Vsn) ->
    Pid ! {self(), vsn, Vsn, Request},
    receive
    …
   end.


好了,通过这些例子,向您展现了版本控制,错误处理,同步执行,超时等等可以非常简单添加到RPC调用中。通过消息接口,用户可以很轻松的定义各种交互。

最后,很多开发中通用的模式我们都为您准备好了,那就是OTP库。

OTP是什么?
OTP是一系列久经实践考验的通用开发库(比如RPC)。OTP的方法没有涵盖所有的错误情况,其只是提供最基本的应用框架。

你可能感兴趣的:(框架,erlang,网络应用,Blog,Facebook)