diff --git a/binding/nodejs/ReadMe.md b/binding/nodejs/ReadMe.md index e824884..d49a6d3 100644 --- a/binding/nodejs/ReadMe.md +++ b/binding/nodejs/ReadMe.md @@ -99,20 +99,57 @@ ip2region>> ## bench 测试 -## 单元测试结果 +```shell +➜ nodejs git:(v2.0-for-nodejs) ✗ node ./tests/bench.app.js --help +usage: Usage node test.app.js [command options] + +ip2region benchmark app + +optional arguments: + -h, --help show this help message and exit + --db DB ip2region binary xdb file path, default: ../../data/ip2region.xdb + --src SRC source ip text file path, default: ../../data/ip.merge.txt + --cache-policy CACHE_POLICY + cache policy: file/vectorIndex/content, default: content + +``` + +例如:通过默认的 data/ip2region.xdb 和 data/ip.merge.txt 文件进行 bench 测试: ```shell -ip2region - ✔ #newWithFileOnly - ✔ #newWithVectorIndex - ✔ #newWithBuffer +➜ nodejs git:(v2.0-for-nodejs) ✗ node ./tests/bench.app.js +options: + dbPath: ../../data/ip2region.xdb + src: ../../data/ip2region.xdb + cache-policy: content -3 passing (10ms) +Bench finished, {cachePolicy: content, total: 683591, took: 5.018973507s, cost: 0.007342070780627597μs/op} +``` + +可以通过分别设置 `cache-policy` 为 file/vectorIndex/content 来测试三种不同缓存实现的效果。 +@Note: 注意 bench 使用的 src 文件要是生成对应 xdb 文件相同的源文件。 + +## 单元测试及覆盖率结果 + +```shell +➜ nodejs git:(v2.0-for-nodejs) ✗ npm run coverage + +... + + ip2region + ✔ #newWithFileOnly and search + ✔ #newWithVectorIndex and search + ✔ #newWithBuffer and search + + + 3 passing (6ms) ----------|---------|----------|---------|---------|---------------------------------- -File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s +File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s ----------|---------|----------|---------|---------|---------------------------------- -All files | 91.17 | 60.71 | 100 | 91.17 | - index.js | 91.17 | 60.71 | 100 | 91.17 | 56,70,80,137,143,173,179,193,201 +All files | 91.58 | 60.71 | 100 | 91.58 | + index.js | 91.58 | 60.71 | 100 | 91.58 | 61,75,90,146,152,187,193,207,215 ----------|---------|----------|---------|---------|---------------------------------- ``` + +Made with ♥ by Wu Jian Ping diff --git a/binding/nodejs/index.js b/binding/nodejs/index.js index 85e24ac..ee076a9 100644 --- a/binding/nodejs/index.js +++ b/binding/nodejs/index.js @@ -1,3 +1,7 @@ +/* + * Created by Wu Jian Ping on - 2022/07/22. + */ + const fs = require('fs') // 常量定义 @@ -15,7 +19,6 @@ 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) { @@ -172,7 +175,7 @@ class Searcher { const diff = process.hrtime(startTime) - const took = (diff[0] * NS_PER_SEC + diff[1]) / 1e6 + const took = (diff[0] * 1e9 + diff[1]) / 1e6 return { region: result, ioCount: ioStatus.ioCount, took } } } diff --git a/binding/nodejs/package-lock.json b/binding/nodejs/package-lock.json index 8d5b21d..bfd3025 100644 --- a/binding/nodejs/package-lock.json +++ b/binding/nodejs/package-lock.json @@ -1954,6 +1954,12 @@ "type-check": "~0.4.0" } }, + "linebyline": { + "version": "1.3.0", + "resolved": "http://npm.greatld.com:4873/linebyline/-/linebyline-1.3.0.tgz", + "integrity": "sha512-3fpIYMrSU77OCf89hjXKuCx6vGwgWEu4N5DDCGqgZ1BF0HYy9V8IbQb/3+VWIU17iBQ83qQoUokH0AhPMOTi7w==", + "dev": true + }, "locate-path": { "version": "2.0.0", "resolved": "http://npm.greatld.com:4873/locate-path/-/locate-path-2.0.0.tgz", diff --git a/binding/nodejs/package.json b/binding/nodejs/package.json index 7c2225c..7228876 100644 --- a/binding/nodejs/package.json +++ b/binding/nodejs/package.json @@ -1,5 +1,5 @@ { - "name": " node-ip2region", + "name": "node-ip2region", "version": "2.0.0", "description": "official nodejs client of ip2region", "main": "index.js", @@ -22,10 +22,11 @@ "eslint-plugin-import": "^2.26.0", "eslint-plugin-n": "^15.2.4", "eslint-plugin-promise": "^6.0.0", + "linebyline": "^1.3.0", "mocha": "^10.0.0", "nyc": "^15.1.0" }, "engines": { "node": ">=8.0.0" } -} +} \ No newline at end of file diff --git a/binding/nodejs/tests/bench.app.js b/binding/nodejs/tests/bench.app.js new file mode 100644 index 0000000..1c14aca --- /dev/null +++ b/binding/nodejs/tests/bench.app.js @@ -0,0 +1,113 @@ +/* + * Created by Wu Jian Ping on - 2022/07/22. + */ + +const Searcher = require('..') +const { ArgumentParser } = require('argparse') +const readline = require('linebyline') + +// 处理输入参数 +const parser = new ArgumentParser({ + add_help: true, + description: 'ip2region benchmark app', + prog: 'node test.app.js', + usage: 'Usage %(prog)s [command options]' +}) + +parser.add_argument('--db', { help: 'ip2region binary xdb file path, default: ../../data/ip2region.xdb' }) +parser.add_argument('--src', { help: 'source ip text file path, default: ../../data/ip.merge.txt' }) +parser.add_argument('--cache-policy', { help: 'cache policy: file/vectorIndex/content, default: content' }) + +const args = parser.parse_args() +const dbPath = args.db || '../../data/ip2region.xdb' +const src = args.src || '../../data/ip.merge.txt' +const cachePolicy = args.cache_policy || 'content' + +// 创建searcher对象 +const createSearcher = () => { + let searcher = null + let vectorIndex = null + let buffer = null + + switch (cachePolicy) { + case 'file': + searcher = Searcher.newWithFileOnly(dbPath) + break + case 'vectorIndex': + vectorIndex = Searcher.loadVectorIndexFromFile(dbPath) + searcher = Searcher.newWithVectorIndex(dbPath, vectorIndex) + break + default: + buffer = Searcher.loadContentFromFile(dbPath) + searcher = Searcher.newWithBuffer(buffer) + } + console.log('options: ') + console.log(` dbPath: ${dbPath}`) + console.log(` src: ${dbPath}`) + console.log(` cache-policy: ${cachePolicy}`) + console.log('') + return searcher +} + +const ipToInt = ip => { + // 切割IP + const ps = ip.split('.') + // 将各段转成int + const i0 = parseInt(ps[0]) + const i1 = parseInt(ps[1]) + const i2 = parseInt(ps[2]) + const i3 = parseInt(ps[3]) + + // 假如使用移位操作的话,这边可能产生负数 + return i0 * 256 * 256 * 256 + i1 * 256 * 256 + i2 * 256 + i3 +} + +const intToIp = ip => { + const i0 = Math.floor(ip / (256 * 256 * 256)) + const i1 = Math.floor((ip % (256 * 256 * 256)) / (256 * 256)) + const i2 = Math.floor((ip % (256 * 256)) / 256) + const i3 = ip % 256 + + return `${i0}.${i1}.${i2}.${i3}` +} + +const searcher = createSearcher() + +// 开始时间 + +const startTime = process.hrtime() +let total = 0 + +// 程序主入口 +const main = async () => { + const rl = readline(src) + rl + .on('line', async (line, lineCount, byteCount) => { + const list = line.split('|') + const sip = list[0] + const eip = list[1] + const sipInt = ipToInt(sip) + const eipInt = ipToInt(eip) + + const mipInt = Math.floor((sipInt + eipInt) / 2) + const mip = intToIp(mipInt) + + await searcher.search(mip) + total++ + }) + .on('error', err => { + console.log(err) + process.exit(1) + }) +} + +process.on('exit', code => { + if (code === 0) { + // 这边只算个总时间就够了 + const diff = process.hrtime(startTime) + const took = (diff[0] * 1e9 + diff[1]) / 1e9 + console.log(`Bench finished, {cachePolicy: ${cachePolicy}, total: ${total}, took: ${took}s, cost: ${total === 0 ? 0 : ((diff[0] * 1e9 + diff[1]) / 1e6) / total}μs/op}`) + } +}) + +main() diff --git a/binding/nodejs/tests/benchmark.js b/binding/nodejs/tests/benchmark.js index 29625ad..fef7ea7 100644 --- a/binding/nodejs/tests/benchmark.js +++ b/binding/nodejs/tests/benchmark.js @@ -1,3 +1,7 @@ +/* + * Created by Wu Jian Ping on - 2022/07/22. + */ + const Benchmark = require('benchmark') const path = require('path') const Searcher = require('..') diff --git a/binding/nodejs/tests/function.test.js b/binding/nodejs/tests/function.test.js index ab7c959..bdfd524 100644 --- a/binding/nodejs/tests/function.test.js +++ b/binding/nodejs/tests/function.test.js @@ -1,3 +1,7 @@ +/* + * Created by Wu Jian Ping on - 2022/07/22. + */ + const { expect } = require('chai') const path = require('path') const Searcher = require('..') diff --git a/binding/nodejs/tests/test.app.js b/binding/nodejs/tests/test.app.js index 99a9c4f..26abbd0 100644 --- a/binding/nodejs/tests/test.app.js +++ b/binding/nodejs/tests/test.app.js @@ -1,5 +1,8 @@ +/* + * Created by Wu Jian Ping on - 2022/07/22. + */ + const Searcher = require('../') -const path = require('path') const { ArgumentParser } = require('argparse') // 处理输入参数 @@ -7,21 +10,18 @@ const parser = new ArgumentParser({ add_help: true, description: 'ip2region test app', prog: 'node test.app.js', - usage: 'Usage %(prog)s ' + usage: 'Usage %(prog)s [command options]' }) -parser.add_argument('-d', '--db', { help: `ip2region binary xdb file path, default: ${path.join(__dirname, '..', '..', '..', 'data', 'ip2region.xdb')}` }) -parser.add_argument('-c', '--cache-policy', { help: 'cache policy: file/vectorIndex/content, default: content' }) +parser.add_argument('--db', { help: 'ip2region binary xdb file path, default: ../../data/ip2region.xdb' }) +parser.add_argument('--cache-policy', { help: 'cache policy: file/vectorIndex/content, default: content' }) const args = parser.parse_args() -const db = args.db -const policy = args.cache_policy +const dbPath = args.db || '../../data/ip2region.xdb' +const cachePolicy = args.cache_policy || 'content' // 创建searcher对象 const createSearcher = () => { - const dbPath = db || '../../data/ip2region.xdb' - const cachePolicy = policy || 'content' - let searcher = null let vectorIndex = null let buffer = null @@ -38,7 +38,7 @@ const createSearcher = () => { buffer = Searcher.loadContentFromFile(dbPath) searcher = Searcher.newWithBuffer(buffer) } - console.log('parameters: ') + console.log('options: ') console.log(` dbPath: ${dbPath}`) console.log(` cache-policy: ${cachePolicy}`) console.log('') @@ -58,7 +58,7 @@ const readlineSync = () => { const searcher = createSearcher() -async function main () { +const main = async () => { console.log('type \'quit\' to exit') while (true) { process.stdout.write('ip2region>> ')