mirror of
https://github.com/bufanyun/hotgo.git
synced 2025-11-11 19:53:55 +08:00
发布v2.15.1版本,更新内容请查看:https://github.com/bufanyun/hotgo/blob/v2.0/docs/guide-zh-CN/start-update-log.md
This commit is contained in:
45
server/internal/library/location/cache.go
Normal file
45
server/internal/library/location/cache.go
Normal file
@@ -0,0 +1,45 @@
|
||||
// Package location
|
||||
// @Link https://github.com/bufanyun/hotgo
|
||||
// @Copyright Copyright (c) 2024 HotGo CLI
|
||||
// @Author Ms <133814250@qq.com>
|
||||
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
|
||||
package location
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf/v2/container/gmap"
|
||||
"sync"
|
||||
)
|
||||
|
||||
type IpCache struct {
|
||||
sync.Mutex
|
||||
data *gmap.Map
|
||||
}
|
||||
|
||||
var (
|
||||
cache = &IpCache{
|
||||
data: gmap.New(true),
|
||||
}
|
||||
)
|
||||
|
||||
func (c *IpCache) Contains(ip string) bool {
|
||||
return c.data.Contains(ip)
|
||||
}
|
||||
|
||||
func (c *IpCache) SetIpCache(ip string, data *IpLocationData) {
|
||||
if c.data.Size() > 10000 {
|
||||
c.data.Pops(2000)
|
||||
}
|
||||
c.data.Set(ip, data)
|
||||
}
|
||||
|
||||
func (c *IpCache) GetIpCache(ip string) (*IpLocationData, error) {
|
||||
value := c.data.Get(ip)
|
||||
data1, ok := value.(*IpLocationData)
|
||||
if !ok {
|
||||
c.data.Remove(ip)
|
||||
err := fmt.Errorf("data assertion failed in the cache ip:%v", ip)
|
||||
return nil, err
|
||||
}
|
||||
return data1, nil
|
||||
}
|
||||
@@ -8,8 +8,8 @@ package location
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/v2/container/gmap"
|
||||
"github.com/gogf/gf/v2/encoding/gcharset"
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/net/ghttp"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
@@ -52,14 +52,12 @@ type WhoisRegionData struct {
|
||||
Err string `json:"err"`
|
||||
}
|
||||
|
||||
var cacheMap *gmap.Map
|
||||
|
||||
func init() {
|
||||
cacheMap = gmap.New(true)
|
||||
}
|
||||
var (
|
||||
defaultRetry int64 = 3 // 默认重试次数
|
||||
)
|
||||
|
||||
// WhoisLocation 通过Whois接口查询IP归属地
|
||||
func WhoisLocation(ctx context.Context, ip string) (*IpLocationData, error) {
|
||||
func WhoisLocation(ctx context.Context, ip string, retry ...int64) (*IpLocationData, error) {
|
||||
response, err := g.Client().Timeout(10*time.Second).Get(ctx, whoisApi+ip)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -72,8 +70,22 @@ func WhoisLocation(ctx context.Context, ip string) (*IpLocationData, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 利用重试机制缓解高并发情况下限流的影响
|
||||
// 毕竟这是一个免费的接口,如果你对IP归属地定位要求毕竟高,可以考虑换个付费接口
|
||||
if response.StatusCode != 200 {
|
||||
retryCount := defaultRetry
|
||||
if len(retry) > 0 {
|
||||
retryCount = retry[0]
|
||||
}
|
||||
if retryCount > 0 {
|
||||
retryCount--
|
||||
return WhoisLocation(ctx, ip, retryCount)
|
||||
}
|
||||
}
|
||||
|
||||
var who *WhoisRegionData
|
||||
if err = gconv.Struct([]byte(str), &who); err != nil {
|
||||
if err = gconv.Scan([]byte(str), &who); err != nil {
|
||||
err = gerror.Newf("WhoisLocation Scan err:%v, str:%v", err, str)
|
||||
return nil, err
|
||||
}
|
||||
return &IpLocationData{
|
||||
@@ -109,17 +121,6 @@ func Cz88Find(ctx context.Context, ip string) (*IpLocationData, error) {
|
||||
}, nil
|
||||
}
|
||||
|
||||
// IsJurisByIpTitle 判断地区名称是否为直辖市
|
||||
func IsJurisByIpTitle(title string) bool {
|
||||
lists := []string{"北京市", "天津市", "重庆市", "上海市"}
|
||||
for i := 0; i < len(lists); i++ {
|
||||
if gstr.Contains(lists[i], title) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// GetLocation 获取IP归属地信息
|
||||
func GetLocation(ctx context.Context, ip string) (data *IpLocationData, err error) {
|
||||
if !validate.IsIp(ip) {
|
||||
@@ -130,18 +131,18 @@ func GetLocation(ctx context.Context, ip string) (data *IpLocationData, err erro
|
||||
return // nil, fmt.Errorf("must be a public ip:%v", ip)
|
||||
}
|
||||
|
||||
if cacheMap.Contains(ip) {
|
||||
value := cacheMap.Get(ip)
|
||||
data1, ok := value.(*IpLocationData)
|
||||
if !ok {
|
||||
cacheMap.Remove(ip)
|
||||
err = fmt.Errorf("data assertion failed in the cache ip:%v", ip)
|
||||
return
|
||||
}
|
||||
return data1, nil
|
||||
if cache.Contains(ip) {
|
||||
return cache.GetIpCache(ip)
|
||||
}
|
||||
|
||||
mode := g.Cfg().MustGet(ctx, "hotgo.ipMethod", "cz88").String()
|
||||
cache.Lock()
|
||||
defer cache.Unlock()
|
||||
|
||||
if cache.Contains(ip) {
|
||||
return cache.GetIpCache(ip)
|
||||
}
|
||||
|
||||
mode := g.Cfg().MustGet(ctx, "system.ipMethod", "cz88").String()
|
||||
switch mode {
|
||||
case "whois":
|
||||
data, err = WhoisLocation(ctx, ip)
|
||||
@@ -150,10 +151,7 @@ func GetLocation(ctx context.Context, ip string) (data *IpLocationData, err erro
|
||||
}
|
||||
|
||||
if err == nil && data != nil {
|
||||
if cacheMap.Size() > 20000 {
|
||||
cacheMap.Clear()
|
||||
}
|
||||
cacheMap.Set(ip, data)
|
||||
cache.SetIpCache(ip, data)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
72
server/internal/library/location/location_test.go
Normal file
72
server/internal/library/location/location_test.go
Normal file
@@ -0,0 +1,72 @@
|
||||
// Package location_test
|
||||
// @Link https://github.com/bufanyun/hotgo
|
||||
// @Copyright Copyright (c) 2024 HotGo CLI
|
||||
// @Author Ms <133814250@qq.com>
|
||||
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
|
||||
package location_test
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/v2/encoding/gjson"
|
||||
"github.com/gogf/gf/v2/os/gctx"
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
"github.com/gogf/gf/v2/test/gtest"
|
||||
"hotgo/internal/library/location"
|
||||
"sync"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var ip = "120.12.151.65"
|
||||
|
||||
func Test_GetLocation(t *testing.T) {
|
||||
ctx := gctx.New()
|
||||
data, err := location.GetLocation(ctx, ip)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
t.Logf("data:%v", gjson.New(data).String())
|
||||
}
|
||||
|
||||
func Test_ParallelGetLocation(t *testing.T) {
|
||||
ctx := gctx.New()
|
||||
start := gtime.Now()
|
||||
|
||||
t.Log("start")
|
||||
for i := 0; i < 10; i++ {
|
||||
data, err := location.GetLocation(ctx, ip)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
t.Logf("index:%v, data:%v", i, gjson.New(data).String())
|
||||
}
|
||||
|
||||
t.Logf("总耗时:%vs", gtime.Now().Sub(start).Seconds())
|
||||
}
|
||||
|
||||
func Test_ConcurrentGetLocation(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
ctx := gctx.New()
|
||||
start := gtime.Now()
|
||||
var wg sync.WaitGroup
|
||||
|
||||
t.Log("start")
|
||||
for i := 0; i < 10; i++ {
|
||||
wg.Add(1)
|
||||
index := i
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
|
||||
data, err := location.GetLocation(ctx, ip)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
t.Logf("index:%v, data:%v", index, gjson.New(data).String())
|
||||
}()
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
t.Logf("总耗时:%vs", gtime.Now().Sub(start).Seconds())
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user