Strom序列化机制
Storm 中的 tuple可以包含任何类型的对象。由于Storm 是一个分布式系统,所以在不同的任务之间传递消息时Storm必须知道怎样序列化、反序列化消息对象。
Storm 使用 Kryo库对对象进行序列化。Kryo 是一个灵活、快速的序列化库。Storm 默认支持基础类型、string、byte arrays、ArrayList、HashMap、HashSet 以及 Clojure 的集合类型的序列化。如果需要在tuple中使用其他的对象类型,就需要注册一个自定义的序列化器。
原文和作者一起讨论:http://www.cnblogs.com/intsmaze/p/7044042.html
微信:intsmaze
避免微信回复重复咨询问题,技术咨询请博客留言。
自定义序列化
TORM使用Kryo来序列化。要实现自定义序列化器,我们需要使用Kryo注册新的序列化器。添加自定义序列化器是通过拓扑配置的topology.kryo.register属性完成的。它需要一个注册的列表,每个注册项可以采取两种形式:
1:类名注册,在这种情况下,Storm将使用Kryo的FieldsSerializer来序列化该类。这可能是也可能不是该类最好的选择,更多的细节可以查看Kryo文档。
2:实现了com.esotericsoftware.kryo.Serializer接口的类名注册的映射。
Storm为拓扑配置里的注册序列化提供了帮助。Config类中有一个名为registerSerialization的方法,可以把注册添加到配置中。
public void registerSerialization(Class klass);
public void registerSerialization(Class klass, Class<? extends Serializer> serializerClass);
java序列化
一个拓扑中不同的任务传递消息时Storm发现了一个没有注册序列化器的类型,它会使用 Java 序列化器来代替,如果这个对象无法被Java序列化器序列化,Storm 就会抛出异常。
注意,Java 自身的序列化机制非常耗费资源,而且不管在 CPU 的性能上还是在序列化对象的大小上都没有优势。强烈建议读者在生产环境中运行topology 的时候注册一个自定义的序列化器。
可以通过将 Config.TOPOLOGY_FALL_BACK_ON_JAVA_SERIALIZATION 配置为 false 的方式来将禁止序列化器回退到Java的序列化机制。
Config.setFallBackOnJavaSerialization(conf,false);这个时候如果storm使用java序列化就会抛出异常告诉开发人员去注册一个kryo序列化。
实现storm序列化
创建传输的对象。
package cn.intsmaze.serializable.bean;
public class Person {
private int age;
private Studnet studnet;
private ArrayList arrayList=new ArrayList();
private LinkedList linkedList=new LinkedList();
public Person() {
}
public Person(int age,Studnet s) {
this.age = age;
this.studnet=s;
arrayList.add("ArrayList中的"+s.getName());
linkedList.add("linkedList中的"+s.getName());
}
@Override
public String toString() {
return "Person [age=" + age + ", studnet=" + studnet + ", arrayList="
+ arrayList + ", linkedList=" + linkedList + "]";
}
get(),set()......
} package cn.intsmaze.serializable.bean;
public class Studnet {
private String name;
public Studnet() {
}
public Studnet(String name) {
this.name = name;
}
@Override
public String toString() {
return "Studnet [name=" + name + "]";
}
get(),set()......
}
spout和bolt的实现,spout每次会创建一个person对象将该对象发送到bolt,bolt类接收到该对象将该对象打印出来。
package cn.intsmaze.serializable;
import cn.intsmaze.serializable.bean.Person;
import cn.intsmaze.serializable.bean.Studnet;
public class SpoutBean extends BaseRichSpout {
SpoutOutputCollector collector;
public void open(Map conf, TopologyContext context,
SpoutOutputCollector collector) {
this.collector = collector;
}
public void nextTuple() {
Studnet s=new Studnet("xiaoxi");
collector.emit(new Values(new Person(100,s)));
Utils.sleep(500);
}
public void declareOutputFields(OutputFieldsDeclarer declarer) {
declarer.declare(new Fields("person"));
}
} package cn.intsmaze.serializable;
import cn.intsmaze.serializable.bean.Person;
public class BoltBean extends BaseBasicBolt {
public void prepare(Map stormConf, TopologyContext context) {
super.prepare(stormConf, context);
}
public void execute(Tuple input, BasicOutputCollector collector) {
Person person = (Person)input.getValueByField("person");
System.out.println("接收到spout节点传来的数据:"+person);
}
public void declareOutputFields(OutputFieldsDeclarer declarer) {
}
}
场景一:
使用public void registerSerialization(Class klass);
package cn.intsmaze.serializable;
import java.util.LinkedList;
import cn.intsmaze.serializable.bean.Person;
import cn.intsmaze.serializable.bean.Studnet;
public class TopologyBean {
public static void main(String[] args) throws Exception {
TopologyBuilder builder = new TopologyBuilder();
builder.setSpout("spout", new SpoutBean(), 1);
builder.setBolt("bolt", new BoltBean(), 1).shuffleGrouping("spout");
Config conf = new Config();
conf.registerSerialization(Person.class);
conf.registerSerialization(Studnet.class);
//注释掉后,但Studnet没实现java序列化,则会报错。有两种方法,一种注册该类,一种实现java序列化。
conf.registerSerialization(LinkedList.class);
//这里如果注释掉,则会使用java序列化方式,如果我们取消掉禁止使用java序列化方法,则会提示注册LinkedList类报错。
conf.setNumWorkers(2);
// Config.setFallBackOnJavaSerialization(conf, false);//禁止使用java语言自己的序列化
StormSubmitter.submitTopologyWithProgressBar(args[0], conf, builder.createTopology());
}
}
第11行,我们注册person类使用Kryo序列化,person对象除了有基本类型int字段外,还有arraylist,linkedlist类型以及自定义的student类型。arraylist类型是storm默认已经提供了支持。
这里如果我们不对linkedlist类型和自定义类型student进行注册则该拓扑在运行时则会报无法序列化student类型异常。
这个时候有两种办法解决:
一种就是使student实现java的public class Studnet implements Serializable接口,则该拓扑会成功运行。因为storm如果发现传输的对象如果没有注册为Kryo,则就会使用java的序列化对象,而linkedlist默认已经实现了该接口,所以才会出现前面报student对象无法序列化,然后使得student实现java的序列化接口即可。
第二种方案就是,我们对student类进行注册conf.registerSerialization(Studnet.class);。
虽然linkedlist不注册,会默认使用java的序列化,但是出于效率的考虑,我们将其注册为Kryo。
提示:因为有些集合类型,storm没有提供序列化支持,但是实现了java序列化接口,所以如果我们不加以控制,会使用java序列化而拖累整个系统。所以推荐使用
Config.setFallBackOnJavaSerialization(conf, false);禁止使用java语言自己的序列化来可以在本地模式时及时发现报错信息,将问题尽早解决。
场景二:
我们使用kryo序列化,但是有时候我们并不希望传输对象的所有字段,而只是传输对象的某些字段,从而进一步提高消息的传递速率,这个时候我们可以使用kryo的自定义序列化机制来指定传输的值。
package cn.intsmaze.serializable.bean;
import java.util.ArrayList;
import java.util.LinkedList;
import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryo.Serializer;
import com.esotericsoftware.kryo.io.Input;
import com.esotericsoftware.kryo.io.Output;
public class PersonSerializable extends Serializer<Person>{
@Override
public Person read(Kryo kryo, Input input, Class<Person> arg2) {
System.out.println("序列化");
Person person=new Person();
person.setAge(input.readInt());
person.setArrayList(kryo.readObject(input, ArrayList.class));
person.setLinkedList(kryo.readObject(input, LinkedList.class));//该类型,storm默认不支持,所以要在topology中注册该类型,如果不注册,则会使用java序列化。 person.setStudnet(kryo.readObject(input, Studnet.class));//该类型,storm默认不支持,所以要在topology中注册该类型,如果不注册,且java序列化没有实现,则会报错。
return person;
}
@Override
public void write(Kryo kryo, Output output, Person person) {
System.out.println("反序列化");
output.writeInt(person.getAge());
kryo.writeObject(output, person.getArrayList());
kryo.writeObject(output, person.getLinkedList());
kryo.writeObject(output, person.getStudnet());
}
}
package cn.intsmaze.serializable;
import java.util.LinkedList;
import cn.intsmaze.serializable.bean.Person;
import cn.intsmaze.serializable.bean.PersonSerializable;
import cn.intsmaze.serializable.bean.Studnet;
public class TopologyBean {
public static void main(String[] args) throws Exception {
TopologyBuilder builder = new TopologyBuilder();
builder.setSpout("spout", new SpoutBean(), 1);
builder.setBolt("bolt", new BoltBean(), 1).shuffleGrouping("spout");
Config conf = new Config();
conf.registerSerialization(Studnet.class);
conf.registerSerialization(LinkedList.class);
conf.registerSerialization(Person.class, PersonSerializable.class);
conf.setNumWorkers(2);
StormSubmitter.submitTopologyWithProgressBar(args[0], conf,
builder.createTopology());
}
}
因为PersonSerializable类中指定了要传输person对象的int,studne,ArrayList,LinkedList 类型。
如果我们注释掉第12行 conf.registerSerialization(Studnet.class);且Studnet类没有实现java的序列化,则拓扑的任务间传递消息进行序列化时就会报无法序列化该类的错误,感兴趣的同学可以试试注释掉该行,看看storm会报什么异常。
第13行,我们必须注册对LinkedList序列化,storm默认支持了对ArrayList类的序列化,但没有提供对LinkedList序列化,需要我们手动注册,如果不注册,因为LinkedList实现了java的序列化接口,所以会使用java序列化,则不会报错。
强烈建议,在开发中就算注册了kyro序列化方式,也要设置该conf.setFallBackOnJavaSerialization(false)方法来禁止使用java序列化方式,因为实际开发中,核心架构搭建好了,会让团队成员直接在现成架构上编写,他们不需要了解storm的一些机制,但是这也带来问题,一种场景就是,开发人员对传输对象增加了一个LinkedList字段,但是他没有注册序列化类,storm就会对LinkedList使用java序列化,就会拖累系统的性能,所以在架构的时候,通过设置禁止java序列化方法,就可以在测试中及时发现问题所在。
补充:上面的所有一切,在本地运行以及部署到集群时,work数量设置为1时,都不会生效的。因为同一个对象公有一个内存,不会涉及网络传输的,也就不需要序列化和反序列化。
生产场景回顾:
本人intsmaze生产上遇见的问题:storm工程中对传输对象使用了conf.registerSerialization(Person.class, PersonSerializable.class);方式来指定序列化该对象的某些字段。初级程序员在storm工程上开发时,因为业务需要对传输对象增加了一个字段,但是没有在PersonSerializable中序列化和反序列化该对象。恰巧的时,初级工程师本地模式和准生产测试时,topology的work的数量都为1,导致对象在bolt和bolt节点传输时并没有走序列化方式,结果测试一切正常,但是上生产后,因为work数量是10个,立马在后一个bolt中报大量的空指针异常,造成很严重的生产问题。
Strom序列化机制的更多相关文章
- 深入挖掘.NET序列化机制——实现更易用的序列化方案
.NET框架为程序员提供了“序列化和反序列化”这一有力的工具,使用它,我们能很容易的将内存中的对象图转化为字节流,并在需要的时候再将其恢复.这一技术的典型应用场景包括[1] : 应用程序运行状态的持久 ...
- Hadoop阅读笔记(六)——洞悉Hadoop序列化机制Writable
酒,是个好东西,前提要适量.今天参加了公司的年会,主题就是吃.喝.吹,除了那些天生话唠外,大部分人需要加点酒来作催化剂,让一个平时沉默寡言的码农也能成为一个喷子!在大家推杯换盏之际,难免一些画面浮现脑 ...
- Java序列化机制
java的序列化机制支持将对象序列化为本地文件或者通过网络传输至别处, 而反序列化则可以读取流中的数据, 并将其转换为java对象. 被序列化的类需要实现Serializable接口, 使用Objec ...
- Thrift 个人实战--Thrift 的序列化机制
前言: Thrift作为Facebook开源的RPC框架, 通过IDL中间语言, 并借助代码生成引擎生成各种主流语言的rpc框架服务端/客户端代码. 不过Thrift的实现, 简单使用离实际生产环境还 ...
- 由浅入深了解Thrift之服务模型和序列化机制
一.Thrift介绍 Thrift是一个软件框架,用来进行可扩展且跨语言的服务的开发.它结合了功能强大的软件堆栈和代码生成引擎.其允许你定义一个简单的定义文件中的数据类型和服务接口.以作为输入文件,编 ...
- 1 weekend110的复习 + hadoop中的序列化机制 + 流量求和mr程序开发
以上是,weekend110的yarn的job提交流程源码分析的复习总结 下面呢,来讲weekend110的hadoop中的序列化机制 1363157985066 13726230503 ...
- hadoop序列化机制与java序列化机制对比
1.采用的方法: java序列化机制采用的ObjectOutputStream 对象上调用writeObject() 方法: Hadoop 序列化机制调用对象的write() 方法,带一个DataOu ...
- hrift 的序列化机制
Thrift 个人实战--Thrift 的序列化机制 前言: Thrift作为Facebook开源的RPC框架, 通过IDL中间语言, 并借助代码生成引擎生成各种主流语言的rpc框架服务端/客户端代码 ...
- 输入和输出--java序列化机制
对象的序列化 什么是Java对象的序列化? 对象序列化的目标是将对象保存到磁盘上,或允许在网络中直接传输对象.对象序列化机制允许把内存中的Java对象转换成与平台无关的二进制流,从而保存或者传输.其他 ...
随机推荐
- tpcc-mysql的使用
1.tpcc-mysql的业务逻辑及其相关的几个表作用: New-Order:新订单,一次完整的订单事务,几乎涉及到全部表 Payment:支付,主要对应 orders.histo ...
- routeProvider路由的使用
先创建一个主程序文件index.html,内容如下: <!DOCTYPE html> <html ng-app="myApp"> <head> ...
- php学习之目录
一. 关于php中dirname(_file_)的使用 php中定义了一个很有用的常数,即 __file__ 这个内定常数是当前php程序的就是完整路径(路径+文件名). 即使这个文件被其他文件引用( ...
- 个人对于epollhup的理解
1.原因 由于最近对于异步connect函数的测试,发现提前将一个套接字加入epoll监听队列会不断爆出epollhup事件 2.示例 ........ iEpoll = epoll_create( ...
- 随笔-关于公网IP无法访问服务器的解决办法
笔者的环境: windows server 2008 r2 .IIS,php,MySql. 理论上来讲,服务器,其实就是一个大型计算机,我们通过访问服务器的某个端口请求某个资源. 正常情况下,如果没有 ...
- centos手动配置IP和DNS
手动设置ip地址 如果虚拟机不能自动获取IP,只能手动配置,配置方法如下: 输入命令 #vi /etc/sysconfig/network-scripts/ifcfg-eth0 [编辑网卡的配置文件] ...
- 【JAVAWEB学习笔记】04_JavaScript
晨读单词: onmouseover:鼠标移入 onmouseout:鼠标移出 attribute:属性 node:节点 document:文档 element:元素 textNode:文本节点 app ...
- 腾讯云更换yum源
最近公司使用了云服务器代替了机房的一些服务器,然而选择了腾讯云,个人观点通过比较还是阿里云好了点,可能为了使用腾讯云的一些其他功能吧,书归正文. 由于腾讯的源网站经常挂,在我使用过的一台腾讯云主机的时 ...
- javascript之深入剖析this
this的重要性不言而喻,比如面试题经常考到,其次,如果彻底理解了this,那么对理解框架源码及编写高质量代码都有很大的帮助.本文就是要深入剖析this的几种情况,理解了原理,以后妈妈再也不用担心你的 ...
- JavaScript性能优化 DOM编程
最近在研读<高性能JavaScript>,在此做些简单记录.示例代码可在此处查看到. 一.DOM 1)DOM和JavaScript 文档对象模型(DOM)是一个独立于语言的,用于操作XML ...