路还得继续!
我重新构想了思路,直接用JAVA的API获取HBASE的历史数据,然后传递给R,这样可以避免了R通过thrift调用!
于是我重写了第二个版本的MapReduce,如下:
package mytest;
import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FSDataOutputStream;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.hbase.HBaseConfiguration;
import org.apache.hadoop.hbase.client.HTable;
import org.apache.hadoop.hbase.client.Result;
import org.apache.hadoop.hbase.client.ResultScanner;
import org.apache.hadoop.hbase.client.Scan;
import org.apache.hadoop.io.NullWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.InputSplit;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.Mapper;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.input.FileSplit;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
import org.apache.hadoop.mapreduce.lib.output.NullOutputFormat;
public class MapRLanguage2 {
public static int mapNum = 12;
public static final String RDIR_H="hdfs://bd110:9000/user/hadoop/";
public static final String RDIR_L="/home/hadoop/yibei/R/";
public static class RMapper extends Mapper<Object, Text, NullWritable, NullWritable>{
HTable table = null;
public void setup(Context context
) throws IOException, InterruptedException {
Configuration hbaseConf = HBaseConfiguration.create();
table = new HTable(hbaseConf, "kpinfo");
}
public void cleanup(Context context
) throws IOException, InterruptedException {
table.close();
}
//每个文件只有一行
public void map(Object key, Text value, Context context ) throws IOException, InterruptedException {
String args[] = value.toString().split("\\|");
//先准备存入的文件名称 例如:[Map].cell.kpi
InputSplit inputSplit = context.getInputSplit();
String fileName = ((FileSplit)inputSplit).getPath().getName();
//获取数据存入文件中
String cellarr[] = args[2].split(",");
String kpiarr[] = args[3].split(",");
//获取开始时间
String datedir = context.getConfiguration().get("datedir");
for(int i=0;i<cellarr.length;i++){
//创建文件
for(int j=0;j<kpiarr.length;j++){
File ldir = new File(RDIR_L+datedir+"/");
ldir.mkdirs();
File lfile = new File(RDIR_L+datedir+"/"+fileName+"."+cellarr[i]+"."+kpiarr[j]);
lfile.createNewFile();
}
}
//访问HBASE,获取历史历史数据
Scan scan = new Scan();
scan.addFamily("h".getBytes()); //扫描列簇
scan.setCaching(10000);//一次取1万
ResultScanner scanner = table.getScanner(scan);
//扫描列簇获取每一行的值
for(Result result: scanner){
//扫描当前行的每一个字段 ,2个"h".getBytes()看似多余,其实不然,理解为获取根据a列簇扫描获取的行记录,当然可以获取b列簇的值
for(Map.Entry<byte[], byte[]> entry : result.getFamilyMap("h".getBytes()).entrySet()){
String column = new String(entry.getKey());
String data = new String(entry.getValue());
System.out.println(column+","+data);
}
}
scanner.close();
//文件路径传入给R
// Rengine re=new Rengine(new String[] { "--vanilla" }, false,null);
// if (!re.waitForR()) {
// System.out.println("Cannot load R");
// return;
// }
// re.eval("setwd('"+RDIR_L+"')");
// re.eval("source('main2.R')");
// String rcmd="kpi.forecastByDBWithTime('"+args[4]+"','"+args[5]+"','"+args[6]+"','"+args[7]+"','"+args[1]+"','" + args[0] +"','" + args[2] +"','" + args[3] + "')";
// re.eval(rcmd);
// re.end();
}
}
public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException {
String dbtbl = "stat_plan_result_"+args[0].trim();
String dbname = args[1].trim();
String cells = args[2].trim();
String kpis = args[3].trim();
String hbtime = args[4].trim();
String hetime = args[5].trim();
String fbtime = args[6].trim();
String fetime = args[7].trim();
//2种思路,1种设置多少Size拆分一个Map使用FileInputFormat.setMaxInputSplitSize(),另1种利用每个文件一个Map的特性处理它。按文件处理也可以使用job.setInputFormatClass();
// String cells="1,2,3,4,5,6,7,8,9,10,11,12,13,14";
String cellarr[] = cells.split(",");
String datedir = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date());
String currentDir =RDIR_H+datedir+"/in/";
String currentoutDir =RDIR_H+datedir+"/out/";
if(cellarr.length<=mapNum){
for(int i =0;i<cellarr.length;i++){
//输出文件
String filepath = currentDir+i;
FileSystem fs = FileSystem.get(URI.create(filepath), new Configuration());
FSDataOutputStream out = fs.create(new Path(filepath));
out.write((dbtbl+"|"+dbname+"|"+cellarr[i]+"|"+kpis+"|"+hbtime+"|"+hetime+"|"+fbtime+"|"+fetime).getBytes("UTF-8"));
out.close();
}
}else{
int s = cellarr.length/mapNum;
int y = cellarr.length%mapNum;
for(int i =0,j=y;i<mapNum;i++){
//2种分配方案,1种是循环每个cell,加入文件。另1种是计算出,每次分配的数量。
int cellNum=s;
if(j>0){
cellNum+=1;
j--;
}
String celldata="";
for(int k =0;k < cellNum; k++){
//每个Map的小区,从cellarr中获取。
int cellindex=k+i*s+(cellNum>s?i:y);
celldata+=cellarr[cellindex]+",";
}
String filepath = currentDir+i;
FileSystem fs = FileSystem.get(URI.create(filepath), new Configuration());
FSDataOutputStream out = fs.create(new Path(filepath));
out.write((dbtbl+"|"+dbname+"|"+celldata.substring(0,celldata.length()-1)+"|"+kpis+"|"+hbtime+"|"+hetime+"|"+fbtime+"|"+fetime).getBytes("UTF-8"));
out.close();
}
}
Job job = Job.getInstance(new Configuration());
job.getConfiguration().setInt("mapreduce.task.timeout", 0);//关闭超时
job.getConfiguration().setBoolean("mapreduce.map.speculative", false);//关闭推测执行
job.getConfiguration().setStrings("datedir", datedir);//设置一个目录
job.setJarByClass(MapRLanguage2.class);
job.setMapperClass(RMapper.class);
//job.setOutputKeyClass(NullWritable.class);//和setMapOutputKeyClass的区别?也许是没有reduce的时候指的是一样的,有reduce的时候是只reduce的输出?
//job.setOutputValueClass(NullWritable.class);//同上
job.setNumReduceTasks(0);//没有Rudeuce输出
job.setOutputFormatClass(NullOutputFormat.class);//不需要输出 可以关闭setOutputKeyClass和setOutputValueClass?
FileInputFormat.addInputPath(job,new Path(currentDir));
FileOutputFormat.setOutputPath(job,new Path(currentoutDir));
System.exit(job.waitForCompletion(true) ? 0 : 1);
}
}
但后台错误如故,我尝试修改代码做一些测试,发现程序是能够正常返回,甚至结果也是对的。我陷入苦恼。
我翻阅了大量的资料,并没有太多信息能给我灵感,查看官方HBASE文档显示:
HBASE-0.98 相对 HADOOP-2.6是NT(NOT TEST),于是我决定升级至HBASE1.1.1。
做一些简单的描述
下载解压后,进入conf目录
regionservers
bd107
bd108
hbase-env.sh
export JAVA_HOME=/usr/local/jdk1.7 --打开注释并修改对应位置
hbase-site.xml
<property>
<name>hbase.cluster.distributed</name> --开启分布式
<value>true</value>
</property>
<property>
<name>hbase.rootdir</name> --存入hdfs的位置
<value>hdfs://bd110:9000/hbase1</value>
</property>
<property>
<name>hbase.zookeeper.property.dataDir</name> --zookeeper需要用的
<value>/home/hadoop/zookeeperDir</value>
</property>
<property>
<name>dfs.replication</name> --测试了一把,貌似可以把dfs的默认3份给覆盖变成2了
<value>2</value>
</property>
<property>
<name>hbase.zookeeper.quorum</name> --哪些机器启动zookeeper,需要为奇数个
<value>bd107,bd108,bd110</value>
</property>
然后修改环境变量,启动HBASE后测试。进入hbase shell,执行list操作。
ERROR: Can't get master address from ZooKeeper; znode data == null --第一个错误
ERROR: org.apache.hadoop.hbase.PleaseHoldException: Master is initializing --第二个错误
at org.apache.hadoop.hbase.master.HMaster.checkInitialized(HMaster.java:2177)
at org.apache.hadoop.hbase.master.MasterRpcServices.getTableDescriptors(MasterRpcServices.java:821)
at org.apache.hadoop.hbase.protobuf.generated.MasterProtos$MasterService$2.callBlockingMethod(MasterProtos.java:48468)
at org.apache.hadoop.hbase.ipc.RpcServer.call(RpcServer.java:2112)
at org.apache.hadoop.hbase.ipc.CallRunner.run(CallRunner.java:101)
at org.apache.hadoop.hbase.ipc.RpcExecutor.consumerLoop(RpcExecutor.java:130)
at org.apache.hadoop.hbase.ipc.RpcExecutor$1.run(RpcExecutor.java:107)
at java.lang.Thread.run(Thread.java:745)
这2个错误网上都能检索到蛮多,反正我也是莫名其妙就好了。
第一个错误,印象中,重启hbase 就可以。
第二个错误,很奇怪,Master is initializing 既然ing结尾,是不是告诉你hbase正在初始化,叫你等待?反正我检索了下资料,啥也没干,再次执行命令突然就好了。
接着重新建表,写了一个Mapreduce用来导入数据,这个测试倒是很正常,代码如下。
package mytest;
import java.io.IOException;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.hbase.HBaseConfiguration;
import org.apache.hadoop.hbase.client.HTable;
import org.apache.hadoop.hbase.client.Put;
import org.apache.hadoop.hbase.util.Bytes;
import org.apache.hadoop.io.NullWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.Mapper;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.NullOutputFormat;
public class ImportCSV {
//['rtime','rnc','mo','R99_UL_User','R99_DL_User','HSDPA_Users','HSUPA_Users','R99_UL_throughput','R99_DL_throughput','HSDPA_Throughput','HSUPA_Throughput','R99_UL_volume_PS','R99_DL_volume_PS','HSDPA_volume','HSUPA_volume']
public static class CSVMapper extends Mapper<Object, Text, NullWritable, NullWritable>{
HTable table = null;
public void setup(Context context
) throws IOException, InterruptedException {
Configuration hbaseConf = HBaseConfiguration.create();
table = new HTable(hbaseConf, Bytes.toBytes("kpinfo"));
}
public void cleanup(Context context
) throws IOException, InterruptedException {
table.close();
}
public void map(Object key, Text value, Context context ) throws IOException, InterruptedException {
String args[] = value.toString().split("\t");
Put put = new Put(Bytes.toBytes(args[2]+"_"+args[0].replaceAll("-", "").replaceAll(":", "").replace(" ", "")));// 设置rowkey
put.add(Bytes.toBytes("h"),Bytes.toBytes("R99_UL_User"), Bytes.toBytes(args[3]));
put.add(Bytes.toBytes("h"),Bytes.toBytes("R99_DL_User"), Bytes.toBytes(args[4]));
put.add(Bytes.toBytes("h"),Bytes.toBytes("HSDPA_Users"), Bytes.toBytes(args[5]));
put.add(Bytes.toBytes("h"),Bytes.toBytes("HSUPA_Users"), Bytes.toBytes(args[6]));
put.add(Bytes.toBytes("h"),Bytes.toBytes("R99_UL_throughput"), Bytes.toBytes(args[7]));
put.add(Bytes.toBytes("h"),Bytes.toBytes("R99_DL_throughput"), Bytes.toBytes(args[8]));
put.add(Bytes.toBytes("h"),Bytes.toBytes("HSDPA_Throughput"), Bytes.toBytes(args[9]));
put.add(Bytes.toBytes("h"),Bytes.toBytes("HSUPA_Throughput"), Bytes.toBytes(args[10]));
put.add(Bytes.toBytes("h"),Bytes.toBytes("R99_UL_volume_PS"), Bytes.toBytes(args[11]));
put.add(Bytes.toBytes("h"),Bytes.toBytes("R99_DL_volume_PS"), Bytes.toBytes(args[12]));
put.add(Bytes.toBytes("h"),Bytes.toBytes("HSDPA_volume"), Bytes.toBytes(args[13]));
put.add(Bytes.toBytes("h"),Bytes.toBytes("HSUPA_volume"), Bytes.toBytes(args[14]));
table.put(put);
}
}
public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException {
Job job = Job.getInstance(new Configuration());
job.getConfiguration().setInt("mapreduce.task.timeout", 0);//关闭超时
job.getConfiguration().setBoolean("mapreduce.map.speculative", false);//关闭推测执行
job.setJarByClass(ImportCSV.class);
job.setMapperClass(CSVMapper.class);
job.setNumReduceTasks(0);//没有Rudeuce输出
job.setOutputFormatClass(NullOutputFormat.class);//不需要输出 无法输出结果 --可以关闭setOutputKeyClass和setOutputValueClass?
FileInputFormat.addInputPath(job,new Path("/user/hadoop/csv/in"));
//FileOutputFormat.setOutputPath(job,new Path("/user/hadoop/csv/out"));
System.exit(job.waitForCompletion(true) ? 0 : 1);
}
}
一切看似那么美好,但最终测试,问题仍然没有解决。
tips:
有一个关于limit.conf的问题,我曾自己推算过:
这个表有4个Region,一个列簇应该只有一个Store,每个Store里面有多少个StoreFile,我还不不知道怎么去确认它。
就算并发Map数为10,根据公式测算,也远远达不到瓶颈。况且抛出的错误,也并未指向limit.conf。
但是我还是对它也进行了一次修改如下:
cat /etc/security/limits.conf
hadoop - nofile 32768
hadoop - nproc 32768
修改后注销,ulimit -a 测试通过重启后hadoop相关服务测试,问题如故!