Skip to content

使用Java的文件随机访问功能防止读取整个IP数据库到内存 #30

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions src/main/java/net/ipip/ipdb/BaseStation.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package net.ipip.ipdb;

import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
Expand All @@ -14,7 +15,7 @@ public class BaseStation {
private Reader reader;

public BaseStation(String name) throws IOException,InvalidDatabaseException {
this.reader = new Reader(name);
this.reader = new RandomAccessReader(name);
}

public BaseStation(InputStream in) throws IOException, InvalidDatabaseException {
Expand All @@ -23,8 +24,11 @@ public BaseStation(InputStream in) throws IOException, InvalidDatabaseException

public boolean reload(String name) {
try {
Reader r = new Reader(name);
this.reader = r;
Reader old = this.reader;
this.reader = new RandomAccessReader(name);
if (old instanceof Closeable) {
((Closeable) old).close();
}
} catch (Exception e) {
return false;
}
Expand Down
10 changes: 7 additions & 3 deletions src/main/java/net/ipip/ipdb/City.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package net.ipip.ipdb;

import java.io.InputStream;
import java.io.Closeable;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashMap;
Expand All @@ -14,7 +15,7 @@ public class City {
private Reader reader;

public City(String name) throws IOException,InvalidDatabaseException {
this.reader = new Reader(name);
this.reader = new RandomAccessReader(name);
}

public City(InputStream in) throws IOException,InvalidDatabaseException {
Expand All @@ -23,8 +24,11 @@ public City(InputStream in) throws IOException,InvalidDatabaseException {

public boolean reload(String name) {
try {
Reader r = new Reader(name);
this.reader = r;
Reader old = this.reader;
this.reader = new RandomAccessReader(name);
if (old instanceof Closeable) {
((Closeable) old).close();
}
} catch (Exception e) {
return false;
}
Expand Down
3 changes: 2 additions & 1 deletion src/main/java/net/ipip/ipdb/CityInfo.java
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,8 @@ public DistrictInfo getDistrictInfo() {
if (str == null) {
return null;
}
Map<String,String> info = JSONObject.parseObject(str, Map.class);
@SuppressWarnings("unchecked")
Map<String,String> info = JSONObject.parseObject(str, Map.class);

String[] data = new String[8];
data[0] = this.getCountryName();
Expand Down
10 changes: 7 additions & 3 deletions src/main/java/net/ipip/ipdb/District.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package net.ipip.ipdb;

import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
Expand All @@ -14,7 +15,7 @@ public class District {
private Reader reader;

public District(String name) throws IOException,InvalidDatabaseException {
this.reader = new Reader(name);
this.reader = new RandomAccessReader(name);
}

public District(InputStream in) throws IOException, InvalidDatabaseException {
Expand All @@ -23,8 +24,11 @@ public District(InputStream in) throws IOException, InvalidDatabaseException {

public boolean reload(String name) {
try {
Reader r = new Reader(name);
this.reader = r;
Reader old = this.reader;
this.reader = new RandomAccessReader(name);
if (old instanceof Closeable) {
((Closeable) old).close();
}
} catch (Exception e) {
return false;
}
Expand Down
10 changes: 7 additions & 3 deletions src/main/java/net/ipip/ipdb/IDC.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package net.ipip.ipdb;

import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
Expand All @@ -14,7 +15,7 @@ public class IDC {
private Reader reader;

public IDC(String name) throws IOException,InvalidDatabaseException {
this.reader = new Reader(name);
this.reader = new RandomAccessReader(name);
}

public IDC(InputStream in) throws IOException, InvalidDatabaseException {
Expand All @@ -23,8 +24,11 @@ public IDC(InputStream in) throws IOException, InvalidDatabaseException {

public boolean reload(String name) {
try {
Reader r = new Reader(name);
this.reader = r;
Reader old = this.reader;
this.reader = new RandomAccessReader(name);
if (old instanceof Closeable) {
((Closeable) old).close();
}
} catch (Exception e) {
return false;
}
Expand Down
5 changes: 4 additions & 1 deletion src/main/java/net/ipip/ipdb/IPFormatException.java
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
package net.ipip.ipdb;


public class IPFormatException extends Exception {

public IPFormatException(String name) {
super(name);
}

public IPFormatException(Throwable cause) {
super(cause);
}
}
4 changes: 4 additions & 0 deletions src/main/java/net/ipip/ipdb/InvalidDatabaseException.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,8 @@ public class InvalidDatabaseException extends IOException {
public InvalidDatabaseException(String message) {
super(message);
}

public InvalidDatabaseException(Throwable cause) {
super(cause);
}
}
127 changes: 127 additions & 0 deletions src/main/java/net/ipip/ipdb/RandomAccessReader.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
package net.ipip.ipdb;

import java.io.*;
import java.lang.reflect.Field;
import java.nio.charset.StandardCharsets;

import com.alibaba.fastjson.JSONObject;

import sun.misc.Unsafe;

/**
* @author mckuhei
* */
public class RandomAccessReader extends Reader implements Closeable {

private static final Unsafe U = getUnsafe();
private RandomAccessFile handle;
private long baseOffset;

public RandomAccessReader(String file) throws IOException, InvalidDatabaseException {
this(new File(file));
}

public RandomAccessReader(File file) throws IOException, InvalidDatabaseException {
try {
this.fileSize = (int) file.length();
RandomAccessFile handle = this.handle = new RandomAccessFile(file, "r");
int metaLength = handle.readInt();
byte[] metaBytes = new byte[metaLength];
handle.readFully(metaBytes);

String metaString = new String(metaBytes);

MetaData meta = JSONObject.parseObject(metaString, MetaData.class);
this.nodeCount = meta.nodeCount;
this.meta = meta;

this.baseOffset = handle.getFilePointer();

/** for ipv4 */
if (0x01 == (this.meta.IPVersion & 0x01))
{
int node = 0;
for (int i = 0; i < 96 && node < this.nodeCount; i++) {
if (i >= 80) {
node = this.readNode(node, 1);
} else {
node = this.readNode(node, 0);
}
}

this.v4offset = node;
}
} catch (FileNotFoundException e) {
throw e;
} catch (IOException e) {
throw new InvalidDatabaseException(e);
}
}

@Override
int readNode(int node, int index) {
int off = node * 8 + index * 4;
RandomAccessFile handle = this.handle;
try {
synchronized (handle) {
handle.seek(this.baseOffset + off);
return handle.readInt();
}
} catch (IOException e) {
U.throwException(e);
}
throw new InternalError("Should not reach here");
}

@Override
String resolve(int node) throws InvalidDatabaseException {
final int resoloved = node - this.nodeCount + this.nodeCount * 8;
if (resoloved >= this.fileSize) {
throw new InvalidDatabaseException("database resolve error");
}

RandomAccessFile handle = this.handle;

try {
byte[] data;
synchronized (handle) {
handle.seek(this.baseOffset + resoloved);
int size = handle.readUnsignedShort();

if (this.fileSize < (resoloved + 2 + size)) {
throw new InvalidDatabaseException("database resolve error");
}

data = new byte[size];
handle.readFully(data);
}
return new String(data, StandardCharsets.UTF_8);
} catch (IOException e) {
U.throwException(e);
}
throw new InternalError("Should not reach here");
}

private static Unsafe getUnsafe() {
try {
Field f = Unsafe.class.getDeclaredField("theUnsafe");
f.setAccessible(true);
return (Unsafe) f.get(null);
} catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) {
throw new InternalError("Unable to get unsafe", e);
}
}

@Override
protected void finalize() {
close();
}

public void close() {
try {
this.handle.close();
} catch (IOException e) {
U.throwException(e);
}
}
}
Loading