开发中经常会碰到将IP转为地域的问题,所以以下记录Hive中自定义UDF来解析IP。

使用到的地域库位maxmind公司的geoIP2数据库,分为免费版GeoLite2-City.mmdb和收费版GeoIP2-City.mmdb,不管哪个版本,开发的接口都是相同。

开发环境:

hive-2.3.0

hadoop 2.7.3

jdk 1.8

1. 新建maven项目regionParse,加入以下依赖包

<dependency>
<groupId>org.apache.hive</groupId>
<artifactId>hive-exec</artifactId>
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>com.maxmind.db</groupId>
<artifactId>maxmind-db</artifactId>
<version>1.2.2</version>
</dependency>
<dependency>
<groupId>com.maxmind.geoip2</groupId>
<artifactId>geoip2</artifactId>
<version>2.11.0</version>
</dependency>

2.1 新建IP2Region.java,继承UDF(中文版)

package com.gw.udf;

import java.io.File;
import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException; import org.apache.hadoop.hive.ql.exec.UDF;
//import org.apache.log4j.Logger; import com.gw.util.Constants;
import com.gw.util.geoip2.GWDatabaseReader; //重写了com.maxmind.geoip2.DatabaseReader类
import com.maxmind.geoip2.exception.GeoIp2Exception;
import com.maxmind.geoip2.model.CityResponse;
import com.maxmind.geoip2.record.City;
import com.maxmind.geoip2.record.Country;
import com.maxmind.geoip2.record.Subdivision; public class IP2Region extends UDF { // private static Logger log = Logger.getLogger(IP2Region.class); private static GWDatabaseReader reader = null; static {
String path = IP2Region.class.getResource("/").getFile().toString();
try {
System.out.println(path + Constants.geoIPFile);
File database = new File(path + Constants.geoIPFile);
reader = new GWDatabaseReader.Builder(database).build(); } catch (IOException e) {
// log.error(e.getMessage());
} } @SuppressWarnings("finally")
public static String evaluate(String ip,int i) {
InetAddress ipAddress;
String region = null;
try {
ipAddress = InetAddress.getByName(ip);
CityResponse response = null;
response = reader.city(ipAddress);
Country country = response.getCountry();
Subdivision subdivision = response.getMostSpecificSubdivision();
City city = response.getCity();
if(country!=null && i==0){
region = country.getNames().get("zh-CN") != null ? country.getNames().get("zh-CN") : country.getNames().get("en") ;
}
else if(subdivision!=null && i == 1 ){ region = Constants.provinceCN.get(subdivision.getIsoCode()) != null ?
Constants.provinceCN.get(subdivision.getIsoCode()) :
subdivision.getNames().get("en") ;
}
else if(city!=null && i == 2){
String province = city.getNames().get("zh-CN");
if(province != null && province.lastIndexOf("市") > 0){
province = province.substring(0,province.length()-1);
}
region = province != null ? province : city.getNames().get("en");
} // Postal postal = response.getPostal();
//获取经纬度,暂时不需要
// Location location = response.getLocation();
// System.out.println(location.getLatitude()); // 44.9733
// System.out.println(location.getLongitude()); // -93.2323 } catch (Exception e) {
// log.error(e.getMessage());
} finally{
return region;
} } public static void main(String[] args) throws UnknownHostException,
IOException, GeoIp2Exception {
String ip = "223.104.6.25";
System.out.println(evaluate(ip,0) + ":" + evaluate(ip,1) + ":" + evaluate(ip,2)); } }

2.2 新建IP2RegionEN.java, 继承UDF(英文版)

package com.gw.udf;

import java.io.File;
import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException; import org.apache.hadoop.hive.ql.exec.UDF;
//import org.apache.log4j.Logger; import com.gw.util.Constants;
import com.gw.util.geoip2.GWDatabaseReader;
import com.maxmind.geoip2.exception.GeoIp2Exception;
import com.maxmind.geoip2.model.CityResponse;
import com.maxmind.geoip2.record.City;
import com.maxmind.geoip2.record.Country;
import com.maxmind.geoip2.record.Subdivision; public class IP2RegionEN extends UDF { // private static Logger log = Logger.getLogger(IP2Region.class); private static GWDatabaseReader reader = null; static {
String path = IP2RegionEN.class.getResource("/").getFile().toString();
try {
System.out.println(path + Constants.geoIPFile);
File database = new File(path + Constants.geoIPFile);
reader = new GWDatabaseReader.Builder(database).build(); } catch (IOException e) {
// log.error(e.getMessage());
} } @SuppressWarnings("finally")
public static String evaluate(String ip,int i) {
InetAddress ipAddress;
String region = null;
try {
ipAddress = InetAddress.getByName(ip);
CityResponse response = null;
response = reader.city(ipAddress);
Country country = response.getCountry();
Subdivision subdivision = response.getMostSpecificSubdivision();
City city = response.getCity();
if(country!=null && i==0){
region = country.getNames().get("en") ;
}
else if(subdivision!=null && i == 1 ){ region = subdivision.getNames().get("en") ;
}
else if(city!=null && i == 2){
region = city.getNames().get("en");
} // Postal postal = response.getPostal();
//获取经纬度,暂时不需要
// Location location = response.getLocation();
// System.out.println(location.getLatitude()); // 44.9733
// System.out.println(location.getLongitude()); // -93.2323 } catch (Exception e) {
// log.error(e.getMessage());
} finally{
return region;
} } public static void main(String[] args) throws UnknownHostException,
IOException, GeoIp2Exception {
String ip = "223.104.6.25";
System.out.println(evaluate(ip,0) + ":" + evaluate(ip,1) + ":" + evaluate(ip,2)); } }

3. 由于免费的数据库解析出来的中文省份含有简称比如福建直接显示为闽,比较坑。

为便于统一,所以自己定义了一个isocode的转换类Constants.java 用于省份的统一显示。

package com.gw.util;

import java.util.HashMap;
import java.util.Map; public class Constants { public static final String geoIPFile= "GeoLite2-City.mmdb";
public static final String geollFile = "geo.csv";
public static final Map<String,String> provinceCN = new HashMap<String,String>(); static {
provinceCN.put("AH","安徽");
provinceCN.put("BJ","北京");
provinceCN.put("CQ","重庆");
provinceCN.put("FJ","福建");
provinceCN.put("GD","广东");
provinceCN.put("GS","甘肃");
provinceCN.put("GX","广西");
provinceCN.put("GZ","贵州");
provinceCN.put("HA","河南");
provinceCN.put("HB","湖北");
provinceCN.put("HE","河北");
provinceCN.put("HI","海南");
provinceCN.put("HK","香港");
provinceCN.put("HL","黑龙江");
provinceCN.put("HN","湖南");
provinceCN.put("JL","吉林");
provinceCN.put("JS","江苏");
provinceCN.put("JX","江西");
provinceCN.put("LN","辽宁");
provinceCN.put("MO","澳门");
provinceCN.put("NM","内蒙古");
provinceCN.put("NX","宁夏");
provinceCN.put("QH","青海");
provinceCN.put("SC","四川");
provinceCN.put("SD","山东");
provinceCN.put("SH","上海");
provinceCN.put("SN","陕西");
provinceCN.put("SX","山西");
provinceCN.put("TJ","天津");
provinceCN.put("TW","台湾");
provinceCN.put("XJ","新疆");
provinceCN.put("XZ","西藏");
provinceCN.put("YN","云南");
provinceCN.put("ZJ","浙江");
} }

4. 在测试过程中发现已有的geoip2的jar和hive系统中其他jar有冲突,所以又修改了geoip2的部分源码。

最后,打包编译提交到hive的external_lib(自定义包统一存放的文件夹)中。

5. 测试结果:

ip2region(ip,0)表示国家

ip2region(ip,1)表示省份

ip2region(ip,2)表示城市

select ip,ip2region(ip,0),ip2region(ip,1),ip2region(ip,2) from xxx_table where ip is not null limit 300;

测试结果看到部分城市给出的是拼音(Shijiazhuang),这个免费库还是有点坑爹,暂时可以将就使用了。

中文版本如下:

英文版本如下:

最后附上整个部署的说明:

1. 将数据库文件添加到hive的路径 $HIVE_HOME/conf目录下
以后如果有IP数据库城市的更新,直接更新这个目录下的文件。
省份文件固定,不需要修改。
数据库文件有三个:geoCN.csv、geoEN.csv、GeoLite2-City.mmdb 2. 删除$HIVE_HOME/lib/目录下 geoip2-0.4.0.jar、maxminddb-0.2.0.jar 文件
使用maxmind-db-1.2.2.jar、geoip2-2.11.0.jar替换放入。 3. 将编译的regionalParse-0.0.1-SNAPSHOT.jar放到external_lib目录。 4. 修改.hiverc文件,添加以下内容
add jar /usr/local/hive/external_lib/regionalParse-0.0.1-SNAPSHOT.jar; create temporary function ll2province as 'com.gw.udf.LL2Province';
create temporary function ll2provinceen as 'com.gw.udf.LL2ProvinceEN';
create temporary function indexof as 'com.gw.udf.IndexOf';
create temporary function ip2region as 'com.gw.udf.IP2Region';
create temporary function ip2regionen as 'com.gw.udf.IP2RegionEN'; 5. 重启hiveServer2 #!/bin/sh
nohup $HIVE_HOME/bin/hive --service hiveserver2 & 6. 测试方法: 测试中文:
select ip,ip2region(ip,0),ip2region(ip,1),ip2region(ip,2),
ll2province(substring(coordinator, 0, indexof(coordinator, ":")), substring(coordinator, indexof(coordinator, ":") + 2))
from xxx where ip is not null limit 10; 测试英文:
select ip,ip2regionen(ip,0),ip2regionen(ip,1),ip2regionen(ip,2),
ll2provinceen(substring(coordinator, 0, indexof(coordinator, ":")), substring(coordinator, indexof(coordinator, ":") + 2))
from xxx where ip is not null limit 10;

Hive UDF IP解析(二):使用geoip2数据库自定义UDF的更多相关文章

  1. Hive UDF IP解析(一):依赖包兼容性问题

    Java依赖环境: <dependency> <groupId>org.apache.hive</groupId> <artifactId>hive-e ...

  2. 自定义UDF函数应用异常

    自定义UDF函数应用异常 版权声明:本文为yunshuxueyuan原创文章.如需转载请标明出处: http://www.cnblogs.com/sxt-zkys/QQ技术交流群:299142667 ...

  3. Hive框架基础(二)

    * Hive框架基础(二) 我们继续讨论hive框架 * Hive的外部表与内部表 内部表:hive默认创建的是内部表 例如: create table table001 (name string , ...

  4. IP工具类-自己动手做个ip解析器

    IP工具类-自己动手做个ip解析器 一.资料准备 导入依赖包:

  5. hive 存储,解析,处理json数据

    hive 处理json数据总体来说有两个方向的路走 1.将json以字符串的方式整个入Hive表,然后通过使用UDF函数解析已经导入到hive中的数据,比如使用LATERAL VIEW json_tu ...

  6. Spring Security 解析(二) —— 认证过程

    Spring Security 解析(二) -- 认证过程   在学习Spring Cloud 时,遇到了授权服务oauth 相关内容时,总是一知半解,因此决定先把Spring Security .S ...

  7. 企业运维实践-Nginx使用geoip2模块并利用MaxMind的GeoIP2数据库实现处理不同国家或城市的访问最佳实践指南

    关注「WeiyiGeek」公众号 设为「特别关注」每天带你玩转网络安全运维.应用开发.物联网IOT学习! 希望各位看友[关注.点赞.评论.收藏.投币],助力每一个梦想. 本章目录 目录 0x00 前言 ...

  8. MySQL 系列(二) 你不知道的数据库操作

    第一篇:MySQL 系列(一) 生产标准线上环境安装配置案例及棘手问题解决 第二篇:MySQL 系列(二) 你不知道的数据库操作 本章内容: 查看\创建\使用\删除 数据库 用户管理及授权实战 局域网 ...

  9. .net Core 调用微信Jsapi接口,H5解析二维码

    项目里需要用到扫描二维码,自己实现,不会. 找到了两种解决方案: 通过reqrcode.js,这是一个前端解析二维码内容的js库.如果二维码比较清晰,用这种效果也不错 调用微信扫一扫功能,这种效果很好 ...

随机推荐

  1. 基于 html5 geolocation来获取经纬度地址(copy)

    geolocation来获取经纬度地址 以前如果要获取互联网用户所在地都是根据用户的IP地址来获取地理位置,这样获取到的数据和真实数据有很大的偏差.为了获取更加精确的位置,可以使用了html5的geo ...

  2. 1.Java基础-面向对象编程思想(封装继承多态接口)

    封装: 1.定义:隐藏对象的属性和实现细节,仅对外公开接口,控制在程序中属性的读和修改的访问级别. 2.封装的目的是:增强安全性和简化编程,使用者不必了解具体的实现细节,而只是要通过外部接口,一特定的 ...

  3. 浅谈 .NET 中的对象引用、非托管指针和托管指针

    目录 前言 一.对象引用 二.值传递和引用传递 三.初识托管指针和非托管指针 四.非托管指针 1.非托管指针不能指向对象引用 2.类成员指针 五.托管指针 前言 本文主要是以 C# 为例介绍 .NET ...

  4. 转:void *

    void的含义 void即“无类型”,void *则为“无类型指针”,可以指向任何数据类型. void指针使用规范①void指针可以指向任意类型的数据,亦即可用任意数据类型的指针对void指针赋值.例 ...

  5. Storm 简单介绍

    Nimbus :负责资源分配和任务调度, 把任务相关的元信息写入Zookeeper 对应文件夹. Supervisor :负责接受nimbus 分配的任务,启动和停止属于自己管理的worker 进程. ...

  6. 在T-SQL语句中访问远程数据库

    1.启用Ad Hoc Distributed Queries 在使用openrowset/opendatasource前搜先要启用Ad Hoc Distributed Queries服务,因为这个服务 ...

  7. Token:服务端身份验证的流行方案【转】

    01- 身份认证 服务端提供资源给客户端,但是某些资源是有条件的.所以服务端要能够识别请求者的身份,然后再判断所请求的资源是否可以给请求者. token是一种身份验证的机制,初始时用户提交账号数据给服 ...

  8. 【Android】6.4 DatePickerDialog和TimePickerDialog

    分类:C#.Android.VS2015: 创建日期:2016-02-08 一.简介 在Android应用中,日期选择对话框和时间选择对话框是分别提供的. 日期选择对话框(DatePickerDial ...

  9. Can you share some Scala List class examples?

    Scala List FAQ: Can you share some Scala List class examples? The Scala List class may be the most c ...

  10. scrollTop兼容封装

    <!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8&quo ...