-
Notifications
You must be signed in to change notification settings - Fork 1
/
15293292942972.html
518 lines (300 loc) · 19.7 KB
/
15293292942972.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
<!doctype html>
<html class="no-js" lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>
深入理解 RPC 之协议篇 - Junkman
</title>
<link href="atom.xml" rel="alternate" title="Junkman" type="application/atom+xml">
<link rel="stylesheet" href="asset/css/foundation.min.css" />
<link rel="stylesheet" href="asset/css/docs.css" />
<script src="asset/js/vendor/modernizr.js"></script>
<script src="asset/js/vendor/jquery.js"></script>
<script src="asset/highlightjs/highlight.pack.js"></script>
<link href="asset/highlightjs/styles/github.css" media="screen, projection" rel="stylesheet" type="text/css">
<script>hljs.initHighlightingOnLoad();</script>
<script type="text/javascript">
function before_search(){
var searchVal = 'site:panlw.github.io ' + document.getElementById('search_input').value;
document.getElementById('search_q').value = searchVal;
return true;
}
</script>
</head>
<body class="antialiased hide-extras">
<div class="marketing off-canvas-wrap" data-offcanvas>
<div class="inner-wrap">
<nav class="top-bar docs-bar hide-for-small" data-topbar>
<section class="top-bar-section">
<div class="row">
<div style="position: relative;width:100%;"><div style="position: absolute; width:100%;">
<ul id="main-menu" class="left">
<li id=""><a target="self" href="index.html">Home</a></li>
<li id=""><a target="_self" href="archives.html">Archives</a></li>
</ul>
<ul class="right" id="search-wrap">
<li>
<form target="_blank" onsubmit="return before_search();" action="http://google.com/search" method="get">
<input type="hidden" id="search_q" name="q" value="" />
<input tabindex="1" type="search" id="search_input" placeholder="Search"/>
</form>
</li>
</ul>
</div></div>
</div>
</section>
</nav>
<nav class="tab-bar show-for-small">
<a href="javascript:void(0)" class="left-off-canvas-toggle menu-icon">
<span> Junkman</span>
</a>
</nav>
<aside class="left-off-canvas-menu">
<ul class="off-canvas-list">
<li><a href="index.html">HOME</a></li>
<li><a href="archives.html">Archives</a></li>
<li><a href="about.html">ABOUT</a></li>
<li><label>Categories</label></li>
<li><a href="Infra.html">Infra</a></li>
<li><a href="Coding.html">Coding</a></li>
<li><a href="Modeling.html">Modeling</a></li>
<li><a href="Archtecting.html">Archtecting</a></li>
</ul>
</aside>
<a class="exit-off-canvas" href="#"></a>
<section id="main-content" role="main" class="scroll-container">
<script type="text/javascript">
$(function(){
$('#menu_item_index').addClass('is_active');
});
</script>
<div class="row">
<div class="large-8 medium-8 columns">
<div class="markdown-body article-wrap">
<div class="article">
<h1>深入理解 RPC 之协议篇</h1>
<div class="read-more clearfix">
<span class="date">2018/6/18</span>
<span>posted in </span>
<span class="posted-in"><a href='RPC&NIO.html'>RPC&NIO</a></span>
<span class="comments">
</span>
</div>
</div><!-- article -->
<div class="article-content">
<ul>
<li> Protocol 在 RPC 中的层次关系</li>
<li> Dubbo 中的协议</li>
<li> Motan 中的协议</li>
<li> 总结</li>
</ul>
<p>协议(Protocol)是个很广的概念,RPC 被称为远程过程调用协议,HTTP 和 TCP 也是大家熟悉的协议,也有人经常拿 RPC 和 RESTFUL 做对比,后者也可以被理解为一种协议… 我个人偏向于把 “协议” 理解为不同厂家不同用户之间的“约定”,而在 RPC 中,协议的含义也有多层。</p>
<h2 id="toc_0">Protocol 在 RPC 中的层次关系</h2>
<p>翻看 dubbo 和 motan 两个国内知名度数一数二的 RPC 框架(或者叫服务治理框架可能更合适)的文档,他们都有专门的一章介绍自身对多种协议的支持。RPC 框架是一个分层结构,从我的这个《深入理解 RPC》系列就可以看出,是按照分层来介绍 RPC 的原理的,前面已经介绍过了传输层,序列化层,动态代理层,他们各自负责 RPC 调用生命周期中的一环,而协议层则是凌驾于它们所有层之上的一层。简单描述下各个层之间的关系:</p>
<blockquote>
<p>protocol 层主要用于配置 refer(发现服务) 和 exporter(暴露服务) 的实现方式,transport 层定义了传输的方式,codec 层诠释了具体传输过程中报文解析的方式,serialize 层负责将对象转换成字节,以用于传输,proxy 层负责将这些细节屏蔽。</p>
<p>它们的包含关系如下:protocol > transport > codec > serialize</p>
</blockquote>
<p>motan 的 Protocol 接口可以佐证这一点:</p>
<pre><code class="language-java">public interface Protocol {
<T> Exporter<T> export(Provider<T> provider, URL url);
<T> Referer<T> refer(Class<T> clz, URL url, URL serviceUrl);
void destroy();
}
</code></pre>
<p>我们都知道 RPC 框架支持多种协议,由于协议处于框架层次的较高位置,任何一种协议的替换,都可能会导致服务发现和服务注册的方式,传输的方式,以及序列化的方式,而不同的协议也给不同的业务场景带来了更多的选择,下面就来看看一些常用协议。</p>
<h2 id="toc_1">Dubbo 中的协议</h2>
<h3 id="toc_2">dubbo://</h3>
<p>Dubbo 缺省协议采用单一长连接和 NIO 异步通讯,适合于小数据量高并发的服务调用,以及服务消费者机器数远大于服务提供者机器数的情况。</p>
<p>反之,Dubbo 缺省协议不适合传送大数据量的服务,比如传文件,传视频等,除非请求量很低。</p>
<p>适用场景:常规远程服务方法调用</p>
<h3 id="toc_3">rmi://</h3>
<p>RMI 协议采用 JDK 标准的 <code>java.rmi.*</code> 实现,采用阻塞式短连接和 JDK 标准序列化方式。</p>
<p>适用场景:常规远程服务方法调用,与原生 RMI 服务互操作</p>
<h3 id="toc_4">hessian://</h3>
<p>Hessian 协议用于集成 Hessian 的服务,Hessian 底层采用 Http 通讯,采用 Servlet 暴露服务,Dubbo 缺省内嵌 Jetty 作为服务器实现。</p>
<p>Dubbo 的 Hessian 协议可以和原生 Hessian 服务互操作,即:</p>
<ul>
<li><p>提供者用 Dubbo 的 Hessian 协议暴露服务,消费者直接用标准 Hessian 接口调用</p></li>
<li><p>或者提供方用标准 Hessian 暴露服务,消费方用 Dubbo 的 Hessian 协议调用。</p></li>
</ul>
<p>Hessian 在之前介绍过,当时仅仅是用它来作为序列化工具,但其本身其实就是一个协议,可以用来做远程通信。</p>
<p>适用场景:页面传输,文件传输,或与原生 hessian 服务互操作</p>
<h3 id="toc_5">http://</h3>
<p>基于 HTTP 表单的远程调用协议,采用 Spring 的 HttpInvoker 实现</p>
<p>适用场景:需同时给应用程序和浏览器 JS 使用的服务。</p>
<h3 id="toc_6">webserivice://</h3>
<p>基于 WebService 的远程调用协议,基于 Apache CXF 的 <code>frontend-simple</code> 和 <code>transports-http</code> 实现。</p>
<p>可以和原生 WebService 服务互操作,即:</p>
<ul>
<li><p>提供者用 Dubbo 的 WebService 协议暴露服务,消费者直接用标准 WebService 接口调用,</p></li>
<li><p>或者提供方用标准 WebService 暴露服务,消费方用 Dubbo 的 WebService 协议调用</p></li>
</ul>
<p>适用场景:系统集成,跨语言调用</p>
<h3 id="toc_7">thrift://</h3>
<p>当前 dubbo 支持的 thrift 协议是对 thrift 原生协议的扩展,在原生协议的基础上添加了一些额外的头信息,比如 service name,magic number 等。</p>
<h3 id="toc_8">memcached://</h3>
<p>基于 memcached 实现的 RPC 协议</p>
<h3 id="toc_9">redis://</h3>
<p>基于 Redis 实现的 RPC 协议。</p>
<blockquote>
<p>dubbo 支持的众多协议详见 <a href="http://dubbo.io/books/dubbo-user-book/references/protocol/dubbo.html">http://dubbo.io/books/dubbo-user-book/references/protocol/dubbo.html</a></p>
</blockquote>
<p>dubbo 的一个分支 dangdangdotcom/dubbox 扩展了 REST 协议</p>
<h3 id="toc_10">rest://</h3>
<p>JAX-RS 是标准的 Java REST API,得到了业界的广泛支持和应用,其著名的开源实现就有很多,包括 Oracle 的 Jersey,RedHat 的 RestEasy,Apache 的 CXF 和 Wink,以及 restlet 等等。另外,所有支持 JavaEE 6.0 以上规范的商用 JavaEE 应用服务器都对 JAX-RS 提供了支持。因此,JAX-RS 是一种已经非常成熟的解决方案,并且采用它没有任何所谓 vendor lock-in 的问题。</p>
<p>JAX-RS 在网上的资料非常丰富,例如下面的入门教程:</p>
<ul>
<li><p>Oracle 官方的 tutorial:<a href="http://docs.oracle.com/javaee/7/tutorial/doc/jaxrs.htm">http://docs.oracle.com/javaee/7/tutorial/doc/jaxrs.htm</a></p></li>
<li><p>IBM developerWorks 中国站文章:<a href="http://www.ibm.com/developerworks/cn/java/j-lo-jaxrs/">http://www.ibm.com/developerworks/cn/java/j-lo-jaxrs/</a></p></li>
</ul>
<p>更多的资料请自行 google 或者百度一下。就学习 JAX-RS 来说,一般主要掌握其各种 annotation 的用法即可。</p>
<blockquote>
<p>注意:dubbo 是基于 JAX-RS 2.0 版本的,有时候需要注意一下资料或 REST 实现所涉及的版本。</p>
</blockquote>
<p>适用场景:跨语言调用</p>
<p>千米网也给 dubbo 贡献了一个扩展协议:<a href="https://github.com/dubbo/dubbo-rpc-jsonrpc">https://github.com/dubbo/dubbo-rpc-jsonrpc</a></p>
<h3 id="toc_11">jsonrpc://</h3>
<h4 id="toc_12">Why HTTP</h4>
<p>在互联网快速迭代的大潮下,越来越多的公司选择 nodejs、django、rails 这样的快速脚本框架来开发 web 端应用 而后端的服务用 Java 又是最合适的,这就产生了大量的跨语言的调用需求。<br/>
而 http、json 是天然合适作为跨语言的标准,各种语言都有成熟的类库<br/>
虽然 Dubbo 的异步长连接协议效率很高,但是在脚本语言中,这点效率的损失并不重要。</p>
<h4 id="toc_13">Why Not RESTful</h4>
<p>Dubbox 在 RESTful 接口上已经做出了尝试,但是 REST 架构和 dubbo 原有的 RPC 架构是有区别的,<br/>
区别在于 REST 架构需要有资源 (Resources) 的定义, 需要用到 HTTP 协议的基本操作 GET、POST、PUT、DELETE 对资源进行操作。<br/>
Dubbox 需要重新定义接口的属性,这对原有的 Dubbo 接口迁移是一个较大的负担。<br/>
相比之下,RESTful 更合适互联网系统之间的调用,而 RPC 更合适一个系统内的调用,<br/>
所以我们使用了和 Dubbo 理念较为一致的 JsonRPC</p>
<p>JSON-RPC 2.0 规范 和 JAX-RS 一样,也是一个规范,JAVA 对其的支持可参考 jsonrpc4j</p>
<p>适用场景:跨语言调用</p>
<h2 id="toc_14">Motan 中的协议</h2>
<h3 id="toc_15">motan://</h3>
<p>motan 协议之于 motan,地位等同于 dubbo 协议之于 dubbo,两者都是各自默认的且都是自定义的协议。内部使用 netty 进行通信(旧版本使用 netty3 ,最新版本支持 netty4),默认使用 hessian 作为序列化器。</p>
<p>适用场景:常规远程服务方法调用</p>
<h3 id="toc_16">injvm://</h3>
<p>顾名思义,如果 Provider 和 Consumer 位于同一个 jvm,motan 提供了 injvm 协议。这个协议是 jvm 内部调用,不经过本地网络,一般在服务化拆分时,作为过渡方案使用,可以通过开关机制在本地和远程调用之间进行切换,等过渡完成后再去除本地实现的引用。</p>
<h3 id="toc_17">grpc:// 和 yar://</h3>
<p>这两个协议的诞生缘起于一定的历史遗留问题,moton 是新浪微博开源的,而其内部有很多 PHP 应用,为解决跨语言问题,这两个协议进而出现了。</p>
<p>适用场景:较为局限的跨语言调用</p>
<h3 id="toc_18">restful://</h3>
<p>motan 在 0.3.1 (2017-07-11) 版本发布了 restful 协议的支持(和 dubbo 的 rest 协议本质一样),dubbo 默认使用 jetty 作为 http server,而 motan 使用则是 netty 。主要实现的是 java 对 restful 指定的规范,即 javax.ws.rs 包下的类。</p>
<p>适用场景:跨语言调用</p>
<h3 id="toc_19">motan2://</h3>
<p>motan 1.0.0 (2017-10-31) 版本发布了 motan2 协议,用于对跨语言的支持,不同于 restful,jsonrpc 这样的通用协议,motan2 把请求的一些元数据作为单独的部分传输,更适合不同语言解析。</p>
<p>适用场景:跨语言调用</p>
<blockquote>
<p>Motan is a cross-language remote procedure call(RPC) framework for rapid development of high performance distributed services.</p>
<p>Motan-go is golang implementation.</p>
<p>Motan-PHP is PHP client can interactive with Motan server directly or through Motan-go agent.</p>
<p>Motan-openresty is a Lua(Luajit) implementation based on Openresty</p>
</blockquote>
<p>从 motan 的 changeLog 以及 github 首页的介绍来看,其致力于打造成一个跨语言的服务治理框架,这倒是比较亦可赛艇的事。</p>
<h3 id="toc_20">面向未来的协议</h3>
<p>motan 已经支持 motan2://,计划支持 mcq://,kafka:// … 支持更多的协议,以应对复杂的业务场景。对这个感兴趣的朋友,可以参见这篇文章:<a href="http://mp.weixin.qq.com/s?__biz=MzU0MTMyMDg1NQ==&mid=2247483710&idx=1&sn=5daea9c54b9835b660dac5b0ce02f462&scene=21#wechat_redirect">http://mp.weixin.qq.com/s/XZVCHZZzCX8wwgNKZtsmcA</a></p>
<h2 id="toc_21">总结</h2>
<p>如果仅仅是将 dubbo,motan 作为一个 RPC 框架使用,那大多人会选择其默认的协议(dubbo 协议,motan 协议),而如果是有历史遗留原因,如需要对接异构系统,就需要替换成其他协议了。大多数互联网公司选择自研 RPC 框架,或者改造自己的协议,都是为了适配自身业务的特殊性,协议层的选择非常重要。</p>
</div>
<div class="row">
<div class="large-6 columns">
<p class="text-left" style="padding:15px 0px;">
<a href="15295434052821.html"
title="Previous Post: Open Sourcing Zuul 2">« Open Sourcing Zuul 2</a>
</p>
</div>
<div class="large-6 columns">
<p class="text-right" style="padding:15px 0px;">
<a href="15293283879436.html"
title="Next Post: 精讲 Redis 内存模型">精讲 Redis 内存模型 »</a>
</p>
</div>
</div>
<div class="comments-wrap">
<div class="share-comments">
<script type="text/javascript" src="//s7.addthis.com/js/300/addthis_widget.js#pubid=ra-5ae58078c0d7b2ab"></script>
</div>
</div>
</div><!-- article-wrap -->
</div><!-- large 8 -->
<div class="large-4 medium-4 columns">
<div class="hide-for-small">
<div id="sidebar" class="sidebar">
<div id="site-info" class="site-info">
<div class="site-a-logo"><img src="./asset/img/logo.jpg" /></div>
<h1>Junkman</h1>
<div class="site-des">“拾荒者”一词来自凯文・凯利的《失控》中关于机器学习的故事(“收集癖好机”如何完成他的收集工作)。</div>
<div class="social">
<a target="_blank" class="github" target="_blank" href="https://github.com/panlw/" title="GitHub">GitHub</a>
<a target="_blank" class="rss" href="atom.xml" title="RSS">RSS</a>
</div>
</div>
<div id="site-categories" class="side-item ">
<div class="side-header">
<h2>Categories</h2>
</div>
<div class="side-content">
<p class="cat-list">
<a href="Infra.html"><strong>Infra</strong></a>
<a href="Coding.html"><strong>Coding</strong></a>
<a href="Modeling.html"><strong>Modeling</strong></a>
<a href="Archtecting.html"><strong>Archtecting</strong></a>
</p>
</div>
</div>
<div id="site-categories" class="side-item">
<div class="side-header">
<h2>Recent Posts</h2>
</div>
<div class="side-content">
<ul class="posts-list">
<li class="post">
<a href="15517999043443.html">The Art of Crafting Architectural Diagrams</a>
</li>
<li class="post">
<a href="15517997955971.html">为什么说我们需要软件架构图?</a>
</li>
<li class="post">
<a href="15516128677869.html">DNS Servers That Offer Privacy and Filtering</a>
</li>
<li class="post">
<a href="15516123108194.html">Airbnb's Migration from Monolith to Services</a>
</li>
<li class="post">
<a href="15516097487470.html">Events As First-Class Citizens</a>
</li>
</ul>
</div>
</div>
</div><!-- sidebar -->
</div><!-- hide for small -->
</div><!-- large 4 -->
</div><!-- row -->
<div class="page-bottom clearfix">
<div class="row">
<p class="copyright">Copyright © 2015
Powered by <a target="_blank" href="http://www.mweb.im">MWeb</a>,
Theme used <a target="_blank" href="http://github.com">GitHub CSS</a>.</p>
</div>
</div>
</section>
</div>
</div>
<script src="asset/js/foundation.min.js"></script>
<script>
$(document).foundation();
function fixSidebarHeight(){
var w1 = $('.markdown-body').height();
var w2 = $('#sidebar').height();
if (w1 > w2) { $('#sidebar').height(w1); };
}
$(function(){
fixSidebarHeight();
})
$(window).load(function(){
fixSidebarHeight();
});
</script>
<script src="asset/chart/all-min.js"></script><script type="text/javascript">$(function(){ var mwebii=0; var mwebChartEleId = 'mweb-chart-ele-'; $('pre>code').each(function(){ mwebii++; var eleiid = mwebChartEleId+mwebii; if($(this).hasClass('language-sequence')){ var ele = $(this).addClass('nohighlight').parent(); $('<div id="'+eleiid+'"></div>').insertAfter(ele); ele.hide(); var diagram = Diagram.parse($(this).text()); diagram.drawSVG(eleiid,{theme: 'simple'}); }else if($(this).hasClass('language-flow')){ var ele = $(this).addClass('nohighlight').parent(); $('<div id="'+eleiid+'"></div>').insertAfter(ele); ele.hide(); var diagram = flowchart.parse($(this).text()); diagram.drawSVG(eleiid); } });});</script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.1/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script><script type="text/x-mathjax-config">MathJax.Hub.Config({TeX: { equationNumbers: { autoNumber: "AMS" } }});</script>
</body>
</html>