HBase轻量级框架Parrot

HBase轻量级框架Parrot

 

目录

一、Parrot是什么?

二、Parrot的数据源配置与连接

三、使用无状态的实体Bean执行CRUD

四、Parrot所支持的原生HBase特性;

五、Parrot中被废弃的事物操作;

六、Parrot的设计实现;

 

  一、Parrot是什么?

         在弄清楚Parrot是什么之前,你需要首先弄清楚HBase是什么,HBase能够适用于哪些场景?当这一切你都明白后,再开看看如何使用Parrot

         Parrot是鹦鹉的单词,或许源于家族遗传因素,本人对鸟类一直比较感冒,所以故用此名作为该项目的名字。Parrot是一种超轻量级的HBase框架,它对代码的侵入极低,且不依赖于任何容器,也就是说最简单的Java工程中也能够跑起来。就目前而言,市面上几乎没有成熟的HBase框架,那么Parrot诞生的目的已经很明确了,就是为了开发实惠!

         目前开发人员在程序中大部分都是直接使用HBase Client API操作HBase。笔者使用该工具在项目中倒腾了差不多有半年多的时间,不得不说,使用原生的HBase Client操作HBase确实是一件非常痛苦的事情,不仅开发耗时,且代码冗余度极高。所以我在HBase Client API的基础之上,轻量级的封装了一层,以达到极致简化的目的,这便是Parrot的重。Parrot目前的最新版本是1.1,后续版本中,Parrot将会继续迭代。

         Parrot的优点如下:

   降低原本90%的工作量,使你只需关注于业务;

   使用无状态的Entity Bean方式,对HBase进行CRUD操作;

   低侵入式设计,与业务耦合极低;

   支持从Diamond中进行数据源信息的配置和加载;

   数据检索的自动映射操作;

   支持HBase原生的TTableInterface接口;

 

Parrot缺省依赖DiamondHBase Client API等相关构件!

 

二、Parrot的数据源配置与连接

         Parrot不支持HBase缺省的基于配置文件的方式配置数据源信息,之所以这么做是因为在大规模的项目场景中,资源配置最好是集中式的,以此避免每一次的项目部署都需要修改集群节点中的配置文件,所以Parrot选用了淘宝的Diamond最为集中式配置中心。

ParrotDiamond中的数据源配置信息(dataId=hbase_info,组名=DEFAULT_GROUP):

rootdir=hdfs://CNSZ141222:30000/hbase

distributed=true

quorum=CNSZ141222,CNSZ141223,CNSZ141224

maxSize=1000

        

其中rootdir属性配置了HDFS的地址,distributed属性配置了HBase是否集群,quorum属性配置了ZK的与IP地址对应的机器名,最后一个属性maxSize则用于配置TablePool的连接数。

当你第一次使用Parrot的时候,你需要现将上述信息配置在Diamond中。然后在程序中你只需通过如下语句,便可以建立与HBase的连接:

publicclass ParrotTest {

  privatestatic ConnectionConfig conn;

  @BeforeClass

  publicstaticvoid testConnection() {

       conn = new ParrotConnectionConfig();

  }

}

}

 

ConnectionConfig接口是Parrot的数据源接口,支持从Diamond集中式配置中心获取HBase的数据源信息并建立与HBase的会话连接。前面我们说过Parrot能够及时响应Diamond的变化,一旦Diamond中的配置信息发生变化,Parrot便会重新调用ConnectionConfig接口中的loadDataSource()方法,重新初始化数据源信息并关闭内部打开的TablePool连接。

 

ConnectionConfig接口的常用方法如下:

   MetaDataTablegetTable():获取一个用于操作HBase元数据的Table对象.  

   ParrotTablegetTable(java.lang.Class entityClass):根据实体Bean中的信息从HBase中获取指定的Table对象;

   ParrotTemplategetTemplate():获取一个用于执行数据检索的ParrotTemplate对象;

   voidloadDataSource(com.taobao.diamond.manager.ManagerListener listener):从Diamond集中式配置中心获取出HBase的数据源信息;

 

三、使用无状态的实体Bean执行CRUD

使用无状态的实体Bean操作HBase,翻译白话点,就是使用对象的方式操作HBase。光从名字就能够听出来,既然使用了面向对象特性,那么操作HBase必然比使用原生的HBase Client API更简单,不过Parrot不仅支持实体Bean的方式对HBase执行CRUD操作,并且对于数据检索,Parrot还能够做到将检索的数据结果集,自动映射到实体Bean上。

 

在使用实体Bean操作HBase之前,实体Bean必须添加@Entity注解,如下所示:

@Entity(tableName = "my_table", cloumnFamily = { "info" })

 

上述住接种,tableName属性指定了HBase的表名,cloumnFamily则指定了列族,很遗憾的是目前Parrot1.0的版本中,只能够暂时支持一个列族。不过这并不我们的使用,且按常理说,列族的数量越少,HBase的执行效率越快,这也是实时。

定义实体Bean对象:

@Entity(tableName = "my_table", cloumnFamily = { "info" })

publicclass Bean1 {

     private String userName, passWord;

 

     public String getUserName() {

         returnuserName;

     }

 

     publicvoid setUserName(String userName) {

         this.userName = userName;

     }

 

     public String getPassWord() {

         returnpassWord;

     }

 

     publicvoid setPassWord(String passWord) {

         this.passWord = passWord;

     }

}

 

使用Parrot操作实体Bean对象执行CRUD操作:

privatestatic ConnectionConfig conn;

     @BeforeClass

     publicstaticvoid testConnection() {

         conn = new ParrotConnectionConfig();

     }

 

     @Test

     publicvoid testInsert() throws Exception {

         ParrotTable table = conn.getTable(Bean1.class);

         Bean1 entityObj = new Bean1();

         entityObj.setUserName("JohnGao");

         entityObj.setPassWord("123456");

         table.insert(entityObj, "key1");

     }

 

     @Test

     publicvoid testInserts() throws Exception {

         ParrotTable table = conn.getTable(Bean1.class);

         Bean1 entityObj1 = new Bean1();

         entityObj1.setUserName("JohnGao1");

         entityObj1.setPassWord("123456");

         Bean1 entityObj2 = new Bean1();

         entityObj2.setUserName("JohnGao2");

         entityObj2.setPassWord("123456");

         List<Bean1> entityObjs = new ArrayList<Bean1>();

         entityObjs.add(entityObj1);

         entityObjs.add(entityObj2);

         List<String> rowKeys = new ArrayList<String>();

         rowKeys.add("key1");

         rowKeys.add("key2");

         table.insert(entityObjs, rowKeys);

     }

 

     @Test

     publicvoid testUpdate() throws Exception {

         ParrotTable table = conn.getTable(Bean1.class);

         Bean1 entityObj = new Bean1();

         entityObj.setUserName("JohnGao1");

         entityObj.setPassWord("1111111");

         table.update(entityObj, "key1");

     }

 

     @Test

     publicvoid testUpdates() throws Exception {

         ParrotTable table = conn.getTable(Bean1.class);

         Bean1 entityObj1 = new Bean1();

         entityObj1.setUserName("JohnGao1");

         entityObj1.setPassWord("2222222");

         Bean1 entityObj2 = new Bean1();

         entityObj2.setUserName("JohnGao2");

         entityObj2.setPassWord("33333333");

         List<Bean1> entityObjs = new ArrayList<Bean1>();

         entityObjs.add(entityObj1);

         entityObjs.add(entityObj2);

         List<String> rowKeys = new ArrayList<String>();

         rowKeys.add("key1");

         rowKeys.add("key2");

         table.update(entityObjs, rowKeys);

     }

 

     @Test

     publicvoid testDelete() throws Exception {

         ParrotTable table = conn.getTable(Bean1.class);

         table.delete("key1");

     }

 

     @Test

     publicvoid testDeletes() throws Exception {

         ParrotTable table = conn.getTable(Bean1.class);

         List<String> rowKeys = new ArrayList<String>();

         rowKeys.add("key1");

         rowKeys.add("key2");

         table.delete(rowKeys);

     }

 

     @Test

     publicvoid testGet1() throws Exception {

         ParrotTemplate template = conn.getTemplate();

         Bean1 bean = (Bean1) template.get(Bean1.class, "key1");

         System.out.println(bean.getUserName());

         System.out.println(bean.getPassWord());

     }

 

     @Test

     publicvoid testGet2() throws Exception {

         ParrotTemplate template = conn.getTemplate();

         List<String> rowKeys = new ArrayList<String>();

         rowKeys.add("key1");

         rowKeys.add("key2");

         List<Bean1> beans = (List<Bean1>) template.get(Bean1.class, rowKeys);

         for (Bean1 bean : beans) {

              System.out.println(bean.getUserName());

              System.out.println(bean.getPassWord());

         }

     }

 

     @Test

     publicvoid testGet3() throws Exception {

         ParrotTemplate template = conn.getTemplate();

         Object[] objs = template.get(Bean1.class,

                   new String[] { "key1", "key2" });

         for (Object obj : objs) {

              Bean1 bean = (Bean1) obj;

              System.out.println(bean.getUserName());

              System.out.println(bean.getPassWord());

         }

     }

 

     @Test

     publicvoid testfind1() throws Exception {

         ParrotTemplate template = conn.getTemplate();

         List<Bean1> beans = template.find(Bean1.class, "key", null, null);

         for (Bean1 bean : beans) {

              System.out.println(bean.getUserName());

              System.out.println(bean.getPassWord());

         }

     }

 

     @Test

     publicvoid testfind2() throws Exception {

         SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

         ParrotTemplate template = conn.getTemplate();

         List<Bean1> beans = template.find(Bean1.class, "key",

                   format.parse("2014-05-23 00:00:00").getTime(),

                   format.parse("2014-05-24 00:00:00").getTime());

         for (Bean1 bean : beans) {

              System.out.println(bean.getUserName());

              System.out.println(bean.getPassWord());

         }

     }

 

     @Test

     publicvoid testfind3() throws Exception {

         ParrotTemplate template = conn.getTemplate();

         List<Bean1> beans = template

                   .find(Bean1.class, "key", null, null, 0, 10);

         for (Bean1 bean : beans) {

              System.out.println(bean.getUserName());

              System.out.println(bean.getPassWord());

         }

     }

 

     @Test

     publicvoid testfind4() throws Exception {

         SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

         ParrotTemplate template = conn.getTemplate();

         List<Bean1> beans = template.find(Bean1.class, "key",

                   format.parse("2014-05-22 00:00:00").getTime(),

                   format.parse("2014-05-23 00:00:00").getTime(), 0, 10);

         for (Bean1 bean : beans) {

              System.out.println(bean.getUserName());

              System.out.println(bean.getPassWord());

         }

}

 

上述程序中我们很好的使用了Parrot的对象特性对HBase进行了CRUD操作,那么ConnectionConfig中包含2getTable()方法,上述程序中使用getTable()方法成功的获取到了一个ParrotTable对象,该对象代表着可以用于操作HBase Table的一个表对象。

如果需要执行查询,我们则可以使用ConnectionConfiggetTemplate()方法获取一个ParrotTemplate对象,不仅支持通过索引的get操作,也能够支持scan扫描的find方式。

当我们使用完ParrotTable或者ParrotTemplate后,Parrot会自动管理其中的连接,无需开发人员手动进行资源的释放,并且使用ParrotTemplate执行数据检索的时候,结果集会自动被映射到实体Bean中返回,而无需开发人员手动进行赋值操作。

除了对Table进行CRUD操作,我们还可以通过getTable()方法获取到MetaDateTable对象,该对象用于执行HBase中的元数据操作,比如表的创建和删除,使用方式如下所示:

@Test

     publicvoid testDropTable() throws Exception {

         MetaDataTable table = conn.getTable();

         table.drop(Bean1.class);

     }

 

     @Test

     publicvoid testCreateTable() throws Exception {

         MetaDataTable table = conn.getTable();

         table.create(Bean1.class);

     }

 

     @Test

     publicvoid testCreateTables() throws Exception {

         MetaDataTable table = conn.getTable();

         List<Class> classes = new ArrayList<Class>();

         classes.add(Bean1.class);

         classes.add(Bean2.class);

         table.create(classes);

     }

 

     @Test

     publicvoid testDropTables() throws Exception {

         MetaDataTable table = conn.getTable();

         List<Class> classes = new ArrayList<Class>();

         classes.add(Bean1.class);

         classes.add(Bean2.class);

         table.drop(classes);

     }

 

四、Parrot所支持的原生HBase特性

         Parrot不满足于我们的业务场景时怎么办?这个时候可以使用ConnectionConfig接口中提供的getHTableInterface()方法,该方法会返回一个HTableInterface对象,该对象就是HBase的原生Table对象,开发人员可以使用该对象完成HBaseCRUD操作,如下所示:

     @Test

     publicvoid testHTableInterface() throws Exception {

         /* HBase的原生操作 */

         SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

         ParrotTable parrotTable = conn.getTable(Bean1.class);

         HTableInterface table = parrotTable.getHTableInterface();

         Scan can = new Scan();

         can.setStartRow(Bytes.toBytes("key"));

         can.setTimeRange(format.parse("2014-05-23 00:00:00").getTime(), format

                   .parse("2014-05-24 00:00:00").getTime());

         ResultScanner rts = table.getScanner(can);

         Result rt = null;

         while ((rt = rts.next()) != null) {

              System.out.println(Bytes.toString(rt.getValue("info".getBytes(),

                       "userName".getBytes())));

              System.out.println(Bytes.toString(rt.getValue("info".getBytes(),

                       "passWord".getBytes())));

         }

         parrotTable.setAutoFlush(table);

     }

 

    一旦你在程序中使用原生的HTableInterface,则默认不再受到Parrot的管理,所有的资源则需要自己释放,所有的赋值操作则需要自己手动完成,Parrot将不会再负责结果集映射。

 

五、Parrot中被废弃的事物操作

     刚开始设计Parrot的时候,我的想象非常美好。那就是在Parrot中添加事物支持,哪怕是“伪事物”也可以。但这一切都是错误的开始。

    我们知道HBase0.94版本之前,可以说是根本没有提供事物这个操作的,但0.94版本之后却可以支持弱事物,所谓弱事物指的就是支持同一行中的多个putdelete操作,但跨行、跨表、跨Region则不行,那么我当时的思路是提供Annotation的方式实现事物的AOP横切管理,当有多个事物执行的时候,无论是否在同一个表或者同一个region中,我们只需要做到putdelete失败就回滚,回滚的操作比较复杂,put的话就对应deletedelete就对应put,这样一来就无需顾虑是否是跨行、跨表、跨Region了。

    想象是美好的,但现实的残酷是,delete失败后,执行回滚,我不仅要保存delete之前的数据状态,且回滚时,我如何保证数据正确落入到删除之前的region中?如何确保插入的时间戳与数据真实insert前是一致的?等等这些问题折腾了一天,所以彻底死心宣布Parrot废弃事物,目前只能等待后续HBase版本的更新支持是事物后,再说!

 

六、Parrot的设计实现;

没时间写了,以后再说,主要是没建模工具,懒得画图。

你可能感兴趣的:(HBase、Parrot)