自定义UDTF和hive自定义函数的永久注册

首先声明我的参考博客:http://blog.csdn.net/kent7306/article/details/50200061    http://blog.csdn.net/bdchome/article/details/45843559

一:

说明一下我的整体业务需求:

公司要求将nginx日志数据通过flume存到HDFS,然后通过hive处理将数据插入到一张贴源表里面。

nginx日志数据格式为:16/Jun/2017:16:42:18 +0800^{'data':[{'product':'aAPP','userAgent':'864682030329437|LON-AL00','clickElement':'e_login_login','userId':null,'clickTime':'1497602537'}]}

然后通过hive的load方法指定分割符"^"load进一张临时表,然后通过自定义函数处理数据得到符合自己需要的数据格式。

二:hive自定义函数有三种形式:UDF、UDAF、UDTF

UDF:一进一出

UDAF:多进一出   类似sum()、min()、avg()、max()

UDTF:一进多出,通俗来说就是进来一个字段返回多个字段,符合本需求

三:先通过图片直观的了解一下本需求前后的对比

nginx日志数据:

 

通过load方法load进临时表的数据(只有两个字段:time_stamp string,name string)name为json格式的数据

经过自定义函数处理后的数据:(只处理name字段)

四:对于本需求有了详细的了解之后,废话不多说直接撸代码

自定义UDTF代码:

 

package myUDF;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;

import net.sf.json.JSONArray;
import net.sf.json.JSONObject;

import org.apache.hadoop.hive.ql.exec.UDFArgumentException;
import org.apache.hadoop.hive.ql.metadata.HiveException;
import org.apache.hadoop.hive.ql.udf.generic.GenericUDTF;
import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspector;
import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspectorFactory;
import org.apache.hadoop.hive.serde2.objectinspector.PrimitiveObjectInspector;
import org.apache.hadoop.hive.serde2.objectinspector.StructObjectInspector;
import org.apache.hadoop.hive.serde2.objectinspector.primitive.PrimitiveObjectInspectorFactory;
/**
 * 对APPlog数据的解析处理UDTF
 * 处理数据json格式
 * {'data':[{'product':'aAPP','userAgent':'860209036790711|vivo X6D','clickElement':'e_main_moreInfo','userId':'3916482','clickTime':'1497502470'}]}
 * {'data':[{'product':'iAPP|1.2.0','userAgent':'fc95b2415cc54dd5b3999891acee985e|iPhone6s','clickElement':'e_main_moreInfo','userId':'3960209','clickTime':'1497502470'}]}
 * @author 
 *
 */
public class AppJsonTransformUDTF extends GenericUDTF {  
	  
    private PrimitiveObjectInspector stringOI = null;  

    /**
     * 定义输入、输出格式的类
     */
    @Override  
    public StructObjectInspector initialize(ObjectInspector[] args) throws UDFArgumentException {  

      if (args.length != 1) {  
        throw new UDFArgumentException("NameParserGenericUDTF() takes exactly one argument");  
      }  

      if (args[0].getCategory() != ObjectInspector.Category.PRIMITIVE  
          && ((PrimitiveObjectInspector) args[0]).getPrimitiveCategory() != PrimitiveObjectInspector.PrimitiveCategory.STRING) {  
        throw new UDFArgumentException("NameParserGenericUDTF() takes a string as a parameter");  
      }  
        
      // 输入格式(inspectors)  
      stringOI = (PrimitiveObjectInspector) args[0];  

      // 输出格式(inspectors) -- 有属性的对象  
      List<String> fieldNames = new ArrayList<String>(7);  
      List<ObjectInspector> fieldOIs = new ArrayList<ObjectInspector>(7);  
      fieldNames.add("product");  //设备
      fieldOIs.add(PrimitiveObjectInspectorFactory.javaStringObjectInspector);  
      fieldNames.add("version");  //APP版本
      fieldOIs.add(PrimitiveObjectInspectorFactory.javaStringObjectInspector); 
      fieldNames.add("device_number");  //设备号
      fieldOIs.add(PrimitiveObjectInspectorFactory.javaStringObjectInspector);  
      fieldNames.add("model");  //型号
      fieldOIs.add(PrimitiveObjectInspectorFactory.javaStringObjectInspector);  
      fieldNames.add("clickElement");  //埋点元素
      fieldOIs.add(PrimitiveObjectInspectorFactory.javaStringObjectInspector);  
      fieldNames.add("userId");  //用户ID
      fieldOIs.add(PrimitiveObjectInspectorFactory.javaStringObjectInspector);  
      fieldNames.add("clickTime");  //点击时间
      fieldOIs.add(PrimitiveObjectInspectorFactory.javaStringObjectInspector);
      return ObjectInspectorFactory.getStandardStructObjectInspector(fieldNames, fieldOIs);  
    }  
         
    /**
     * 处理数据的主要逻辑
     * @param APP_log_data
     * @return
     */
    public ArrayList<Object[]> processInputRecord(String APP_log_data){  
    	
    	String[] jsonKey = new String[]{"product","userAgent","clickElement","userId","clickTime"};
          ArrayList<Object[]> result = new ArrayList<Object[]>();  
          
          // 忽略null值与空值  
          if (APP_log_data == null || APP_log_data.isEmpty()) {  
            return result;  
          }  
           
          JSONObject jsonObject = JSONObject.fromObject(APP_log_data);
    		JSONArray jsonArray = jsonObject.getJSONArray("data");
    		JSONObject log_data = jsonArray.getJSONObject(0);
    		
    		String product1 ;//设备
        	String product2 ;//APP版本
    		
    		String product = log_data.getString(jsonKey[0]);
            if(product.indexOf("|")!=-1){
            	//iOS数据有版本号
            	product1 = product.split("\\|")[0];
           	 product2 = product.split("\\|")[1]; 
            }else {
            	//安卓数据没有版本号
            	product1 = product;
                product2 = "null";
            }
            
    		String userAgents = log_data.getString(jsonKey[1]);
    		String device_number = userAgents.split("\\|")[0];   //设备号
    		String model = userAgents.split("\\|")[1];       //设备型号
    		String clickElement = log_data.getString(jsonKey[2]);
    		String userId = log_data.getString(jsonKey[3]);
    		String clickTime_stamp = log_data.getString(jsonKey[4]); 
    		
    		/**
    		 * 将十位java时间戳转换为时间
    		 */
    		SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            long lt = new Long(clickTime_stamp);
            Date date = new Date(lt* 1000L);
            String clickTime = simpleDateFormat.format(date);
             
            //将数据返回
            result.add(new Object[] { product1, product2, device_number, model, clickElement, userId, clickTime});
            return result;  
    }  
      
    /**
     * 读到一行数据先进入此方法
     */
    @Override  
    public void process(Object[] record) throws HiveException { 
    	

      final String name = stringOI.getPrimitiveJavaObject(record[0]).toString();  
      //调用数据处理逻辑
      ArrayList<Object[]> results = processInputRecord(name);  

      Iterator<Object[]> it = results.iterator();  
        
      //将数据传输出去
      while (it.hasNext()){  
          Object[] r = it.next();  
          forward(r);  
      }  
    }  

    @Override  
    public void close() throws HiveException {  
      // do nothing  
    }  
}  


第一次写,代码可能有点冗余,但是业务实现了就OK。

 

具体的每个方法是什么作用参考:http://blog.csdn.net/kent7306/article/details/50200061

五:将自定义的UDTF打包上传

建议在$HIVE_HOME目录下创建auxlib目录,将jar包上传到auxlib目录下,这样就不用每次在hive的命令行里敲 add jar ///path 了。

创建临时函数 process_names

 

hive> CREATE TEMPORARY FUNCTION process_names as 'myUDF.AppJsonTransformUDTF';

使用临时函数

 

 

select process_names(name) from test_wcf.test_udtf;



在测试的过程中出现了多次class not found的错,主要是因为要解析json数据有很多jar包缺少

 

所有jar:

还出现了多次数组下标越界的情况,修改java代码重新打包上传即可,注意重新上传jar包以后要退出hive的命令行重新进入,然后创建函数,否则可能新jar包不起作用。

六:在测试好临时函数以后,就可以创建永久函数了

 

create function  default.AppJsTsfmUDTF as 'myUDF.AppJsonTransformUDTF'


查看函数:

 

 

hive> show functions;

 

 

 

 

 

找到自定义函数就OK了,如果不放心,就退出hive命令行重新进入,然后show functions 即可。

  • 14
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值