Java obtains the corresponding attribution based on the IP address

1 Introduction

Recently, major platforms have added the function of displaying the speaker’s IP address in the comment area, such as Bilibili, Weibo, Zhihu, etc. Next, let’s talk about how to obtain the IP address in Java

2 Get the IP address

There are many ways to obtain an IP address in Java, so I won’t introduce them one by one. The most commonly used method to obtain an IP address is given for reference only. The code is as follows:

@Slf4j
public class IpUtils {
    
    

    /**
     * 获取ip地址
     * @param request request对象
     * @return 返回对应IP地址
     */
    public static String getIpAddress(HttpServletRequest request){
    
    
        String ipAddress = null;
        try {
    
    
            ipAddress = request.getHeader("X-Forwarded-For");
            if (ipAddress != null && ipAddress.length() != 0 && !"unknown".equalsIgnoreCase(ipAddress)) {
    
    
                // 多次反向代理后会有多个ip值,第一个ip才是真实ip
                if (ipAddress.contains(",")) {
    
    
                    ipAddress = ipAddress.split(",")[0];
                }
            }
            if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
    
    
                ipAddress = request.getHeader("Proxy-Client-IP");
            }
            if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
    
    
                ipAddress = request.getHeader("WL-Proxy-Client-IP");
            }
            if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
    
    
                ipAddress = request.getHeader("HTTP_CLIENT_IP");
            }
            if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
    
    
                ipAddress = request.getRemoteAddr();
            }
        }catch (Exception e) {
    
    
            log.error("IPUtils ERROR ",e);
        }
        return ipAddress;
    }
    
    /**
     * 获取mac地址
     */
    public static String getMacAddress() throws Exception {
    
    
        // 取mac地址
        byte[] macAddressBytes = NetworkInterface.getByInetAddress(InetAddress.getLocalHost()).getHardwareAddress();
        // 下面代码是把mac地址拼装成String
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < macAddressBytes.length; i++) {
    
    
            if (i != 0) {
    
    
                sb.append("-");
            }
            // mac[i] & 0xFF 是为了把byte转化为正整数
            String s = Integer.toHexString(macAddressBytes[i] & 0xFF);
            sb.append(s.length() == 1 ? 0 + s : s);
        }
        return sb.toString().trim().toUpperCase();
    }
    
}

Explain a few nouns that appear here:

  • X-Forwarded-For : An HTTP extension header, mainly for the Web server to obtain the real IP address of the visiting user. For each IP address, each value is separated by a comma + space. The leftmost is the IP address of the original client. If there are multiple layers of proxies in the middle, each layer of proxies will append the IP of the client connecting to it to X-Forwarded-For right.
  • Proxy-Client-IP : This is usually only available through the request of the Apache http server. When using Apache http as a proxy, the Proxy-Client-IP request header is generally added
  • WL-Proxy-Client-IP : It is also a header added to the weblogic plug-in through the Apache http server.
  • X-Real-IP : generally only record the real requesting client IP
  • HTTP_CLIENT_IP : HTTP header sent by the proxy server. If it is a "super anonymous proxy", return a value of none.

3 Introduction to Ip2region

3.1 99.9% accuracy

The data aggregates the data of some well-known IP to place name query providers. These are their official accuracy rates. After testing, they are indeed more accurate than classic pure IP positioning.

The data of ip2region is aggregated from the open API or data of the following service providers (the upgrade program requests 2 to 4 times per second):

  • 01, >80%, Taobao IP address library, http://ip.taobao.com/%5C
  • 02,≈10%,GeoIP,https://geoip.com/%5C
  • 03, ≈2%, pure IP library, http://www.cz88.net/%5C

Remarks: If the above-mentioned open APIs or data are not available for open data, ip2region will stop the data update service.

3.2 Multi-query client support

The integrated clients include: java, C#, php, c, python, nodejs, php extension (php5 and php7), golang, rust, lua, lua_c, nginx.
insert image description here

3.3 Ip2region V2.0 Features

3.3.1 Standardized data format

The region information of each ip data segment has a fixed format: Country|Region|Province|City|ISP, only most of the data in China is accurate to the city, and some data in other countries can only be located in the country, and the options before and after are all 0.

3.3.2 Data deduplication and compression

The xdb format generation program will automatically deduplicate and compress some data. The default all IP data, the generated ip2region.xdb database is 11MiB, and the size of the database will gradually increase with the increase of data detail.

3.3.3 Extremely fast query response

Even for queries based entirely on xdb files, the response time for a single query is at the level of ten microseconds.

You can enable memory-accelerated queries in the following two ways:

  • vIndex index cache: Use a fixed 512KiB memory space to cache vector index data, reduce one IO
    disk operation, and keep the average query efficiency stable between 10-20 microseconds.
  • xdb entire file cache: load the entire xdb file into the memory, the memory usage is equal to the size of the xdb file, no disk IO operations, and maintain query efficiency at the microsecond level.

3.3.4 Extremely fast query response

The xdb in v2.0 format supports 100 million-level IP data segment lines, and the region information can also be fully customized. For example, you can add data for specific business needs in the region, such as: GPS information/International Uniform Geographical Information Code/Zip Code wait. That is, you can use ip2region to manage your own IP location data.

4 Get the ip2region.xdb file

Why do you want to get the ip2region.xdb file? Because the ip domain is based on this file

4.1 Direct access

I have packaged the file to Baidu Netdisk, friends can download it directly, the address is as follows:
Link: https://pan.baidu.com/s/1JSB0z2Cwl4umujKUzpBhFA
Extraction code: 0zz5

4.2 Packing by yourself

Download project:
Link: https://pan.baidu.com/s/1wHjUoHCmyH_PVcB9nf3cLw
Extraction code: aq5y
Enter the target folder and execute the command:

java -jar ip2region-maker-1.0.0.jar --src=./ip.merge.txt --dst=./ip2region.xdb

5 Use Ip2region V2.0 to obtain the corresponding province and city through the IP address

5.1 Three built-in query algorithms

All query clients have a single query at the level of 0.x milliseconds, with three built-in query algorithms

  • memory algorithm : the entire database is loaded into the memory, and a single query is within 0.1x milliseconds, and the single query of the C language client is at the level of 0.00x milliseconds.
  • Binary algorithm : based on binary search, based on the ip2region.db file, does not need to be loaded into memory, and a single query is at the level of 0.x milliseconds.
  • b-tree algorithm : based on btree algorithm, based on ip2region.db file, no need to load into memory, word query at 0.x millisecond level, faster than binary algorithm.

5.2 Specific implementation method

5.2.1 Introduce ip2region dependency

<dependency>
    <groupId>org.lionsoul</groupId>
    <artifactId>ip2region</artifactId>
    <version>2.6.4</version>
</dependency>

5.2.2 ip2region.xdb file, put it in the project resources directory

insert image description here

5.2.3 Convert user ip address based entirely on ip2region.xdb file

    /**
     * 获取ip属地(文件扫描)
     * @param ip IP地址
     * @return 返回地址
     */
    public static String getCityInfoByFile(String ip) {
    
    
        // 1、创建 searcher 对象
        String dbPath = "D:\\gitProject\\workRecord\\src\\main\\resources\\ip\\ip2region.xdb";
        Searcher searcher;
        try {
    
    
            searcher = Searcher.newWithFileOnly(dbPath);
        } catch (IOException e) {
    
    
            log.error("failed to create searcher with `{}`: ", dbPath, e);
            return null;
        }

        // 2、查询
        try {
    
    
            long sTime = System.nanoTime();
            String region = searcher.search(ip);
            long cost = TimeUnit.NANOSECONDS.toMicros(System.nanoTime() - sTime);
            log.info("{region: {}, ioCount: {}, took: {} μs}", region, searcher.getIOCount(), cost);
            return region;
        } catch (Exception e) {
    
    
            log.info("failed to search({}): ", ip, e);
        }
        return null;
		// 3、备注:并发使用,每个线程需要创建一个独立的 searcher 对象单独使用。
    }

    public static void main(String[] args) throws Exception {
    
    
        getCityInfoByFile("111.0.72.130");
    }

output:
insert image description here

5.2.4 Cache the VectorIndex index and convert the user ip address

We can load the VectorIndex data from the xdb file in advance, and then cache it globally. Using the global VectorIndex cache every time a Searcher object is created can reduce a fixed IO operation, thereby speeding up the query and reducing IO pressure.

    /**
     * 获取ip属地(缓存 VectorIndex 索引)
     * @param ip IP地址
     * @return 返回地址
     */
    public static String getCityInfoByVectorIndex(String ip) {
    
    
        String dbPath = "D:\\gitProject\\workRecord\\src\\main\\resources\\ip\\ip2region.xdb";
        // 1、从 dbPath 中预先加载 VectorIndex 缓存,并且把这个得到的数据作为全局变量,后续反复使用。
        byte[] vIndex;
        try {
    
    
            vIndex = Searcher.loadVectorIndexFromFile(dbPath);
        } catch (Exception e) {
    
    
            log.error("failed to load vector index from `{}`: ", dbPath, e);
            return null;
        }

        // 2、使用全局的 vIndex 创建带 VectorIndex 缓存的查询对象。
        Searcher searcher;
        try {
    
    
            searcher = Searcher.newWithVectorIndex(dbPath, vIndex);
        } catch (Exception e) {
    
    
            log.error("failed to create vectorIndex cached searcher with `{}`: ", dbPath, e);
            return null;
        }

        // 3、查询
        try {
    
    
            long sTime = System.nanoTime();
            String region = searcher.search(ip);
            long cost = TimeUnit.NANOSECONDS.toMicros(System.nanoTime() - sTime);
            log.info("{region: {}, ioCount: {}, took: {} μs}\n", region, searcher.getIOCount(), cost);
            return region;
        } catch (Exception e) {
    
    
            log.error("failed to search({}): ", ip, e);
        }
        return null;
		// 备注:每个线程需要单独创建一个独立的 Searcher 对象,但是都共享全局的制度 vIndex 缓存。
    }

    public static void main(String[] args) throws Exception {
    
    
        getCityInfoByVectorIndex("111.0.72.130");
    }

output:
insert image description here

5.2.5 Cache the entire xdb data and convert the user ip address

We can also preload the entire ip2region.xdb data into memory, and then create a query object based on this data to implement a completely file-based query, similar to the previous memory search.

    /**
     * 获取ip属地(缓存整个 xdb 数据)
     * @param ip IP地址
     * @return 返回地址
     */
    public static String getCityInfoByMemorySearch(String ip) {
    
    
        String dbPath = "D:\\gitProject\\workRecord\\src\\main\\resources\\ip\\ip2region.xdb";

        // 1、从 dbPath 加载整个 xdb 到内存。
        byte[] cBuff;
        try {
    
    
            cBuff = Searcher.loadContentFromFile(dbPath);
        } catch (Exception e) {
    
    
            log.error("failed to load content from `{}`: ", dbPath, e);
            return null;
        }

        // 2、使用上述的 cBuff 创建一个完全基于内存的查询对象。
        Searcher searcher;
        try {
    
    
            searcher = Searcher.newWithBuffer(cBuff);
        } catch (Exception e) {
    
    
            log.error("failed to create content cached searcher: ", e);
            return null;
        }

        // 3、查询
        try {
    
    
            long sTime = System.nanoTime();
            String region = searcher.search(ip);
            long cost = TimeUnit.NANOSECONDS.toMicros(System.nanoTime() - sTime);
            log.info("{region: {}, ioCount: {}, took: {} μs}\n", region, searcher.getIOCount(), cost);
        } catch (Exception e) {
    
    
            log.error("failed to search({}): ", ip, e);
        }
        return null;
        // 备注:并发使用,用整个 xdb 数据缓存创建的查询对象可以安全的用于并发,也就是你可以把这个 searcher 对象做成全局对象去跨线程访问。
    }

    public static void main(String[] args) throws Exception {
    
    
        getCityInfoByMemorySearch("111.0.72.130");
    }

output:
insert image description here

6 Summary

This is the end of the introduction to obtain the corresponding attribution according to the IP address. Friends who have questions can discuss it in the comment area

Guess you like

Origin blog.csdn.net/qq_37284798/article/details/130005988