rpc框架之 avro 学习 2 - 高效的序列化
同一类框架,后出现的总会吸收之前框架的优点,然后加以改进,avro在序列化方面相对thrift就是一个很好的例子。借用Apache Avro 与 Thrift 比较 一文中的几张图来说明一下,avro在序列化方面的改进:
1、无需强制生成目标语言代码

avro提供了二种使用方式,一种称之为Sepcific方式,这跟thrift基本一致,都是写定义IDL文件,然后用编译器(或插件)生成目标class,另一种方式是Generic,这种方式下,不用生成目标代码,而是采用动态加载定义文件的方式,将 FieldName - FieldValue,以Map<K,V>的方式存储。
2、scheme/tag信息不重复写入二进制数据,存储及传输更高效

上图是thrift的存储格式,每块数据前都有一个tag用于标识数据域的类型及编号(这部分tag信息可以理解为数据域的meta信息),如果传输一个List集合,集合中的每条记录,这部分meta信息实际是重复存储的,多少有些浪费。

这是avro的改进,avro抛弃了对Filed编号的做法,而是直接在class的头部,把所有schema元数据信息包含在内(见下面的java代码),这样,client与server二端其实都已经知道数据的schema(架构模式)信息,仅仅在client与server通讯初始化,首次传输即可,以后无需再传递这部分信息,提升了网络传输效率。类似刚才的List集合这种情况,这部分信息也需要重复存储到2进制数据中,反序列化时,也不需再关注schema的信息,存储空间更小。
package yjmyzz.avro.study.dto;
@SuppressWarnings("all")
@org.apache.avro.specific.AvroGenerated
public class QueryParameter extends org.apache.avro.specific.SpecificRecordBase implements org.apache.avro.specific.SpecificRecord {
public static final org.apache.avro.Schema SCHEMA$ = new org.apache.avro.Schema.Parser().parse("{\"type\":\"record\",\"name\":\"QueryParameter\",\"namespace\":\"yjmyzz.avro.study.dto\",\"fields\":[{\"name\":\"ageStart\",\"type\":\"int\"},{\"name\":\"ageEnd\",\"type\":\"int\"}]}");
public static org.apache.avro.Schema getClassSchema() {
return SCHEMA$;
}
//...
}
这是avro生成的java代码,从源代码可以印证Schema确实已经包含在java代码内。
关于avro的序列化,可以用下面的代码测试一下:
package yjmyzz.avro.test; import org.apache.avro.Schema;
import org.apache.avro.generic.GenericData;
import org.apache.avro.generic.GenericDatumReader;
import org.apache.avro.generic.GenericDatumWriter;
import org.apache.avro.generic.GenericRecord;
import org.apache.avro.io.*;
import org.apache.avro.specific.SpecificDatumReader;
import org.apache.avro.specific.SpecificDatumWriter;
import org.junit.Assert;
import org.junit.Test;
import yjmyzz.avro.study.dto.QueryParameter;
import java.io.ByteArrayOutputStream;
import java.io.IOException; public class SerializeTest { @Test
public void test() throws IOException { QueryParameter queryParameter = getQueryParameter(); //****** 1 Specific 方式-序列化*******// ByteArrayOutputStream out1 = new ByteArrayOutputStream();
DatumWriter<QueryParameter> writer1 = new SpecificDatumWriter<QueryParameter>(QueryParameter.class);
BinaryEncoder encoder1 = EncoderFactory.get().binaryEncoder(out1, null);
writer1.write(queryParameter, encoder1);
encoder1.flush();
out1.close();
byte[] byte1 = out1.toByteArray();
System.out.println("Avro Specific二进制序列后的byte数组长度:" + byte1.length); //反序列化
DatumReader<QueryParameter> reader1 = new SpecificDatumReader<QueryParameter>(QueryParameter.class);
Decoder decoder1 = DecoderFactory.get().binaryDecoder(out1.toByteArray(), null);
QueryParameter result1 = reader1.read(null, decoder1);
Assert.assertEquals(queryParameter.getAgeStart(), result1.getAgeStart());
Assert.assertEquals(queryParameter.getAgeEnd(), result1.getAgeEnd()); //**我是万恶的分割线***// //****** 2 Genericy 方式-序列化*******//
Schema.Parser parser = new Schema.Parser();
//Schema schema = parser.parse(new File("/Users/jimmy/Work/Code/avro/avro-contract/src/main/avro/QueryParameter.avsc"));
Schema schema = parser.parse(getClass().getResourceAsStream("/QueryParameter.avsc")); //根据schema创建一个record示例(跟反射的思想有点类似)
GenericRecord datum = new GenericData.Record(schema);
datum.put("ageStart", 1);
datum.put("ageEnd", 5); //序列化
ByteArrayOutputStream out2 = new ByteArrayOutputStream();
DatumWriter<GenericRecord> writer2 = new GenericDatumWriter<GenericRecord>(schema);
Encoder encoder2 = EncoderFactory.get().binaryEncoder(out2, null);
writer2.write(datum, encoder2);
encoder2.flush();
out2.close();
byte[] byte2 = out2.toByteArray();
System.out.println("Avro Generic二进制序列后的byte数组长度:" + byte2.length); //反序列化
DatumReader<GenericRecord> reader2 = new GenericDatumReader<GenericRecord>(schema);
Decoder decoder2 = DecoderFactory.get().binaryDecoder(out2.toByteArray(), null);
GenericRecord result2 = reader2.read(null, decoder2);
Assert.assertEquals(datum.get("ageStart"), result2.get("ageStart"));
Assert.assertEquals(datum.get("ageEnd"), result2.get("ageEnd"));
} private QueryParameter getQueryParameter() {
QueryParameter query = new QueryParameter();
query.setAgeStart(1);
query.setAgeEnd(5);
return query;
}
}
Avro Specific二进制序列后的byte数组长度:2
Avro Generic二进制序列后的byte数组长度:2
与前一篇thrift中的序列化结果相比,存储占用的空间比thrift的TCompactProtocol还要小,确实在序列化方面avro做得更好。
但是,凡事总有二面性,虽然avro在序列化方面做了不少改进,但是其RPC的实现并没有做出太多的创新,默认提供的HttpServer、NettyServer都是直接用的其它开源产品实现,不象Thrift自己提供了全新的实现,所以在RPC的性能方面,avro仍有很多可以优化的空间,默认情况下,从我自己测试的情况下,avro是不敌thrift的。但具体能优化到什么程度,就看使用的人在网络通讯、网络协议方面的功底了,有朋友说avro使用c#语言开发Server与Client端,对源代码优化后,可达到每秒20~30万的处理数。
rpc框架之 avro 学习 2 - 高效的序列化的更多相关文章
- rpc框架之avro 学习 1 - hello world
avro是hadoop的一个子项目,提供的功能与thrift.Protocol Buffer类似,都支持二进制高效序列化,也自带RPC机制,但是avro使用起来更简单,无需象thrift那样生成目标语 ...
- rpc框架之gRPC 学习 - hello world
grpc是google在github于2015年开源的一款RPC框架,虽然protobuf很早google就开源了,但是google一直没推出正式的开源框架,导致github上基于protobuf的r ...
- rpc框架: thrift/avro/protobuf 之maven插件生成java类
thrift.avro.probobuf 这几个rpc框架的基本思想都差不多,先定义IDL文件,然后由各自的编译器(或maven插件)生成目标语言的源代码,但是,根据idl生成源代码这件事,如果每次都 ...
- rpc框架之 thrift 学习 1 - 安装 及 hello world
thrift是一个facebook开源的高效RPC框架,其主要特点是跨语言及二进制高效传输(当然,除了二进制,也支持json等常用序列化机制),官网地址:http://thrift.apache.or ...
- rpc框架之 thrift 学习 2 - 基本概念
thrift的基本构架: 上图源自:http://jnb.ociweb.com/jnb/jnbJun2009.html 底层Underlying I/O以上的部分,都是由thrift编译器生成的代码, ...
- 服务化实战之 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 框架到入门 grpc (下)
之前手动实现了一次简陋的 rpc 调用,为了简单使用了 json 编码信息,其实这是非常不可靠的,go 中 json 解析会有一些问题,比如整数会变成浮点数,而且 json 字符串比较占空间. gRP ...
- 一个入门rpc框架的学习
一个入门rpc框架的学习 参考 huangyong-rpc 轻量级分布式RPC框架 该程序是一个短连接的rpc实现 简介 RPC,即 Remote Procedure Call(远程过程调用),说得通 ...
随机推荐
- 高性能 Socket 组件 HP-Socket v3.1.3 正式发布
HP-Socket 是一套通用的高性能 Windows Socket 组件,提供服务端组件(IOCP 模型)和客户端组件(Event Select 模型),广泛适用于 Windows 平台的 TCP/ ...
- jQuery静态方法noop,camelCase,nodeName,trim使用和源码分析
noop方法 jQuery.noop()函数是一个空函数,它什么也不做. 当某些时候你需要传入函数参数,而且希望它什么也不做的时候,你可以使用该函数,也无需再新建一个空的函数. 示例: // 传入一个 ...
- JavaScript实现拖拽元素对齐到网格(每次移动固定距离)
这几天在做一个拖拽元素的附加功能,就是对齐到网格,实际上就是确定好元素的初始位置,然后拖拽元素时,每次移动固定的距离.让元素都可以在网格内对齐.先上效果图,然后在详细说明一下细节问题 做了一个gif图 ...
- [Android]一个干净的架构(翻译)
以下内容为原创,欢迎转载,转载请注明 来自天天博客:http://www.cnblogs.com/tiantianbyconan/p/5276587.html 一个干净的架构 原文:https://b ...
- nodejs的child_process同步异步
nodejs是一种单线程模型,但是,使用nodejs的child_process模块可以实现多进程任务.利用child_process可以创建子进程,实现子进程和主进程之间的通信. nodejs v0 ...
- AsyncHttpClient 源码分析
上一篇文章从功能和用法上对AsyncHttpClient做了个大致介绍,今天我们和以往一样,从内部实现.原理的角度带领大家看看 其工作机制,以便加深理解.写程序越多,我发现每接触一个新东西,都会有强烈 ...
- .NET框架设计(常被忽视的C#设计技巧)
阅读目录: 1.开篇介绍 2.尽量使用Lambda匿名函数调用代替反射调用(走进声明式设计) 3.被忽视的特性(Attribute)设计方式 4.扩展方法让你的对象如虎添翼(要学会使用扩展方法的设计思 ...
- 使用eclipse开发Morphline的Java代码段
背景:morphline是一个轻量级的etl工具.除了提供标准化的方法之外,还可以定制化的开发java片段.定制化的java片段会在加载时被作为一个独立的类编译,对源数据作处理. morphline关 ...
- 烂泥:Postfix邮件服务器搭建之准备工作
说实话,Postfix邮件服务器的搭建是一件很麻烦的事情,需要各种软件之间的配置和调试.在写这篇文章之前,我也是搭建测试了不下于10次才算把整个流程给走通,今天刚好有时间把整个搭建过程记录下来. 在正 ...
- 烂泥:Postfix邮件服务器搭建之软件安装与配置
本文由ilanniweb提供友情赞助,首发于烂泥行天下 想要获得更多的文章,可以关注我的微信ilanniweb Postfix邮件服务器的搭建需要使用到几个软件,分别是cyrus-sasl.postf ...