1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
| package main
import (
"flag"
"fmt"
"io/ioutil"
"net/http"
"strings"
"sync"
"time"
"gopkg.in/yaml.v3"
)
var (
timeOut int
)
type FLink struct {
Name string `yaml:"name"`
Desc string `yaml:"desc"`
Avatar string `yaml:"avatar"`
Link string `yaml:"link"`
Visible bool `yaml:"visible"`
Skip bool `yaml:"skip"`
Attempts int `yaml:"-"`
}
type FLinkGroup struct {
Title string `yaml:"FLinksTitle"`
Desc string `yaml:"FLinksDesc"`
List []FLink `yaml:"FLinksList"`
}
func main() {
// 读取命令行参数
filePath := flag.String("f", "", "友情链接的Yaml文件路径")
flag.IntVar(&timeOut, "t", 3, "检测网站的超时时长")
help := flag.Bool("h", false, "显示帮助信息")
flag.Parse()
// 验证文件路径是否为空
if *help || *filePath == "" {
flag.Usage()
return
}
// 读取YAML文件
data, err := ioutil.ReadFile(*filePath)
if err != nil {
fmt.Println("读取文件失败:", err)
return
}
// 解析YAML文件
var groups []FLinkGroup
err = yaml.Unmarshal(data, &groups)
if err != nil {
fmt.Println("解析YAML文件失败:", err)
return
}
// 统计站点数量
totalLinks := 0
for _, group := range groups {
totalLinks += len(group.List)
}
// 创建并发控制的通道
ch := make(chan FLink)
wg := sync.WaitGroup{}
// 启动并发检测
for groupIndex, group := range groups {
for linkIndex := range group.List {
wg.Add(1)
go checkLink(&groups[groupIndex].List[linkIndex], ch, &wg)
}
}
// 启动结果处理
go func() {
wg.Wait()
close(ch)
}()
// 统计结果
successCount := 0
failureCount := 0
failureLinks := make(map[string]string)
startTime := time.Now()
for link := range ch {
if link.Visible {
successCount++
} else {
failureCount++
failureLinks[link.Name] = link.Link
}
// 输出检测进度
finishLinks := successCount + failureCount
progress := float64(finishLinks) / float64(totalLinks) * 100
remainingTime := time.Since(startTime).Seconds() / progress * (100 - progress)
fmt.Printf("正在检测:%d/%d,检测进度:%.2f%%,预计剩余时间:%.2f秒\n", finishLinks, totalLinks, progress, remainingTime)
}
// 输出统计信息
fmt.Println("\n站点检测统计信息")
fmt.Println(strings.Repeat("-", 30))
fmt.Printf("总共检测站点:%-d\n", totalLinks)
fmt.Printf("正常访问站点:%-d\n", successCount)
fmt.Printf("失去连接站点:%-d\n", failureCount)
fmt.Printf("检测总共耗时:%-.2f秒\n", time.Since(startTime).Seconds())
fmt.Println(strings.Repeat("-", 30))
fmt.Println("")
// 输出连接失败的站点名称和地址
if failureLinks != nil && len(failureLinks) > 0 {
fmt.Println("连接失败的站点列表")
fmt.Println(strings.Repeat("-", 50))
for name, link := range failureLinks {
fmt.Printf("%s:%s\n", name, link)
}
fmt.Println(strings.Repeat("-", 50))
fmt.Println("")
}
// 将结果保存到原文件
resultData, err := yaml.Marshal(groups)
if err != nil {
fmt.Println("保存结果到文件失败:", err)
return
}
err = ioutil.WriteFile(*filePath, resultData, 0644)
if err != nil {
fmt.Println("保存结果到文件失败:", err)
return
}
fmt.Printf("结果已保存到%s文件\n", *filePath)
}
func checkLink(link *FLink, ch chan<- FLink, wg *sync.WaitGroup) {
defer wg.Done()
if link.Skip {
link.Visible = true
} else {
// 默认重试3次
for i := 0; i < 3; i++ {
// 等待时间逐次增加
if i > 1 {
time.Sleep(time.Duration(i) * time.Second)
}
// 发起HTTP请求
//fmt.Printf("正在检测%s....\n", link.Link)
client := http.Client{Timeout: time.Duration(timeOut) * time.Second}
resp, err := client.Get(link.Link)
if err == nil && resp.StatusCode == http.StatusOK {
link.Visible = true
ch <- *link
return
}
}
// 所有重试都失败,设置visible为false
link.Visible = false
}
ch <- *link
}
|