为什么说要搞定微服务架构,先搞定 RPC 框架?
StaNewbigin
8年前
<p>第一章聊了【 <a href="http://www.open-open.com/lib/view/open1472132696878.html" rel="nofollow,noindex">“为什么要进行服务化,服务化究竟解决什么问题”</a> 】</p> <p>第二章聊了【 <a href="http://mp.weixin.qq.com/s?__biz=MjM5ODYxMDA5OQ==&mid=2651959530&idx=1&sn=ff84bd74745eee1577e7dfef8ce66bbe&scene=21#wechat_redirect" rel="nofollow,noindex">“微服务的服务粒度选型”</a> 】</p> <p>今天开始聊一些 微服务的实践 ,第一块, RPC 框架的原理及实践 ,为什么说要搞定微服务架构,先搞定RPC框架呢?</p> <h2><strong>一、需求缘起 </strong></h2> <p>服务化的一个好处就是,不限定服务的提供方使用什么技术选型,能够实现大公司跨团队的技术解耦 ,如下图:</p> <p><img src="https://simg.open-open.com/show/ae0e57b822a7e78af459ba1b5591af26.png"></p> <p>服务 A 是欧洲团队提供服务,欧洲团队的技术背景是 Java ,可以用 Java 实现服务;</p> <p>服务 B 是美洲团队提供服务,可以用 C++ 实现服务;</p> <p>服务 C 是中国团队提供服务,可以用 Go 实现服务;</p> <p>服务的上游调用方,按照接口、协议即可完成对远端服务的调用。</p> <p>但实际上, 99.9% 的公司的团队规模有限,技术团队人数也有限,基本是使用同一套技术体系来调用和提供服务 的:</p> <p><img src="https://simg.open-open.com/show/0d2f74d973534f0b371baade4b77989c.png"></p> <p>这样的话, <strong> 如果没有统一的服务框架, RPC 框架 </strong> , 各个团队的服务提供方就需要各自实现一套序列化、反序列化、网络框架、连接池、收发线程、超时处理、状态机等“业务之外”的重复技术劳动 ,造成整体的低效。所以, <strong> 统一 RPC 框架 </strong> 把上述“业务之外”的技术劳动统一处理, 是服务化首要解决的问题 。</p> <p>在达成【“使用统一的 RPC 框架”是正确的道路】这个一致的前提下,本文期望用简单通俗的言语简述一下一个通用 RPC 框架的技术点与实现。</p> <h2><strong>二、 RPC 背景与过程 </strong></h2> <p>什么是 RPC ( Remote Procedure Call Protocol ),远程过程调用?</p> <p>先来看下什么是本地函数调用,当我们写下:</p> <p>int result = Add(1, 2);</p> <p><img src="https://simg.open-open.com/show/180ef823583d3d106ad2abed60e51d7c.png"></p> <p>这段代码的时候,我们知道,我们传入了 1 , 2 两个入参数,调用了本地代码段中的一个 Add 函数,得到了 result 出参。此时, 传入数据,传出数据,代码段在同一个进程空间里,这是本地函数调用 。</p> <p>那有没有办法,我们能够调用一个跨进程(所以叫 “ 远程 ” ,典型的,这个进程部署在另一台服务器上)的函数呢 ?</p> <p><img src="https://simg.open-open.com/show/c33a4f95a53af8da320283790eb8f531.png"> </p> <p>最容易想到的, 两个进程约定一个协议格式,使用 Socket 通信,来传输【入参】【调用哪个函数】【出参】 。</p> <p>假设请求报文协议是一个 11 字节的字节流:</p> <p><img src="https://simg.open-open.com/show/0eb425a35fa7829554d11f38ccf705ef.png"></p> <p>( 1 )前 3 个字节填入函数名</p> <p>( 2 )中间 4 个字节填入第一个参数</p> <p>( 3 )末尾 4 个字节填入第二个参数</p> <p>同时可以设计响应报文协议是一个 4 字节的字节流:</p> <p><img src="https://simg.open-open.com/show/43693255b5fad70b6c22d26610143c34.png"></p> <p>即处理结果。</p> <p>调用方的代码可能变为:</p> <p>request = MakePacket(“add”, 1, 2);</p> <p>SendRequest_ToService_B(request);</p> <p>response = RecieveRespnse_FromService_B();</p> <p>int result = unMakePacket(respnse);</p> <p>简单解释一下:</p> <p>( 1 )讲传入参数变为字节流</p> <p>( 2 )将字节流发给服务 B</p> <p>( 3 )从服务 B 接受返回字节流</p> <p>( 4 )将返回字节流变为传出参数</p> <p>服务方的代码可能变为:</p> <p>request = RecieveRequest();</p> <p>args/function = unMakePacket(request);</p> <p>result = Add(1, 2);</p> <p>response = MakePacket(result);</p> <p>SendResponse(response);</p> <p>这个过程也很好理解:</p> <p>( 1 )服务端收到字节流</p> <p>( 2 )将字节流转为函数名与参数</p> <p>( 3 )本地调用函数得到结果</p> <p>( 4 )将结果转变为字节流</p> <p>( 5 )将字节流发送给调用方</p> <p><img src="https://simg.open-open.com/show/565e4756cb260c13b5b5b0c306d8ae96.png"></p> <p>这个过程用一张图描述如上,调用方与服务方的处理步骤都是非常清晰的。 <strong>这个过程存在最大的问题是什么呢?</strong></p> <p>回答:调用方太麻烦了,每次都要关注很多底层细节</p> <p>( 1 )入参到字节流的转化,即序列化应用层协议细节</p> <p>( 2 ) socket 发送,即网络传输协议细节</p> <p>( 3 ) socket 接受</p> <p>( 4 )字节流到出参的转化,即反序列化应用层协议细节</p> <p>能不能调用层不关注这个细节呢?</p> <p>回答:可以, RPC 框架就是解决这个问题的,它能够让调用方“像调用本地函数一样调用远端的函数(服务)” 。</p> <h2><strong>三、 RPC 框架职责 </strong></h2> <p>通过上面的讨论, RPC 框架要向调用方屏蔽各种复杂性,要向服务提供方也屏蔽各类复杂性 :</p> <p>( 1 )调用方感觉就像调用本地函数一样</p> <p>( 2 )服务提供方感觉就像实现一个本地函数一样来实现服务</p> <p>所以整个 RPC 框架又分为 client 部分与 server 部分,负责把整个非( 1 )( 2 )的各类复杂性屏蔽,这些复杂性就是 RPC 框架的职责。</p> <p><img src="https://simg.open-open.com/show/45f6f2840912487a4a796bd61221836b.jpg"> 再细化一些, client 端又包含:序列化、反序列化、连接池管理、负载均衡、故障转移、队列管理,超时管理、异步管理等等等等职责。</p> <p>server 端包含:服务端组件、服务端收发包队列、 io 线程、工作线程、序列化反序列化、上下文管理器、超时管理、异步回调等等等等职责。</p> <p>however ,因为篇幅有限,这些细节不做深入展开。</p> <p> </p> <h2><strong>四、结论 </strong></h2> <p>( 1 ) RPC 框架是架构微服务化的首要基础组件 ,它能大大降低架构微服务化的成本,提高调用方与服务提供方的研发效率,屏蔽跨进程调用函数(服务)的各类复杂细节</p> <p>( 2 ) RPC 框架的 <strong>职责</strong> 是: 让调用方感觉就像调用本地函数一样调用远端函数、让服务提供方感觉就像实现一个本地函数一样来实现服务</p> <p> </p> <p>来自:http://mp.weixin.qq.com/s?__biz=MjM5ODYxMDA5OQ==&mid=2651959553&idx=1&sn=c1084e91875721c5f6baf544450afa38&scene=0</p> <p> </p>