Go语言开发通过本地数据xdb文件​查询获取IP地址的归属地区及运营商名称

news2024/11/28 20:53:38

说明:

用本地数据,离线识别ip属地,用于显示用户ip属地,不依赖第三方的api接口,本地数据包解析,解析速度快10微秒级别的查询效率。返回数据固定格式:国家|区域|省份|城市|ISP,例如:中国|0|云南省|昆明市|移动。不同

特性:

1、IP 数据管理框架

​xdb​​ 支持亿级别的 IP 数据段行数,默认的 region 信息都固定了格式:​​国家|区域|省份|城市|ISP​​,缺省的地域信息默认是0。 region 信息支持完全自定义,例如:你可以在 region 中追加特定业务需求的数据,例如:GPS信息/国际统一地域信息编码/邮编等。也就是你完全可以使用 ipregion 来管理你自己的 IP 定位数据。

2、数据去重和压缩

​xdb​​ 格式生成程序会自动去重和压缩部分数据,默认的全部 IP 数据,生成的 ipregion.xdb 数据库是 11MiB,随着数据的详细度增加数据库的大小也慢慢增大。

3、极速查询响应

即使是完全基于 ​​xdb​​ 文件的查询,单次查询响应时间在十微秒级别,可通过如下两种方式开启内存加速查询:

  • ​vIndex​​ 索引缓存 :使用固定的 ​​512KiB​​ 的内存空间缓存 vector index 数据,减少一次 IO 磁盘操作,保持平均查询效率稳定在10-20微秒之间。
  • ​xdb​​​ 整个文件缓存:将整个 ​​xdb​​​ 文件全部加载到内存,内存占用等同于 ​​xdb​​ 文件大小,无磁盘 IO 操作,保持微秒级别的查询效率。

使用方法:

1.下封装解析插件和

  到这:下载代码地址,在右边的“代码下载”下载插件代码,并在下面的代码附件下载 下载“xdb数据文件”解压后,复制ipregion.xdb文件到resource/static目录下。

下载到的插件代码目录

├── plugin                       # 扩展插件目录
│   ├── ipregion                # “ip地址解析属地区”目录文件夹
│   │    ├── searcher.go       # 编写业务
│   │    └── util.go           # 工具函数        
│   └── ipregion.go             # “ip地址解析属地区”文件 用于app开发时调用

其中代码插件源码:

searcher.go文件的源码如:

// ---
// ipregion database v2.0 searcher.
// @Note this is a Not thread safe implementation.
//
// @Author gofly
// @Date   2024/08/19
package ipregion

import (
	"encoding/binary"
	"fmt"
	"os"
	"path/filepath"
)

const (
	HeaderInfoLength      = 256
	VectorIndexRows       = 256
	VectorIndexCols       = 256
	VectorIndexSize       = 8
	SegmentIndexBlockSize = 14
)

// --- Index policy define

type IndexPolicy int

const (
	VectorIndexPolicy IndexPolicy = 1
	BTreeIndexPolicy  IndexPolicy = 2
)

func (i IndexPolicy) String() string {
	switch i {
	case VectorIndexPolicy:
		return "VectorIndex"
	case BTreeIndexPolicy:
		return "BtreeIndex"
	default:
		return "unknown"
	}
}

// --- Header define

type Header struct {
	// data []byte
	Version       uint16
	IndexPolicy   IndexPolicy
	CreatedAt     uint32
	StartIndexPtr uint32
	EndIndexPtr   uint32
}

func NewHeader(input []byte) (*Header, error) {
	if len(input) < 16 {
		return nil, fmt.Errorf("invalid input buffer")
	}

	return &Header{
		Version:       binary.LittleEndian.Uint16(input),
		IndexPolicy:   IndexPolicy(binary.LittleEndian.Uint16(input[2:])),
		CreatedAt:     binary.LittleEndian.Uint32(input[4:]),
		StartIndexPtr: binary.LittleEndian.Uint32(input[8:]),
		EndIndexPtr:   binary.LittleEndian.Uint32(input[12:]),
	}, nil
}

// --- searcher implementation

type Searcher struct {
	handle *os.File

	// header info
	header  *Header
	ioCount int

	// use it only when this feature enabled.
	// Preload the vector index will reduce the number of IO operations
	// thus speedup the search process
	vectorIndex []byte

	// content buffer.
	// running with the whole xdb file cached
	contentBuff []byte
}

func baseNew(vIndex []byte, cBuff []byte) (*Searcher, error) {
	var err error
	path, _ := os.Getwd()
	dbFile := filepath.Join(path, "/resource/static/ipregion.xdb")
	// content buff first
	if cBuff != nil {
		return &Searcher{
			vectorIndex: nil,
			contentBuff: cBuff,
		}, nil
	}

	// open the xdb binary file
	handle, err := os.OpenFile(dbFile, os.O_RDONLY, 0600)
	if err != nil {
		return nil, err
	}

	return &Searcher{
		handle:      handle,
		vectorIndex: vIndex,
	}, nil
}

func NewWithFileOnly() (*Searcher, error) {
	return baseNew(nil, nil)
}

func NewWithVectorIndex(vIndex []byte) (*Searcher, error) {
	return baseNew(vIndex, nil)
}

func NewWithBuffer(cBuff []byte) (*Searcher, error) {
	return baseNew(nil, cBuff)
}

func (s *Searcher) Close() {
	if s.handle != nil {
		err := s.handle.Close()
		if err != nil {
			return
		}
	}
}

// GetIOCount return the global io count for the last search
func (s *Searcher) GetIOCount() int {
	return s.ioCount
}

// SearchByStr find the region for the specified ip string
func (s *Searcher) SearchByStr(str string) (string, error) {
	ip, err := CheckIP(str)
	if err != nil {
		return "", err
	}

	return s.Search(ip)
}

// Search find the region for the specified long ip
func (s *Searcher) Search(ip uint32) (string, error) {
	// reset the global ioCount
	s.ioCount = 0

	// locate the segment index block based on the vector index
	var il0 = (ip >> 24) & 0xFF
	var il1 = (ip >> 16) & 0xFF
	var idx = il0*VectorIndexCols*VectorIndexSize + il1*VectorIndexSize
	var sPtr, ePtr = uint32(0), uint32(0)
	if s.vectorIndex != nil {
		sPtr = binary.LittleEndian.Uint32(s.vectorIndex[idx:])
		ePtr = binary.LittleEndian.Uint32(s.vectorIndex[idx+4:])
	} else if s.contentBuff != nil {
		sPtr = binary.LittleEndian.Uint32(s.contentBuff[HeaderInfoLength+idx:])
		ePtr = binary.LittleEndian.Uint32(s.contentBuff[HeaderInfoLength+idx+4:])
	} else {
		// read the vector index block
		var buff = make([]byte, VectorIndexSize)
		err := s.read(int64(HeaderInfoLength+idx), buff)
		if err != nil {
			return "", fmt.Errorf("read vector index block at %d: %w", HeaderInfoLength+idx, err)
		}

		sPtr = binary.LittleEndian.Uint32(buff)
		ePtr = binary.LittleEndian.Uint32(buff[4:])
	}

	// fmt.Printf("sPtr=%d, ePtr=%d", sPtr, ePtr)

	// binary search the segment index to get the region
	var dataLen, dataPtr = 0, uint32(0)
	var buff = make([]byte, SegmentIndexBlockSize)
	var l, h = 0, int((ePtr - sPtr) / SegmentIndexBlockSize)
	for l <= h {
		m := (l + h) >> 1
		p := sPtr + uint32(m*SegmentIndexBlockSize)
		err := s.read(int64(p), buff)
		if err != nil {
			return "", fmt.Errorf("read segment index at %d: %w", p, err)
		}

		// decode the data step by step to reduce the unnecessary operations
		sip := binary.LittleEndian.Uint32(buff)
		if ip < sip {
			h = m - 1
		} else {
			eip := binary.LittleEndian.Uint32(buff[4:])
			if ip > eip {
				l = m + 1
			} else {
				dataLen = int(binary.LittleEndian.Uint16(buff[8:]))
				dataPtr = binary.LittleEndian.Uint32(buff[10:])
				break
			}
		}
	}

	//fmt.Printf("dataLen: %d, dataPtr: %d", dataLen, dataPtr)
	if dataLen == 0 {
		return "", nil
	}

	// load and return the region data
	var regionBuff = make([]byte, dataLen)
	err := s.read(int64(dataPtr), regionBuff)
	if err != nil {
		return "", fmt.Errorf("read region at %d: %w", dataPtr, err)
	}

	return string(regionBuff), nil
}

// do the data read operation based on the setting.
// content buffer first or will read from the file.
// this operation will invoke the Seek for file based read.
func (s *Searcher) read(offset int64, buff []byte) error {
	if s.contentBuff != nil {
		cLen := copy(buff, s.contentBuff[offset:])
		if cLen != len(buff) {
			return fmt.Errorf("incomplete read: readed bytes should be %d", len(buff))
		}
	} else {
		_, err := s.handle.Seek(offset, 0)
		if err != nil {
			return fmt.Errorf("seek to %d: %w", offset, err)
		}

		s.ioCount++
		rLen, err := s.handle.Read(buff)
		if err != nil {
			return fmt.Errorf("handle read: %w", err)
		}

		if rLen != len(buff) {
			return fmt.Errorf("incomplete read: readed bytes should be %d", len(buff))
		}
	}

	return nil
}

util.go源码:

package ipregion

import (
	"fmt"
	"strconv"
	"strings"
)

// 工具函数
var shiftIndex = []int{24, 16, 8, 0}

func CheckIP(ip string) (uint32, error) {
	var ps = strings.Split(strings.TrimSpace(ip), ".")
	if len(ps) != 4 {
		return 0, fmt.Errorf("invalid ip address `%s`", ip)
	}

	var val = uint32(0)
	for i, s := range ps {
		d, err := strconv.Atoi(s)
		if err != nil {
			return 0, fmt.Errorf("the %dth part `%s` is not an integer", i, s)
		}

		if d < 0 || d > 255 {
			return 0, fmt.Errorf("the %dth part `%s` should be an integer bettween 0 and 255", i, s)
		}

		val |= uint32(d) << shiftIndex[i]
	}

	// convert the ip to integer
	return val, nil
}

3.调用ip解析方法

import引入插件包

import (
	"gofly/utils/plugin"
)

在接口业务中使用, data, err := plugin.NewIpRegion(ip),调用NewIpRegion()方法即可解析出IP归属地信息,代码如下:

// get请求获取ip属地
func (api *Iptest) GetIpRegion(c *gf.GinCtx) {
	ip := c.DefaultQuery("ip", "")
	data, err := plugin.NewIpRegion(ip)
	gf.Success().SetMsg("获取ip属地").SetData(data).SetExdata(err).Regin(c)
}

这里给大家补充一个gofly框架获取用户请求IP地址函数  ip := gf.GetIp(c),其中c是gin框架请求上下文*gf.GinCtx

插件调用测试:

我们添加一个接口测试调用data, err := plugin.NewIpRegion(ip) 返回数据为:中国|0|云南省|昆明市|移动 ,如下图:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2062360.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

c++11(三)

一、可变参数 1、可变参数模板 c语言中的 scanf 和 printf 可以支持我们传入任意个数的参数&#xff0c;原理就是用了参数包。 //可变参数包 template<class ...Args> void Print(Args... args) {} Args&#xff1a;模板参数包 args&#xff1a;函数形参参数包 声明…

检查linux系统中异常进程

1、查看非root运行的进程 [rootbastion-IDC ~]# ps -U root -u root -N 2、查看root运行的进程 [rootbastion-IDC ~]# ps -u root 注意&#xff1a;UID为0的进程&#xff0c;查看该进程所打开的端口和文件 [rootbastion-IDC ~]#ps -ef 查看进程 [rootbastion-IDC ~]# l…

Lesson 77 Terrible toothache

Lesson 77 Terrible toothache 词汇 appointment n. 预约 构成&#xff1a;point v. 指&#xff0c;指向 用法&#xff1a;point to 人 / 物    指着&#xff0c;指向……    point out 指出&#xff08;问题&#xff09; 相关&#xff1a;game point 局点    matc…

statsmodels学习笔记

statsmodels学习笔记 统计模型、假设检验和数据探索。statsmodels是一个python模块&#xff0c;提供了用于估计许多不同统计模型的类和函数&#xff0c;以及用于统计测试和统计数据探索。每个估计器都有一个广泛的结果统计列表。根据现有的统计软件包对结果进行测试&#xff0c…

【C++】深入解析C/C++内存管理:new与delete的使用及原理

C语法相关知识点可以通过点击以下链接进行学习一起加油&#xff01;命名空间缺省参数与函数重载C相关特性类和对象-上篇类和对象-中篇类和对象-下篇日期类 本章将分享C为何放弃malloc/free系列&#xff0c;选择新系列new/delete去管理内存。深度探索new/delete的使用及其原理,m…

VBA注释 (<*> + <*>)

在VBA&#xff08;Visual Basic for Applications&#xff09;中&#xff0c;注释是一种用于向代码中添加说明或解释文本的方法&#xff0c;这些文本不会被执行。注释对于理解代码的目的、逻辑或特定部分的代码功能非常有帮助&#xff0c;尤其是在处理复杂或长的代码时。 一、…

当《黑神话:悟空》遇上openKylin,国产力量的极致碰撞!

万众瞩目的国产3A游戏巨作《黑神话&#xff1a;悟空》终于上线啦&#xff01;&#xff01;&#xff01; 在正式发售后不到24小时&#xff0c;Steam在线玩家峰值突破222万&#xff0c;在Steam所有游戏在线玩家历史峰值中排名第二。第一拨玩家纷纷晒出好评&#xff0c;称这款现象…

Python安装Crypto库报错:ModuleNotFoundError: No module named ‘Crypto‘

目录 from Crypto.Cipher import AES 1.解决方法 1、卸载Crypto和pycrypto库 2、安装pycryptodome库 二、另一种解决方法&#xff08;看的别人遇到的情况&#xff0c;我没有遇到这种情况&#xff09; from Crypto.Cipher import AES 在网上搜的教程使用第三方库实现AES算法…

消息中心业务系统集成方案:提升企业信息流动性与协作效率

在信息化时代&#xff0c;企业的业务系统之间需要实现高效的信息流动与协作&#xff0c;以支持动态的业务需求和快速的决策过程。消息中心作为企业信息管理的重要组成部分&#xff0c;通过整合各类消息和通知&#xff0c;能够提升信息传递的效率和准确性。本文将详细探讨消息中…

Nginx 配置指南

一、Nginx 简介 1.1 概述 Nginx 是一款高性能、轻量级的开源 Web 服务器和反向代理服务器&#xff0c;以其可靠性、丰富的功能和简单的配置而闻名。由 Igor Sysoev 开发&#xff0c;最初用于解决 C10K 问题&#xff0c;与传统的 Web 服务器相比&#xff0c;Nginx 采用异步事件…

使用stream()流合并两个列表

List<Author>结构如下&#xff1a; List<Reader>结构如下&#xff1a; 需求&#xff1a;将Author列表和Reader列表根据相同id合并到一个列表中 private static void mergeList() {List<Author> authors Author.getAuthors();List<Reader> readers …

阅读、分析和维护高质量开源软件有感——小计一笔

目录 一、问题分析 软件开发问题分析 动机 学什么 目的 二、要求 阅读 理解 运用 分析 评估 认知 三、案例选择 MiNotes”开源软件 方式 实践支撑软件工具 操作流程 应该学到的知识 学习过程 四、任务与输出 1.阅读开源软件 2.标注开源软件 3.分析开源…

iLogtail 开源两周年:感恩遇见,畅想未来

早在上世纪 60 年代&#xff0c;早期的计算机&#xff08;例如 ENIAC 和 IBM 的大型机&#xff09;在操作过程中会输出一些基本的状态信息和错误报告&#xff0c;这些记录通常通过打印机输出到纸带或纸卡上&#xff0c;用于跟踪操作流程和调试&#xff0c;最早期的日志系统借此…

前端必备:高效处理树形数据与数组的实用函数

​&#x1f308;个人主页&#xff1a;前端青山 &#x1f525;系列专栏&#xff1a;Vue篇 &#x1f516;人终将被年少不可得之物困其一生 依旧青山,本期给大家带来Vuet篇专栏内容:Vue-树形数据处理|数组:实用函数封装 大家好&#xff0c;依旧青山&#xff0c;在开发项目过程中&a…

3、springboot时代背景

一、微服务 二、分布式 三、云原生 原生应用如何上云。 Cloud Native 上云的困难 服务自愈弹性伸缩服务隔离自动化部署灰度发布流量治理...... 上云的解决

人工智能算法工程师(中级)课程21-深度学习中各种优化器算法的应用与实践、代码详解

大家好&#xff0c;我是微学AI&#xff0c;今天给大家介绍一下人工智能算法工程师(中级)课程21-深度学习中各种优化器算法的应用与实践、代码详解。本文将介绍PyTorch框架下的几种优化器&#xff0c;展示如何使用PyTorch中的优化器&#xff0c;我们将使用MNIST数据集和一个简单…

云游戏畅玩黑神话悟空:使用 NVIDIA 4090 体验极致画质

​ 黑神话悟空 爽啦&#xff01;没有好配置又想玩《黑神话&#xff1a;悟空》的朋友们都爽啦&#xff01;自己没有好的 GPU&#xff0c;体验《黑神话&#xff1a;悟空》时画质不好玩的不舒心&#xff1f;厚德云来帮你解决问题&#xff01;厚德云上线了《黑神话&#xff1a;悟空…

机器学习第五十三周周报 MAG

文章目录 week53 MAG摘要Abstract1. 题目2. Abstract3. 预测标准3.1 问题提出3.2 数据预处理3.3 模型架构MAG3.4 时域故障模式识别3.5 故障检测器 4. 文献解读4.1 Introduction4.2 创新点4.3 实验过程4.4 实验结果4.5 结果分析 5. 结论小结参考文献 week53 MAG 摘要 本周阅读…

【ASPLOS2024】RECom:通过编译器技术加速推荐模型推理,论文中选并获得荣誉奖项!

2024年5月&#xff0c;关于推荐模型自动编译优化的论文《RECom: A Compiler Approach to Accelerate Recommendation Model Inference with Massive Embedding Columns》在系统领域顶会ASPLOS 2024上中选并进行了展示&#xff0c;并被授予了Distinguished Artifact Award 荣誉&…

基于springboot的招聘系统的设计与实现

TOC springboot614基于springboot的招聘系统的设计与实现--论文 研究背景 近年来&#xff0c;由于计算机技术和互联网技术的快速发展&#xff0c;使得所有企事业单位内部都是数字化、信息化、无纸化的发展趋势&#xff0c;随着趋势的发展&#xff0c;各种决策系统、辅助系统…