小明思考

Just a software engineer
posts - 124, comments - 36, trackbacks - 0, articles - 0
  BlogJava :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理

Learn From HBase/Bigtable

Posted on 2012-03-07 10:42 小明 阅读(2037) 评论(0)  编辑  收藏 所属分类: 分布式计算

学习软件有三个境界,第一个境界是会使用它,第二个境界是懂得背后的原理,明白它的架构体系,第三个境界学习他的所长,为我所用。研究HBase/BigTable架构和源码一段时间后,我总结了一些东西可以供我们在设计分布式系统借鉴使用。

1. 使用可信任的分布式组件来搭建自己的分布式系统。
设 计一个可靠,健壮的分布式系统是比较困难的。我们知道,为了防止SPOF(Single Point Of Failure)问题,我们要分散风险,把数据放在多个nodes上面去,但是这样带来了是数据的同步问题和版本问题,解决这个问题需要运用复杂的 Paxos协议,系统的复杂度自然就升高了。另外一个需要解决的问题是分布式锁和事件通知机制,以及全局信息共享,设计这些都需要大量的精力和仔细的研 究。

HBase就不用考虑这些问题,它把数据的同步和冗余问题交给了Hadoop,把锁机制和全局共享交给了Zookeeper,这大大简化了HBase的设计。

所以我们设计系统的时候,也要尽量利用这些可靠,稳定的组件。目前比较流行和稳定的有:
分布式文件系统 - HDFS
分布式锁和目录 - Zookeeper
缓存 - MemCached
消息队列 - ActiveMQ

2.避免单点问题(SPOF)
设计分布式系统要时刻考虑到失败,不单是软件可能失败,硬件也可能挂掉,所以我们系统里面就不能有不可替代的角色。

HBase 使用Master Server来监控所有的Region Server,一旦其中的一台出现问题,在其上的Region将会被转移到其他的Region Server,避免了服务中断。而Master Server也可以多台备选,一台挂掉之后,其他的备胎则会”继承遗志“,从而让整个系统得以生存。

那 HBase如何做到这个呢,一个是使用”心跳机制”,即Region Server要主动定期向Master汇报状况,另外一个是利用zookeeper里面的”生命节点“,每个server在启动后要在ZK里面注册,一旦 这个server挂掉,它在ZK里面的节点就会消失,监听这个节点的server就会得到通知。

3.利用不变性提高系统的吞吐量
我们知道,很多进程/线程修改同一个东西的时候,我们就需要锁机制来避免冲突。但是锁带来的问题是系统性能下降。如果对于一个只读的对象,就不需要锁了。

HBase 在设计存储的时候考虑到这一点,最新的数据是放在memory里面,以提高性能。但是memory是有限的,我们不可能让数据一直放在memory里面, 所以我们需要定时把这些数据写到HDFS/磁盘上面。一种设计是写到一个可修改的大文件中去,这样对这个文件的读写就需要加锁了。HBase是每次都写到 一个新的文件中,一旦文件创建后,这个文件将不能被修改,就是所谓的create-one-read-many。当然这样也有一个问题,就是时间长了,会 有很多的小文件,每次查找,需要查找这所有的文件,降低了系统的性能,HBase会定时的合并这些小文件生成一个大文件。

4.利用索引块提高文件的查询速度
HBase的存储文件(HFile)是用来存储很多排序后的Key-Value的,如何设计一种支持快速随机查询和压缩的文件是一个有意思的话题。

HFile 在文件的尾部增加了索引块,但是不可能对任何一个rowkey都做索引,这样的话索引块会很大,而且也不利于压缩。HFile的做法是定义一个Data Block的大小,这样就把数据划分了一个一个的Block,索引只针对这些block做,Block是可以被压缩的。当查询一个rowkey的时候,如 果没有cache的话,首先使用二分法定位到具体的block,然后再解压,遍历查询具体的key。

HFile这样的设计兼顾了速度和文件大小的平衡。

5.自定义RPC机制提供更大的灵活性
HBase/Hadoop 都没有利用标准的Java远程调用规范RMI,而是自己搞了一套。这样做的好处有几点,一是减少网络流量,我们知道,java RMI使用了java serlizable来传递参数,java序列化有很多无关的类信息,都占用不少的空间,而且这会带来对java版本的依赖。二是带来更大的灵活性,你可 以在其中加入版本检查,权限认证等。

那 HBase是怎么设计这个RPC呢?首先它定义了一个writable接口,来代替java序列化,实现这个接口就等于告诉HBase,怎么把这个对象写 到RPC流中去。使用RPC的时候,需要先写一个服务器端和客户端共用的interface,这个interface必须继承 VersionedProtocol来处理版本问题 。HBase利用Java的动态反射机制(Proxy.newProxyInstance)来生成代 理对象,这样当Client调用代理对象的时候,Client就会把参数打包,发送到服务器端,然后等待返回结果。服务器会根据interface查找到 具体的实现的对象,调用该对象的方法来执行远程调用。详细的做法可以参考HBase/Hadoop的源码。

6.内嵌Web Server增强系统的透明度
当一个后台进程启动之后,我们如何了解这个进程的内部状态呢?传统方法是通过进程管理器或者Debug log来看进程的情况,但是这些信息很有限。

HBase利用jetty在进程内部启动了一个web server,就可以即时的显示一些系统内部的信息,非常的方便。

利 用Jetty支持jsp非常的容易,下面是一个示例的代码。注意的是,需要把jasper-runtime-5.5.12.jar,jasper- compiler-5.5.12.jar,jasper-compiler-jdt-5.5.12.jar,jsp-2.1.jar,jsp-api- 2.1.jar等jar包放在classpath里面,否则会出现页面解析错误。

server = new Server(port);

server.setSendServerVersion(false);
server.setSendDateHeader(false);
server.setStopAtShutdown(true);

WebAppContext wac = new WebAppContext();
wac.setContextPath("/");
wac.setWar("./webapps/job");
server.setHandler(wac);
server.setStopAtShutdown(true);


只有注册用户登录后才能发表评论。


网站导航: