查询返回对象增加took(us)和ioCount
This commit is contained in:
parent
743bbd204b
commit
4fde2363fa
|
|
@ -66,6 +66,29 @@ try {
|
||||||
|
|
||||||
## 查询测试
|
## 查询测试
|
||||||
|
|
||||||
|
可以通过 `java -jar ip2region-{version}.jar search` 命令来测试查询:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
➜ java git:(v2.0_xdb) ✗ java -jar target/ip2region-2.6.0.jar search
|
||||||
|
java -jar ip2region-{version}.jar search [command options]
|
||||||
|
options:
|
||||||
|
--db string ip2region binary xdb file path
|
||||||
|
--cache-policy string cache policy: file/vectorIndex/content
|
||||||
|
```
|
||||||
|
|
||||||
|
例如:使用默认的 data/ip2region.xdb 文件进行查询测试:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
➜ java git:(v2.0_xdb) ✗ java -jar target/ip2region-2.6.0.jar search --db=../../data/ip2region.xdb
|
||||||
|
ip2region xdb searcher test program, cachePolicy: vectorIndex
|
||||||
|
type 'quit' to exit
|
||||||
|
ip2region>> 1.2.3.4
|
||||||
|
{region: 美国|0|华盛顿|0|谷歌, ioCount: 7, took: 82 μs}
|
||||||
|
ip2region>>
|
||||||
|
```
|
||||||
|
|
||||||
|
输入 ip 即可进行查询测试,也可以分别设置 `cache-policy` 为 file/vectorIndex/content 来测试三种不同缓存实现的查询效果。
|
||||||
|
|
||||||
## bench 测试
|
## bench 测试
|
||||||
|
|
||||||
## 单元测试结果
|
## 单元测试结果
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ const IP_REGEX = /((25[0-5]|2[0-4]\d|((1\d{2})|([1-9]?\d)))\.){3}(25[0-5]|2[0-4]
|
||||||
const getStartEndPtr = Symbol('#getStartEndPtr')
|
const getStartEndPtr = Symbol('#getStartEndPtr')
|
||||||
const getBuffer = Symbol('#getBuffer')
|
const getBuffer = Symbol('#getBuffer')
|
||||||
const openFilePromise = Symbol('#openFilePromise')
|
const openFilePromise = Symbol('#openFilePromise')
|
||||||
|
const NS_PER_SEC = 1e9
|
||||||
|
|
||||||
class Searcher {
|
class Searcher {
|
||||||
constructor (dbFile, vectorIndex, buffer) {
|
constructor (dbFile, vectorIndex, buffer) {
|
||||||
|
|
@ -27,7 +28,7 @@ class Searcher {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async [getStartEndPtr] (idx, fd) {
|
async [getStartEndPtr] (idx, fd, ioStatus) {
|
||||||
if (this._vectorIndex) {
|
if (this._vectorIndex) {
|
||||||
// 区域二分索引的开始地址
|
// 区域二分索引的开始地址
|
||||||
// idx 开始读取 4 个字节,用小端字节序解码得到一个整数
|
// idx 开始读取 4 个字节,用小端字节序解码得到一个整数
|
||||||
|
|
@ -37,20 +38,21 @@ class Searcher {
|
||||||
const ePtr = this._vectorIndex.readUInt32LE(idx + 4)
|
const ePtr = this._vectorIndex.readUInt32LE(idx + 4)
|
||||||
return { sPtr, ePtr }
|
return { sPtr, ePtr }
|
||||||
} else {
|
} else {
|
||||||
const buf = await this[getBuffer](256 + idx, 8, fd)
|
const buf = await this[getBuffer](256 + idx, 8, fd, ioStatus)
|
||||||
const sPtr = buf.readUInt32LE()
|
const sPtr = buf.readUInt32LE()
|
||||||
const ePtr = buf.readUInt32LE(4)
|
const ePtr = buf.readUInt32LE(4)
|
||||||
return { sPtr, ePtr }
|
return { sPtr, ePtr }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async [getBuffer] (offset, length, fd) {
|
async [getBuffer] (offset, length, fd, ioStatus) {
|
||||||
if (this._buffer) {
|
if (this._buffer) {
|
||||||
return this._buffer.subarray(offset, offset + length)
|
return this._buffer.subarray(offset, offset + length)
|
||||||
} else {
|
} else {
|
||||||
// 从文件中读取
|
// 从文件中读取
|
||||||
const buf = Buffer.alloc(length)
|
const buf = Buffer.alloc(length)
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
|
ioStatus.ioCount += 1
|
||||||
fs.read(fd, buf, 0, length, offset, (err) => {
|
fs.read(fd, buf, 0, length, offset, (err) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
reject(err)
|
reject(err)
|
||||||
|
|
@ -76,6 +78,11 @@ class Searcher {
|
||||||
}
|
}
|
||||||
|
|
||||||
async search (ip) {
|
async search (ip) {
|
||||||
|
const startTime = process.hrtime()
|
||||||
|
const ioStatus = {
|
||||||
|
ioCount: 0
|
||||||
|
}
|
||||||
|
|
||||||
if (!IP_REGEX.test(ip)) {
|
if (!IP_REGEX.test(ip)) {
|
||||||
throw new Error(`IP: ${ip} is invalid`)
|
throw new Error(`IP: ${ip} is invalid`)
|
||||||
}
|
}
|
||||||
|
|
@ -104,7 +111,7 @@ class Searcher {
|
||||||
const idx = i0 * VectorIndexCols * VectorIndexSize + i1 * VectorIndexSize
|
const idx = i0 * VectorIndexCols * VectorIndexSize + i1 * VectorIndexSize
|
||||||
|
|
||||||
// 区域二分索引的开始地址和结束地址
|
// 区域二分索引的开始地址和结束地址
|
||||||
const { sPtr, ePtr } = await this[getStartEndPtr](idx, fd)
|
const { sPtr, ePtr } = await this[getStartEndPtr](idx, fd, ioStatus)
|
||||||
|
|
||||||
// 二分搜索低位
|
// 二分搜索低位
|
||||||
let l = 0
|
let l = 0
|
||||||
|
|
@ -126,7 +133,7 @@ class Searcher {
|
||||||
// 从 p 位置开始读取 SegmentIndexSize = 14 个字节到 buff
|
// 从 p 位置开始读取 SegmentIndexSize = 14 个字节到 buff
|
||||||
// 得到一个完整的上述描述的二分索引项,不过为了减少不必要的操作
|
// 得到一个完整的上述描述的二分索引项,不过为了减少不必要的操作
|
||||||
// 我们是按需要解码,此处 buff 为 p 开始的 14 个 byte 的数据
|
// 我们是按需要解码,此处 buff 为 p 开始的 14 个 byte 的数据
|
||||||
const buff = await this[getBuffer](p, SegmentIndexSize, fd)
|
const buff = await this[getBuffer](p, SegmentIndexSize, fd, ioStatus)
|
||||||
|
|
||||||
// 前面 4 个字节是起始 IP
|
// 前面 4 个字节是起始 IP
|
||||||
const sip = buff.readUInt32LE(0)
|
const sip = buff.readUInt32LE(0)
|
||||||
|
|
@ -149,7 +156,7 @@ class Searcher {
|
||||||
const dataLen = buff.readUInt16LE(8)
|
const dataLen = buff.readUInt16LE(8)
|
||||||
// 10 ~ 13 的 4 个字节是地域数据的地址
|
// 10 ~ 13 的 4 个字节是地域数据的地址
|
||||||
const dataPtr = buff.readUInt32LE(10)
|
const dataPtr = buff.readUInt32LE(10)
|
||||||
const data = await this[getBuffer](dataPtr, dataLen, fd)
|
const data = await this[getBuffer](dataPtr, dataLen, fd, ioStatus)
|
||||||
result = data.toString('utf-8')
|
result = data.toString('utf-8')
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
@ -163,7 +170,10 @@ class Searcher {
|
||||||
fs.close(fd)
|
fs.close(fd)
|
||||||
}
|
}
|
||||||
|
|
||||||
return result
|
const diff = process.hrtime(startTime)
|
||||||
|
|
||||||
|
const took = (diff[0] * NS_PER_SEC + diff[1]) / 1e6
|
||||||
|
return { region: result, ioCount: ioStatus.ioCount, took }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,16 +14,19 @@ const searcher3 = Searcher.newWithFileOnly(dbPath)
|
||||||
describe('ip2region', () => {
|
describe('ip2region', () => {
|
||||||
it('#newWithFileOnly and search', async () => {
|
it('#newWithFileOnly and search', async () => {
|
||||||
const d = await searcher3.search('218.4.167.70')
|
const d = await searcher3.search('218.4.167.70')
|
||||||
expect(d).equal('中国|0|江苏省|苏州市|电信')
|
console.log(d)
|
||||||
|
expect(d.region).equal('中国|0|江苏省|苏州市|电信')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('#newWithVectorIndex and search', async () => {
|
it('#newWithVectorIndex and search', async () => {
|
||||||
const d = await searcher2.search('218.4.167.70')
|
const d = await searcher2.search('218.4.167.70')
|
||||||
expect(d).equal('中国|0|江苏省|苏州市|电信')
|
console.log(d)
|
||||||
|
expect(d.region).equal('中国|0|江苏省|苏州市|电信')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('#newWithBuffer and search', async () => {
|
it('#newWithBuffer and search', async () => {
|
||||||
const d = await searcher1.search('218.4.167.70')
|
const d = await searcher1.search('218.4.167.70')
|
||||||
expect(d).equal('中国|0|江苏省|苏州市|电信')
|
|
||||||
|
expect(d.region).equal('中国|0|江苏省|苏州市|电信')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue