Golang网络故障时如何中止程序运行
Golang网络故障时如何中止程序运行 我在循环中向网站发送GET请求,一旦遇到错误程序就会停止运行,循环中的后续工作也无法继续。请问在Go语言中通常如何处理异常情况?谢谢
如果网站不可用,我会收到一个错误,并且应用程序在请求时退出循环…
更多关于Golang网络故障时如何中止程序运行的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
你说得对,但我该如何捕获这个网络错误?
panic: runtime error: index out of range
你遇到了什么错误?
我从未遇到过在 defer 调用 Close() 时出现任何问题…
func main() {
fmt.Println("hello world")
}
我在网上找到了一个解决方案,它运行正常,循环不会被中断
defer func() {
if r := recover(); r != nil {
log.Println("Recovered in custom function", r)
}
}()
在你未展示的代码片段中,存在导致数组越界的操作。
与其使用 recover 来忽略问题,更应该在之前检查代码,确保正确校验所有返回的 error 值。
我认为你犯了一个非常大的错误。你必须检查代码中的问题,而不是从panic中恢复。程序中出现panic是不正常的。panic是非常严重的错误,只能在特殊情况下进行恢复,否则你必须找出导致错误的原因并进行修复(在你的具体案例中是索引越界问题)。
但如果我不需要在错误后返回呢?目前任何错误都会中断循环……应用程序停止……
我遇到这个错误,并包含了布尔值(真/假)检查,但还是出现错误
panic: runtime error: index out of range
我的循环停止了(在其他语言中我处理了异常,程序会继续运行……但在这里却不行。
在 Go 语言中,没有异常机制。
当函数执行失败时,要么通过返回值返回 error 类型,要么在发生严重错误时触发 panic。您可以使用 defer 从 panic 中恢复,具体方法参见 Defer, Panic, and Recover。
如需进一步帮助,建议尝试编写一个能复现问题的最小代码示例。
func main() {
fmt.Println("hello world")
}
NobbZ:
我不清楚在你展示的代码中什么会导致数组越界恐慌,因为没有任何通过索引访问切片的操作,因此我们已知的代码不可能产生这种恐慌。
在 -http.postform() 请求之后
但问题甚至不在于 resp==nil…,我尝试手动处理这些错误原因,但什么都没发现。我采用了恢复机制来处理,对我来说这个方法有效
// 代码示例
func example() {
// 此处添加代码
}
NobbZ:
失败函数要么在其返回值中包含
error,要么在出现严重问题时触发 panic。你可以使用defer从 panic 中恢复,如 Defer, Panic, and Recover 所述。
我使用 defer 关闭了响应体(defer rest.Body()),但随后遇到了错误,导致应用程序停止工作。如何确保循环不会因任何错误而中断,并保持继续运行?
可能最简单的做法是:如果出现错误,不要关闭响应体。
if err != nil {
checkerror(err) // 或者直接打印错误
return
}
或者,你可以让 checkerror() 返回一个布尔值,然后利用这个返回值:
func checkerror(err error) bool {
if err != nil {
fmt.Println(err)
return true
}
return false
}
…
if checkerror(err) { return }
附言:众所周知我不太喜欢 Go 的错误处理方式,但我更不赞同使用那些错误处理函数。错误应该在返回时就地处理。
以下是示例
func main(){
for i := 0; i < 10; i++ {
Simplereq()
}
}
func Simplereq() {
var s, _ = url.ParseQuery("name=Golberg&email=mymail+ffggff@mail.com&locale=en&terms_accepted=on")
apiUrl := "https://www.activityinfo.org/signUp"
resp, err := http.PostForm(apiUrl,s)
checkerror(err)
defer resp.Body.Close()
fmt.println(resp.StatusCode)
}
func check error(err error){
if err!=nil{
fmt.println(err)
}
}
ext:
但如果我不需要在错误后执行返回操作呢?
那么将 return 替换为应该执行的操作。
无论如何,如果 http.PostForm() 返回了错误,那么 resp 将会是 nil,因此任何尝试访问其字段的操作都会导致程序崩溃。
在你展示的代码中,当 err != nil 时,对 resp 进行任何有意义的操作都是不可能的。
ext:
我遇到了这个错误,并且包含了布尔值(真/假)的检查,但我还是收到了错误信息
panic: runtime error: index out of range
我不清楚在你展示的代码中是什么导致了数组越界崩溃,因为代码中没有通过索引访问切片的部分,因此已知的代码不会产生这种崩溃。
ext:
我在网上找到了一个解决方案,[使用
defer和recover]
这种情况确实非常少见。是的,这是一种处理错误的方式,但到目前为止,我总是能够通过其他方式处理错误,从而完全避免程序崩溃的发生。
而且,这种方式与在打印 err 值后立即执行 return 的效果基本相同。因为在目前展示的代码中,checkerror 之后的所有操作都会因为空指针而导致程序崩溃。
@NobbZ 说得对,我也高度怀疑这不是导致问题的代码。这只是一些概念验证。你提供的代码如果不修改一些错误(比如把 fmt.println 改成 fmt.Println,把 check error 改成 checkerror)是无法运行的。
当出现 panic 时,会显示堆栈跟踪信息,最上面一行显示了导致问题的代码行。例如运行以下代码:
package main
import (
"fmt"
"net/http"
"net/url"
)
func main() {
for i := 0; i < 10; i++ {
Simplereq()
}
}
func Simplereq() {
var s, _ = url.ParseQuery("name=Golberg&email=mymail+ffggff@mail.com&locale=en&terms_accepted=on")
apiUrl := "https://www.activityinfo.org/signUp"
resp, err := http.PostForm(apiUrl, s)
checkerror(err)
defer resp.Body.Close()
panic("Ouch!")
fmt.Println(resp.StatusCode)
}
func checkerror(err error) {
if err != nil {
fmt.Println(err)
}
}
将产生以下输出:
$ go run main.go
panic: Ouch!
goroutine 1 [running]:
main.Simplereq()
/Users/xxx/yyy/main.go:24 +0x184
main.main()
/Users/xxx/yyy/main.go:12 +0x2a
exit status 2
你可以看到导致 panic 的是 main.go 文件的第 24 行。如果 panic 发生在库函数内部,你必须沿着堆栈跟踪向下查找,找到来自你代码的调用。
在Go语言中处理网络请求异常时,通常不会直接中止整个程序,而是通过错误处理机制来优雅地管理异常情况。以下是几种常见的处理方式:
1. 使用错误检查与重试机制
通过检查error返回值,可以决定是否继续执行或进行重试。
package main
import (
"fmt"
"net/http"
"time"
)
func main() {
urls := []string{
"https://httpbin.org/get",
"https://invalid-url",
"https://httpbin.org/status/200",
}
for _, url := range urls {
err := makeRequest(url)
if err != nil {
fmt.Printf("请求 %s 失败: %v\n", url, err)
// 可以选择继续执行下一个请求
continue
}
}
}
func makeRequest(url string) error {
client := &http.Client{Timeout: 5 * time.Second}
resp, err := client.Get(url)
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return fmt.Errorf("非200状态码: %d", resp.StatusCode)
}
fmt.Printf("请求 %s 成功\n", url)
return nil
}
2. 使用带重试的请求函数
对于临时性网络故障,可以实现重试逻辑:
func makeRequestWithRetry(url string, maxRetries int) error {
client := &http.Client{Timeout: 5 * time.Second}
for i := 0; i < maxRetries; i++ {
resp, err := client.Get(url)
if err != nil {
if i == maxRetries-1 {
return err
}
time.Sleep(time.Duration(i+1) * time.Second)
continue
}
defer resp.Body.Close()
if resp.StatusCode == http.StatusOK {
fmt.Printf("请求 %s 成功 (第%d次尝试)\n", url, i+1)
return nil
}
if i == maxRetries-1 {
return fmt.Errorf("最终失败,状态码: %d", resp.StatusCode)
}
time.Sleep(time.Duration(i+1) * time.Second)
}
return nil
}
3. 使用context实现超时控制
通过context可以设置请求超时,避免长时间阻塞:
func makeRequestWithContext(url string, timeout time.Duration) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
if err != nil {
return err
}
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return fmt.Errorf("状态码错误: %d", resp.StatusCode)
}
fmt.Printf("请求 %s 成功\n", url)
return nil
}
4. 并发请求的错误处理
当使用goroutine并发请求时,可以通过channel收集错误:
func concurrentRequests(urls []string) {
errCh := make(chan error, len(urls))
for _, url := range urls {
go func(u string) {
err := makeRequest(u)
errCh <- err
}(url)
}
for range urls {
if err := <-errCh; err != nil {
fmt.Printf("请求失败: %v\n", err)
}
}
}
在Go中,推荐的做法是检查每个可能返回错误的操作,并根据业务需求决定是继续执行、重试还是记录错误。直接调用os.Exit()或panic()通常只在遇到不可恢复的错误时使用。

