Perkembangan platform data besar memungkinkan Anda memproses berbagai jenis data tidak terstruktur dan semi-terstruktur. Sebagai contoh, Anda dapat mengonversi alamat IP menjadi geolokasi. Topik ini menjelaskan cara menggunakan fungsi yang ditentukan pengguna (UDF) MaxCompute untuk mengonversi alamat IPv4 atau IPv6 menjadi geolokasi.
Prasyarat
Pastikan persyaratan berikut telah dipenuhi:
Klien MaxCompute telah diinstal.
Untuk informasi lebih lanjut tentang cara menginstal dan mengonfigurasi klien MaxCompute, lihat Instal dan konfigurasikan klien MaxCompute.
MaxCompute Studio telah diinstal. Untuk informasi lebih lanjut, lihat Instal MaxCompute Studio.
Informasi latar belakang
Untuk mengonversi alamat IPv4 atau IPv6 menjadi geolokasi, Anda harus mengunduh file pustaka alamat IP yang mencakup alamat IP tersebut, lalu mengunggah file tersebut sebagai sumber daya ke proyek MaxCompute. Setelah mengembangkan dan membuat UDF MaxCompute berdasarkan file pustaka alamat IP, Anda dapat memanggil UDF dalam pernyataan SQL untuk mengonversi alamat IP menjadi geolokasi.
Catatan Penggunaan
File pustaka alamat IP yang disediakan dalam topik ini hanya untuk referensi. Anda harus memelihara file pustaka alamat IP sesuai dengan kebutuhan bisnis Anda. File ini mencakup beberapa teks dalam bahasa Cina, namun hal tersebut tidak mempengaruhi hasil pengujian.
Prosedur
Untuk mengonversi alamat IPv4 atau IPv6 menjadi geolokasi menggunakan UDF MaxCompute, ikuti langkah-langkah berikut:
Langkah 1: Unggah file pustaka alamat IP
Unggah file pustaka alamat IP sebagai sumber daya ke proyek MaxCompute Anda. Sumber daya ini digunakan saat membuat UDF MaxCompute pada langkah-langkah selanjutnya.
Langkah 2: Hubungkan ke proyek MaxCompute
Hubungkan ke proyek MaxCompute dan buat modul Java MaxCompute.
Langkah 3: Tulis UDF MaxCompute
Tulis UDF MaxCompute menggunakan IntelliJ IDEA.
Langkah 4: Buat UDF MaxCompute
Buat UDF MaxCompute.
Langkah 5: Panggil UDF MaxCompute untuk mengonversi alamat IP menjadi geolokasi
Panggil UDF MaxCompute yang Anda buat dalam pernyataan SQL untuk mengonversi alamat IP menjadi geolokasi.
Langkah 1: Unggah file pustaka alamat IP
Unduh file pustaka alamat IP ke mesin lokal Anda, ekstrak file tersebut untuk mendapatkan file ipv4.txt dan ipv6.txt, lalu tempatkan file-file tersebut di direktori instalasi klien MaxCompute,
...\odpscmd_public\bin.File pustaka alamat IP yang disediakan dalam topik ini hanya untuk referensi. Anda harus memelihara file pustaka alamat IP sesuai dengan kebutuhan bisnis Anda. File ini mencakup beberapa teks dalam bahasa Cina, namun hal tersebut tidak mempengaruhi hasil pengujian.
Mulai klien MaxCompute dan masuk ke proyek MaxCompute tempat Anda ingin mengunggah file ipv4.txt dan ipv6.txt.
Jalankan perintah
add fileuntuk mengunggah kedua file tersebut sebagai sumber daya file ke proyek MaxCompute.Contoh perintah:
add file ipv4.txt -f; add file ipv6.txt -f;Untuk informasi lebih lanjut tentang cara menambahkan sumber daya, lihat Tambahkan sumber daya.
(Debugging lokal) Simpan file ipv4.txt dan ipv6.txt di direktori
warehouse/example_project/_resources_proyek lokal Anda.
Langkah 2: Hubungkan ke proyek MaxCompute
Hubungkan ke proyek MaxCompute. Untuk informasi lebih lanjut, lihat Kelola koneksi proyek.
Buat modul Java MaxCompute. Untuk informasi lebih lanjut, lihat Buat modul Java MaxCompute.
Langkah 3: Tulis UDF MaxCompute
Buat kelas Java.
Kelas Java digunakan untuk menulis UDF MaxCompute pada sublangkah berikutnya.
Mulai IntelliJ IDEA. Di panel navigasi sisi kiri tab Project, pilih , klik kanan java, lalu pilih .

Di dalam kotak dialog New Java Class, masukkan nama kelas, tekan Enter, kemudian inputkan kode di editor kode.
Anda harus membuat tiga kelas Java. Bagian berikut menunjukkan nama dan kode dari kelas-kelas ini. Anda dapat menggunakan ulang kode tanpa modifikasi.
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 { /** * Konversi tipe data alamat IP dari STRING ke LONG. * * @param ipInString * Alamat IP bertipe STRING. * @return Kembalikan alamat IP bertipe 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(); } /** * Konversi tipe data alamat IP dari STRING ke LONG. * * @param ipInString * Alamat IP bertipe STRING. * @return Kembalikan alamat IP bertipe STRING yang dikonversi dari BIGINT. */ 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(); } /** * Konversi tipe data alamat IP dari BIGINT ke STRING. * * @param ipInBigInt * Alamat IP bertipe BIGINT. * @return Kembalikan alamat IP bertipe STRING. */ public static String BigIntToString(BigInteger ipInBigInt) { byte[] bytes = ipInBigInt.toByteArray(); byte[] unsignedBytes = Arrays.copyOfRange(bytes, 1, bytes.length); // Hapus bit tanda. try { String ip = InetAddress.getByAddress(unsignedBytes).toString(); return ip.substring(ip.indexOf('/') + 1).trim(); } catch (UnknownHostException e) { throw new RuntimeException(e); } } /** * Konversi tipe data alamat IPv6 menjadi byte bertanda 17. */ private static byte[] ipv6ToBytes(String ipv6) { byte[] ret = new byte[17]; ret[0] = 0; int ib = 16; boolean comFlag=false;// Flag IPv4/IPv6. if (ipv6.startsWith(":"))// Hapus titik dua (:) dari awal alamat IPv6. ipv6 = ipv6.substring(1); String groups[] = ipv6.split(":"); for (int ig=groups.length - 1; ig > -1; ig--) {// Pemindaian balik. if (groups[ig].contains(".")) { // Baik alamat IPv4 maupun IPv6 ada. 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])) { // Kompresi panjang nol. Hitung jumlah grup yang hilang. int zlg = 9 - (groups.length + (comFlag ? 1 : 0)); while (zlg-- > 0) {// Atur grup-grup ini menjadi 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; } /** * Konversi tipe data alamat IPv4 menjadi byte bertanda 5. */ private static byte[] ipv4ToBytes(String ipv4) { byte[] ret = new byte[5]; ret[0] = 0; // Temukan posisi titik (.) dalam alamat IP bertipe STRING. int position1 = ipv4.indexOf("."); int position2 = ipv4.indexOf(".", position1 + 1); int position3 = ipv4.indexOf(".", position2 + 1); // Konversi alamat IP bertipe STRING antara titik (.) menjadi INTEGER. 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 ipAdress Alamat IPv4 atau IPv6 bertipe STRING. * @return 4:IPv4, 6:IPv6, 0: Alamat IP tidak valid. * @throws Exception */ public static int isIpV4OrV6(String ipAdress) throws Exception { InetAddress address = InetAddress.getByName(ipAdress); if (address instanceof Inet4Address) return 4; else if (address instanceof Inet6Address) return 6; return 0; } /* * Periksa apakah alamat IP termasuk dalam bagian IP tertentu. * * ipSection Bagian IP yang dipisahkan oleh tanda hubung (-). * * Alamat IP yang akan diperiksa. */ 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.72.63")); } 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; } }
Tulis UDF MaxCompute.
Di panel navigasi sisi kiri tab Project, pilih , klik kanan java, lalu pilih .

Di kotak dialog Create new MaxCompute java class, klik UDF dan masukkan nama kelas di bidang Name. Lalu, tekan Enter dan masukkan kode di editor kode.
Kode berikut menunjukkan cara menulis UDF berdasarkan kelas Java bernama IpLocation. Anda dapat menggunakan ulang kode tanpa modifikasi.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; } // Alamat IPv4 digunakan. 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)// Alamat IPv6 digunakan. { int i = binarySearchIPV6(ipV6ObjsArray, IpUtils.StringToBigIntString(ip)); if(i>=0) { IpV6Obj ipV6Obj = ipV6ObjsArray[i]; return ipV6Obj.city+","+ipV6Obj.province; }else{ return null; } } else{// Alamat IP tidak valid. 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() { } } }
Persiapkan data uji untuk debugging lokal.
Di direktori
warehouse/example_project/__tables__/wc_in2/p1=2/p2=1/proyek lokal Anda, buka file data.Masukkan alamat IP yang termasuk dalam file ipv4.txt di kolom terakhir file data dan simpan perubahan. Anda dapat memasukkan tiga alamat IP.
Debug UDF MaxCompute untuk memeriksa apakah kode berjalan seperti yang diharapkan.
Untuk informasi lebih lanjut tentang cara debug UDAF, lihat Lakukan run lokal untuk debug UDF.
Klik kanan skrip UDF MaxCompute yang Anda tulis dan pilih Run.
Di kotak dialog Run/Debug Configurations, konfigurasikan parameter yang diperlukan dan klik OK. Gambar berikut menunjukkan contohnya.
Jika tidak ada kesalahan yang dilaporkan, kode berhasil dijalankan. Anda dapat melanjutkan ke langkah-langkah berikutnya. Jika terjadi kesalahan, Anda dapat melakukan pemecahan masalah berdasarkan informasi kesalahan yang ditampilkan di IntelliJ IDEA.CatatanPengaturan parameter pada gambar sebelumnya hanya untuk referensi.
Langkah 4: Buat UDF MaxCompute
Klik kanan skrip UDF MaxCompute yang telah dikompilasi dan pilih Deploy to server….

Di kotak dialog Package a jar, submit resource and register function, konfigurasikan parameter-parameter tersebut.
Untuk informasi lebih lanjut tentang parameter-parameter tersebut, lihat Kemas program Java, unggah paket, dan buat UDF MaxCompute.
Extra resources: Anda harus memilih file pustaka alamat IP ipv4.txt dan ipv6.txt yang Anda unggah di Langkah 1. Dalam topik ini, fungsi yang dibuat diberi nama ipv4_ipv6_aton.
Langkah 5: Panggil UDF MaxCompute untuk mengonversi alamat IP menjadi geolokasi
Eksekusi pernyataan SQL SELECT untuk memanggil UDF MaxCompute guna mengonversi alamat IPv4 atau IPv6 menjadi geolokasi.
Pernyataan contoh:
Konversi alamat IPv4 menjadi geolokasi
select ipv4_ipv6_aton('116.11.XX.XX');Hasil berikut akan dikembalikan:
Beihai, Guangxi Zhuang Autonomous RegionKonversi alamat IPv6 menjadi geolokasi
select ipv4_ipv6_aton('2001:0250:080b:0:0:0:0:0');Hasil berikut akan dikembalikan:
Baoding, Hebei Province