从0开始实现一个代理池
语言Go,数据库Redis
简单设计
最近因为需要,着手搭建了一个代理池,这里时间关系,我也不科普什么是代理池了,直接切入正题,我们需要实现什么?话不多说看图,整个架构包含了Worker,Getter,DB,API等几大模块。
经过一顿思考,大概的架构图就是如上所示了,代理池具备的能力如下所示:
1.获取代理和校验代理分离。
2.代理解析,代理解析我们通过proxy_getter实现,当新增代理的时候,我们只需要在其中补齐拉取方法即可。
3.redis数据库,用于缓存拿到的代理的数据,当然因为我们获取的数据会比较频繁,所以此时如果使用MySQL的话压力比较大,所以这里使用redis。
4.获取前进行校验,确保代理IP是可用的。
数据结构
type ProxyGetter struct {
Id string `json:"id"` // 生成的随机ID
Name string `json:"name"` // 代理名称
Host string `json:"host"` // 代理IP与Host
Deadline string `json:"deadline"` // 过期时间
LastCheckTime string `json:"lastCheckTime"` // 最后一次校验时间
CheckCount int64 `json:"checkCount"` // 校验次数
Lock bool `json:"lock"` // 是否锁定(有的业务不能与其他业务一起共享IP所以需要独占)
IsHttps bool `json:"isHttps"` // 是否支持https
}
代理池所提供的数据结构如上图所示,主要包括了唯一Id,代理名称,过期时间,是否锁定等。
具体实现
通过上述设计,代理池架构如下所示,api是获取代理池的入口,提供了get,delete,getAll,clear等接口,用于对代理池的操作。
r.GET("/get", get)
r.GET("/delete", delete)
r.GET("/getAll", getAll)
r.GET("/clear", clear)
common模块主要是proxy getter和工具类的实现,其中proxy_getter主要是用于代理的获取与解析。
这里我提供了一个demo用于开发者进行参考。
func QinGuo() []*ProxyGetter {
var proxyResult = make([]*ProxyGetter, 0)
var result map[string]interface{}
resp, err := http.Get("") // 填写申请的URL,发起Get请求
if err != nil {
log.Fatalln("request error!")
return nil
}
if body, err := ioutil.ReadAll(resp.Body); err == nil {
err = json.Unmarshal(body, &result)
}
// 解析结果
if result["Code"].(float64) == 0 {
data := result["Data"].([]interface{})
for i := 0; i < len(data); i++ {
value := data[i].(map[string]interface{})
proxy := &ProxyGetter{
Id: uuid.New().String(),
Host: value["host"].(string),
Deadline: value["deadline"].(string),
Name: "QinGuo",
LastCheckTime: time.Now().String(),
CheckCount: 0,
Lock: false,
}
proxyResult = append(proxyResult, proxy)
}
}
// 返回获取到的代理
return proxyResult
}
config存放了代理池的配置,其中包括了Version,Name等基础配置,我们只需要适当的修改即可完成配置,其中最核心的就是Proxy参数,用于配置代理池。
var (
Version = 1.0 // 版本号
Name = "proxy pool" // 名称
CronCheck = "@every 1m" // 校验定时
CronPull = "@every 1m" // 拉取定时
Proxy = map[string]interface{}{ // 填写解析方法
"QinGuo": common.QinGuo,
}
DBKey = "proxy_pool"
DBHost = "" // Redis的主机地址
DBPassword = "" // Redis的密码
DB = 0
ProxyAuth = "" // 代理权限,当然也可以设置白名单
HttpsValidUrl = "https://www.qq.com" // https代理校验的URL
HttpValidUrl = "http://httpbin.org" // http代理校验的URL
)
最后
通过上述的编写,我们就完成了代理池的开发工作,我们可以通过main中的配置完成运行,main的配置如下所示。
func main() {
switch *runType {
case "schedule":
runSchedule()
case "server":
runServer()
default:
log.Fatalln("please input current type!")
return
}
log.Printf("proxy start success!\nversion: %0.1f \n name: %s", config.Version, config.Name)
}
运行
./main -type server // 服务端主要提供暴露给使用方调用的接口
./main -type schedule // 开启定时任务,主要负责拉取代理并进行校验
代码仓库:https://github.com/guanjiangtao/proxy_pool大佬们路过给个star吧