Files
GoProxy/fetcher/ip_query.go
isboyjc f55209d8d3 feat: init
2026-03-29 03:31:59 +08:00

153 lines
3.2 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package fetcher
import (
"context"
"encoding/json"
"fmt"
"net/http"
"time"
"golang.org/x/time/rate"
)
// IPQueryLimiter 全局IP查询限流器
var IPQueryLimiter *rate.Limiter
// InitIPQueryLimiter 初始化限流器
func InitIPQueryLimiter(rps int) {
IPQueryLimiter = rate.NewLimiter(rate.Limit(rps), rps*2)
}
// GetExitIPInfo 通过代理获取出口 IP 和地理位置(多源降级)
func GetExitIPInfo(client *http.Client) (string, string) {
// 等待限流令牌
if IPQueryLimiter != nil {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
if err := IPQueryLimiter.Wait(ctx); err != nil {
return "", ""
}
}
// 优先级1ip-api.com
if ip, loc := tryIPAPI(client); ip != "" {
return ip, loc
}
// 优先级2ipapi.co
if ip, loc := tryIPAPICo(client); ip != "" {
return ip, loc
}
// 优先级3ipinfo.io
if ip, loc := tryIPInfo(client); ip != "" {
return ip, loc
}
// 优先级4仅获取IP
if ip := tryHTTPBinIP(client); ip != "" {
return ip, "UNKNOWN"
}
return "", ""
}
// tryIPAPI 尝试 ip-api.com
func tryIPAPI(client *http.Client) (string, string) {
resp, err := client.Get("http://ip-api.com/json/?fields=status,country,countryCode,city,query")
if err != nil {
return "", ""
}
defer resp.Body.Close()
var result struct {
Status string `json:"status"`
Query string `json:"query"`
Country string `json:"country"`
CountryCode string `json:"countryCode"`
City string `json:"city"`
}
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil || result.Status != "success" {
return "", ""
}
location := result.CountryCode
if result.City != "" {
location = fmt.Sprintf("%s %s", result.CountryCode, result.City)
}
return result.Query, location
}
// tryIPAPICo 尝试 ipapi.co
func tryIPAPICo(client *http.Client) (string, string) {
resp, err := client.Get("https://ipapi.co/json/")
if err != nil {
return "", ""
}
defer resp.Body.Close()
var result struct {
IP string `json:"ip"`
City string `json:"city"`
CountryCode string `json:"country_code"`
}
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
return "", ""
}
location := result.CountryCode
if result.City != "" {
location = fmt.Sprintf("%s %s", result.CountryCode, result.City)
}
return result.IP, location
}
// tryIPInfo 尝试 ipinfo.io
func tryIPInfo(client *http.Client) (string, string) {
resp, err := client.Get("https://ipinfo.io/json")
if err != nil {
return "", ""
}
defer resp.Body.Close()
var result struct {
IP string `json:"ip"`
City string `json:"city"`
Country string `json:"country"`
}
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
return "", ""
}
location := result.Country
if result.City != "" {
location = fmt.Sprintf("%s %s", result.Country, result.City)
}
return result.IP, location
}
// tryHTTPBinIP 尝试 httpbin仅获取IP
func tryHTTPBinIP(client *http.Client) string {
resp, err := client.Get("https://httpbin.org/ip")
if err != nil {
return ""
}
defer resp.Body.Close()
var result struct {
Origin string `json:"origin"`
}
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
return ""
}
return result.Origin
}