我的官方微博:www.weibo.com/vanwelldotcn
我的官方扣群:102934900
笔者现在主要从事移动互联网相关的一些架构工作,并且负责现在公司内部的平台架构的搭建,那么做移动互联网就一定会涉及到移动终端的IP访问记录,并跟据IP进行定向的广告投放。
比如现在通地IP可以直接获取IP所对应的省份,那么数据结构应该如下:
IP | 省份
上面只是笔者模拟的一个表结构,那么具体的IP对应省份,是对终端客户登录时后台DB保存的记录;但是服务器端的IP库,肯定是如下的一个IP段结构
IP开始段 | IP结束段 | 省份
笔者通过H2内存数据库加载了上面的IP库,然后进行了压力测试;结果发现,在压力大的情况下性格会下降的非常厉害,其实笔记收集到的IP库大小只有40多W行数据而已,50M的数据量。
在这种情况下,笔者就通过另一种方法实现了IP库段的查询,性能达到了20W+TPS,只是在奔腾双核的机器下面的测试结果,思路如下:
1.通过TreeMap实现
2.以IP库的起始段为key(IP库的IP地址转化成long型)
3.取出比目标IP小,离的最近的IP对应[key值]
4.然后将查找到的value值和目标IP值进行范围比较
5.符合则对应此IP段,不符合则未查找到IP库对应的范围地址
RangeRow.java
public class RangeRow<T extends Comparable, V> implements Serializable {
private static final long serialVersionUID = -289623726849550889L;
private T begin;
private T end;
private V value;
private Object attachment;
public T getBegin() {
return begin;
}
public void setBegin(T begin) {
this.begin = begin;
}
public T getEnd() {
return end;
}
public void setEnd(T end) {
this.end = end;
}
public V getValue() {
return value;
}
public void setValue(V value) {
this.value = value;
}
public Object getAttachment() {
return attachment;
}
public void setAttachment(Object attachment) {
this.attachment = attachment;
}
}
IpProvinceRow.java
public class IpProvinceRow extends RangeRow<Long, String> {
private static final long serialVersionUID = 8411106544005822554L;
private String city;
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
}
RangeSearchEngine.java
public class RangeSearchEngine<T extends Comparable, V> {
private TreeMap<T, RangeRow<T, V>> cache = new TreeMap<T, RangeRow<T, V>>();
@SuppressWarnings("unchecked")
public V getValue(T key) {
//
RangeRow<T, V> row = getRow(key);
if (row == null) {
return null;
}
//
T end = row.getEnd();
if (end == null) {
return null;
}
if (end.compareTo(key) > 0) {
return row.getValue();
}
return null;
}
public RangeRow<T, V> getRow(T key) {
if (key == null) {
return null;
}
//
Entry<T, RangeRow<T, V>> entry = cache.floorEntry(key);
if (entry == null || entry.getValue() == null) {
return null;
}
//
return entry.getValue();
}
public void putAll(Map<T, RangeRow<T, V>> map) {
cache.putAll(map);
}
public RangeRow<T, V> put(RangeRow<T, V> row) {
return cache.put(row.getBegin(), row);
}
public RangeRow<T, V> remove(T key) {
return cache.remove(key);
}
public void clear() {
cache.clear();
}
public TreeMap<T, RangeRow<T, V>> getCache() {
return cache;
}
public TreeMap<T, RangeRow<T, V>> setCache(TreeMap<T, RangeRow<T, V>> newCache) {
if (newCache == null) {
throw new NullPointerException();
}
//
TreeMap<T, RangeRow<T, V>> before = cache;
cache = newCache;
//
return before;
}
}
IpProvinceSearchEngine.java
public class IpProvinceSearchEngine extends RangeSearchEngine<Long, String> {
public static final String SPLITER = "\\|";
//
private Logger logger = LoggerFactory.getLogger(getClass());
/**
* @Title: load
* @Description: 载入Ip库文件
* @author Administrator
* @param dataFile
* @throws FileNotFoundException
* @throws IOException
* @return void 返回类型
*/
public void load(String dataFile) throws FileNotFoundException, IOException {
TreeMap<Long, RangeRow<Long, String>> map = new TreeMap<Long, RangeRow<Long, String>>();
BufferedReader file = new BufferedReader(new FileReader(dataFile));
try {
//
String content = null;
while ((content = file.readLine()) != null) {
String[] parts = content.split(SPLITER);
if (parts.length < 3) {
logger.warn("parse line failed, " + content);
continue;
}
//
try {
IpProvinceRow row = new IpProvinceRow();
row.setBegin(Long.valueOf(parts[0]));
row.setEnd(Long.valueOf(parts[1]));
row.setValue(parts[2]);
//
if (parts.length > 3) {
row.setCity(parts[3]);
}
//
map.put(row.getBegin(), row);
} catch (Exception e) {
logger.warn("parse row failed:" + content);
}
}
//
setCache(map);
} finally {
try {
file.close();
} catch (IOException e) {
}
}
}
/**
* @Title: getProvince
* @Description: 获取省份信息
* @author Administrator
* @param @param ip
* @param @return 设定文件
* @return String 返回类型
*/
public String getProvince(String ip) {
//
long intIp = convertIpToLong(ip);
//
return getValue(intIp);
}
/**
* @Title: convertIpToLong
* @Description: 转换Ip为256进制整数
* @author Kolor
* @param ip
* @return long
*/
public static long convertIpToLong(String ip) {
String[] checkIp = ip.split("\\.", 4);
long intIp = 0;
for (int i = 3, j = 0; i >= 0 && j <= 3; i--, j++) {
intIp += Integer.parseInt(checkIp[j]) * Math.pow(256, i);
}
return intIp;
}
public static void main(String[] args) throws FileNotFoundException, IOException {
String file = "C:\\Users\\Administrator.PC-20110801LBXQ\\Desktop\\ip\\ip.data";
IpProvinceSearchEngine engine = new IpProvinceSearchEngine();
//
long beginTime = System.currentTimeMillis();
engine.load(file);
long endTime = System.currentTimeMillis();
System.out.println("load cost " + (endTime - beginTime));
//
System.out.println(engine.getProvince("202.101.15.61"));
}
}
附件中是已经收集好的IP库,不能保证最全,但是已经比较全了。:)
希望大家喜欢这个工具类,比较实用;且性能较高,希望有同样数据段查找的数量有限的需求也可以采用类似的方案。