IP地址归属地在线查询平台 您所在的位置:网站首页 在线ip地址所在地查询 IP地址归属地在线查询平台

IP地址归属地在线查询平台

#IP地址归属地在线查询平台| 来源: 网络整理| 查看: 265

一.项目介绍

1.背景

根据IP得到位置,加标签

进行大数据分析,比如淘宝推荐等提供优质数据

www.ip.cn 等 查询IP

2.需求

IP 分析 归属地信息 , 查找在毫秒内完成

IP地址库,公网都是开放的

IANA : 国际组织,负责公网IP维护分发

3.技术栈

Eclipse ,JavaSE中面向对象、IO流、二分法算法、Base64编码、工具类封装

4.目标

通过开发IP地址归属地查询平台,我们需要对JavaSE综合技术有所提升,增强实战能力。学习完该项目我们应该具备如下能力:

1 面向对象程序设计

2 工具类封装与使用写法

3 文件IO流

4 字符串处理

5 二分法查找

6 IP地址的不同形式的使用

二 .相关问题

强化项目思维

不管好方法还是差方法,先尝试把功能实现

再考虑对代码进行优化,最优为止

三 .主要思路

1 解析提供的地址库字符串,为结构化数据格式

2 基于结构化数据构建成某个数据结构,加速给定IP地址的查找速度

3 封装成响应的工具类API,开放其响应的方法,即给定IP地址可以再ms内计算得到其位置信息

4 工具类只有一个入参一个出参

入参是IP

出参是地址

四 .研发项目分类

1 应用开发类项目

解决某些特定功能而开发的应用软件,比如QQ,微信,Eclipse等

2 Web开发类 项目

以B/S架构为主,也就是网页形式访问的在线系统,如各类官网等

3 中小型应用类开发项目的流程

1 需求概述 : 需求描述,说清楚你要干什么,为什么做这个

在互联网公司中,根据IP地址获取归属地信息是非常广泛的,开发的这个项目就能解决这个问题

2 需求分析 : 需要根据需求概述,用技术角度来看一下这个项目是否可行

可行性一定是可以做的

需求分析的梳理

三方面 : 输入,输出,已具备的物料(前置条件)

输入 : 给定任意一个IP地址

输出 : 返回IP对应的归属地

前置条件 :

IP地址库

JavaSE

面向对象

IO

常用类

二分法

正则表达式校验

3 开发步骤拆分,可以理解为解耦,把一个拆分为多个

1 读取IP地址库

2 解析IP地址每行数据,进行结构化

找到无结构化数据的规则,进行结构化处理

简单来说,就是根据需求,生成实体类

Entity/model : 实体类,一般该类和数据库表是一一对应的

DTO : 先不管

Pojo : 无特殊意义,纯粹的业务对象

3 把对象加入List中

4 转换为数组,方便二分法操作

为什么不直接转换为数组?

数组长度问题,不清楚有多少行,有多少空行等

ArrayList转数组,不会消耗很多时间,因为底层就是数组

5 解决二分法查找的技术问题

6 完成比较逻辑

7 对外提供访问接口

8 测试

4 细节开发与风险控制

5 BUG修复,调优,标准化

6 正式上线

7 项目总结,项目复盘

五 .代码研发

 无脑读取文件 在test文件夹下 创建 TestFileIO_01.java package com; import java.io.BufferedReader; import java.io.FileInputStream; import java.io.InputStreamReader; import java.io.Reader; /** * 1 定义文件路径 * * 2 通过节点流对接到文本上 * * 3 将节点流转换为字符流 * * 4 通过缓冲流对接到输入流 * * 5 读取 * * 6 关闭流 */ public class TestFileIO_01 { public static void main(String[] args) { // 地址库文件 String ipLibrartPath = "ip_location_relation.txt"; // 字符编码 String encoding = "utf-8"; try ( // 字节流 FileInputStream fis = new FileInputStream(ipLibrartPath); // 转换为字符流 Reader reader = new InputStreamReader(fis, encoding); // 封装缓冲流 BufferedReader br = new BufferedReader(reader);) { String temp = null; while ((temp = br.readLine()) != null) { System.out.println(temp); } } catch (Exception e) { e.printStackTrace(); } } }

 

2.文本文件读取工具类

工具类封装 :

1 先写测试类,确认输入与输出技术问题

2 抽象出了输入与输出,形成方法的入参和出参

3 工具代码实现,测试

2.1工具类

package com.tledu.zrz.util; import java.io.BufferedReader; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStreamReader; import java.io.Reader; import java.util.ArrayList; import java.util.List; /** * 读取文件 * * @author 天亮教育-帅气多汁你泽哥 * @Date 2021年11月11日 上午11:17:26 */ public class FileOperatorUtil { /** * 读取文件,并返回List * * @param txtFilePath * 字符串格式的文件路径 * @param encoding * 字符编码 * @return * @throws IOException */ public static List getLineList(String txtFilePath, String encoding) throws IOException { List lineLine = new ArrayList(); // 字节流 FileInputStream fis = new FileInputStream(txtFilePath); // 转换为字符流 Reader reader = new InputStreamReader(fis, encoding); // 封装缓冲流 BufferedReader br = new BufferedReader(reader); String temp = null; while ((temp = br.readLine()) != null) { lineLine.add(temp); } // 关闭资源 br.close(); return lineLine; } }

2.1. 测试工具类

package com; import java.io.IOException; import java.util.List; import com.tledu.zrz.util.FileOperatorUtil; /** * 测试工具类 * * @author 天亮教育-帅气多汁你泽哥 * @Date 2021年11月11日 上午11:06:32 */ public class TestFileIO_02 { public static void main(String[] args) throws IOException { // 地址库文件 String ipLibrartPath = "ip_location_relation.txt"; // 字符编码 String encoding = "utf-8"; List lineList = FileOperatorUtil.getLineList(ipLibrartPath, encoding); for (String string : lineList) { System.out.println(string); } } }

 3. 数据结构化

1 可以找无结构化数据的组织规则,进行抽象,转换为结构化

2 根据数据组织进行数据抽象

3 解析数据,保存到对应的对象中

Model,pojo,dto

4. 抽象pojo类

4.1 抽象封装

package com.tledu.zrz.pojo; /** * 结构化实体类 * * @author 天亮教育-帅气多汁你泽哥 * @Date 2021年11月11日 上午11:29:50 */ public class IPAndLocationPojo { @Override public String toString() { return "IPAndLocationPojo [startIP=" + startIP + ", endIP=" + endIP + ", location=" + location + "]"; } /** * 起始IP */ private String startIP; /** * 结束IP */ private String endIP; /** * 归属地 */ private String location; public String getStartIP() { return startIP; } public void setStartIP(String startIP) { this.startIP = startIP; } public String getEndIP() { return endIP; } public void setEndIP(String endIP) { this.endIP = endIP; } public String getLocation() { return location; } public void setLocation(String location) { this.location = location; } public IPAndLocationPojo(String startIP, String endIP, String location) { super(); this.startIP = startIP; this.endIP = endIP; this.location = location; } public IPAndLocationPojo() { super(); } }

4.2 数据拆分,转换为对象

package com; import java.io.IOException; import java.util.ArrayList; import java.util.List; import com.tledu.zrz.pojo.IPAndLocationPojo; import com.tledu.zrz.util.FileOperatorUtil; /** * 转换为结构化对象 * * @author 天亮教育-帅气多汁你泽哥 * @Date 2021年11月11日 上午11:32:39 */ public class TestNonStructToStruct_01 { public static void main(String[] args) throws IOException { // 地址库文件 String ipLibrartPath = "ip_location_relation.txt"; // 字符编码 String encoding = "utf-8"; List lineList = FileOperatorUtil.getLineList(ipLibrartPath, encoding); List ipAndLocationPojos = new ArrayList(); // 遍历 获取每一行 for (String string : lineList) { if (string == null || string.trim().equals("")) { continue; } // 以\t分割,得到三个列 String[] columnArray = string.split("\t"); // 创建结构化对象 IPAndLocationPojo ipAndLocationPojo = new IPAndLocationPojo( columnArray[0], columnArray[1], columnArray[2]); // 保存到集合中 ipAndLocationPojos.add(ipAndLocationPojo); } // 遍历集合 for (IPAndLocationPojo ipAndLocationPojo : ipAndLocationPojos) { System.out.println(ipAndLocationPojo); } } }

5. 封装DataProcessManager类

5.1 把数据封装到对象中并保存list里面

package com.tledu.zrz.manager; import com.tledu.zrz.pojo.IPAndLocationPojo; import com.tledu.zrz.util.FileOperatorUtil; import java.io.IOException; import java.util.ArrayList; import java.util.List; /** * 该类为项目中管理类 * * @author 天亮教育-帅气多汁你泽哥 * @Date 2021年11月11日 上午11:45:19 */ public class DataProcessManager { /** * 把读取到的List 转换为 List * * @param ipLibrartPath * @param encoding * @return * @throws IOException */ public static List getPojoList(String ipLibrartPath, String encoding) throws IOException { List lineList = FileOperatorUtil.getLineList(ipLibrartPath, encoding); List ipAndLocationPojos = new ArrayList(); // 遍历 获取每一行 for (String string : lineList) { if (string == null || string.trim().equals("")) { continue; } // 以\t分割,得到三个列 String[] columnArray = string.split("\t"); // 创建结构化对象 IPAndLocationPojo ipAndLocationPojo = new IPAndLocationPojo( columnArray[0], columnArray[1], columnArray[2]); // 保存到集合中 ipAndLocationPojos.add(ipAndLocationPojo); } return ipAndLocationPojos; } }

5.2 测试

package com; import java.io.IOException; import java.util.ArrayList; import java.util.List; import com.tledu.zrz.manager.DataProcessManager; import com.tledu.zrz.pojo.IPAndLocationPojo; import com.tledu.zrz.util.FileOperatorUtil; /** * 转换为结构化对象 * * @author 天亮教育-帅气多汁你泽哥 * @Date 2021年11月11日 上午11:32:39 */ public class TestNonStructToStruct_02 { public static void main(String[] args) throws IOException { // 地址库文件 String ipLibrartPath = "ip_location_relation.txt"; // 字符编码 String encoding = "utf-8"; List ipAndLocationPojos = DataProcessManager.getPojoList(ipLibrartPath, encoding); for (IPAndLocationPojo ipAndLocationPojo : ipAndLocationPojos) { System.err.println(ipAndLocationPojo); } } }

6. 结构化集合转换为数组

package com; import java.io.IOException; import java.util.ArrayList; import java.util.List; import com.tledu.zrz.manager.DataProcessManager; import com.tledu.zrz.pojo.IPAndLocationPojo; public class TestListToArray_01 { public static void main(String[] args) throws IOException { List list = new ArrayList(); list.add("a"); list.add("b"); list.add("c"); list.add("d"); String[] arr = new String[list.size()]; list.toArray(arr); for (String string : arr) { System.out.println(string); } // 业务数据 // 地址库文件 String ipLibrartPath = "ip_location_relation.txt"; // 字符编码 String encoding = "utf-8"; List ipAndLocationPojos = DataProcessManager .getPojoList(ipLibrartPath, encoding); IPAndLocationPojo[] ipLocationPojoArr = new IPAndLocationPojo[ipAndLocationPojos .size()]; ipAndLocationPojos.toArray(ipLocationPojoArr); for (IPAndLocationPojo ipAndLocationPojo : ipLocationPojoArr) { System.out.println(ipAndLocationPojo); } } }

7. 对象数组排序

7.1 实现排序

1. 自定义实现

冒泡排序

2. API

collections,Arrays

7.2 排序注意事项

被排序的元素必须具有可比性

实现Comparable接口

有比较器类Comparator

7.3 排序案例

package com; import java.util.Arrays; public class TestArrySort_01 { public static void main(String[] args) { // 数字 : 从小到大 // 字符串 : ASCII码 // 日期 : 自然日期 // 因为 Integer,String,Date 都实现了Comparable接口 int[] intArray = { 1, 13, 24, 7, 4 }; Arrays.sort(intArray); for (int i : intArray) { System.out.println(i); } A a1 = new A(18, "张三1"); A a2 = new A(19, "张三2"); A a3 = new A(15, "张三3"); A a4 = new A(12, "张三4"); A[] a = { a1, a2, a3, a4 }; Arrays.sort(a); for (A a5 : a) { System.out.println(a5); } } } class A implements Comparable{ private int age; private String name; public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public A(int age, String name) { super(); this.age = age; this.name = name; } @Override public String toString() { return "A [age=" + age + ", name=" + name + "]"; } @Override public int compareTo(A o) { return age-o.age; } }

7.4 总结

要么实现排序的类 实现Comparable接口

7.5业务问题

由于IP是字符串,而字符串比较ASCII码

比如 1.1.6.32  , 1.1.128.23 理论上应该是后面的大

但是按照ASCII码比较的话,前面的大

所以我们需要把IP转换为long类型进行比较即可

package com.tledu.zrz.util; /** * IP和long类型之间的转换 * * @author 天亮教育-帅气多汁你泽哥 * @Date 2021年11月11日 下午2:51:10 */ public class IPUtil { public static void main(String[] args) { long x = ipToLong("120.211.144.131"); System.out.println(x); String s = longToIp(x); System.out.println(s); } private static Long ipToLong(String ipStr) { long ipLong = 0; if (ipStr != null && ipStr.length() > 0) { // 将ip(点分十进制的形式 a.b.c.d) 地址按.分割 String[] ipSplit = ipStr.split("\\."); try { if (ipSplit.length != 4) { throw new Exception("IP Format Error"); } for (int i = 0; i < ipSplit.length; i++) { int temp = Integer.parseInt(ipSplit[i]); ipLong += temp * (1L >> 24)); ipStr.append("."); // 将高8位设置0,然后右移16位 ipStr.append(((ipLong & 0x00FFFFFF) >>> 16));// 获得高8位,6个f对应的是24个1,最高8位设置空为0,之后右移16位将前面多余的16位去掉,以下类推即可 ipStr.append("."); // 将高16位设置0,然后右移8位 ipStr.append((ipLong & 0x0000FFFF) >>> 8); // 前16位 // 设置0,移除低8位,16个1,高16位设置为0 ipStr.append("."); // 将高24位设置0 ipStr.append(ipLong & 0x000000FF); // 前24位 设置0,留低8位,8个1,高24位设置为0 } catch (Exception e) { e.printStackTrace(); } return ipStr.toString(); } }

7.6 pojo添加衍生字段

实体类中衍生两个字段,用来存储转换为long类型之后的值

package com.tledu.zrz.pojo; import com.tledu.zrz.util.IPUtil; /** * 结构化实体类 * * @author 天亮教育-帅气多汁你泽哥 * @Date 2021年11月11日 上午11:29:50 */ public class IPAndLocationPojo implements Comparable { @Override public String toString() { return "IPAndLocationPojo [startIP=" + startIP + ", endIP=" + endIP + ", location=" + location + "]"; } /** * 起始IP */ private String startIP; // 衍生字段 private long startIPLong; private long endIPLong; /** * 结束IP */ private String endIP; public long getStartIPLong() { return startIPLong; } public void setStartIPLong(long startIPLong) { this.startIPLong = startIPLong; } public long getEndIPLong() { return endIPLong; } public void setEndIPLong(long endIPLong) { this.endIPLong = endIPLong; } /** * 归属地 */ private String location; public String getStartIP() { return startIP; } public void setStartIP(String startIP) { this.startIP = startIP; } public String getEndIP() { return endIP; } public void setEndIP(String endIP) { this.endIP = endIP; } public String getLocation() { return location; } public void setLocation(String location) { this.location = location; } public IPAndLocationPojo(String startIP, String endIP, String location) { super(); this.startIP = startIP; this.endIP = endIP; this.location = location; this.startIPLong = IPUtil.ipToLong(startIP); this.endIPLong = IPUtil.ipToLong(endIP); } public IPAndLocationPojo() { super(); } @Override public int compareTo(IPAndLocationPojo o) { // 因为IP段没有交集,所以使用起始和结束 比较 都是可以的 long result = startIPLong - o.startIPLong; if (result > 0) { return 1; } else if (result < 0) { return -1; } else { return 0; } } }

7.7 测试pojo排序

package com; import java.io.IOException; import java.util.Arrays; import java.util.List; import com.tledu.zrz.manager.DataProcessManager; import com.tledu.zrz.pojo.IPAndLocationPojo; public class TestArrySort_02 { public static void main(String[] args) throws IOException { // 地址库文件 String ipLibrartPath = "ip_location_relation.txt"; // 字符编码 String encoding = "utf-8"; // 获取数据 List ipAndLocationPojos = DataProcessManager .getPojoList(ipLibrartPath, encoding); // 转换为数组 IPAndLocationPojo[] ipLocationPojoArr = new IPAndLocationPojo[ipAndLocationPojos .size()]; ipAndLocationPojos.toArray(ipLocationPojoArr); // 排序 Arrays.sort(ipLocationPojoArr); // 测试首位元素 System.out.println(ipLocationPojoArr[0]); System.out.println(ipLocationPojoArr[ipLocationPojoArr.length-1]); } }

7.8 封装排序

package com.tledu.zrz.manager; import com.tledu.zrz.pojo.IPAndLocationPojo; import com.tledu.zrz.util.FileOperatorUtil; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; /** * 该类为项目中管理类 * * @author 天亮教育-帅气多汁你泽哥 * @Date 2021年11月11日 上午11:45:19 */ public class DataProcessManager { /** * 将对象集合转换为对象数组并排序 * * @param pojoList * @return */ public static IPAndLocationPojo[] convertListToArrayAndSort( List pojoList) { // 转换为数组 IPAndLocationPojo[] ipLocationPojoArr = new IPAndLocationPojo[pojoList .size()]; pojoList.toArray(ipLocationPojoArr); // 排序 Arrays.sort(ipLocationPojoArr); return ipLocationPojoArr; } /** * 把读取到的List 转换为 List * * @param ipLibrartPath * @param encoding * @return * @throws IOException */ public static List getPojoList(String ipLibrartPath, String encoding) throws IOException { List lineList = FileOperatorUtil.getLineList(ipLibrartPath, encoding); List ipAndLocationPojos = new ArrayList(); // 遍历 获取每一行 for (String string : lineList) { if (string == null || string.trim().equals("")) { continue; } // 以\t分割,得到三个列 String[] columnArray = string.split("\t"); // 创建结构化对象 IPAndLocationPojo ipAndLocationPojo = new IPAndLocationPojo( columnArray[0], columnArray[1], columnArray[2]); // 保存到集合中 ipAndLocationPojos.add(ipAndLocationPojo); } return ipAndLocationPojos; } }

7.9 测试

package com; import java.io.IOException; import java.util.Arrays; import java.util.List; import com.tledu.zrz.manager.DataProcessManager; import com.tledu.zrz.pojo.IPAndLocationPojo; public class TestArrySort_03 { public static void main(String[] args) throws IOException { // 地址库文件 String ipLibrartPath = "ip_location_relation.txt"; // 字符编码 String encoding = "utf-8"; // 获取数据 List ipAndLocationPojos = DataProcessManager .getPojoList(ipLibrartPath, encoding); // 转换为数组 IPAndLocationPojo[] ipLocationPojoArr = DataProcessManager.convertListToArrayAndSort(ipAndLocationPojos); // 测试首位元素 System.out.println(ipLocationPojoArr[0]); System.out.println(ipLocationPojoArr[ipLocationPojoArr.length-1]); } }

8. 二分法

二分法查询,必须建立在已排序的基础上

8.1 基本类型

public static void main(String[] args) { // 基础类型 int[] intArray = {1,3,5,7,9,11,100,900}; int aid = 9200; int startIndex = 0; int endIndex = intArray.length-1; int m = (endIndex+startIndex)/2; while (startIndex intArray[m]) { startIndex = m+1; }else{ endIndex = m -1; } m=(startIndex+endIndex)/2; } System.out.println("没找着"); }

8,2 引用/复杂类型

public static void main(String[] args) { User u1 = new User(18); User u2 = new User(19); User u3 = new User(20); User u4 = new User(21); User u5 = new User(22); // 复杂类型 User[] intArray = { u1, u2, u3, u4, u5 }; int aid = 23; int startIndex = 0; int endIndex = intArray.length - 1; int m = (endIndex + startIndex) / 2; while (startIndex intArray[m].getAge()) { startIndex = m + 1; } else { endIndex = m - 1; } m = (startIndex + endIndex) / 2; } System.out.println("没找着"); }

8.3 业务类实现

public static void main(String[] args) throws IOException { // 地址库文件 String ipLibrartPath = "ip_location_relation.txt"; // 字符编码 String encoding = "utf-8"; // 获取数据 List ipAndLocationPojos = DataProcessManager .getPojoList(ipLibrartPath, encoding); // 转换为数组 IPAndLocationPojo[] ipLocationPojoArr = DataProcessManager .convertListToArrayAndSort(ipAndLocationPojos); // 用户输入的IP // String aid = "125.152.1.22"; String aid = "120.211.144.131"; // 转换为long类型 long ipLong = IPUtil.ipToLong(aid); int startIndex = 0; int endIndex = ipLocationPojoArr.length - 1; int m = (endIndex + startIndex) / 2; while (startIndex ipLocationPojoArr[m].getEndIPLong()) { startIndex = m + 1; } else if (ipLong < ipLocationPojoArr[m].getStartIPLong()) { endIndex = m - 1; } else { System.out.println(ipLocationPojoArr[m]); return; } m = (startIndex + endIndex) / 2; } System.out.println("没找着"); }

9. ip地址对象的范围形式数据的二分法实现

9.1 业务类功能实现

package com.tledu.zrz.manager; import com.tledu.zrz.pojo.IPAndLocationPojo; import com.tledu.zrz.util.FileOperatorUtil; import com.tledu.zrz.util.IPUtil; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; /** * 该类为项目中管理类 * * @author 天亮教育-帅气多汁你泽哥 * @Date 2021年11月11日 上午11:45:19 */ public class DataProcessManager { /** * 业务类对应的二分法 * * @param ipLocationPojoArr * 数组 * @param ip * ip * @return 索引 */ public static int binaraySearch(IPAndLocationPojo[] ipLocationPojoArr, String ip) { // 转换为long类型 long ipLong = IPUtil.ipToLong(ip); int startIndex = 0; int endIndex = ipLocationPojoArr.length - 1; int m = (endIndex + startIndex) / 2; while (startIndex ipLocationPojoArr[m].getEndIPLong()) { startIndex = m + 1; } else if (ipLong < ipLocationPojoArr[m].getStartIPLong()) { endIndex = m - 1; } else { return m; } m = (startIndex + endIndex) / 2; } return -1; } /** * 将对象集合转换为对象数组并排序 * * @param pojoList * @return */ public static IPAndLocationPojo[] convertListToArrayAndSort( List pojoList) { // 转换为数组 IPAndLocationPojo[] ipLocationPojoArr = new IPAndLocationPojo[pojoList .size()]; pojoList.toArray(ipLocationPojoArr); // 排序 Arrays.sort(ipLocationPojoArr); return ipLocationPojoArr; } /** * 把读取到的List 转换为 List * * @param ipLibrartPath * @param encoding * @return * @throws IOException */ public static List getPojoList(String ipLibrartPath, String encoding) throws IOException { List lineList = FileOperatorUtil.getLineList(ipLibrartPath, encoding); List ipAndLocationPojos = new ArrayList(); // 遍历 获取每一行 for (String string : lineList) { if (string == null || string.trim().equals("")) { continue; } // 以\t分割,得到三个列 String[] columnArray = string.split("\t"); // 创建结构化对象 IPAndLocationPojo ipAndLocationPojo = new IPAndLocationPojo( columnArray[0], columnArray[1], columnArray[2]); // 保存到集合中 ipAndLocationPojos.add(ipAndLocationPojo); } return ipAndLocationPojos; } }

9.2 测试

package com; import java.io.IOException; import java.util.List; import com.tledu.zrz.manager.DataProcessManager; import com.tledu.zrz.pojo.IPAndLocationPojo; import com.tledu.zrz.util.IPUtil; public class TestBinaraySearch_02 { public static void main(String[] args) throws IOException { // 地址库文件 String ipLibrartPath = "ip_location_relation.txt"; // 字符编码 String encoding = "utf-8"; // 获取数据 List ipAndLocationPojos = DataProcessManager .getPojoList(ipLibrartPath, encoding); // 转换为数组 IPAndLocationPojo[] ipLocationPojoArr = DataProcessManager .convertListToArrayAndSort(ipAndLocationPojos); // 用户输入的IP String ip = "125.152.1.22"; // String ip = "120.211.144.131"; int index = DataProcessManager.binaraySearch(ipLocationPojoArr, ip); System.out.println(ipLocationPojoArr[index]); } }

10.工具类封装

到目前为止,已经把二分法搞定了,已经可以实现功能了,测试代码就相当于客户端

但是目前客户端知道的信息还是比较多,客户只关心 入参和出参是什么就可以

至于我们用什么编码,用什么存储,客户根本不关心

此时我们需要提供一个对外访问的方法,该方法入参就是IP 出参就是归属地

10.1 编码

package com.tledu.zrz.manager; import com.tledu.zrz.pojo.IPAndLocationPojo; import com.tledu.zrz.util.FileOperatorUtil; import com.tledu.zrz.util.IPUtil; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; /** * 该类为项目中管理类 * * @author 天亮教育-帅气多汁你泽哥 * @Date 2021年11月11日 上午11:45:19 */ public class DataProcessManager { /** * 对外提供的接口,入参IP出参地址 * * @param ip * @return */ public static String getLocation(String ip) { // 地址库文件 String ipLibrartPath = "ip_location_relation.txt"; // 字符编码 String encoding = "utf-8"; // 获取数据 List ipAndLocationPojos = null; try { ipAndLocationPojos = DataProcessManager.getPojoList(ipLibrartPath, encoding); } catch (IOException e) { e.printStackTrace(); } // 转换为数组 IPAndLocationPojo[] ipLocationPojoArr = DataProcessManager .convertListToArrayAndSort(ipAndLocationPojos); int index = DataProcessManager.binaraySearch(ipLocationPojoArr, ip); return ipLocationPojoArr[index].getLocation(); } /** * 业务类对应的二分法 * * @param ipLocationPojoArr * 数组 * @param ip * ip * @return 索引 */ private static int binaraySearch(IPAndLocationPojo[] ipLocationPojoArr, String ip) { // 转换为long类型 long ipLong = IPUtil.ipToLong(ip); int startIndex = 0; int endIndex = ipLocationPojoArr.length - 1; int m = (endIndex + startIndex) / 2; while (startIndex ipLocationPojoArr[m].getEndIPLong()) { startIndex = m + 1; } else if (ipLong < ipLocationPojoArr[m].getStartIPLong()) { endIndex = m - 1; } else { return m; } m = (startIndex + endIndex) / 2; } return -1; } /** * 将对象集合转换为对象数组并排序 * * @param pojoList * @return */ private static IPAndLocationPojo[] convertListToArrayAndSort( List pojoList) { // 转换为数组 IPAndLocationPojo[] ipLocationPojoArr = new IPAndLocationPojo[pojoList .size()]; pojoList.toArray(ipLocationPojoArr); // 排序 Arrays.sort(ipLocationPojoArr); return ipLocationPojoArr; } /** * 把读取到的List 转换为 List * * @param ipLibrartPath * @param encoding * @return * @throws IOException */ private static List getPojoList(String ipLibrartPath, String encoding) throws IOException { List lineList = FileOperatorUtil.getLineList(ipLibrartPath, encoding); List ipAndLocationPojos = new ArrayList(); // 遍历 获取每一行 for (String string : lineList) { if (string == null || string.trim().equals("")) { continue; } // 以\t分割,得到三个列 String[] columnArray = string.split("\t"); // 创建结构化对象 IPAndLocationPojo ipAndLocationPojo = new IPAndLocationPojo( columnArray[0], columnArray[1], columnArray[2]); // 保存到集合中 ipAndLocationPojos.add(ipAndLocationPojo); } return ipAndLocationPojos; } }

10.2 测试

package com; import java.io.IOException; import com.tledu.zrz.manager.DataProcessManager; public class TestBinaraySearch_03 { public static void main(String[] args) throws IOException { // String ip = "125.152.1.22"; String ip = "120.211.144.131"; String location = DataProcessManager.getLocation(ip); System.out.println(location); } }

10.3 优化

功能实现了,像以下情况,同一个生命周期中,如果操作两次及两次以上

那么 会导致 ip_location_relation地址库被解析多次,并且结构化多次,并且排序多次

带来的性能变弱

因为在一个生命周期当前,只解析一次,结构化一次,排序一次即可,每次获取归属地的时候,只需要进行二分法操作即可

可以使用静态代码块解决

 

package com.tledu.zrz.manager; import com.tledu.zrz.pojo.IPAndLocationPojo; import com.tledu.zrz.util.FileOperatorUtil; import com.tledu.zrz.util.IPUtil; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; /** * 该类为项目中管理类 * * @author 天亮教育-帅气多汁你泽哥 * @Date 2021年11月11日 上午11:45:19 */ public class DataProcessManager { private static IPAndLocationPojo[] ipLocationPojoArr = null; static { // 地址库文件 String ipLibrartPath = "ip_location_relation.txt"; // 字符编码 String encoding = "utf-8"; // 获取数据 List ipAndLocationPojos = null; try { ipAndLocationPojos = DataProcessManager.getPojoList(ipLibrartPath, encoding); } catch (IOException e) { e.printStackTrace(); } // 转换为数组 ipLocationPojoArr = DataProcessManager .convertListToArrayAndSort(ipAndLocationPojos); } /** * 对外提供的接口,入参IP出参地址 * * @param ip * @return */ public static String getLocation(String ip) { int index = DataProcessManager.binaraySearch(ipLocationPojoArr, ip); return ipLocationPojoArr[index].getLocation(); } /** * 业务类对应的二分法 * * @param ipLocationPojoArr * 数组 * @param ip * ip * @return 索引 */ public static int binaraySearch(IPAndLocationPojo[] ipLocationPojoArr, String ip) { // 转换为long类型 long ipLong = IPUtil.ipToLong(ip); int startIndex = 0; int endIndex = ipLocationPojoArr.length - 1; int m = (endIndex + startIndex) / 2; while (startIndex ipLocationPojoArr[m].getEndIPLong()) { startIndex = m + 1; } else if (ipLong < ipLocationPojoArr[m].getStartIPLong()) { endIndex = m - 1; } else { return m; } m = (startIndex + endIndex) / 2; } return -1; } /** * 将对象集合转换为对象数组并排序 * * @param pojoList * @return */ public static IPAndLocationPojo[] convertListToArrayAndSort( List pojoList) { // 转换为数组 IPAndLocationPojo[] ipLocationPojoArr = new IPAndLocationPojo[pojoList .size()]; pojoList.toArray(ipLocationPojoArr); // 排序 Arrays.sort(ipLocationPojoArr); return ipLocationPojoArr; } /** * 把读取到的List 转换为 List * * @param ipLibrartPath * @param encoding * @return * @throws IOException */ public static List getPojoList(String ipLibrartPath, String encoding) throws IOException { List lineList = FileOperatorUtil.getLineList(ipLibrartPath, encoding); List ipAndLocationPojos = new ArrayList(); // 遍历 获取每一行 for (String string : lineList) { if (string == null || string.trim().equals("")) { continue; } // 以\t分割,得到三个列 String[] columnArray = string.split("\t"); // 创建结构化对象 IPAndLocationPojo ipAndLocationPojo = new IPAndLocationPojo( columnArray[0], columnArray[1], columnArray[2]); // 保存到集合中 ipAndLocationPojos.add(ipAndLocationPojo); } return ipAndLocationPojos; } }

10.4 测试

 

 11. 入口类

package com.tledu.zrz.controller; import java.util.Scanner; import com.tledu.zrz.manager.DataProcessManager; /** * 程序入口 * * @author 天亮教育-帅气多汁你泽哥 * @Date 2021年11月11日 下午4:41:42 */ public class SystemController { public static void main(String[] args) { Scanner scanner = new Scanner(System.in); while (true) { System.out.println("请输入IP : "); String ip = scanner.nextLine(); long startTime = System.currentTimeMillis(); String location = DataProcessManager.getLocation(ip); long endTime = System.currentTimeMillis(); System.out.println(location + " : " + (endTime - startTime)); } } }

12. bug优化

用户输入 需要严格校验,比如IP地址不正确,还有必要去查询吗?

12.1 技术问题

正则表达式 Pattern和Matcher Pattern是正则表达式引擎 Matcher是匹配器 Matches : 全词匹配 Find : 任意位置 lookingAt : 从前往后匹配 package com; import java.util.regex.Matcher; import java.util.regex.Pattern; public class TestRegex_01 { public static void main(String[] args) { String regex = "\\d{11}"; String tel = "131131131121"; // 创建引擎 Pattern pattern = Pattern.compile(regex); // 创建匹配器 Matcher matcher = pattern.matcher(tel); // System.out.println(matcher.matches()); // System.out.println(matcher.find()); System.out.println(matcher.lookingAt()); tel = "我的电话是13113113111"; regex = "我的电话是(\\d{11})"; pattern = Pattern.compile(regex); matcher = pattern.matcher(tel); matcher.find(); System.out.println(matcher.group(1)); } }

12.2 封装工具类

package com.tledu.zrz.util; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * 正则表达式工具类 * * @author 天亮教育-帅气多汁你泽哥 * @Date 2021年11月12日 上午9:37:50 */ public class RegexUtil { /** * 格式验证 * * @param regex * @param input * @return */ public static boolean isValid(String regex, String input) { Pattern pattern = Pattern.compile(regex); // 创建匹配器 Matcher matcher = pattern.matcher(input); return matcher.matches(); } /** * 数据提取 * * @param regex * @param input * @param groupIndex * @return */ public static String getMatchContent(String regex, String input, int groupIndex) { Pattern pattern = Pattern.compile(regex); // 创建匹配器 Matcher matcher = pattern.matcher(input); if (matcher.find()) { return matcher.group(groupIndex); } return null; } public static String getMatchContent(String regex, String input) { return getMatchContent(regex, input, 0); } }

12.3 测试工具类

package com; import com.tledu.zrz.util.RegexUtil; public class TestRegex_02 { public static void main(String[] args) { String regex = "\\d{11}"; String tel = "13113113111"; System.out.println(RegexUtil.isValid(regex, tel)); tel = "我的电话是13113113111"; regex = "我的电话是(\\d{11})"; System.out.println(RegexUtil.getMatchContent(regex, tel,1)); } }

12.4 业务问题

package com.tledu.zrz.util; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * 正则表达式工具类 * * @author 天亮教育-帅气多汁你泽哥 * @Date 2021年11月12日 上午9:37:50 */ public class RegexUtil { private static String regexIP = "((25[0-5]|2[0-4]\\d|[1]{1}\\d{1}\\d{1}|[1-9]{1}\\d{1}|\\d{1})($|(?!\\.$)\\.)){4}"; /** * 校验IP * @param ip * @return */ public static boolean isValidIP(String ip) { return isValid(regexIP, ip); } /** * 格式验证 * * @param regex * @param input * @return */ public static boolean isValid(String regex, String input) { Pattern pattern = Pattern.compile(regex); // 创建匹配器 Matcher matcher = pattern.matcher(input); return matcher.matches(); } /** * 数据提取 * * @param regex * @param input * @param groupIndex * @return */ public static String getMatchContent(String regex, String input, int groupIndex) { Pattern pattern = Pattern.compile(regex); // 创建匹配器 Matcher matcher = pattern.matcher(input); if (matcher.find()) { return matcher.group(groupIndex); } return null; } public static String getMatchContent(String regex, String input) { return getMatchContent(regex, input, 0); } }

12.5 测试

package com; import com.tledu.zrz.util.RegexUtil; public class TestRegex_03 { public static void main(String[] args) { String ip = "1.1.322.5"; System.out.println(RegexUtil.isValidIP(ip)); } }

12.6 应用

校验应该写在对外提供的接口处

getLocation

 13. 性能测试

13.1 相关技术

13.1.1硬件

内存,CPU.磁盘,网络等 都可以实现性能提高

13.1.2 软件

13.1.2.1 直接调优

哪里慢,就调哪里,不需要花里胡哨的

主要指算法问题,内核层面,难度较大,大部分都是非直接调优

13.1.2.2 迂回调优

通过设计,策略,可以通过不用面对底层优化的难题

13.1.2.3 迂回调优方向

缓存策略

通过添加时间断点,测试得出,结构化耗时较多

一开始,我们一次运行中,先后校验两个IP,需要结构化两次

后来我们使用静态语句块解决了这个问题

做到一次生命周期中,只会结构化一次

现在面临的问题是,需要让多次生命周期,使用同一个结构化之后的对象,就可以解决当前的问题

可以使用序列化和反序列化解决

 13.1.3 序列化和反序列化

序列化 : 将内存中对象保存到硬盘中

反序列化 : 把硬盘中对象载入到内存

被序列化的对象必须实现serlizable接口

13.2 首次调优

13.2.1 技术问题

package com; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; public class TestSerinalizable_01 { public static void main(String[] args) throws Exception{ User user =new User(11); ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("D:/x")); oos.writeObject(user); oos.close(); ObjectInputStream ois = new ObjectInputStream(new FileInputStream("D:/x")); Object o = ois.readObject(); System.out.println(o); } } class User implements Serializable{ private int age; public User(int age) { super(); this.age = age; } @Override public String toString() { return "User [age=" + age + "]"; } }

13.2.2 封装工具类

package com.tledu.zrz.util; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; public class SerDeUtil { /** * 写入 * * @param obj * @param objFilePath * @throws IOException */ public static void saveObject(Object obj, String objFilePath) throws IOException { ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream( objFilePath)); oos.writeObject(obj); oos.close(); } /** * 读取 * * @param objFilePath * @return * @throws ClassNotFoundException * @throws IOException */ public static Object getObj(String objFilePath) throws ClassNotFoundException, IOException { ObjectInputStream ois = new ObjectInputStream(new FileInputStream( objFilePath)); Object o = ois.readObject(); return o; } }

13.2.3 测试

package com; import com.tledu.zrz.util.SerDeUtil; public class TestSerinalizable_02 { public static void main(String[] args) throws Exception{ User user =new User(11); SerDeUtil.saveObject(user, "D:/a"); System.out.println(SerDeUtil.getObj("D:/a")); } }

13.2.4 初始化优化

只有第一次才序列化,其余的都反序列化即可

判断该文件是否存在,如果存在,就反序列化获取,如果不存在,就序列化写出

注意

public class IPAndLocationPojo implements Serializable, Comparable { package com.tledu.zrz.manager; import com.tledu.zrz.pojo.IPAndLocationPojo; import com.tledu.zrz.util.FileOperatorUtil; import com.tledu.zrz.util.IPUtil; import com.tledu.zrz.util.RegexUtil; import com.tledu.zrz.util.SerDeUtil; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; /** * 该类为项目中管理类 * * @author 天亮教育-帅气多汁你泽哥 * @Date 2021年11月11日 上午11:45:19 */ public class DataProcessManager { private static IPAndLocationPojo[] ipLocationPojoArr = null; static { // 地址库文件 String ipLibrartPath = "ip_location_relation.txt"; // 字符编码 String encoding = "utf-8"; // 文件地址 String serde_obj_filepath = "ipLibObj.data"; // 判断序列化文件是否存在 boolean isInit = new File(serde_obj_filepath).exists(); // 存在就反序列化获取 if (isInit) { try { Object object = SerDeUtil.getObj(serde_obj_filepath); ipLocationPojoArr = (IPAndLocationPojo[]) object; } catch (ClassNotFoundException | IOException e) { e.printStackTrace(); } }else{ // 不存在就序列化写出 // 获取数据 List ipAndLocationPojos = null; try { ipAndLocationPojos = DataProcessManager.getPojoList(ipLibrartPath, encoding); } catch (IOException e) { e.printStackTrace(); } // 转换为数组 ipLocationPojoArr = DataProcessManager .convertListToArrayAndSort(ipAndLocationPojos); try { SerDeUtil.saveObject(ipLocationPojoArr, serde_obj_filepath); } catch (IOException e) { e.printStackTrace(); } } } /** * 对外提供的接口,入参IP出参地址 * * @param ip * @return */ public static String getLocation(String ip) { if (RegexUtil.isValidIP(ip)) { int index = DataProcessManager.binaraySearch(ipLocationPojoArr, ip); return index == -1 ? "未找到" : ipLocationPojoArr[index].getLocation(); }else{ return "IP地址格式不正确,请重新输入"; } } /** * 业务类对应的二分法 * * @param ipLocationPojoArr * 数组 * @param ip * ip * @return 索引 */ public static int binaraySearch(IPAndLocationPojo[] ipLocationPojoArr, String ip) { // 转换为long类型 long ipLong = IPUtil.ipToLong(ip); int startIndex = 0; int endIndex = ipLocationPojoArr.length - 1; int m = (endIndex + startIndex) / 2; while (startIndex ipLocationPojoArr[m].getEndIPLong()) { startIndex = m + 1; } else if (ipLong < ipLocationPojoArr[m].getStartIPLong()) { endIndex = m - 1; } else { return m; } m = (startIndex + endIndex) / 2; } return -1; } /** * 将对象集合转换为对象数组并排序 * * @param pojoList * @return */ public static IPAndLocationPojo[] convertListToArrayAndSort( List pojoList) { // 转换为数组 IPAndLocationPojo[] ipLocationPojoArr = new IPAndLocationPojo[pojoList .size()]; pojoList.toArray(ipLocationPojoArr); // 排序 Arrays.sort(ipLocationPojoArr); return ipLocationPojoArr; } /** * 把读取到的List 转换为 List * * @param ipLibrartPath * @param encoding * @return * @throws IOException */ public static List getPojoList(String ipLibrartPath, String encoding) throws IOException { List lineList = FileOperatorUtil.getLineList(ipLibrartPath, encoding); List ipAndLocationPojos = new ArrayList(); // 遍历 获取每一行 for (String string : lineList) { if (string == null || string.trim().equals("")) { continue; } // 以\t分割,得到三个列 String[] columnArray = string.split("\t"); // 创建结构化对象 IPAndLocationPojo ipAndLocationPojo = new IPAndLocationPojo( columnArray[0], columnArray[1], columnArray[2]); // 保存到集合中 ipAndLocationPojos.add(ipAndLocationPojo); } return ipAndLocationPojos; } }

13.3 IO调优

加入序列化和反序列化之后,导致效率更低,并且第二个运行要比第一次还慢,说明反序列化有问题

缓冲流 : 缓冲流是IO流的缓冲区,用来提高IO的效率

首次 7000

非首次 20000

文件大小为 38.3M

13.3.1 引入缓冲流

package com.tledu.zrz.util; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; public class SerDeUtil { /** * 对象写出,引入缓冲流 * * @param obj * @param objFilePath * @param cacheByyeArrayLength * @throws IOException */ public static void saveObject(Object obj, String objFilePath, int cacheByyeArrayLength) throws IOException { FileOutputStream fos = new FileOutputStream(objFilePath); // 字节数组缓冲流 ByteArrayOutputStream baos = new ByteArrayOutputStream( cacheByyeArrayLength); ObjectOutputStream oos = new ObjectOutputStream(baos); oos.writeObject(obj); byte[] bytes = baos.toByteArray(); fos.write(bytes); oos.close(); fos.close(); } /** * 获取,引入缓冲流 * * @param objFilePath * @param cacheByyeArrayLength * @return * @throws ClassNotFoundException * @throws IOException */ public static Object getObj(String objFilePath, int cacheByyeArrayLength) throws ClassNotFoundException, IOException { FileInputStream fis = new FileInputStream(objFilePath); byte[] bytes = new byte[cacheByyeArrayLength]; fis.read(bytes); fis.close(); ByteArrayInputStream bais = new ByteArrayInputStream(bytes); ObjectInputStream ois = new ObjectInputStream(bais); Object o = ois.readObject(); ois.close(); return o; } /** * 写入 * * @param obj * @param objFilePath * @throws IOException */ public static void saveObject(Object obj, String objFilePath) throws IOException { ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream( objFilePath)); oos.writeObject(obj); oos.close(); } /** * 读取 * * @param objFilePath * @return * @throws ClassNotFoundException * @throws IOException */ public static Object getObj(String objFilePath) throws ClassNotFoundException, IOException { ObjectInputStream ois = new ObjectInputStream(new FileInputStream( objFilePath)); Object o = ois.readObject(); ois.close(); return o; } }

13.3.2 调用处更改

 

 

更改之后 首次 3000  非首次 1000

13.4 代码标准化

 

代码中,出现了好多这些变量,当我们需要更改某一个的时候,不好找

集中管理

13.4.1 StaticValue

package com.tledu.zrz.util; public class StaticValue { // 地址库文件 public static String ipLibrartPath = "ip_location_relation.txt"; // 字符编码 public static String encoding = "utf-8"; // 文件地址 public static String serde_obj_filepath = "ipLibObj.data"; // 根据文件大小,计算长度 public static int cacheByteArrayLength = 40*1024*1024; }

13.4.2 应用

// 判断序列化文件是否存在 File file = new File(StaticValue.serde_obj_filepath); boolean isInit = file.exists(); // 存在就反序列化获取 if (isInit) { try { Object object = SerDeUtil.getObj( StaticValue.serde_obj_filepath, StaticValue.cacheByteArrayLength); ipLocationPojoArr = (IPAndLocationPojo[]) object; } catch (ClassNotFoundException | IOException e) { e.printStackTrace(); } } else { // 不存在就序列化写出 // 获取数据 List ipAndLocationPojos = null; try { ipAndLocationPojos = DataProcessManager.getPojoList( StaticValue.ipLibrartPath, StaticValue.encoding); } catch (IOException e) { e.printStackTrace(); } // 转换为数组 ipLocationPojoArr = DataProcessManager .convertListToArrayAndSort(ipAndLocationPojos); try { SerDeUtil.saveObject(ipLocationPojoArr, StaticValue.serde_obj_filepath, StaticValue.cacheByteArrayLength); } catch (IOException e) { e.printStackTrace(); } }

14. 调优进阶

14.1.1 IO优化

直接优化 : 提高IO效率

间接优化 : 数据大小也会应该效率问题

文件大小变动

首次2500 非首次 520-600

 

 



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

      专题文章
        CopyRight 2018-2019 实验室设备网 版权所有