博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
在hadoop的编程中输入输出参数路径的设定
阅读量:4188 次
发布时间:2019-05-26

本文共 7415 字,大约阅读时间需要 24 分钟。

在hadoop的编程中,如果你是手写MapReduce来处理一些数据,那么就避免不了输入输出参数路径的设定,hadoop里文件基类FileInputFormat提供了如下几种api来制定:

如上图,里面有
(1)addInputPath(),每次添加一个输入路径Path
(2)addInputPaths, 将多个路径以逗号分割的字符串,作为入参,支持多个路径
(3)setInputPath ,设置一个输入路径Path,会覆盖原来的路径
(4)setInputPath , 设置多个路径,支持Hadoop文件系统重写的Path对象,这在JAVA里是接口。
代码如下:

Java代码  
  1. FileInputFormat.setInputDirRecursive(job, true);//设置可以递归读取目录  
  2. FileInputFormat.addInputPath(job, new Path("path1"));  
  3. FileInputFormat.addInputPaths(job, "path1,path2,path3,path....");  
  4. FileInputFormat.setInputPaths(job, new Path("path1"),new Path("path2"));  
  5. FileInputFormat.setInputPaths(job, "path1,path2,path3,path....");  
FileInputFormat.setInputDirRecursive(job, true);//设置可以递归读取目录			FileInputFormat.addInputPath(job, new Path("path1"));			FileInputFormat.addInputPaths(job, "path1,path2,path3,path....");			FileInputFormat.setInputPaths(job, new Path("path1"),new Path("path2"));			FileInputFormat.setInputPaths(job, "path1,path2,path3,path....");

而真正用的时候,我们只需要根据业务使用上面的其中一个路径即可。
ok知道怎么,传入路径了,下面来看下,如何在HDFS上过滤出,自己想要的文件或目录,HDFS系统的路径默认是支持正则过滤的,这一点非常强大,只要我们会写正则,我们几乎可以过滤任何我们想要的路径或文件。
详细内容请查阅这个链接
下面散仙就举个实际项目应用中的例子,这样能帮助大家更好的理解和使用它。
先看下面的一个HDFS上的存储结构图:
这是一个按日期每天生成的一个文件夹,当然这里可以有很多分维度的法,比如按照年,月,日,小时,来划分,具体情况应跟业务结合考虑。
看下,直接的根目录的下一级目录:
ok,存储结构清楚了,那么现在提几个需求
(1)只过滤出pv目录下的数据
(2)只过滤出uv目录下的数据
(3)只过滤出keyword目录下的数据
(4)只过滤出pv和uv的数据或者叫以v结尾的数据
(5)过滤2015年的数据
(6)过滤出某个时间范围内的数据比如2015-04-10到2015-04-17时间范围下的pv的数据
其实前个需求很简单都是一种需求:
hadoop里的FileStatus类是支持路径通配的,对应的写法如下:

Java代码  
  1. FileSystem fs = FileSystem.get(conf);  
  2. //  
  3. //过滤pv或uv的目录数据  
  4.   String basepath="/user/d1/DataFileShare/Search/*/*/{pv,uv}";  
  5. //过滤v结尾的目录数据  
  6.   String basepath="/user/d1/DataFileShare/Search//*/*/*v";  
  7. //过滤uv的数据  
  8.   String basepath="/user/d1/DataFileShare/Search//*/*/uv";  
  9. //过滤pv的数据  
  10.   String basepath="/user/d1/DataFileShare/Search//*/*/pv";  
  11.   
  12. //过滤2015年的pv的数据  
  13. String basepath="/user/d1/DataFileShare/Search/2015*/*/pv";    
  14. //获取globStatus  
  15. FileStatus[] status = fs.globStatus(new Path(basepath));  
  16. for(FileStatus f:status){  
  17.     //打印全路径,  
  18.     System.out.println(f.getPath().toString());  
  19.     //打印最后一级目录名  
  20.     //System.out.println(f.getPath().getName());  
  21. }  
FileSystem fs = FileSystem.get(conf);	        //	        //过滤pv或uv的目录数据//	        String basepath="/user/d1/DataFileShare/Search/*/*/{pv,uv}";	        //过滤v结尾的目录数据//	        String basepath="/user/d1/DataFileShare/Search//*/*/*v";	        //过滤uv的数据//	        String basepath="/user/d1/DataFileShare/Search//*/*/uv";	        //过滤pv的数据//	        String basepath="/user/d1/DataFileShare/Search//*/*/pv";	        	        //过滤2015年的pv的数据	        String basepath="/user/d1/DataFileShare/Search/2015*/*/pv";  	        //获取globStatus	        FileStatus[] status = fs.globStatus(new Path(basepath));	        for(FileStatus f:status){	        	//打印全路径,	        	System.out.println(f.getPath().toString());	        	//打印最后一级目录名	        	//System.out.println(f.getPath().getName());	        }

最后一个复杂,直接使用正则,会比较繁琐,而且假如有一些其他的逻辑在里面会比较难控制,比如说你拿到这个日期,会从redis里面再次匹配,是否存在,然后在做某些决定。
hadoop在globStatus的方法里,提供了一个路径重载,根据PathFilter类,通过正则再次过滤出我们需要的文件即可,使用此类,我们可以以更灵活的方式,操作,过滤路径,比如说上面的那个日期范围的判断,我们就可以根据全路径中,截取出日期,再做一些判断,并且可以再次过滤低级的路径,比如是pv,uv或keyword的路径。
实例代码如下:
调用代码:

Java代码  
  1. FileStatus[] status = fs.globStatus(new Path(basepath),new RegexExcludePathAndTimeFilter(rexp_date,rexp_business, "2015-04-04",  "2015-04-06"));  
FileStatus[] status = fs.globStatus(new Path(basepath),new RegexExcludePathAndTimeFilter(rexp_date,rexp_business, "2015-04-04",  "2015-04-06"));

处理代码:

Java代码  
  1. /** 
  2.      * 实现PathFilter接口使用正则过滤 
  3.      * 所需数据 
  4.      * 加强版,按时间范围,路径过滤 
  5.      * @author qindongliang 
  6.      * 大数据交流群:(1号群) 376932160 (2号群) 415886155 
  7.      *  
  8.      * **/  
  9.     static class RegexExcludePathAndTimeFilter implements PathFilter{  
  10.          //日期的正则  
  11.          private final  String regex;  
  12.          //时间开始过滤  
  13.          private final  String start;  
  14.          //时间结束过滤  
  15.          private final  String end;  
  16.          //业务过滤  
  17.          private final  String regex_business;  
  18.        
  19.         public RegexExcludePathAndTimeFilter(String regex,String regex_business,String start,String end) {  
  20.             this.regex=regex;  
  21.             this.start=start;  
  22.             this.end=end;  
  23.             this.regex_business=regex_business;  
  24.         }  
  25.           
  26.         @Override  
  27.         public boolean accept(Path path) {  
  28.             String data[]=path.toString().split("/");  
  29.             String date=data[7];  
  30.             String business=data[9];  
  31.             return  Pattern.matches(regex_business, business)&&Pattern.matches(regex,date) && TimeTools.checkDate(start, end, date);  
  32.         }  
  33.           
  34.           
  35.           
  36.     }  
  37.       
  38.     /**日期比较的工具类**/  
  39.     static class TimeTools{  
  40.           
  41.         final static String DATE_FORMAT="yyyy-MM-dd";  
  42.           
  43.         final static SimpleDateFormat sdf=new SimpleDateFormat(DATE_FORMAT);  
  44.           
  45.         public static boolean cnull(String checkString){  
  46.             if(checkString==null||checkString.equals("")){  
  47.                 return false;  
  48.             }  
  49.             return true;  
  50.         }  
  51.           
  52.         /** 
  53.          * @param start 开始时间 
  54.          * @param end 结束时间 
  55.          * @param path 比较的日期路径 
  56.          * **/  
  57.         public static boolean checkDate(String start,String end,String path){  
  58.             long startlong=0;  
  59.             long endlong=0;  
  60.             long pathlong=0;  
  61.             try{  
  62.              if(cnull(start)){  
  63.                  startlong=sdf.parse(start).getTime();  
  64.              }  
  65.              if(cnull(end)){  
  66.                  endlong=sdf.parse(end).getTime();  
  67.              }  
  68.              if(cnull(path)){  
  69.                  pathlong=sdf.parse(path).getTime();  
  70.              }  
  71.             //当end日期为空时,只取start+的日期  
  72.             if(end==null||end.equals("")){  
  73.                 if(pathlong>=startlong){  
  74.                     return true;  
  75.                 }else{  
  76.                     return false;  
  77.                 }  
  78.             }else{
    //当end不为空时,取日期范围直接比较  
  79.                 //过滤在规定的日期范围之内  
  80.                 if(pathlong>=startlong&&pathlong<=endlong){  
  81.                     return true;  
  82.                 }else{  
  83.                     return false;  
  84.                 }  
  85.                   
  86.             }  
  87.               
  88.             }catch(Exception e){  
  89.                 log.error("路径日期转换异常: 开始日期:  "+start+"  结束日期 "+end+"  比较日期: "+path+"  异常: "+e);  
  90.             }  
  91.               
  92.             return false;  
  93.         }  
/**	 * 实现PathFilter接口使用正则过滤	 * 所需数据	 * 加强版,按时间范围,路径过滤	 * @author qindongliang	 * 大数据交流群:(1号群) 376932160 (2号群) 415886155	 * 	 * **/	static class RegexExcludePathAndTimeFilter implements PathFilter{		 //日期的正则		 private final  String regex;		 //时间开始过滤		 private final  String start;		 //时间结束过滤		 private final  String end;		 //业务过滤		 private final  String regex_business;	 		public RegexExcludePathAndTimeFilter(String regex,String regex_business,String start,String end) {			this.regex=regex;			this.start=start;			this.end=end;			this.regex_business=regex_business;		}				@Override		public boolean accept(Path path) {			String data[]=path.toString().split("/");			String date=data[7];			String business=data[9];			return  Pattern.matches(regex_business, business)&&Pattern.matches(regex,date) && TimeTools.checkDate(start, end, date);		}							}		/**日期比较的工具类**/	static class TimeTools{				final static String DATE_FORMAT="yyyy-MM-dd";				final static SimpleDateFormat sdf=new SimpleDateFormat(DATE_FORMAT);				public static boolean cnull(String checkString){			if(checkString==null||checkString.equals("")){				return false;			}			return true;		}				/**		 * @param start 开始时间		 * @param end 结束时间		 * @param path 比较的日期路径		 * **/		public static boolean checkDate(String start,String end,String path){			long startlong=0;			long endlong=0;			long pathlong=0;			try{			 if(cnull(start)){				 startlong=sdf.parse(start).getTime();			 }			 if(cnull(end)){				 endlong=sdf.parse(end).getTime();			 }			 if(cnull(path)){				 pathlong=sdf.parse(path).getTime();			 }			//当end日期为空时,只取start+的日期			if(end==null||end.equals("")){				if(pathlong>=startlong){					return true;				}else{					return false;				}			}else{//当end不为空时,取日期范围直接比较				//过滤在规定的日期范围之内				if(pathlong>=startlong&&pathlong<=endlong){					return true;				}else{					return false;				}							}						}catch(Exception e){				log.error("路径日期转换异常: 开始日期:  "+start+"  结束日期 "+end+"  比较日期: "+path+"  异常: "+e);			}						return false;		}

总结:
(1)如果只是简单的路径过滤,那么直接在路径中就使用正则通配是最简单强大的。
(2)如果是比较复杂的路径过滤,建议自定义PathFilter来封装过滤代码。
(3)如果是在建设初期的就把各个文件夹目录文件的存储规划好,这样是最好不过了,比如上面的pv是一个文件夹,然后下面是各个日期,uv是一个文件夹,然后下面是各种日期,这样从业务的角度就按维度切分好,那么我们处理起来也是非常方便的,这也就是Hive里面对应的分区功能,有了分区,我们就可以按需所取,尽量避免不必要的一些额外操作。

转载地址:http://xxjoi.baihongyu.com/

你可能感兴趣的文章
CDN和Web Cache领域相关的经典书籍推荐
查看>>
在Oracle VM VirtualBox中如何安装64位虚拟机系统
查看>>
安装和使用Oracle VM VirtualBox中的要点,注意事项和遇到的问题
查看>>
ATS上的hosting.config和volume.config文件解读
查看>>
将日志中的指定字段对齐显示输出
查看>>
Linux上chown命令的高级用法
查看>>
利用sort对多字段排序
查看>>
Windows 10完美识别3TB硬盘实录
查看>>
在CentOS 6.x上安装luajit 2.0.4
查看>>
Linux下使用diff和patch制作及打补丁(已经实践可行!)
查看>>
ThinkPad T420更换SSD实录
查看>>
在Ubuntu 16.04.1 LTS上安装ats 5.3.2
查看>>
在CentOS 6.8 x86_64上安装ATS 6.2.1实录
查看>>
在CentOS 6.9 x86_64上玩转OpenResty 1.13.6.1中的resty-cli模块
查看>>
Spring中的Bean是有生命周期
查看>>
FreeMarker是一个用Java语言编写的模板引擎
查看>>
Markdown的语法简洁明
查看>>
hadoop的部署总共有3种类型
查看>>
部署安装hadoop
查看>>
sqoop是什么
查看>>