RPC框架基本原理(三):调用链路分析
本文主要阐述下RPC调用过程中的寻址,序列化,以及服务端调用问题。
寻址
随机寻址
从可用列表中,随机选择地址
一致性寻址
可用服务地址一致性hash管理:根据可服务的地址,构造treemap,计算crc16 ccitt码时,加入虚拟节点数量,指向同一个可用地址。
for (String addr : list) {
for (int i = 0; i < VIRTUAL_NODE_SIZE; i++) {
ketamaMap.put(CRC16.getSlot(addr + VIRTUAL_NODE_SPLITER + i), addr);
}
}
当新加入服务时,确保之前服务的key是一致的,新加的key离散到treemap中。
调用方生成种子编码:CRC16 CCITT计算出种子。
寻址,采用treemap的tailmap(有序map的特性)。
找出比种子大的区间map,返回区间map的第一个,如果区间map为空,返回整个map的第一个。
CRC16 CCITT编码
//地址表
private static final int LOOKUP_TABLE[] = {0x0000, 0x1021, 0x2042, 0x3063,
0x4084, 0x50A5, 0x60C6, 0x70E7, 0x8108, 0x9129, 0xA14A, 0xB16B,
0xC18C, 0xD1AD, 0xE1CE, 0xF1EF, 0x1231, 0x0210, 0x3273, 0x2252,
0x52B5, 0x4294, 0x72F7, 0x62D6, 0x9339, 0x8318, 0xB37B, 0xA35A,
0xD3BD, 0xC39C, 0xF3FF, 0xE3DE, 0x2462, 0x3443, 0x0420, 0x1401,
0x64E6, 0x74C7, 0x44A4, 0x5485, 0xA56A, 0xB54B, 0x8528, 0x9509,
0xE5EE, 0xF5CF, 0xC5AC, 0xD58D, 0x3653, 0x2672, 0x1611, 0x0630,
0x76D7, 0x66F6, 0x5695, 0x46B4, 0xB75B, 0xA77A, 0x9719, 0x8738,
0xF7DF, 0xE7FE, 0xD79D, 0xC7BC, 0x48C4, 0x58E5, 0x6886, 0x78A7,
0x0840, 0x1861, 0x2802, 0x3823, 0xC9CC, 0xD9ED, 0xE98E, 0xF9AF,
0x8948, 0x9969, 0xA90A, 0xB92B, 0x5AF5, 0x4AD4, 0x7AB7, 0x6A96,
0x1A71, 0x0A50, 0x3A33, 0x2A12, 0xDBFD, 0xCBDC, 0xFBBF, 0xEB9E,
0x9B79, 0x8B58, 0xBB3B, 0xAB1A, 0x6CA6, 0x7C87, 0x4CE4, 0x5CC5,
0x2C22, 0x3C03, 0x0C60, 0x1C41, 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD,
0xAD2A, 0xBD0B, 0x8D68, 0x9D49, 0x7E97, 0x6EB6, 0x5ED5, 0x4EF4,
0x3E13, 0x2E32, 0x1E51, 0x0E70, 0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC,
0xBF1B, 0xAF3A, 0x9F59, 0x8F78, 0x9188, 0x81A9, 0xB1CA, 0xA1EB,
0xD10C, 0xC12D, 0xF14E, 0xE16F, 0x1080, 0x00A1, 0x30C2, 0x20E3,
0x5004, 0x4025, 0x7046, 0x6067, 0x83B9, 0x9398, 0xA3FB, 0xB3DA,
0xC33D, 0xD31C, 0xE37F, 0xF35E, 0x02B1, 0x1290, 0x22F3, 0x32D2,
0x4235, 0x5214, 0x6277, 0x7256, 0xB5EA, 0xA5CB, 0x95A8, 0x8589,
0xF56E, 0xE54F, 0xD52C, 0xC50D, 0x34E2, 0x24C3, 0x14A0, 0x0481,
0x7466, 0x6447, 0x5424, 0x4405, 0xA7DB, 0xB7FA, 0x8799, 0x97B8,
0xE75F, 0xF77E, 0xC71D, 0xD73C, 0x26D3, 0x36F2, 0x0691, 0x16B0,
0x6657, 0x7676, 0x4615, 0x5634, 0xD94C, 0xC96D, 0xF90E, 0xE92F,
0x99C8, 0x89E9, 0xB98A, 0xA9AB, 0x5844, 0x4865, 0x7806, 0x6827,
0x18C0, 0x08E1, 0x3882, 0x28A3, 0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E,
0x8BF9, 0x9BD8, 0xABBB, 0xBB9A, 0x4A75, 0x5A54, 0x6A37, 0x7A16,
0x0AF1, 0x1AD0, 0x2AB3, 0x3A92, 0xFD2E, 0xED0F, 0xDD6C, 0xCD4D,
0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9, 0x7C26, 0x6C07, 0x5C64, 0x4C45,
0x3CA2, 0x2C83, 0x1CE0, 0x0CC1, 0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C,
0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8, 0x6E17, 0x7E36, 0x4E55, 0x5E74,
0x2E93, 0x3EB2, 0x0ED1, 0x1EF0,};
//计算CRC码
/**
* Create a CRC16 checksum from the bytes. implementation is from
* mp911de/lettuce, modified with some more optimizations
*
* @param bytes
* @return CRC16 as integer value
*/
public static int getCRC16(byte[] bytes) {
int crc = 0x0000;
for (byte b : bytes) {
crc = ((crc << 8) ^ LOOKUP_TABLE[((crc >>> 8) ^ (b & 0xFF)) & 0xFF]);
}
return crc & 0xFFFF;
}
序列化
序列化和反序列化在我们软件设计中有很多的应该场景,例如:
1、对象的持久化,以及从储存媒介还原对象。如果需要将一个对象保持到储存媒介中,我们需要先将这个对象进行序列化编码,然后将编码后的数据写入到储存媒介中。
2、进程之间的数据交互。在典型分布式服务架构中,面向服务的架构之间通常是通过RPC的方式来进行服务交互,在使用RPC的过程中,会将远程方法的参数等信息进行序列化,编码成字节序列,在网络中传输。服务方收到这个数据包后会对数据进行解码,进行反序列化。即将字节序列转换成参数对象;
3、网络中传输对象;使用分布式的缓存系统,缓存对象的获取以及对象放入缓存,都会使用序列化及反序列化;
Java序列化
1 简介
Java 对象序列化是 JDK 1.1 中引入的一组开创性特性之一,用于作为一种将 Java 对象的状态转换为字节数组,以便存储或传输的机制,以后,仍可以将字节数组转换回 Java 对象原有的状态。
实际上,序列化的思想是 “冻结” 对象状态,传输对象状态(写到磁盘、通过网络传输等等),然后 “解冻” 状态,重新获得可用的 Java 对象。所有这些事情的发生有点像是魔术,这要归功于 ObjectInputStream/ObjectOutputStream 类、完全保真的元数据以及程序员愿意用Serializable 标识接口标记他们的类,从而 “参与” 这个过程。
2 优点
使用非常方便,对象只要实现接口Serializable ,就可以被序列化;如果交互双方都是Java环境,那么使用Java自带的序列化机制还是最方便的了;
3 缺点
1、不能跨语言。Java自带的序列化,只适合Java语言使用。如果存在夸语言的应用场景,那就行不通了;
2、从序列化及反序列化的性能来看,Java自带的序列化性能是比较差的。无论是从时间,还是空间上看,性能都已经低的;
Protocol Buffers
1 简介
Protocol buffers是Google开源的一个序列化框架;是一个用来序列化结构化数据的技术,支持多种语言诸如C++、Java以及Python语言,可以使用该技术来持久化数据或者序列化成网络传输的数据。在Google 几乎所有它内部的RPC协议和文件格式都是采用PB。它具有灵活、高效、自动化的特点;相比较一些其他的XML技术而言,该技术的一个明显特点就是更加节省空间(以二进制流存储)、速度更快以及更加灵活。
通常,编写一个protocol buffers应用需要经历如下三步:
1、定义消息格式文件,最好以proto作为后缀名
2、使用Google提供的protocol buffers编译器来生成代码文件,一般为.h和.cc文件,主要是对消息格式以特定的语言方式描述
3、使用protocol buffers库提供的API来编写应用程序
2 优点
1、平台无关、语言无关
2、高性能 比XML块20-100倍
3、体积小 比XML小3-10倍
4、使用简单
5、兼容性好
3 缺点
1、使用时首先需要定义消息格式文件,然后生成代码,代码不是纯pojo,对于代码有一定的侵入性;
2、使用方便些上,不适合那种动态类型的数据。因为使用他之前必须先定义好格式文件;
XML类
1 简介
XML 序列化用处很多,包括对象持久化和数据传输。但是一些 XML 序列化技术实现起来可能很复杂。XStream 是一个轻量级的、简单易用的开放源代码 Java™ 库,用于将 Java 对象序列化为 XML 或者再转换回来;
使用 XStream 不用任何映射就能实现多数 Java 对象的序列化。在生成的 XML 中对象名变成了元素名,类中的字符串组成了 XML 中的元素内容。使用 XStream 序列化的类不需要实现 Serializable 接口。XStream 是一种序列化工具而不是数据绑定工具,就是说不能从 XML 或者 XML Schema Definition (XSD) 文件生成类。
2 优点
Xml最大的优点是可阅读性;
1、XStream 不关心序列化/反序列化的类的字段的可见性。
2、序列化/反序列化类的字段不需要 getter 和 setter 方法。
3、序列化/反序列化的类不需要有默认构造函数。
4、不需要修改类,使用 XStream 就能直接序列化/逆序列化任何第三方类。
3 缺点
1、xml序列化性能差,无论是空间还是时间,性能都不高;
JSON类
1 简介
JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式。 易于人阅读和编写。同时也易于机器解析和生成。 它基于JavaScript Programming Language, Standard ECMA-262 3rd Edition - December 1999的一个子集。
JSON采用完全独立于语言的文本格式,但是也使用了类似于C语言家族的习惯(包括C, C++, C#, Java, JavaScript, Perl, Python等)。 这些特性使JSON成为理想的数据交换语言。
JSON 已经是 JavaScript 标准的一部分。目前,主流的浏览器对 JSON 支持都非常完善。应用 JSON,我们可以从 XML 的解析中摆脱出来,对那些应用 Ajax 的 Web 2.0 网站来说,JSON 确实是目前最灵活的轻量级方案
JSON非常适合Web浏览器端和服务端的数据交换;
2 优点
1、跨平台,跨语言实现;
2、轻量级,非常容易阅读与编写;
3 缺点
1、貌似除了FastJSON的性能比较好外,其他JSON库的性能不是很高;
Hessian
1 简介
Hessian是Resin的开源的,一个基于二进制的RPC服务框架。他内部有一套完善的序列化及反序列化组件,采用二进制的序列化协议。Hessian使用简单。协议性能比较高效,有各种语言的实现版本;
2 优点
1、跨平台,跨语言实现;
2、使用简单,性能相对来说,比较高效;
3 缺点
1、 各版本之间好像不太兼容。有些版本有BUG;
Avro
1 简介
Avro是Hadoop中的一个子项目,也是Apache中一个独立的项目,Avro是一个基于二进制数据传输高性能的中间件。在Hadoop的其他项目中例如HBase(Ref)和Hive(Ref)的Client端与服务端的数据传输也采用了这个工具,Avro可以做到将数据进行序列化,适用于远程或本地大批量数据交互。
在传输的过程中Avro对数据二进制序列化后 节约数据存储空间 和 网络传输带宽。做个比方:有一个100平方的房子,本来能放100件东西,现在期望借助某种手段能让原有面积的房子能存放比原来多150件以上或者更多的东西,就好比数据存放在缓存中,缓存是精贵的,需要充分的利用缓存有限的空间,存放更多的数据。再例如网络带宽的资源是有限的,希望原有的带宽范围能传输比原来高大的数据量流量,特别是针对结构化的数据传输和存储,这就是Avro存在的意义和价值。
Avro还可以做到在同一系统中支持多种不同语言,也有点类似Apache的另一个产品:Thrift(Ref),对于Thrift不同的是Avro更加具有灵活性,Avro可以支持对定义的数据结构(Schema)动态加载,利于系统扩展。
2 优点
1、跨平台,跨语言实现;
2、Avro最大的特点是序列化数据不要带字段标签,这样他序列化的后的数据非常精简,在空间上站很大的优势;
3、性能优秀;无论是空间还是时间,都比较好;
3 缺点
1、 依赖Schema文件;使用前必须定义数据的Schema格式文件,编码及解码都依赖于Schema文件
2、 各版本之间好像不太兼容。有些版本有BUG;
Apache Thrift
Apache Thrift 是 Facebook 实现的一种高效的、支持多种编程语言的远程服务调用的框架 ,同时也提供了序列化的功能;
Thrift源于大名鼎鼎的Facebook之手,在2007年facebook提交Apache基金会将Thrift作为一个开源项目,对于当时的facebook来说创造thrift是为了解决Facebook系统中各系统间大数据量的传 输通信以及系统之间语言环境不同需要跨平台的特性。所以thrift可以支持多种程序语言,例如: C++, C#, Cocoa, Erlang, Haskell, Java, Ocami, Perl, PHP, Python, Ruby, Smalltalk. 在多种不同的语言之间通信thrift可以作为二进制的高性能的通讯中间件,支持数据(对象)序列化和多种类型的RPC服务。Thrift适用于程序对程 序静态的数据交换,需要先确定好他的数据结构,他是完全静态化的,当数据结构发生变化时,必须重新编辑IDL文件,代码生成,再编译载入的流程,跟其他IDL工具相比较可以视为是Thrift的弱项,Thrift适用于搭建大型数据交换及存储的通用工具,对于大型系统中的内部数据传输相对于JSON和xml无论在性能、传输大小上有明显的优势;
Kryo
Kryo 是一个快速高效的Java对象图形序列化框架,它原生支持java,且在java的序列化上甚至优于google著名的序列化框架protobuf。
Kryo只适用于Java平台,如果只考虑Java平台,Kryo是一个不错的选择。期性能非常的好。
MessagePack
MessagePack是一个基于二进制高效的对象序列化Library用于跨语言通信。 它可以像JSON那样,在许多种语言之间交换结构对象;但是它比JSON更快速也更轻巧。 支持Python、Ruby、Java、C/C++、Javascript等众多语言。
性能比较
服务端调用
同步模型只能在调用方法中,将所有的逻辑顺序执行,如下图所示:
从时序图可以看出,如果用户执行逻辑比较耗时(红色部分)很容易造成HSFBizProcessor线程全部卡在这些逻辑上,造成线程池满的问题。
使用HSF服务端异步处理的方式,将耗时的部分从HSF的处理线程中移到用户的服务端,使得HSF服务端线程能够快速释放,这样可以 保证 整个机器的服务吞吐量处于较高水平,进而提升稳定性(假设耗时的方法调用频度不高)。如下图所示:
想一下之前提到的ListenableFuture,它的含义就是异步计算结束后,帮我做一些事情。我们把这个逻辑套用过来,当ClientThread完成计算之后,让框架帮忙把数据写出去,这样就解决了客户端写数据的问题了。
是不是感觉使用一个ListenableFuture作为处理的依据还是很不习惯?其实我们只需要识别三个条件因素就可以了,第一,谁负责计算(调用Future.set(Object obj)),第二,计算成功之后谁来做什么,第三,计算结果是什么。在这个例子里面,计算结果是RPCResult,负责计算的是客户端ClientThread,而计算成功之后是框架负责将数据写出。
RPC框架基本原理(三):调用链路分析的更多相关文章
- RPC框架基本原理(一):服务注册
什么是RPC框架 RPC整个过程涉及四类对象:客户端.客户端代理.服务端和服务端代理.RPC要求客户端和服务端之间约定好调用接口和传输格式(如JSON,Xml等),客户端在调用该接口时,由客户端的代理 ...
- 带你手写基于 Spring 的可插拔式 RPC 框架(三)通信协议模块
在写代码之前我们先要想清楚几个问题. 我们的框架到底要实现什么功能? 我们要实现一个远程调用的 RPC 协议. 最终实现效果是什么样的? 我们能像调用本地服务一样调用远程的服务. 怎样实现上面的效果? ...
- RPC框架基本原理(二):客户端注册
客户端的注册流程如下 核心功能主要如下: 1.生成调用远程HSF服务的代理 此代理的效果为生成ServiceMetadata中指定的interface的代理,调用时可将代理转型为服务接口,并进行直接的 ...
- 远程服务调用RPC框架介绍,微服务架构介绍和RPC框架对比,dubbo、SpringClound对比
远程服务调用RPC框架介绍,微服务架构介绍和RPC框架对比,dubbo.SpringClound对比 远程服务调用RPC框架介绍,RPC简单的来说就是像调用本地服务一样调用远程服务. 分布式RPC需要 ...
- RPC框架
RPC框架实现 - 路由控制篇 2015-04-27 22:26 by bangerlee, 499 阅读, 1 评论, 收藏, 编辑 RPC(Remote Procedure Call,远程过程调用 ...
- 服务化实战之 dubbo、dubbox、motan、thrift、grpc等RPC框架比较及选型
转自: http://blog.csdn.net/liubenlong007/article/details/54692241 概述 前段时间项目要做服务化,所以我比较了现在流行的几大RPC框架的优缺 ...
- dubbo、dubbox、motan、thrift、grpc等RPC框架比较及选型
概述 前段时间项目要做服务化,所以我比较了现在流行的几大RPC框架的优缺点以及使用场景,最终结合本身项目的实际情况选择了使用dubbox作为rpc基础服务框架.下面就简单介绍一下RPC框架技术选型的过 ...
- 为什么说要搞定微服务架构,先搞定RPC框架?
今天开始聊一些微服务的实践,第一块,RPC框架的原理及实践,为什么说要搞定微服务架构,先搞定RPC框架呢? 一.需求缘起 服务化的一个好处就是,不限定服务的提供方使用什么技术选型,能够实现大公司跨团队 ...
- RPC框架小结
为什么说要搞定微服务架构,先搞定RPC框架? 1. 为什么说要搞定微服务架构,先搞定RPC框架? 如果没有统一的服务框架,RPC框架,各个团队的服务提供方就需要各自实现一套序列化.反序列化.网络框架. ...
随机推荐
- 使用Freemarker创建word文档
最近做一个项目,本来是直接在网页上查看文本信息,然后给客户直接打印的,但是发现也许是浏览器还是打印机的原因,总之,有个客户打印出来的格式始终与其他的不同,没办法,最后想到了直接将数据库中的信息生成一个 ...
- crontab 实现定时脚本执行完毕再执行下一次
0 4 * * 1 flock -xn /tmp/oneSendTemp.lock -c /home/check_a/h_demand.sh > /dev/null 2>&1
- H5自带表单验证
HTML5自带的表单验证 转载:https://www.web-tinker.com/article/20781.html HTML5对表单元素提供了patern属性,它接受一个正则表达式.表单提交时 ...
- js实现浏览器通知功能
概述 Notification API是浏览器的通知接口,用于在用户的桌面(而不是网页上)显示通知信息,桌面电脑和手机都适用,比如通知用户收到了一封Email.具体的实现形式由浏览器自行部署,对于手机 ...
- solr常用命令
1.启动和关闭 a.启动和重启 启动和重启命令有很多选项让你运行在SolrCloud模式,使用示例配置,以hostname为开头或者非默认端口,指向本地ZooKeeper. bin/solr star ...
- TypeScript 学习二 表达式和循环
表达式: 1,箭头表达式:将function用箭头代替,参数相应进行处理: 用来声明匿名函数,消除了传统匿名函数的this指针问题: 1) 例:简单的方法体为单行的方法,此时不需要大括号和return ...
- ACM第五次积分赛
做出三道题,第二名,总积分上升到第八名,继续加油! SAU-ACM总比赛成绩 姓名 账号 上学期成绩 第一次成绩 第二次成绩 第三次成绩 第四次成绩 第五次成绩 总成绩 张国庆 143401 ...
- awakeFromNib、initWithCoder、initWithFrame三者区别
(1)awakeFromNib和initWithCoder:差别awakeFromNib 从xib或者storyboard加载完毕就会调用initWithCoder: 只要对象是从文件解析来的,就会调 ...
- CodeForces 708B Recover the String
构造. 根据$a[0][0]$可以求得$0$的个数$p$,根据$a[1][1]$可以求得$1$的个数$q$. 如果找不到$p$或$q$,那么就无解. 每一个$0$放到序列中的任何一个位置,假设和前面的 ...
- 简单的socket方法
void sockfunc() { WSADATA Ws; WSAStartup(MAKEWORD(,), &Ws); SOCKET hs = socket(AF_INET, SOCK_STR ...