Puede crear una función definida por el usuario (UDF) de MaxCompute para convertir direcciones IPv4 o IPv6 en geolocalizaciones mediante la consulta de una biblioteca de direcciones IP.
Contexto
La conversión de direcciones IP a geolocalizaciones requiere una biblioteca de direcciones IP. Debe descargar los archivos de esta biblioteca y cargarlos como recursos en su proyecto de MaxCompute. Posteriormente, puede desarrollar y registrar una UDF de MaxCompute que haga referencia a estos recursos, e invocarla en sentencias SQL para resolver direcciones IP a geolocalizaciones.
Los archivos de la biblioteca de direcciones IP proporcionados en este tema tienen únicamente fines demostrativos. Debe mantener su propia biblioteca de direcciones IP según sus requisitos empresariales.
Requisitos previos
Cuenta con installed and configured the MaxCompute client.
Cuenta con installed MaxCompute Studio.
Procedimiento
Complete los siguientes pasos para convertir direcciones IPv4 o IPv6 en geolocalizaciones mediante una UDF de MaxCompute:
-
Step 1: Upload the IP address library files
Cargue los archivos de la biblioteca de direcciones IP como recursos en su proyecto de MaxCompute. La UDF de MaxCompute que creará depende de estos recursos.
-
Step 2: Create a project connection
Conéctese a un proyecto de MaxCompute y cree un módulo Java de MaxCompute.
-
Step 3: Write the MaxCompute UDF
Escriba el código de la UDF de MaxCompute en IntelliJ IDEA.
-
Step 4: Register the MaxCompute UDF
Registre la UDF de MaxCompute.
-
Step 5: Call the MaxCompute UDF to convert IP addresses to geolocations
Invoque la función registrada en una sentencia SQL para convertir las direcciones IP en geolocalizaciones.
Paso 1: Cargar los archivos de la biblioteca de direcciones IP
Descargue los archivos de la biblioteca de direcciones IP en su computadora local, descomprima el paquete para obtener
ipv4.txtyipv6.txt, y colóquelos en el directorio de instalación...\odpscmd_public\bindel cliente de MaxCompute.Install and log on to the MaxCompute client y, a continuación, cambie a su proyecto de MaxCompute de destino.
-
Ejecute el comando
add filepara cargaripv4.txtyipv6.txtcomo recursos de archivo en el proyecto de MaxCompute.ADD file ipv4.txt -f; ADD file ipv6.txt -f;Para obtener más información sobre cómo agregar recursos, consulte Add resources.
(Para depuración local) Copie
ipv4.txtyipv6.txtal directoriowarehouse/example_project/_resources_de su proyecto local.
Paso 2: Crear una conexión de proyecto
Conéctese a un proyecto de MaxCompute. Para obtener más información, consulte Manage project connections.
Cree un módulo Java de MaxCompute. Para obtener más información, consulte Create a MaxCompute Java module.
Paso 3: Escribir la UDF de MaxCompute
-
Cree clases Java.
El código de la UDF en los siguientes pasos utiliza estas clases Java.
En IntelliJ IDEA, en el panel Project, haga clic derecho en el directorio fuente del módulo (src > main > java) y seleccione New > Java Class.
-
En el cuadro de diálogo New Java Class, introduzca el nombre de la clase, presione Enter y, a continuación, introduzca el código en el editor de código.
Cree las siguientes tres clases Java en orden. Puede usar el código proporcionado sin modificaciones.
-
IpUtils
package com.aliyun.odps.udf.utils; import java.math.BigInteger; import java.net.Inet4Address; import java.net.Inet6Address; import java.net.InetAddress; import java.net.UnknownHostException; import java.util.Arrays; public class IpUtils { /** * Converts an IP address from a string to a long. * * @param ipInString * The IP address as a string. * @return The IP address as a long. */ public static long StringToLong(String ipInString) { ipInString = ipInString.replace(" ", ""); byte[] bytes; if (ipInString.contains(":")) bytes = ipv6ToBytes(ipInString); else bytes = ipv4ToBytes(ipInString); BigInteger bigInt = new BigInteger(bytes); // System.out.println(bigInt.toString()); return bigInt.longValue(); } /** * Converts an IP address from a string to a BigInteger string. * * @param ipInString * The IP address as a string. * @return A BigInteger representation of the IP address as a string. */ public static String StringToBigIntString(String ipInString) { ipInString = ipInString.replace(" ", ""); byte[] bytes; if (ipInString.contains(":")) bytes = ipv6ToBytes(ipInString); else bytes = ipv4ToBytes(ipInString); BigInteger bigInt = new BigInteger(bytes); return bigInt.toString(); } /** * Converts an IP address from a BigInteger to a string. * * @param ipInBigInt * The IP address as a BigInteger. * @return The IP address as a string. */ public static String BigIntToString(BigInteger ipInBigInt) { byte[] bytes = ipInBigInt.toByteArray(); byte[] unsignedBytes = Arrays.copyOfRange(bytes, 1, bytes.length); // Remove the sign bit. try { String ip = InetAddress.getByAddress(unsignedBytes).toString(); return ip.substring(ip.indexOf('/') + 1).trim(); } catch (UnknownHostException e) { throw new RuntimeException(e); } } /** * Converts an IPv6 address to a signed byte[17] array. */ private static byte[] ipv6ToBytes(String ipv6) { byte[] ret = new byte[17]; ret[0] = 0; int ib = 16; boolean comFlag = false;// Flag for IPv4-mapped addresses. if (ipv6.startsWith(":"))// Remove the leading colon. ipv6 = ipv6.substring(1); String groups[] = ipv6.split(":"); for (int ig = groups.length - 1; ig > -1; ig--) {// Scan in reverse. if (groups[ig].contains(".")) { // An IPv4-mapped address is found. byte[] temp = ipv4ToBytes(groups[ig]); ret[ib--] = temp[4]; ret[ib--] = temp[3]; ret[ib--] = temp[2]; ret[ib--] = temp[1]; comFlag = true; } else if ("".equals(groups[ig])) { // Zero-length compression is found. Calculate the number of missing groups. int zlg = 9 - (groups.length + (comFlag ? 1 : 0)); while (zlg-- > 0) {// Set these groups to 0. ret[ib--] = 0; ret[ib--] = 0; } } else { int temp = Integer.parseInt(groups[ig], 16); ret[ib--] = (byte) temp; ret[ib--] = (byte) (temp >> 8); } } return ret; } /** * Converts an IPv4 address to a signed byte[5] array. */ private static byte[] ipv4ToBytes(String ipv4) { byte[] ret = new byte[5]; ret[0] = 0; // Find the positions of the dots in the IP address string. int position1 = ipv4.indexOf("."); int position2 = ipv4.indexOf(".", position1 + 1); int position3 = ipv4.indexOf(".", position2 + 1); // Convert the string segments between the dots to integers. ret[1] = (byte) Integer.parseInt(ipv4.substring(0, position1)); ret[2] = (byte) Integer.parseInt(ipv4.substring(position1 + 1, position2)); ret[3] = (byte) Integer.parseInt(ipv4.substring(position2 + 1, position3)); ret[4] = (byte) Integer.parseInt(ipv4.substring(position3 + 1)); return ret; } /** * @param ipAddress The IPv4 or IPv6 string. * @return 4 for IPv4, 6 for IPv6, or 0 for an invalid address. * @throws Exception */ public static int isIpV4OrV6(String ipAddress) throws Exception { InetAddress address = InetAddress.getByName(ipAddress); if (address instanceof Inet4Address) return 4; else if (address instanceof Inet6Address) return 6; return 0; } /* * Checks whether an IP address is within a specified IP range. * * ipSection The IP range, separated by a hyphen (-). * * ip The IP address to validate. */ public static boolean ipExistsInRange(String ip, String ipSection) { ipSection = ipSection.trim(); ip = ip.trim(); int idx = ipSection.indexOf('-'); String beginIP = ipSection.substring(0, idx); String endIP = ipSection.substring(idx + 1); return getIp2long(beginIP) <= getIp2long(ip) && getIp2long(ip) <= getIp2long(endIP); } public static long getIp2long(String ip) { ip = ip.trim(); String[] ips = ip.split("\\."); long ip2long = 0L; for (int i = 0; i < 4; ++i) { ip2long = ip2long << 8 | Integer.parseInt(ips[i]); } return ip2long; } public static long getIp2long2(String ip) { ip = ip.trim(); String[] ips = ip.split("\\."); long ip1 = Integer.parseInt(ips[0]); long ip2 = Integer.parseInt(ips[1]); long ip3 = Integer.parseInt(ips[2]); long ip4 = Integer.parseInt(ips[3]); long ip2long = 1L * ip1 * 256 * 256 * 256 + ip2 * 256 * 256 + ip3 * 256 + ip4; return ip2long; } public static void main(String[] args) { System.out.println(StringToLong("2002:7af3:f3be:ffff:ffff:ffff:ffff:ffff")); System.out.println(StringToLong("54.38.XX.XX")); } private class Invalid{ private Invalid() { } } } -
IpV4Obj
package com.aliyun.odps.udf.objects; public class IpV4Obj { public long startIp ; public long endIp ; public String city; public String province; public IpV4Obj(long startIp, long endIp, String city, String province) { this.startIp = startIp; this.endIp = endIp; this.city = city; this.province = province; } @Override public String toString() { return "IpV4Obj{" + "startIp=" + startIp + ", endIp=" + endIp + ", city='" + city + '\'' + ", province='" + province + '\'' + '}'; } public void setStartIp(long startIp) { this.startIp = startIp; } public void setEndIp(long endIp) { this.endIp = endIp; } public void setCity(String city) { this.city = city; } public void setProvince(String province) { this.province = province; } public long getStartIp() { return startIp; } public long getEndIp() { return endIp; } public String getCity() { return city; } public String getProvince() { return province; } } -
IpV6Obj
package com.aliyun.odps.udf.objects; public class IpV6Obj { public String startIp ; public String endIp ; public String city; public String province; public String getStartIp() { return startIp; } @Override public String toString() { return "IpV6Obj{" + "startIp='" + startIp + '\'' + ", endIp='" + endIp + '\'' + ", city='" + city + '\'' + ", province='" + province + '\'' + '}'; } public IpV6Obj(String startIp, String endIp, String city, String province) { this.startIp = startIp; this.endIp = endIp; this.city = city; this.province = province; } public void setStartIp(String startIp) { this.startIp = startIp; } public String getEndIp() { return endIp; } public void setEndIp(String endIp) { this.endIp = endIp; } public String getCity() { return city; } public void setCity(String city) { this.city = city; } public String getProvince() { return province; } public void setProvince(String province) { this.province = province; } }
-
-
Escriba el código de la UDF de MaxCompute.
En el panel Project, haga clic derecho en el directorio fuente del módulo (src > main > java) y seleccione New > MaxCompute Java.
-
En el cuadro de diálogo Create new MaxCompute java class, haga clic en UDF, introduzca un Name, presione Enter y, a continuación, introduzca el código en el editor de código.
Por ejemplo, asigne el nombre IpLocation a la clase Java. Puede usar el siguiente código sin modificaciones.
package com.aliyun.odps.udf.udfFunction; import com.aliyun.odps.udf.ExecutionContext; import com.aliyun.odps.udf.UDF; import com.aliyun.odps.udf.UDFException; import com.aliyun.odps.udf.utils.IpUtils; import com.aliyun.odps.udf.objects.IpV4Obj; import com.aliyun.odps.udf.objects.IpV6Obj; import java.io.*; import java.util.ArrayList; import java.util.Comparator; import java.util.List; import java.util.stream.Collectors; public class IpLocation extends UDF { public static IpV4Obj[] ipV4ObjsArray; public static IpV6Obj[] ipV6ObjsArray; public IpLocation() { super(); } @Override public void setup(ExecutionContext ctx) throws UDFException, IOException { //IPV4 if(ipV4ObjsArray==null) { BufferedInputStream bufferedInputStream = ctx.readResourceFileAsStream("ipv4.txt"); BufferedReader br = new BufferedReader(new InputStreamReader(bufferedInputStream)); ArrayList<IpV4Obj> ipV4ObjArrayList=new ArrayList<>(); String line = null; while ((line = br.readLine()) != null) { String[] f = line.split("\\|", -1); if(f.length>=5) { long startIp = IpUtils.StringToLong(f[0]); long endIp = IpUtils.StringToLong(f[1]); String city=f[3]; String province=f[4]; IpV4Obj ipV4Obj = new IpV4Obj(startIp, endIp, city, province); ipV4ObjArrayList.add(ipV4Obj); } } br.close(); List<IpV4Obj> collect = ipV4ObjArrayList.stream().sorted(Comparator.comparing(IpV4Obj::getStartIp)).collect(Collectors.toList()); ArrayList<IpV4Obj> basicIpV4DataList=(ArrayList)collect; IpV4Obj[] ipV4Objs = new IpV4Obj[basicIpV4DataList.size()]; ipV4ObjsArray = basicIpV4DataList.toArray(ipV4Objs); } //IPV6 if(ipV6ObjsArray==null) { BufferedInputStream bufferedInputStream = ctx.readResourceFileAsStream("ipv6.txt"); BufferedReader br = new BufferedReader(new InputStreamReader(bufferedInputStream)); ArrayList<IpV6Obj> ipV6ObjArrayList=new ArrayList<>(); String line = null; while ((line = br.readLine()) != null) { String[] f = line.split("\\|", -1); if(f.length>=5) { String startIp = IpUtils.StringToBigIntString(f[0]); String endIp = IpUtils.StringToBigIntString(f[1]); String city=f[3]; String province=f[4]; IpV6Obj ipV6Obj = new IpV6Obj(startIp, endIp, city, province); ipV6ObjArrayList.add(ipV6Obj); } } br.close(); List<IpV6Obj> collect = ipV6ObjArrayList.stream().sorted(Comparator.comparing(IpV6Obj::getStartIp)).collect(Collectors.toList()); ArrayList<IpV6Obj> basicIpV6DataList=(ArrayList)collect; IpV6Obj[] ipV6Objs = new IpV6Obj[basicIpV6DataList.size()]; ipV6ObjsArray = basicIpV6DataList.toArray(ipV6Objs); } } public String evaluate(String ip){ if(ip==null||ip.trim().isEmpty()||!(ip.contains(".")||ip.contains(":"))) { return null; } int ipV4OrV6=0; try { ipV4OrV6= IpUtils.isIpV4OrV6(ip); } catch (Exception e) { return null; } // If the IP address is an IPv4 address. if(ipV4OrV6==4) { int i = binarySearch(ipV4ObjsArray, IpUtils.StringToLong(ip)); if(i>=0) { IpV4Obj ipV4Obj = ipV4ObjsArray[i]; return ipV4Obj.city+","+ipV4Obj.province; }else{ return null; } }else if(ipV4OrV6==6)// If the IP address is an IPv6 address. { int i = binarySearchIPV6(ipV6ObjsArray, IpUtils.StringToBigIntString(ip)); if(i>=0) { IpV6Obj ipV6Obj = ipV6ObjsArray[i]; return ipV6Obj.city+","+ipV6Obj.province; }else{ return null; } }else{// If the IP address is not in IPv4 or IPv6 format. return null; } } @Override public void close() throws UDFException, IOException { super.close(); } private static int binarySearch(IpV4Obj[] array,long ip){ int low=0; int hight=array.length-1; while (low<=hight) { int middle=(low+hight)/2; if((ip>=array[middle].startIp)&&(ip<=array[middle].endIp)) { return middle; } if (ip < array[middle].startIp) hight = middle - 1; else { low = middle + 1; } } return -1; } private static int binarySearchIPV6(IpV6Obj[] array,String ip){ int low=0; int hight=array.length-1; while (low<=hight) { int middle=(low+hight)/2; if((ip.compareTo(array[middle].startIp)>=0)&&(ip.compareTo(array[middle].endIp)<=0)) { return middle; } if (ip.compareTo(array[middle].startIp) < 0) hight = middle - 1; else { low = middle + 1; } } return -1; } private class Invalid{ private Invalid() { } } }
-
Prepare los datos para la depuración local.
En el directorio
warehouse/example_project/__tables__/wc_in2/p1=2/p2=1/de su proyecto local, abra el archivo data.Cambie los datos de la última columna del archivo data por tres direcciones IP cualquiera de
ipv4.txty guarde el archivo.
-
Depure la UDF de MaxCompute para asegurarse de que el código se ejecute correctamente.
Para obtener más información, consulte Debug a UDF by running it locally.
Haga clic derecho en el script de UDF de MaxCompute completado y seleccione Run.
-
En el cuadro de diálogo Run/Debug Configurations, configure los parámetros de ejecución y haga clic en OK.
Como ejemplo, establezca Name en
IpLocationy Main class encom.aliyun.odps.udf.udfFunction.IpLocation. Para los campos específicos de MaxCompute, seleccionelocalyexample_projecten MaxCompute project, introduzcawc_in2en MaxCompute table,p2=1,p1=2en Table partition (en formatop1=v1,p2=v2) ycolcen Table columns (en formatoc1,c2). Establezca Download Record limit en100y Data Column Separator en coma. A continuación, haga clic en OK. Si el código se ejecuta sin errores, puede continuar. De lo contrario, resuelva el problema utilizando el mensaje de error en IntelliJ IDEA.NotaPuede consultar la figura para ver un ejemplo de los parámetros de ejecución.
Paso 4: Registrar la UDF de MaxCompute
Haga clic derecho en el script de UDF de MaxCompute compilado correctamente y seleccione Deploy to server….
-
En el cuadro de diálogo Package a jar, submit resource and register function, configure los parámetros.
Para obtener descripciones de los parámetros, consulte Package, upload, and register. Como ejemplo, seleccione su proyecto de destino en MaxCompute project. En Resource file, seleccione el archivo JAR empaquetado. El campo Resource name se completa automáticamente. Establezca Main class con el nombre completo de la UDF, como
com.aliyun.odps.udf.udfFunction.IpLocation, y en el campo Function name, introduzca un nombre para la función, comoipv4_ipv6_aton. En Extra resources, seleccione los archivosipv4.txtyipv6.txtque cargó en Step 1. Seleccione Force update if already exists y, a continuación, haga clic en OK.
Paso 5: Invocar la UDF para convertir direcciones IP
-
Ejecute una sentencia
SELECTpara invocar la UDF y convertir las direcciones IP en geolocalizaciones.Ejemplos:
-
Convertir una dirección IPv4 en una geolocalización
select ipv4_ipv6_aton('116.11.XX.XX');Salida esperada:
Beihai,Guangxi Zhuang Autonomous Region -
Convertir una dirección IPv6 en una geolocalización
select ipv4_ipv6_aton('2001:0250:080b:0:0:0:0:0');Salida esperada:
Baoding,Hebei Province
-