实战:在Java Web项目中使用HBase

来源:互联网 发布:php程序员招聘 编辑:程序博客网 时间:2024/05/29 13:17

在此之前我们使用MySQL作为数据源,但发现这数据增长速度太快,并且由于种种原因,因此必须使用HBase,所以我们要把Mysql表里面的数据迁移到HBase中,在这里我就不讲解、不争论为什么要使用HBase,HBase是什么了,喜欢的就认真看下去,总有些地方是有用的。

我们要做的3大步骤:

  1. 新建HBase表格。

  2. 把MYSQL数据迁移到HBase中。

  3. 在Java Web项目中读取HBase的数据。

先介绍一下必要的一些环境:HBase的版本:0.98.8-hadoop2

所需的依赖包

[html] view plain copy print?在CODE上查看代码片派生到我的代码片
  1. commons-codec-1.7.jar  
  2. commons-collections-3.2.1.jar  
  3. commons-configuration-1.6.jar  
  4. commons-lang-2.6.jar  
  5. commons-logging-1.1.3.jar  
  6. guava-12.0.1.jar  
  7. hadoop-auth-2.5.0.jar  
  8. hadoop-common-2.5.0.jar  
  9. hbase-client-0.98.8-hadoop2.jar  
  10. hbase-common-0.98.8-hadoop2.jar  
  11. hbase-protocol-0.98.8-hadoop2.jar  
  12. htrace-core-2.04.jar  
  13. jackson-core-asl-1.9.13.jar  
  14. jackson-mapper-asl-1.9.13.jar  
  15. log4j-1.2.17.jar  
  16. mysql-connector-java-5.1.7-bin.jar  
  17. netty-3.6.6.Final.jar  
  18. protobuf-java-2.5.0.jar  
  19. slf4j-api-1.7.5.jar  
  20. slf4j-log4j12-1.7.5.jar  
  21. zookeeper-3.4.6.jar  

如果在你的web项目中有些包已经存在,保留其中一个就好了,免得报奇怪的错误就麻烦了。


步骤1:建表

在此之前,我在Mysql中的业务数据表一共有6个,其结构重复性太高了,首先看看我在HBase里面的表结构:

表名kpi               keyfid+tid+date               簇(family)basegpoweruserateconsumtime                            描述基础信息发电量相关指标可利用率自耗电量累计运行小时数检修小时数利用小时数         列(qualifier)fidtiddatepowerwindspeedunpowertheorycouptimepowernumcpowergpowerruntimechecktimeusetime描述风场ID风机号日期发电量风速弃风电量理论电量耦合度故障时间故障损失电量故障台次当天自耗电量当天发电量当天并网秒数当天检修秒数当天利用秒数

这个表中我们有5个family,其中base Family是对应6个mysql表中的key列, gpower、userate、consum分别对应一个表,time对应3个表。

这个kpi表的rowkey设计是base中的3个qualifier,分别从3个维度查询数据,这样的设计已经可以满足我们的需求了。

具体在HBase中如何建表如何搭建环境自己参考我写的【手把手教你配置HBase完全分布式环境】这篇文章吧。


步骤2:把MySQL数据迁移到HBase


这时我用gpower对应的mysql表来做演示吧,其他表的道理都一样。(这里可能有人会说为什么不用第三方插件直接数据库对数据库迁移,这里我统一回答一下,我不会,我也不需要。)

okay,首先我们来看看代码先吧:

[java] view plain copy print?在CODE上查看代码片派生到我的代码片
  1. import java.io.File;  
  2. import java.sql.Connection;  
  3. import java.sql.DriverManager;  
  4. import java.sql.PreparedStatement;  
  5. import java.sql.ResultSet;  
  6. import java.text.SimpleDateFormat;  
  7. import java.util.ArrayList;  
  8. import java.util.List;  
  9.   
  10. import org.apache.hadoop.conf.Configuration;  
  11. import org.apache.hadoop.hbase.HBaseConfiguration;  
  12. import org.apache.hadoop.hbase.client.HBaseAdmin;  
  13. import org.apache.hadoop.hbase.client.HTable;  
  14. import org.apache.hadoop.hbase.client.Put;  
  15. import org.apache.hadoop.hbase.util.Bytes;  
  16. import org.apache.log4j.BasicConfigurator;  
  17. import org.apache.log4j.Level;  
  18. import org.apache.log4j.Logger;  
  19.   
  20. public class GpowerTransfer{  
  21.       
  22.     private static final String QUOREM = "192.168.103.50,192.168.103.51,192.168.103.52,192.168.103.53,192.168.103.54,192.168.103.55,192.168.103.56,192.168.103.57,192.168.103.58,192.168.103.59,192.168.103.60";//这里是你HBase的分布式集群结点,用逗号分开。  
  23.     private static final String CLIENT_PORT = "2181";//端口  
  24.     private static Logger log = Logger.getLogger(GpowerTransfer.class);  
  25.       
  26.     /** 
  27.      * @param args 
  28.      */  
  29.     public static void main(String[] args) {  
  30.         BasicConfigurator.configure();  
  31.         log.setLevel(Level.DEBUG);  
  32.         String tableName = "kpi";//HBase表名称  
  33.         Configuration conf = HBaseConfiguration.create();  
  34.         conf.set("hbase.zookeeper.quorum", QUOREM);     
  35.         conf.set("hbase.zookeeper.property.clientPort", CLIENT_PORT);  
  36.         try { File workaround = new File(".");  
  37.             System.getProperties().put("hadoop.home.dir", workaround.getAbsolutePath());  
  38.             new File("./bin").mkdirs();  
  39.             new File("./bin/winutils.exe").createNewFile();//这几段奇怪的代码在windows跑的时候不加有时候分报错,在web项目中可以不要,但单独的java程序还是加上去吧,知道什么原因的小伙伴可以告诉我一下,不胜感激。  
  40.             HBaseAdmin admin = new HBaseAdmin(conf);  
  41.             if(admin.tableExists(tableName)){  
  42.                 Class.forName("com.mysql.jdbc.Driver");//首先将mysql中的数据读取出来,然后再插入到HBase中  
  43.                 String url = "jdbc:mysql://192.168.***.***:3306/midb?useUnicode=true&characterEncoding=utf-8";  
  44.                 String username = "********";  
  45.                 String password = "********";  
  46.                 Connection con = DriverManager.getConnection(url, username, password);  
  47.                 PreparedStatement pstmt = con.prepareStatement("select * from kpi_gpower");  
  48.                 ResultSet rs = pstmt.executeQuery();  
  49.                 HTable table = new HTable(conf, tableName);  
  50.                 log.debug(tableName + ":start copying data to hbase...");  
  51.                 List<Put> list = new ArrayList<Put>();  
  52.                 SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");  
  53.                 String base = "base";//family名称  
  54.                 String gpower = "gpower";//family名称  
  55.                 String[] qbase = {"fid","tid","date"};//qualifier名称  
  56.                 String[] qgpower = {"power","windspeed","unpower","theory","coup"};//qualifier名称  
  57.                 while(rs.next()){  
  58.                     String rowKey = rs.getString("farmid") + ":" + (rs.getInt("turbineid")<10?("0"+rs.getInt("turbineid")):rs.getInt("turbineid")) + ":" + sdf.format(rs.getDate("vtime"));//拼接rowkey  
  59.                     Put put = new Put(Bytes.toBytes(rowKey));//新建一条记录,然后下面对相应的列进行赋值  
  60.                     put.add(base.getBytes(), qbase[0].getBytes(), Bytes.toBytes(rs.getString("farmid")));//base:fid  
  61.                     put.add(base.getBytes(), qbase[1].getBytes(), Bytes.toBytes(rs.getInt("turbineid")+""));//base:tid  
  62.                     put.add(base.getBytes(), qbase[2].getBytes(), Bytes.toBytes(rs.getDate("vtime")+""));//base:date  
  63.                     put.add(gpower.getBytes(), qgpower[0].getBytes(), Bytes.toBytes(rs.getFloat("value")+""));//gpower:power  
  64.                     put.add(gpower.getBytes(), qgpower[1].getBytes(), Bytes.toBytes(rs.getFloat("windspeed")+""));//gpower:windspeed  
  65.                     put.add(gpower.getBytes(), qgpower[2].getBytes(), Bytes.toBytes(rs.getFloat("unvalue")+""));//gpower:unvalue  
  66.                     put.add(gpower.getBytes(), qgpower[3].getBytes(), Bytes.toBytes(rs.getFloat("theory")+""));//gpower:theory  
  67.                     put.add(gpower.getBytes(), qgpower[4].getBytes(), Bytes.toBytes(rs.getFloat("coup")+""));//gpower:coup  
  68.                     list.add(put);  
  69.                 }  
  70.                 table.put(list);//这里真正对表进行插入操作  
  71.                 log.debug(tableName + ":completed data copy!");  
  72.                 table.close();//这里要非常注意一点,如果你频繁地对表进行打开跟关闭,性能将会直线下降,可能跟集群有关系。  
  73.             }else{  
  74.                 admin.close();  
  75.                 log.error("table '" + tableName + "' not exisit!");  
  76.                 throw new IllegalArgumentException("table '" + tableName + "' not exisit!");  
  77.             }  
  78.             admin.close();  
  79.         } catch (Exception e) {  
  80.             e.printStackTrace();  
  81.         }   
  82.     }  
  83. }  

在put语句进行add的时候要特别注意:对于int、float、Date等等非String类型的数据,要记得将其转换成String类型,这里我直接用+""解决了,否则在你读取数据的时候就会遇到麻烦了。

步骤3:Java Web项目读取HBase里面的数据

ok,我们成功地把数据迁移到HBase,我们剩下的任务就是在Web应用中读取数据了。

首先我们要确保Web项目中已经把必要的Jar包添加到ClassPath了,下面我对一些HBase的连接做了小封装:

[java] view plain copy print?在CODE上查看代码片派生到我的代码片
  1. import org.apache.hadoop.conf.Configuration;  
  2. import org.apache.hadoop.hbase.HBaseConfiguration;  
  3. import org.apache.hadoop.hbase.client.HBaseAdmin;  
  4.   
  5. /** 
  6.  * @author a01513 
  7.  * 
  8.  */  
  9. public class HBaseConnector {  
  10.       
  11.     private static final String QUOREM = "192.168.103.50,192.168.103.51,192.168.103.52,192.168.103.53,192.168.103.54,192.168.103.55,192.168.103.56,192.168.103.57,192.168.103.58,192.168.103.59,192.168.103.60";  
  12.     private static final String CLIENT_PORT = "2181";  
  13.     private HBaseAdmin admin;  
  14.     private Configuration conf;  
  15.       
  16.       
  17.     public HBaseAdmin getHBaseAdmin(){  
  18.         getConfiguration();  
  19.         try {  
  20.             admin = new HBaseAdmin(conf);  
  21.         } catch (Exception e) {  
  22.             e.printStackTrace();  
  23.         }  
  24.         return admin;   
  25.     }  
  26.       
  27.     public Configuration getConfiguration(){  
  28.         if(conf == null){  
  29.             conf = HBaseConfiguration.create();  
  30.             conf.set("hbase.zookeeper.quorum", QUOREM);     
  31.             conf.set("hbase.zookeeper.property.clientPort", CLIENT_PORT);     
  32.         }  
  33.         return conf;  
  34.     }  
  35. }  

这里的代码基本上跟迁移的那部分代码一样,由于我在其他地方都要重用这些代码,就装在一个地方免得重复写了。

我在Service层做了一下测试,下面看看具体的读取过程:

[java] view plain copy print?在CODE上查看代码片派生到我的代码片
  1. private final String tableName = "kpi";  
  2. @Override  
  3.     public List<GenPowerEntity> getGenPower(String farmid,int ltb,int htb,String start,String end) {  
  4.         List<GenPowerEntity> list = new ArrayList<GenPowerEntity>();  
  5.         HBaseConnector hbaseConn = new HBaseConnector();  
  6.         HBaseAdmin admin = hbaseConn.getHBaseAdmin();  
  7.         try {  
  8.             if(admin.tableExists(tableName)){  
  9.                 HTable table = new HTable(hbaseConn.getConfiguration(), tableName);  
  10.                 Scan scan = new Scan();  
  11.                 scan.addFamily(Bytes.toBytes("base"));  
  12.                 scan.addFamily(Bytes.toBytes("gpower"));  
  13.                 scan.addFamily(Bytes.toBytes("userate"));  
  14.                 String startRowKey = new String();  
  15.                 String stopRowKey = new String();  
  16.                 if("".equals(start) && !"".equals(end)){  
  17.                     stopRowKey = farmid + ":" + Tools.addZero(htb) + ":" + end;  
  18.                     scan.setStopRow(Bytes.toBytes(stopRowKey));  
  19.                 }else if(!"".equals(start) && "".equals(end)){  
  20.                     startRowKey = farmid + ":" + Tools.addZero(ltb) + ":" + start;  
  21.                     scan.setStartRow(Bytes.toBytes(startRowKey));  
  22.                 }else if(!"".equals(start) && !"".equals(end)){  
  23.                     startRowKey = farmid + ":" + Tools.addZero(ltb) + ":" + start;  
  24.                     stopRowKey = farmid + ":" + Tools.addZero(htb) + ":" + end;  
  25.                     scan.setStartRow(Bytes.toBytes(startRowKey));  
  26.                     scan.setStopRow(Bytes.toBytes(stopRowKey));  
  27.                 }else{  
  28.                     table.close();  
  29.                     admin.close();  
  30.                     return null;  
  31.                 }  
  32.                 ResultScanner rsc = table.getScanner(scan);  
  33.                 Iterator<Result> it = rsc.iterator();  
  34.                 List<GenPowerEntity> slist = new ArrayList<GenPowerEntity>();  
  35.                 List<UseRateEntity> ulist = new ArrayList<UseRateEntity>();  
  36.                 String tempRowKey = "";//这个临时rowkey是用来判断一行数据是否已经读取完了的。  
  37.                 GenPowerEntity gpower = new GenPowerEntity();  
  38.                 UseRateEntity userate = new UseRateEntity();  
  39.                 while(it.hasNext()){  
  40.                     for(Cell cell: it.next().rawCells()){  
  41.                         String rowKey = new String(cell.getRowArray(),cell.getRowOffset(),cell.getRowLength(),"UTF-8");  
  42.                         String family = new String(cell.getFamilyArray(),cell.getFamilyOffset(),cell.getFamilyLength(),"UTF-8");  
  43.                         String qualifier = new String(cell.getQualifierArray(),cell.getQualifierOffset(),cell.getQualifierLength(),"UTF-8");  
  44.                         String value = new String(cell.getValueArray(),cell.getValueOffset(),cell.getValueLength(),"UTF-8");//假如我们当时插入HBase的时候没有把int、float等类型的数据转换成String,这里就会乱码了,并且用Bytes.toInt()这个方法还原也没有用,哈哈  
  45.                         System.out.println("RowKey=>"+rowKey+"->"+family+":"+qualifier+"="+value);  
  46.                         if("".equals(tempRowKey))  
  47.                             tempRowKey = rowKey;  
  48.                         if(!rowKey.equals(tempRowKey)){  
  49.                             slist.add(gpower);  
  50.                             ulist.add(userate);  
  51.                             gpower = null;  
  52.                             userate = null;  
  53.                             gpower = new GenPowerEntity();  
  54.                             userate = new UseRateEntity();  
  55.                             tempRowKey = rowKey;  
  56.                         }  
  57.                         switch(family){  
  58.                         case "base":  
  59.                             switch(qualifier){  
  60.                             case "fid":  
  61.                                 gpower.setFarmid(value);  
  62.                                 userate.setFarmid(value);  
  63.                                 break;  
  64.                             case "tid":  
  65.                                 gpower.setTurbineid(Integer.parseInt(value));  
  66.                                 userate.setTurbineid(Integer.parseInt(value));  
  67.                                 break;  
  68.                             case "date":  
  69.                                 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");  
  70.                                 Date date = null;  
  71.                                 try {  
  72.                                     date = sdf.parse(value);  
  73.                                 } catch (ParseException e) {  
  74.                                     e.printStackTrace();  
  75.                                 }  
  76.                                 gpower.setVtime(date);  
  77.                                 userate.setVtime(date);  
  78.                                 break;  
  79.                             }  
  80.                             break;  
  81.                         case "gpower":  
  82.                             switch(qualifier){  
  83.                             case "power":  
  84.                                 gpower.setValue(Float.parseFloat(value));  
  85.                                 break;  
  86.                             case "windspeed":  
  87.                                 gpower.setWindspeed(Float.parseFloat(value));  
  88.                                 break;  
  89.                             case "unpower":  
  90.                                 gpower.setUnvalue(Float.parseFloat(value));  
  91.                                 break;  
  92.                             case "theory":  
  93.                                 gpower.setTvalue(Float.parseFloat(value));  
  94.                                 break;  
  95.                             case "coup":  
  96.                                 gpower.setCoup(Float.parseFloat(value));  
  97.                                 break;  
  98.                             }  
  99.                             break;  
  100.                         case "userate":  
  101.                             switch(qualifier){  
  102.                             case "num":  
  103.                                 userate.setFnum(Integer.parseInt(value));  
  104.                                 break;  
  105.                             case "power":  
  106.                                 userate.setFpower(Float.parseFloat(value));  
  107.                                 break;  
  108.                             case "time":  
  109.                                 userate.setFvalue(Float.parseFloat(value));  
  110.                                 break;  
  111.                             }  
  112.                             break;  
  113.                         }  
  114.                           
  115.                     }  
  116.                 }  
  117.                 rsc.close();  
  118.                 table.close();  
  119.                 admin.close();  
  120.                 ......  
  121.             }  
  122.         } catch (IOException e) {  
  123.             e.printStackTrace();  
  124.         }  
  125.         return list;  
  126.     }  

这是我在Service层中用作测试的一个方法,业务逻辑代码可以直接无视(已经用.....代替了,哈哈),至此我们的所有工作完成,对于更深入的应用,还要靠自己去认真挖掘学习了。
0 0