在hadoop的编程中,若是你是手写MapReduce来处理一些数据,那么就避免不了输入输出参数路径的设定,hadoop里文件基类FileInputFormat提供了以下几种api来制定:
如上图,里面有
(1)addInputPath(),每次添加一个输入路径Path
(2)addInputPaths, 将多个路径以逗号分割的字符串,做为入参,支持多个路径
(3)setInputPath ,设置一个输入路径Path,会覆盖原来的路径
(4)setInputPath , 设置多个路径,支持Hadoop文件系统重写的Path对象,这在JAVA里是接口。
代码以下:
html
Java代码
redis
- 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系统的路径默认是支持正则过滤的,这一点很是强大,只要咱们会写正则,咱们几乎能够过滤任何咱们想要的路径或文件。
详细内容请查阅这个连接http://hadoop.apache.org/docs/current/api/org/apache/hadoop/fs/FileSystem.html#globStatus(org.apache.hadoop.fs.Path)
下面散仙就举个实际项目应用中的例子,这样能帮助你们更好的理解和使用它。
先看下面的一个HDFS上的存储结构图:
这是一个按日期天天生成的一个文件夹,固然这里能够有不少分维度的法,好比按照年,月,日,小时,来划分,具体状况应跟业务结合考虑。
看下,直接的根目录的下一级目录:
ok,存储结构清楚了,那么如今提几个需求
(1)只过滤出pv目录下的数据
(2)只过滤出uv目录下的数据
(3)只过滤出keyword目录下的数据
(4)只过滤出pv和uv的数据或者叫以v结尾的数据
(5)过滤2015年的数据
(6)过滤出某个时间范围内的数据好比2015-04-10到2015-04-17时间范围下的pv的数据
其实前个需求很简单都是一种需求:
hadoop里的FileStatus类是支持路径通配的,对应的写法以下:
apache
Java代码
编程
- 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的路径。
实例代码以下:
调用代码:
api
Java代码
ide
- FileStatus[] status = fs.globStatus(new Path(basepath),new RegexExcludePathAndTimeFilter(rexp_date,rexp_business, "2015-04-04", "2015-04-06"));
处理代码: 工具
Java代码
oop
- /**
- * 实现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里面对应的分区功能,有了分区,咱们就能够按需所取,尽可能避免没必要要的一些额外操做。 大数据