功能介绍
最近想用golang实现一个高性能的ip代理池管理的功能。大致包含的功能点如下:
- 提供一个录入ip的接口,可以批量的导入ip池进入系统中
- 对ip池内的ip进行定时的校验其可用性,无效的丢掉
实现
先定义ip代理的结构
type Proxy struct {
IP string
Port int
Protocol string
}
导数实现
大致的逻辑如下:
- 定义一个导入ip池的方法,可以批量分页的将大量的ip信息输入到项目内
- 通过导入ip的方法获得的ip信息直接放到一个带有缓存的待校验的channel里
- 从待校验的channel里挨个取出ip信息进行有效性校验,校验成功的再放入可用IP的channel里,失败的标记ip失效
- 从可用ip的channel里取出ip信息返回给调用方
下面看代码:
定义Importer
type Importer func(page int, size int) ([]Proxy, error)
再定义一些全局变量
// 实例化的Importer
var importer Importer
// 待校验的ip池
var uncheckIpPool chan Proxy
// 可用Ip池
var availableIpPool chan Proxy
执行导入
func importerRun() {
page, size := 1, 20
for {
// 获取ip信息
proxies, err := importer(page, size)
if err != nil {
continue
}
if len(proxies) < size {
// 不够一页了,从头开始取
page = 1
} else {
page++
}
// 将数据写入待校验的ip池
for _, v := range proxies {
uncheckIpPool <- v
}
}
}
校验实现
对待校验的ip池内的数据进行校验,成功后放入可用ip池。
// checkRun 执行校验
func checkRun() {
for {
p := <-uncheckIpPool
now := time.Now().Unix()
if now < p.CheckTimestamp+300 {
log.Printf("%s 校验未过期", p.String())
availableIpPool <- p
}
if check(p) {
p.CheckTimestamp = time.Now().Unix()
availableIpPool <- p
}
}
}
// check 校验
func check(p Proxy) bool {
urlPath := fmt.Sprintf("http://%s:%d", p.IP, p.Port)
proxyUrl, err := url.Parse(urlPath)
if err != nil {
log.Println("Error parsing proxy URL:", err)
return false
}
log.Printf("check %s \n", urlPath)
// 创建一个Transport对象
transport := &http.Transport{
Proxy: http.ProxyURL(proxyUrl),
}
// 创建一个http.Client对象并设置Transport
client := &http.Client{
Transport: transport,
}
// 创建一个GET请求
req, err := http.NewRequest("GET", "http://www.baidu.com", nil)
if err != nil {
log.Println("Error creating request:", err)
return false
}
// 发送请求并获取响应
resp, err := client.Do(req)
if err != nil {
log.Println("Error sending request:", err)
return false
}
defer resp.Body.Close()
res := resp.StatusCode == http.StatusOK
log.Println(res)
return res
}
取数实现
取数就比较简单了,直接从可用ip池里拿一个就行
func GetProxy() Proxy {
p := <-availableIpPool
return p
}
初始化
在调用上面的一些方法之前要先对包进行初始化
初始化的配置结构如下:
type ProxyConfig struct {
Importer Importer
UncheckIpPoolSize int
AvailableIpPoolSize int
}
初始化方法
func InitPool(cfg ProxyConfig) error {
if cfg.Importer == nil {
return fmt.Errorf("importer 不能为空")
}
importer = cfg.Importer
// 初始化可用ip池缓存
availableIpPoolSize := cfg.AvailableIpPoolSize
if cfg.AvailableIpPoolSize <= 0 {
availableIpPoolSize = 100
}
availableIpPool = make(chan Proxy, availableIpPoolSize)
// 初始化待校验ip池缓存
uncheckIpPoolSize := cfg.UncheckIpPoolSize
if uncheckIpPoolSize <= 0 {
uncheckIpPoolSize = 100
}
uncheckIpPool = make(chan Proxy, uncheckIpPoolSize)
go importerRun()
go checkRun()
return nil
}
测试
实现已经完成了,就做个简单的测试吧
使用gin框架写个简单接口,输出一个可用的代理ip
package main
import (
"github.com/gin-gonic/gin"
"iproxy/core"
"log"
)
// ip池
var proxies = []core.Proxy{
core.NewProxy("43.138.20.156", 80),
core.NewProxy("120.234.203.171", 9002),
core.NewProxy("68.79.25.157", 8089),
core.NewProxy("180.103.127.9", 7890),
core.NewProxy("27.185.0.164", 999),
core.NewProxy("182.92.73.106", 80),
core.NewProxy("182.106.220.252", 9091),
core.NewProxy("39.107.33.254", 8090),
}
func main() {
cfg := core.ProxyConfig{
UncheckIpPoolSize: 5,
AvailableIpPoolSize: 5,
Importer: getImporter(),
}
err := core.InitPool(cfg)
if err != nil {
log.Printf("core.InitPool error:%+v \n", err)
return
}
router := gin.Default()
router.GET("/get_proxy", func(context *gin.Context) {
ip := core.GetProxy()
log.Printf("get_proxy result: %s \n", ip.String())
context.JSONP(200, gin.H{
"code": 200,
"success": true,
"data": core.GetProxy(),
})
})
router.Run(":9090")
}
func getImporter() core.Importer {
return func(page int, size int) ([]core.Proxy, error) {
return proxies, nil
}
}
执行效果如下图:
可以看到,接口的耗时不超过1毫秒,效率非常高,因为所有的取数逻辑,都是纯内存操作.
完
本文为原创,转载请注明出处: golang实现高性能ip代理池管理