zoukankan      html  css  js  c++  java
  • How to Manage Database Timeouts and Cancellations in Go

    这里主要介绍借助request的context,但有几个点要注意,如果借助middleware,则会要求所有的请求都进行处理,你是否需要如此?还有要注意resp的writetimeout,否则如果request在sql处理的timeout大于w处的,基本也是无效的。

    没处理的,先制造一个条件sql

    package main
    
    import (
        "database/sql"
        "fmt"
        "log"
        "net/http"
    
        _ "github.com/lib/pq"
    )
    
    var db *sql.DB
    
    func slowQuery() error {
        _, err := db.Exec("SELECT pg_sleep(10)")
        return err
    }
    
    func main() {
        var err error
    
        db, err = sql.Open("postgres", "postgres://user:pa$$word@localhost/example_db")
        if err != nil {
            log.Fatal(err)
        }
    
        if err = db.Ping(); err != nil {
            log.Fatal(err)
        }
    
        mux := http.NewServeMux()
        mux.HandleFunc("/", exampleHandler)
    
        log.Println("Listening...")
        err = http.ListenAndServe(":5000", mux)
        if err != nil {
            log.Fatal(err)
        }
    }
    
    func exampleHandler(w http.ResponseWriter, r *http.Request) {
        err := slowQuery()
        if err != nil {
            serverError(w, err)
            return
        }
    
        fmt.Fprintln(w, "OK")
    }
    
    func serverError(w http.ResponseWriter, err error) {
        log.Printf("ERROR: %s", err.Error())
        http.Error(w, "Sorry, something went wrong", http.StatusInternalServerError)
    }

    几个处理demo

    package main
    
    import (
        "context" // New import
        "database/sql"
        "fmt"
        "log"
        "net/http"
        "time" // New import
    
        _ "github.com/lib/pq"
    )
    
    var db *sql.DB
    
    func slowQuery(ctx context.Context) error {
        // Create a new child context with a 5-second timeout, using the
        // provided ctx parameter as the parent.
        ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
        defer cancel()
    
        // Pass the child context (the one with the timeout) as the first
        // parameter to ExecContext().
        _, err := db.ExecContext(ctx, "SELECT pg_sleep(10)")
        return err
    }
    
    ...
    
    func exampleHandler(w http.ResponseWriter, r *http.Request) {
        // Pass the request context to slowQuery(), so it can be used as the 
        // parent context.
        err := slowQuery(r.Context())
        if err != nil {
            serverError(w, err)
            return
        }
    
        fmt.Fprintln(w, "OK")
    }
    
    ...
    package main
    
    import (
        "context"
        "database/sql"
        "errors" // New import
        "fmt"
        "log"
        "net/http"
        "time"
    
        _ "github.com/lib/pq"
    )
    
    var db *sql.DB
    
    func slowQuery(ctx context.Context) error {
        ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
        defer cancel()
    
        _, err := db.ExecContext(ctx, "SELECT pg_sleep(10)")
        // If we get a "pq: canceling statement..." error wrap it with the 
        // context error before returning.
        if err != nil && err.Error() == "pq: canceling statement due to user request" {
            return fmt.Errorf("%w: %v", ctx.Err(), err)
        }
    
        return err
    }
    
    ...
    
    func exampleHandler(w http.ResponseWriter, r *http.Request) {
        err := slowQuery(r.Context())
        if err != nil {
            // Check if the returned error equals or wraps context.Canceled and 
            // record a warning if it does.
            switch {
            case errors.Is(err, context.Canceled):
                serverWarning(err)
            default:
                serverError(w, err)
            }
            return
        }
    
        fmt.Fprintln(w, "OK")
    }
    
    func serverWarning(err error) {
        log.Printf("WARNING: %s", err.Error())
    }
    
    ...
    ...
    
    func main() {
        var err error
    
        db, err = sql.Open("postgres", "postgres://user:pa$$word@localhost/example_db")
        if err != nil {
            log.Fatal(err)
        }
    
        // Create a context with a 10-second timeout, using the empty 
        // context.Background() as the parent.
        ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
        defer cancel()
    
        // Use this when testing the connection pool.
        if err = db.PingContext(ctx); err != nil {
            log.Fatal(err)
        }
    
        mux := http.NewServeMux()
        mux.HandleFunc("/", exampleHandler)
    
        log.Println("Listening...")
        err = http.ListenAndServe(":5000", mux)
        if err != nil {
            log.Fatal(err)
        }
    }
    
    ...

    middleware

    func setTimeout(next http.Handler) http.Handler {
        return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
            ctx, cancel := context.WithTimeout(r.Context(), 5*time.Second)
            defer cancel()
    
            // This gives you a copy of the request with a the request context 
            // changed to the new context with the 5-second timeout created 
            // above.
            r = r.WithContext(ctx)
            next.ServeHTTP(w, r)
        })
    }

    原文地址

    https://www.alexedwards.net/blog/how-to-manage-database-timeouts-and-cancellations-in-go

    作者写的很详细,内容其实并不难。

    一个没有高级趣味的人。 email:hushui502@gmail.com
  • 相关阅读:
    文件和网络
    设备支持
    用户界面概述
    介绍
    图形和描画
    应用程序偏好设置
    文本和Web
    人机界面准则:创建优秀的用户界面
    事件处理
    iPhone OS平台:丰富的可能性
  • 原文地址:https://www.cnblogs.com/CherryTab/p/12757886.html
Copyright © 2011-2022 走看看