From 4fde2363fa8079f6e5e158e34a55357785cd53cb Mon Sep 17 00:00:00 2001 From: Wu Jian Ping Date: Thu, 21 Jul 2022 22:31:44 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9F=A5=E8=AF=A2=E8=BF=94=E5=9B=9E=E5=AF=B9?= =?UTF-8?q?=E8=B1=A1=E5=A2=9E=E5=8A=A0took(us)=E5=92=8CioCount?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- binding/nodejs/ReadMe.md | 23 +++++++++++++++++++++++ binding/nodejs/index.js | 24 +++++++++++++++++------- binding/nodejs/tests/function.test.js | 9 ++++++--- binding/nodejs/tests/test.app.js | 0 4 files changed, 46 insertions(+), 10 deletions(-) create mode 100644 binding/nodejs/tests/test.app.js diff --git a/binding/nodejs/ReadMe.md b/binding/nodejs/ReadMe.md index bfa0955..3402ecb 100644 --- a/binding/nodejs/ReadMe.md +++ b/binding/nodejs/ReadMe.md @@ -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 测试 ## 单元测试结果 diff --git a/binding/nodejs/index.js b/binding/nodejs/index.js index 0d4f562..85e24ac 100644 --- a/binding/nodejs/index.js +++ b/binding/nodejs/index.js @@ -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 getBuffer = Symbol('#getBuffer') const openFilePromise = Symbol('#openFilePromise') +const NS_PER_SEC = 1e9 class Searcher { constructor (dbFile, vectorIndex, buffer) { @@ -27,7 +28,7 @@ class Searcher { } } - async [getStartEndPtr] (idx, fd) { + async [getStartEndPtr] (idx, fd, ioStatus) { if (this._vectorIndex) { // 区域二分索引的开始地址 // idx 开始读取 4 个字节,用小端字节序解码得到一个整数 @@ -37,20 +38,21 @@ class Searcher { const ePtr = this._vectorIndex.readUInt32LE(idx + 4) return { sPtr, ePtr } } 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 ePtr = buf.readUInt32LE(4) return { sPtr, ePtr } } } - async [getBuffer] (offset, length, fd) { + async [getBuffer] (offset, length, fd, ioStatus) { if (this._buffer) { return this._buffer.subarray(offset, offset + length) } else { // 从文件中读取 const buf = Buffer.alloc(length) return new Promise((resolve, reject) => { + ioStatus.ioCount += 1 fs.read(fd, buf, 0, length, offset, (err) => { if (err) { reject(err) @@ -76,6 +78,11 @@ class Searcher { } async search (ip) { + const startTime = process.hrtime() + const ioStatus = { + ioCount: 0 + } + if (!IP_REGEX.test(ip)) { throw new Error(`IP: ${ip} is invalid`) } @@ -104,7 +111,7 @@ class Searcher { 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 @@ -126,7 +133,7 @@ class Searcher { // 从 p 位置开始读取 SegmentIndexSize = 14 个字节到 buff // 得到一个完整的上述描述的二分索引项,不过为了减少不必要的操作 // 我们是按需要解码,此处 buff 为 p 开始的 14 个 byte 的数据 - const buff = await this[getBuffer](p, SegmentIndexSize, fd) + const buff = await this[getBuffer](p, SegmentIndexSize, fd, ioStatus) // 前面 4 个字节是起始 IP const sip = buff.readUInt32LE(0) @@ -149,7 +156,7 @@ class Searcher { const dataLen = buff.readUInt16LE(8) // 10 ~ 13 的 4 个字节是地域数据的地址 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') break } @@ -163,7 +170,10 @@ class Searcher { 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 } } } diff --git a/binding/nodejs/tests/function.test.js b/binding/nodejs/tests/function.test.js index 0219ce7..87063b7 100644 --- a/binding/nodejs/tests/function.test.js +++ b/binding/nodejs/tests/function.test.js @@ -14,16 +14,19 @@ const searcher3 = Searcher.newWithFileOnly(dbPath) describe('ip2region', () => { it('#newWithFileOnly and search', async () => { 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 () => { 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 () => { const d = await searcher1.search('218.4.167.70') - expect(d).equal('中国|0|江苏省|苏州市|电信') + + expect(d.region).equal('中国|0|江苏省|苏州市|电信') }) }) diff --git a/binding/nodejs/tests/test.app.js b/binding/nodejs/tests/test.app.js new file mode 100644 index 0000000..e69de29