Créez une fonction définie par l'utilisateur (UDF) MaxCompute pour convertir des adresses IPv4 ou IPv6 en géolocalisations en interrogeant une base de données d'adresses IP.
Contexte
La conversion d'adresses IP en géolocalisations nécessite une base de données d'adresses IP. Téléchargez les fichiers de cette base et importez-les en tant que ressources dans votre projet MaxCompute. Développez et enregistrez ensuite une UDF MaxCompute qui référence ces ressources, puis appelez-la dans des instructions SQL pour résoudre les adresses IP en géolocalisations.
Les fichiers de base de données d'adresses IP fournis dans cette rubrique le sont à titre d'exemple uniquement. Vous devez maintenir votre propre base de données d'adresses IP en fonction de vos besoins métier.
Prérequis
Vous avez installé et configuré le client MaxCompute.
Vous avez installé MaxCompute Studio.
Procédure
Suivez les étapes ci-dessous pour convertir des adresses IPv4 ou IPv6 en géolocalisations à l'aide d'une UDF MaxCompute :
-
Étape 1 : Importer les fichiers de la base de données d'adresses IP
Importez les fichiers de la base de données d'adresses IP en tant que ressources dans votre projet MaxCompute. L'UDF MaxCompute que vous allez créer dépend de ces ressources.
-
Étape 2 : Créer une connexion au projet
Connectez-vous à un projet MaxCompute et créez un module Java MaxCompute.
-
Étape 3 : Écrire l'UDF MaxCompute
Rédigez le code de l'UDF MaxCompute dans IntelliJ IDEA.
-
Étape 4 : Enregistrer l'UDF MaxCompute
Enregistrez l'UDF MaxCompute.
-
Étape 5 : Appeler l'UDF MaxCompute pour convertir des adresses IP en géolocalisations
Appelez la fonction enregistrée dans une instruction SQL pour convertir les adresses IP en géolocalisations.
Étape 1 : Importer les fichiers de la base de données d'adresses IP
Téléchargez les fichiers de la base de données d'adresses IP sur votre machine locale, décompressez l'archive pour extraire
ipv4.txtetipv6.txt, puis placez-les dans le répertoire d'installation...\odpscmd_public\bindu client MaxCompute.Installez et connectez-vous au client MaxCompute, puis basculez vers votre projet MaxCompute cible.
-
Exécutez la commande
add filepour importeripv4.txtetipv6.txten tant que ressources de type fichier dans le projet MaxCompute.ADD file ipv4.txt -f; ADD file ipv6.txt -f;Pour plus d'informations sur l'ajout de ressources, consultez la section Ajouter des ressources.
(Pour le débogage local) Copiez
ipv4.txtetipv6.txtdans le répertoirewarehouse/example_project/_resources_de votre projet local.
Étape 2 : Créer une connexion au projet
Connectez-vous à un projet MaxCompute. Pour plus d'informations, consultez la section Gérer les connexions aux projets.
Créez un module Java MaxCompute. Pour plus d'informations, consultez la section Créer un module Java MaxCompute.
Étape 3 : Écrire l'UDF MaxCompute
-
Créez des classes Java.
Le code de l'UDF présenté dans les étapes suivantes utilise ces classes Java.
Dans IntelliJ IDEA, dans le volet Project, cliquez avec le bouton droit sur le répertoire source du module (src > main > java) et choisissez New > Java Class.
-
Dans la boîte de dialogue New Java Class, saisissez le nom de la classe, appuyez sur Entrée, puis entrez le code dans l'éditeur de code.
Créez les trois classes Java suivantes dans l'ordre indiqué. Vous pouvez utiliser le code fourni sans modification.
-
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; } }
-
-
Rédigez le code de l'UDF MaxCompute.
Dans le volet Project, cliquez avec le bouton droit sur le répertoire source du module (src > main > java) et choisissez New > MaxCompute Java.
-
Dans la boîte de dialogue Create new MaxCompute java class, cliquez sur UDF, saisissez un Name, appuyez sur Entrée, puis entrez le code dans l'éditeur de code.
Par exemple, nommez la classe Java IpLocation. Vous pouvez utiliser le code suivant sans modification.
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() { } } }
-
Préparez les données pour le débogage local.
Dans le répertoire
warehouse/example_project/__tables__/wc_in2/p1=2/p2=1/de votre projet local, ouvrez le fichier data.Remplacez les données de la dernière colonne du fichier data par trois adresses IP quelconques issues de
ipv4.txt, puis enregistrez le fichier.
-
Déboguez l'UDF MaxCompute pour vous assurer que le code s'exécute correctement.
Pour plus d'informations, consultez la section Déboguer une UDF en l'exécutant localement.
Cliquez avec le bouton droit sur le script UDF MaxCompute terminé et sélectionnez Run.
-
Dans la boîte de dialogue Run/Debug Configurations, configurez les paramètres d'exécution et cliquez sur OK.
À titre d'exemple, définissez Name sur
IpLocationet Main class surcom.aliyun.odps.udf.udfFunction.IpLocation. Pour les champs spécifiques à MaxCompute, sélectionnezlocaletexample_projectpour MaxCompute project, saisissezwc_in2pour MaxCompute table,p2=1,p1=2pour Table partition (au formatp1=v1,p2=v2), etcolcpour Table columns (au formatc1,c2). Définissez Download Record limit sur100et Data Column Separator sur une virgule. Ensuite, cliquez sur OK. Si le code s'exécute sans erreur, vous pouvez poursuivre. Sinon, résolvez le problème en vous aidant du message d'erreur affiché dans IntelliJ IDEA.RemarqueReportez-vous à l'illustration pour un exemple des paramètres d'exécution.
Étape 4 : Enregistrer l'UDF MaxCompute
Cliquez avec le bouton droit sur le script UDF MaxCompute compilé avec succès et sélectionnez Deploy to server….
-
Dans la boîte de dialogue Package a jar, submit resource and register function, configurez les paramètres.
Pour une description des paramètres, consultez la section Empaqueter, importer et enregistrer. À titre d'exemple, sélectionnez votre projet cible pour MaxCompute project. Pour Resource file, sélectionnez le fichier JAR empaqueté. Le champ Resource name est automatiquement renseigné. Définissez Main class sur le nom complet de l'UDF, par exemple
com.aliyun.odps.udf.udfFunction.IpLocation, et dans le champ Function name, saisissez un nom pour la fonction, tel queipv4_ipv6_aton. Sous Extra resources, sélectionnez les fichiersipv4.txtetipv6.txtque vous avez importés lors de l'étape 1. Cochez Force update if already exists, puis cliquez sur OK.
Étape 5 : Appeler l'UDF pour convertir des adresses IP
-
Exécutez une instruction
SELECTpour appeler l'UDF et convertir les adresses IP en géolocalisations.Exemples :
-
Convertir une adresse IPv4 en géolocalisation
select ipv4_ipv6_aton('116.11.XX.XX');Résultat attendu :
Beihai,Guangxi Zhuang Autonomous Region -
Convertir une adresse IPv6 en géolocalisation
select ipv4_ipv6_aton('2001:0250:080b:0:0:0:0:0');Résultat attendu :
Baoding,Hebei Province
-