Hive 自定義udf --ip地址解析出歸屬地
1.問題背景:現在我們的流量表里存有用戶的IP地址,有需求需要將ip地址的歸屬地解析出來。結構是 國家-省份-城市-運營商
2.目前使用的是開源的ip庫,調用三方接口不太適合hive udf使用並且都是收費的。
3.開源數據庫調研了純真數據庫 發現ip地址解析的結果誤差比較大,並且返回的結構不太友好。后來使用的是一個開源項目ip2region 。具體介紹可以看下該項目的gitee地址。ip2region 這個數據庫大概有70萬條,比純真的數據庫要全面,並且返回結果的結構比較統一 (_城市Id|國家|區域|省份|城市|ISP_)非常有利於我們進行結果的改造。
4.使用該數據庫
1.首先將數據庫下載下來,在該項目data 目錄下ip2region.db ,放到hdfs上
2.編寫udf 函數
1.pom中引入
<dependency> <groupId>org.lionsoul</groupId> <artifactId>ip2region</artifactId> <version>1.7.2</version> </dependency> <dependency> <groupId>org.apache.hive</groupId> <artifactId>hive-exec</artifactId> <version>2.1.1</version> <scope>provided</scope> </dependency> <dependency> <groupId>org.apache.hadoop</groupId> <artifactId>hadoop-common</artifactId> <version>2.7.3</version> <scope>provided</scope> </dependency> <dependency> <groupId>org.apache.hadoop</groupId> <artifactId>hadoop-hdfs</artifactId> <version>2.7.3</version> <scope>provided</scope> </dependency>
2.udf
public class Ip2region extends UDF { private static Configuration configuration; private static FileSystem fileSystem; private static InputStream in; private static byte[] data; static { //加載數據 ByteArrayOutputStream out = null; try { configuration = new Configuration(); fileSystem = FileSystem.get(URI.create("hdfs:///jars/hive/ip2region.db"), configuration); in = fileSystem.open(new Path("hdfs:///jars/hive/ip2region.db")); out = new ByteArrayOutputStream(); byte[] b = new byte[1024]; while (in.read(b) != -1) { out.write(b); } // 提高性能,將ip2region.db一次從hdfs中讀取出來,緩存到data字節數組中以重用, // 避免每來一條數據讀取一次ip2region.db data = out.toByteArray(); out.close(); in.close(); } catch (Exception e){ e.printStackTrace(); } finally { try { if(out != null) { out.close(); } if(in != null) { in.close(); } } catch (IOException e) { e.printStackTrace(); } } } public String evaluate(String ip) throws Exception{ DbConfig config = new DbConfig(); DbSearcher searcher = new DbSearcher(config,data); DataBlock dataBlock = searcher.memorySearch(ip); String[] split = dataBlock.getRegion().split("\\|"); String addr = convert(split[0])+","+convert(split[2])+","+convert(split[3])+","+convert(split[4]); return addr; } public String convert (String addr) { return addr.equals("0") ? "" :addr; } }
3.打包 需要將ip2region 依賴打入到寫的udf中
<build> <finalName>hive-udf-ip2region</finalName> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>2.3.2</version> <configuration> <source>1.8</source> <target>1.8</target> <encoding>UTF-8</encoding> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-assembly-plugin</artifactId> <version>2.3</version> <configuration> <descriptorRefs> <descriptorRef>jar-with-dependencies</descriptorRef> </descriptorRefs> </configuration> <executions> <!--打包的插件配置--> <execution> <id>make-assembly</id> <phase>package</phase> <goals> <goal>assembly</goal> </goals> </execution> </executions> </plugin> </plugins> </build>
5.使用udf函數
將ip庫和 udf的jar 都傳到hdfs上
進入hive
add jar hdfs:///jars/hive/hive-udf-ip2region-jar-with-dependencies.jar; create temporary function ip2region as 'com.hive.udf.Ip2region'; select ip2region('124.236.223.17');
6.注意:如果ip庫必須傳到hdfs 上,否則分布式跑的時候其他節點會加載不到改數據庫