Home  >  Article  >  Backend Development  >  Record a small pitfall of Golang Recover

Record a small pitfall of Golang Recover

藏色散人
藏色散人forward
2020-12-22 15:56:503595browse

The following column will introduce you to a small pitfall of Golang Recover from the golang tutorial column. I hope it will be helpful to friends in need!

Record a small pitfall of Golang Recover

1.error

Golang has been criticized a lot for its lack of powerful and convenient exception handling. Mechanism, most high-level programming languages, such as Java, PHP, Python, etc., have a try catch mechanism. This exception catching mechanism can very conveniently handle various unexpected situations that may occur during program operation.

Strictly speaking, in Go, errors and exceptions are two different types. Errors generally refer to logical errors generated by the program, or unexpected unexpected situations, and exceptions are generally panic, such as corners. Mark out of bounds, segfault.

For errors, Golang adopts a very primitive method. We must manually handle every error that may occur, and generally return the error to the caller. The following writing method is very common in Go:

package mainimport (
    "errors"
    "fmt")func main() {
    s, err := say()
    if err != nil {
        fmt.Printf("%s\n", err.Error())
    } else {
        fmt.Printf("%s\n", s)
    }}func say() (string, error) {
    // do something
    return "", errors.New("something error")}复制代码

The biggest problem with this writing method is that each error needs to be judged and processed, which is very cumbersome. If we use the try catch mechanism, we can uniformly handle errors that may occur when multiple function calls are called, saving a little code and time. But we are not here to discuss Go’s exception and error handling mechanism today. We will just briefly talk about it here.

2.panic

Generally errors are displayed and returned explicitly by the program, while exceptions are often implicit and unpredictable. , such as the following code:

package mainimport "fmt"func main() {
    fmt.Printf("%d\n", cal(1,2))
    fmt.Printf("%d\n", cal(5,2))
    fmt.Printf("%d\n", cal(5,0)) //panic: runtime error: integer pide by zero 
    fmt.Printf("%d\n", cal(9,5))}func cal(a, b int) int {
    return a / b}复制代码

A panic will occur when performing the third calculation. This error will cause the program to exit, and the following code cannot be executed. Of course, you can say that this kind of error is theoretically predictable, and we just need to deal with it inside the cal function.

However, in actual development, there may be many places where panic occurs, and it is not something that can be seen at a glance. In a Web service, such a panic will cause the entire Web service to hang up, which is particularly dangerous.

3.recover

Although there is no try catch mechanism, Go actually has a similar recovery mechanism. The function is a bit weaker and the usage is very simple. :

package mainimport "fmt"func main() {
    fmt.Printf("%d\n", cal(1, 2))
    fmt.Printf("%d\n", cal(5, 2))
    fmt.Printf("%d\n", cal(5, 0))
    fmt.Printf("%d\n", cal(9, 2))}func cal(a, b int) int {
    defer func() {
        if err := recover(); err != nil {
            fmt.Printf("%s\n", err)
        }
    }()
    return a / b}复制代码

First of all, everyone must understand the role of defer. Simply put, defer is similar to the destructor in object-oriented. It will be executed when this function terminates, even if it is terminated by panic.

So, every time the cal function terminates, it will check whether there is an exception. If it occurs, we can handle it, such as recording logs, so that the program can continue to execute.

4. Pitfalls to note

Generally, the defer recover mechanism is often used in resident process applications, such as Web services. In Go Inside, each Web request will be assigned a goroutine to process. Without any processing, if a panic occurs on a certain request, it will cause the entire service to hang up. This is unacceptable, so it must be used in Web applications. Use recover to ensure that even if an error occurs in one request, other requests will not be affected.

Here I use a small piece of code to simulate:

package mainimport (
    "fmt")func main() {
    requests := []int{12, 2, 3, 41, 5, 6, 1, 12, 3, 4, 2, 31}
    for n := range requests {
        go run(n) //开启多个协程
    }

    for {
        select {}
    }}func run(num int) {
    //模拟请求错误
    if num%5 == 0 {
        panic("请求出错")
    }
    fmt.Printf("%d\n", num)}复制代码

The above code cannot be executed completely, because one of the coroutines will inevitably panic, causing the entire application to hang. The coroutine also stops executing.

The solution is the same as above. We only need to add defer recover to the run function, and the entire program will be very robust. Even if a panic occurs, it will be executed completely.

func run(num int) {
    defer func() {
        if err := recover();err != nil {
            fmt.Printf("%s\n", err)
        }
    }()
    if num%5 == 0 {
        panic("请求出错")
    }
    fmt.Printf("%d\n", num)}复制代码

The above code is just a demonstration. The real pitfall is: if you start other coroutines in the run function, the panic that occurs in this coroutine cannot be recovered, and it will still cause the entire process to hang. , we modified the above example:

func run(num int) {
    defer func() {
        if err := recover(); err != nil {
            fmt.Printf("%s\n", err)
        }
    }()
    if num%5 == 0 {
        panic("请求出错")
    }
    go myPrint(num)}func myPrint(num int) {
    if num%4 == 0 {
        panic("请求又出错了")
    }
    fmt.Printf("%d\n", num)}复制代码

I called another function through the coroutine in the run function, and this function will also panic. You will find that the entire program also hangs. Even if the run function has recover, it has no effect, which means that we still need to add recover to the myPrint function. But if you don't call the myPrint function using a coroutine, you can still capture the recovery by calling it directly.

To summarize, the defer recover mechanism is only for the panic that may be generated by the current function and the directly called function. It cannot handle the panic of other coroutines generated by its call. This is different from the try catch mechanism. .

Theoretically, all places where coroutines are used must be defer recover, so as to ensure that your application is foolproof. However, it can be determined according to the actual situation during development. Adding some functions that are unlikely to go wrong will also affect performance. .

The same goes for Go's web service. The default recovery mechanism can only capture one layer. If you use other coroutines in processing this request, you must be very careful. After all, as long as a panic occurs, the entire The web service will hang.

Finally, to summarize, although Go’s exception handling mechanism is not as efficient as many other languages, it can basically meet the needs. The official is currently improving this, and Go2 may see it.

Recommended: "go Language Tutorial"

The above is the detailed content of Record a small pitfall of Golang Recover. For more information, please follow other related articles on the PHP Chinese website!

Statement:
This article is reproduced at:learnku.com. If there is any infringement, please contact admin@php.cn delete