前段时间 有个统计的需求,有用户的登陆IP,想转成市,然后用于统计。几百万的数据拿来网上查还是慢了些,于是就找一些批量,或者本地的处理方法
有两个方案是备选的:
一、
这边的一个同事提供了一个文件(不清楚哪里搞来的),里面有ipbegin-ipEnd 地市,省 这些字段,用了10来个ip 测试,还是靠谱,格式如下
1551@@1.189.24.0@@1.189.24.255@@黑龙江省哈尔滨市通河县@@联通@@黑龙江省@@哈尔滨市@@HL
1561@@1.189.247.0@@1.189.248.255@@黑龙江省哈尔滨市延寿县@@联通@@黑龙江省@@哈尔滨市@@HL
1571@@1.189.249.0@@1.189.250.255@@黑龙江省哈尔滨市双城区@@联通@@黑龙江省@@哈尔滨市@@HL
1581@@1.189.25.0@@1.189.30.255@@黑龙江省哈尔滨市@@联通@@黑龙江省@@哈尔滨市@@HL
1591@@1.189.251.0@@1.189.254.255@@黑龙江省哈尔滨市@@联通@@黑龙江省@@哈尔滨市@@HL
二、
http://www.ipip.net这个地址,有ip库下载,也提供了各语言的api例子.
后来考虑了下,第一个方案,数据都是现成的,比较有安全感,就选择自己去解析。
具体处理
整个文件有40W行数据,用循环查找是太慢了,考虑到ip有大小之分,于是决定用树.
设计思路
一条数据一个节点,利用大小比较, 构建成平衡树,查找的时候,一个 ip,如果小于ip_begin,则往一个方向,如果大于ip_end 则另一方向,如果在begin和end中间,则此节点为目标节点.
程序设计
java里面有treeMap,把节点扔进去,其自己会构建成一棵树,当然,此节点要实现 compareTo
的方法,把节点和ip的大小比较出来.
除了ip_begin,和ip_end , 此entity 上也加上 targetip用于通过ip取节点时的比较
具体代码
一个节点,包含,ip_begin和ip_end ,ip进来 的时候根据在区间的左中右,返回-1,0,1,具体做法是,TipRange 类写了这个节点, 重写 compareTo 方法,使用的时候,把ip区域列表形成一个treeMap,,在取区域时,ip拿到TipRange,具体请看下面核心两个类
- TipRange
compareTo方法是关键
public class TipRange implements Comparable<TipRange> {
private Integer id;
// ipBegin
private String ipBegin;
// ipEnd
private String ipEnd;
private String regiondesc;
private String typedesc;
private String province;
private String city;
private String provinceCode;
private Date updatetime;
private String targetip;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getIpBegin() {
return ipBegin;
}
public void setIpBegin(String ipBegin) {
this.ipBegin = ipBegin;
}
public String getIpEnd() {
return ipEnd;
}
public void setIpEnd(String ipEnd) {
this.ipEnd = ipEnd;
}
public String getRegiondesc() {
return regiondesc;
}
public void setRegiondesc(String regiondesc) {
this.regiondesc = regiondesc;
}
public String getTypedesc() {
return typedesc;
}
public void setTypedesc(String typedesc) {
this.typedesc = typedesc;
}
public String getProvince() {
return province;
}
public void setProvince(String province) {
this.province = province;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getProvinceCode() {
return provinceCode;
}
public void setProvinceCode(String provinceCode) {
this.provinceCode = provinceCode;
}
public Date getUpdatetime() {
return updatetime;
}
public void setUpdatetime(Date updatetime) {
this.updatetime = updatetime;
}
public String getTargetip() {
return targetip;
}
public void setTargetip(String targetip) {
this.targetip = targetip;
}
@Override
public int compareTo(TipRange targetRange) {
String paramIp = targetRange.getTargetip();
if(StringUtils.isEmpty(paramIp)&&StringUtils.isEmpty(this.getTargetip())){
//两个都没ip为空,说明range和range对比
long currtntipbeginLong = IpRangeUtil.ipStrToLong(ipBegin);
long currentipendLong = IpRangeUtil.ipStrToLong(ipEnd);
long rangeBegin = IpRangeUtil.ipStrToLong(targetRange.getIpBegin());
if(rangeBegin<currtntipbeginLong){
return 1;
}else if (rangeBegin==currtntipbeginLong){
return 0;
}else{
return -1;
}
}
if(StringUtils.isNotEmpty(paramIp)&&StringUtils.isEmpty(this.getTargetip())){
两个都没ip为空,说明range和range对比
long targetIpLong = IpRangeUtil.ipStrToLong(paramIp);
long currtntipbeginLong = IpRangeUtil.ipStrToLong(ipBegin);
long currentipendLong = IpRangeUtil.ipStrToLong(ipEnd);
//判断目标要往哪走
if(targetIpLong<currtntipbeginLong){
return 1;
}else if (targetIpLong>currentipendLong){
return -1;
}else{
return 0;
}
}
if(StringUtils.isEmpty(paramIp)&&StringUtils.isNotEmpty(this.getTargetip())){
//两个都没ip为空,说明range和range对比
long currentIpLong = IpRangeUtil.ipStrToLong(this.getTargetip());
long targetBeginLong = IpRangeUtil.ipStrToLong(targetRange.getIpBegin());
long targetEndLong = IpRangeUtil.ipStrToLong(targetRange.getIpEnd());
//判断目标要往哪走
if(currentIpLong<targetBeginLong){
return -1;
}else if (currentIpLong>targetEndLong){
return 1;
}else{
return 0;
}
}
return -2;
}
}
- TgipRangeServiceImpl
取数据,扔到treemap,其 ipToAddress 方法即为 暴露的api
import com.yp.springboot.util.FileReadUtils;
import com.yp.springboot.util.IFileOperByLine;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import java.io.File;
import java.sql.Date;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
@Service
public class TgipRangeServiceImpl {
private static Map<TipRange, TipRange> cacheIpRangeMap;
@Autowired
public JdbcTemplate jdbcTemplate;
private static Logger logger = LoggerFactory.getLogger("rlog");
public List<TipRange> queryAllByFile() throws Exception{
File file = new File("E:\\codeplace\\n_learn\\java\\springboot\\myexample\\springbootweb\\src\\main\\resources\\regionip.txt");//
final List<TipRange> iprangeList1 = new ArrayList();
FileReadUtils.readFileAndUnionObj(file, new IFileOperByLine() {
@Override
public void toOperLineNoList(String fileName, String linestr) {
String[] arrayText = linestr.split("@@");
Integer id = Integer.valueOf(arrayText[0]);
String ipBegin = arrayText[1];
String ipEnd = arrayText[2];
String regiondesc = arrayText[3];
String typedesc = arrayText[4];
String province = arrayText[5];
String city = arrayText[6];
String provinceCode = arrayText[7];
TipRange tmp = new TipRange();
tmp.setId(id);
tmp.setIpBegin(ipBegin);
tmp.setIpEnd(ipEnd);
tmp.setRegiondesc(regiondesc);
tmp.setTypedesc(typedesc);
tmp.setProvince(province);
tmp.setCity(city);
tmp.setProvinceCode(provinceCode);
//处理
iprangeList1.add(tmp);
}
});
return iprangeList1;
}
/**
*
* @return
*/
@Transactional(propagation = Propagation.NOT_SUPPORTED)
protected Map<TipRange, TipRange> genIpRangeToLogAllMap() throws Exception {
Map<TipRange, TipRange> allMap = new TreeMap<TipRange, TipRange>();
List<TipRange> allList = queryAllByFile();
for (TipRange entityRange : allList) {
allMap.put(entityRange, entityRange);
}
return allMap;
}
private String toTrim(String sstr){
if(sstr==null)return "";
return sstr.trim();
}
@Transactional(propagation = Propagation.NOT_SUPPORTED)
private synchronized void initMap() throws Exception {
cacheIpRangeMap = genIpRangeToLogAllMap();
}
/*
*/
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public TipRange ipToAddress(String ip) throws Exception {
synchronized (this) {
if (cacheIpRangeMap == null) {
initMap();
}
}
TipRange tmp = new TipRange();
tmp.setTargetip(ip);
return cacheIpRangeMap.get(tmp);
}
}
说明:
代码 :https://github.com/huawumingguo/springbootsample/tree/master/springbootweb