2. 接口数据协议

IDE基于Hive部分约定的数据调用约定。

2.1 HQL语法约定

该部分的HQL由Web Server提交至Hive Server端执行。

根据Hive的特点,除了执行部分SELECT语句会启动MapReduce外,其他如alter、load、insert语句等都不会涉及M/R。由此可知,大部分情况下不会有MapReduce的执行过程日志输出,而目前IDE的日志监控部分仅对有MapReduce执行的查询有处理。根据《基于Hive JDBC实现的Web可视化界面方案V0.2.docx》分析得知,Hive JDBC仅对executeQuery和close方法做了真正实现,所有的HQL语句皆通过executeQuery方法传入并执行。因此,是否启动日志监控功能,只要在此方法的HQL参数前添加日志开关标识即可。

日志开关的标识约定为:{TYPE}_{DEST}_{SN}[_{IP}_{PORT}]  #@#@ 。

TYPE:代表语句类型,取值为KILL、QUERY(即select的语句)。

DEST:代表目的地,取值为HBASE、REDIS(FILE、HDFS暂不支持);

SN:为每个Job日志和查询结果数据key的前缀。如,执行A Job的日志单元标识前缀为dataA,那么对于存储在目的地中的key序列为dataA_LOG000001、dataA_LOG000002、dataA_LOG000003、…、dataA_LOGN(N为大于等于0的正整数,在位数不足6位的情况下,前缀补0以达到6位长度),客户端取对应执行日志时,即通过该key值序列逐一取出。而查询结果数据的key前缀为dataA_DATA。

另外特别注意两点:1.编号由000001开始;2.由于语法前缀标识符以下划线”_”分割,那么SN中决不能出现”_”符号,否则数据出错。

IP:目标主机的有效IP地址或域名,该参数在DEST为REDIS时必填,否则可以为空;

PORT:目标主机的有效端口,该参数在DEST为REDIS时必填,否则可以为空。即当DEST为REDIS时,日志开关的标识约定变为:{TYPE}_{DEST}_{SN};

#@#@:为分割标识,在欲新增日志存储方式的语句最前面。#@#@前后各留一个或多个空格。其中TYPE为 KILL时,无需#@#@分隔符及后缀。

下面针对各种情况分别举例:

一、 应用redis转存日志。select * from table 这个查询HQL,假如,流水号设定为20121112151900,redis的ip地址为192.168.8.8,端口为8888,那么查询语句应该更改为:QUERY_REDIS_20121112151900_192.168.8.8_8888 #@#@         select * from table 。

二、 应用hbase转存日志。select * from table 这个查询HQL,假如,流水号设定为20121112151900,那么查询语句应该更改为:QUERY_HBASE_20121112151900  #@#@  select * from table 。

三、 应用redis时kill停止执行任务。 select * from table 这个查询HQL,假如,流水号设定为20121112151900,redis的ip地址为192.168.8.8,端口为8888,那么kill语句应该更改为:KILL_REDIS_20121112151900_192.168.8.8_8888。

四、 应用hbase时kill停止执行任务。 select * from table 这个查询HQL,假如,流水号设定为20121112151900,那么kill语句应该更改为:KILL_HBASE_20121112151900。

调用Statement.executeQuery(sql)执行即可。

2.2 读取日志约定

由Web Server发起读取转存日志的请求。

读取执行日志的方式会随着存放的目的地不同而稍有差异,建议Web Server端封装读取日志的接口,便于灵活切换。下面分别约定从Redis和HBase中读取日志的规则。

从Redis读取日志

redis中以key-value键值对的形式进行存储,其key值是日志开关标识中的SN为前缀,后跟6位从000001开始的序列;value值为具体日志内容的字符串型。

举例说明,假设日志开关的为QUERY_REDIS_20121112151900_192.168.8.8_8888,那么Web Server读取日志时连接的Redis服务端的IP和Port分别为192.168.8.8和8888,再调用redis客户端递增读取以20121112151900_LOG000001、20121112151900_LOG000002、…、20121112151900_LOG00000N(N为大于等于0的正整数,在位数不足6位的情况下,前缀补0,达到6位长度)。

每次读取日志时,同时读取20121112151900_LOG000000的日志标志位,当标志位的以“00”开头时,表示程序正常执行完毕;当以“10”开头时,表示程序仍在执行中;当以“20”开头时,表示程序执行完毕,但是执行过程异常;当以“30”开头时,表示程序异常终止[详见2.5节]。简单代码如下:

Jedis jedis = new Jedis("192.168.8.8", 8888);
int num=1;
String numStr= String.valueOf(i);
numStr=”000000”.subString(0,6- numStr.length())+numStr;
String  loginfo=jedis.get("20121112151900_LOG " + numStr);
System.out.println(loginfo);
String logFlag= jedis.get("20121112151900_LOG000000 " );
if(logFlag !=null && logFlag.startWith(“00”)){
判断状态标志位,并输出提示
}

 

从HBase读取日志

HBase中以rowid-value行键与列族值的条记录形式进行存储,其rowid值是日志开关标识中的SN为前缀,后跟6位从000001开始的序列;value值为具体日志内容的字符串型。

举例说明,假设日志开关的为QUERY_HBASE_20121112151900,那么Web Server读取日志时连接的HBase服务端通过hbase-site.xml中进行配置,再调用HBase客户端递增读取以20121112151900_LOG、20121112151900_LOG、…、20121112151900_LOG 00000N(N为大于等于0的正整数,在位数不足6位的情况下,前缀补0,达到6位长度)或者设定rowid值区间批量读取。

每次读取日志时,同时读取20121112151900_LOG000000的日志标志位,当标志位的以“00”开头时,表示程序正常执行完毕;当以“10”开头时,表示程序仍在执行中;当以“20”开头时,表示程序执行完毕,但是执行过程异常;当以“30”开头时,表示程序异常终止[详见2.5节]。简单代码如下:

1. 读取单条记录

HTable table = new HTable(conf, "IDE_MR_LOG");
int num=1;
String rowKey = String.valueOf(i);
rowKey ="20121112151900_LOG " +”000000”.subString(0,6- numStr.length())+numStr;
Get get = new Get(rowKey.getBytes());
Result rs = table.get(get);
for (KeyValue kv : rs.raw()) {
//System.out.print(new String(kv.getRow()) + " ");
//System.out.print(new String(kv.getFamily()) + ":");
//System.out.print(new String(kv.getQualifier()) + " ");
//System.out.print(kv.getTimestamp() + " ");
System.out.println(new String(kv.getValue()));
String logFlag= jedis.get("20121112151900_LOG000000 " );
if(logFlag !=null && logFlag.startWith(“00”)){
判断状态标志位,并输出提示
}
}

2. 读取多条记录

HTable table = new HTable(conf, "IDE_MR_LOG");
int num=1;
String startRowKey = String.valueOf(i);
startRowKey =” 20121112151900_LOG”+”000000”.subString(0,6- numStr.length())+numStr;
String startRowKey = String.valueOf(i);
String endRowKey =” 20121112151900_LOG”+”000000”.subString(0,6- numStr.length())+numStr;
Scan s = new Scan(startRowKey.getBytes[],endRowKey.getBytes[],);
ResultScanner ss = table.getScanner(s);
for (Result r : ss) {
for (KeyValue kv : rs.raw()) {
//System.out.print(new String(kv.getRow()) + " ");
//System.out.print(new String(kv.getFamily()) + ":");
//System.out.print(new String(kv.getQualifier()) + " ");
//System.out.print(kv.getTimestamp() + " ");
System.out.println(new String(kv.getValue()));
String logFlag= jedis.get("20121112151900_LOG000000 " );
if(logFlag !=null && logFlag.startWith(“00”)){
判断状态标志位,并输出提示
}
}
  }

 

 

 

2.3 读取查询结果数据约定

由Web Server发起读取转存(方便后续可追踪)查询结果数据的请求。

读取执行查询结果数据的方式会随着存放的目的地不同而稍有差异,建议Web Server端封装读取和写入数据的接口,便于灵活切换。下面分别约定从Redis和HBase中读取查询结果数据的规则。

从Redis读取数据

日志在redis中以key-value键值对的形式进行存储,其key值是日志开关标识中的SN为前缀,后跟+”_DATA”组成数据的key;value值为查询列的元数据和结果数据组装而成的JSON格式的字符串。

拼装的JSON格式为:

{
"metadata":["selcolumn1","selcolumn2",...,"selcolumnN"],
"resultdata":
 [
 ["value1","value2",...,"valueN"],
["value1","value2",...,"valueN"],
...,
["value1","value2",...,"valueN"]
]
}

其中,metadata为select语句中查询的列顺序,当列为*时,由元数据ResultSetMetaData取出;

resultdata为select语句中查询的列顺序,对应为每一条记录组合而成的数组结构。

举例说明,假设日志开关的为QUERY_REDIS_20121112151900_192.168.8.8_8888,那么Web Server读取数据时连接的Redis服务端的IP和Port分别为192.168.8.8和8888,再调用redis客户端读取key值为20121112151900_D的值。简单代码如下:

Jedis jedis = new Jedis("192.168.8.8", 8888);
String  jsonData=jedis.get("20121112151900_DATA " );
System.out.println(jsonData);

 

从HBase读取数据

日志在HBase中以rowid-value行键与列族的条记录形式进行存储,其rowid值是日志开关标识中的SN为前缀,后跟“_D”后再接6位从000001开始的序列;value值为一个列族,列族下对应的列由select后接的查询列(全部转换为小写)生成。如,select a,b,c from table,那么在HBase的列族对应为:value:a, value:b, value:c 。

举例说明,假设日志开关的为QUERY_HBASE_20121112151900,那么Web Server读取查询结果数据时连接的HBase服务端通过hbase-site.xml中进行配置,再调用HBase客户端递增读取以20121112151900_DATA000001、20121112151900_DATA000002、…、20121112151900_DATA00000N(N为大于等于0的正整数,在位数不足6位的情况下,前缀补0,达到6位长度)或者设定rowid值区间批量读取。简单代码如下:

1. 读取单条记录

HTable table = new HTable(conf, "IDE_MR_DATA");
int num=1;
String rowKey = String.valueOf(i);
rowKey =” 20121112151900_DATA”+”000000”.subString(0,6- numStr.length())+numStr;
Get get = new Get(rowKey.getBytes());
Result rs = table.get(get);
for (KeyValue kv : rs.raw()) {
System.out.print(new String(kv.getRow()) + " ");
System.out.print(new String(kv.getFamily()) + ":");
System.out.print(new String(kv.getQualifier()) + " ");
//System.out.print(kv.getTimestamp() + " ");
System.out.println(new String(kv.getValue()));
}

2. 读取多条记录

HTable table = new HTable(conf, "IDE_MR_DATA");
int num=1;
String startRowKey = String.valueOf(i);
startRowKey =” 20121112151900_DATA”+”000000”.subString(0,6- numStr.length())+numStr;
String startRowKey = String.valueOf(i);
String endRowKey =” 20121112151900_DATA”+”000000”.subString(0,6- numStr.length())+numStr;
Scan s = new Scan(startRowKey.getBytes[],endRowKey.getBytes[],);
ResultScanner ss = table.getScanner(s);
for (Result r : ss) {
for (KeyValue kv : rs.raw()) {
System.out.print(new String(kv.getRow()) + " ");
System.out.print(new String(kv.getFamily()) + ":");
System.out.print(new String(kv.getQualifier()) + " ");
//System.out.print(kv.getTimestamp() + " ");
System.out.println(new String(kv.getValue()));
}
  }

 

 

2.4 操作日志存储约定

操作日志必须持久化,存储在RMDB中,操作日志的写入由Web Server完成。

操作日志表字段包括:operator,operteStartTime, operteEndTime,operateContent,operateResult,operateRaletiveRowId,分别表示操作人,操作开始时间,操作结束时间,操作内容,操作结果,操作关联的结果集rowId。

举例说明,假设IDE上线运行一段时间后,想追踪运行情况,那么可以从操作日志中读取具体情况。假设查询日志开关SN为20121112151900的操作历史,那么Web Server读取数据时连接的HBase服务端通过hbase-site.xml中进行配置,再调用HBase客户端递增读取以20121112151900_LOG为rowId。代码为JDBC普通代码,此处略去。

2.5 状态标志位约定

序号

编码

说明

1

00#@#@num#@#@概要描述

程序执行完毕,日志输出完毕。num为写入日志最终条数

2

10#@#@num#@#@概要描述

程序正在执行中。其中,num为过渡值。

3

20#@#@num#@#@概要描述

程序执行完毕,但其间报有异常。num为写入日志最终条数

4

30#@#@num#@#@概要描述

程序异常终止,日志输出完毕。num为写入日志最终条数

 

 

 

注意

“#@#@”是编码与中文描述的分隔符,#号后面接中文描述,例如“00#@#@执行完毕”、“20#@#@执行完毕,日志开关中指定的ip和port连接异常”。

读取状态标志位的key或rowid为日志开关标识中SN值加上_LOG000000,即{SN}_LOG000000