在Go中,在HTTP处理程序中使用PGX上下文的正确方法是什么?
更新1 :似乎使用与HTTP请求绑定的上下文可能导致“上下文取消”错误。但是,使用context.background()作为父母似乎工作正常。
// This works, no 'context canceled' errors
ctx, cancel := context.WithTimeout(context.Background(), 100*time.Second)
// However, this creates 'context canceled' errors under mild load
// ctx, cancel := context.WithTimeout(r.Context(), 100*time.Second)
defer cancel()
app.Insert(ctx, record)
(下面更新了代码示例,以制作一个为repro的独立示例),
在GO中,我有一个HTTP处理程序,如以下代码。在此端点的第一个HTTP请求上,我将获得上下文取消
错误。但是,数据实际上插入了数据库。在此端点的后续请求下,没有给出这样的错误,并且数据也成功地插入了数据库中。
问题:我是否在http处理程序和上下文 nofollow noreferrer“> pgx queryrow方法? (如果没有更好的方法吗?)
如果您将此代码复制到main.go并运行进行MAIN.GO
,转到 localhost:4444/create
并保持 CTRL-R
要产生轻度负载,您应该看到一些上下文取消的错误。
package main
import (
"context"
"fmt"
"log"
"math/rand"
"net/http"
"time"
"github.com/jackc/pgx/v4/pgxpool"
)
type application struct {
DB *pgxpool.Pool
}
type Task struct {
ID string
Name string
Status string
}
//HTTP GET /create
func (app *application) create(w http.ResponseWriter, r *http.Request) {
fmt.Println(r.URL.Path, time.Now())
task := &Task{Name: fmt.Sprintf("Task #%d", rand.Int()%1000), Status: "pending"}
// -------- problem code here ----
// This line works and does not generate any 'context canceled' errors
//ctx, cancel := context.WithTimeout(context.Background(), 100*time.Second)
// However, this linegenerates 'context canceled' errors under mild load
ctx, cancel := context.WithTimeout(r.Context(), 100*time.Second)
// -------- end -------
defer cancel()
err := app.insertTask(ctx, task)
if err != nil {
fmt.Println("insert error:", err)
return
}
fmt.Fprintf(w, "%+v", task)
}
func (app *application) insertTask(ctx context.Context, t *Task) error {
stmt := `INSERT INTO task (name, status) VALUES ($1, $2) RETURNING ID`
row := app.DB.QueryRow(ctx, stmt, t.Name, t.Status)
err := row.Scan(&t.ID)
if err != nil {
return err
}
return nil
}
func main() {
rand.Seed(time.Now().UnixNano())
db, err := pgxpool.Connect(context.Background(), "postgres://test:test123@localhost:5432/test")
if err != nil {
log.Fatal(err)
}
log.Println("db conn pool created")
stmt := `CREATE TABLE IF NOT EXISTS public.task (
id uuid NOT NULL DEFAULT gen_random_uuid(),
name text NULL,
status text NULL,
PRIMARY KEY (id)
); `
_, err = db.Exec(context.Background(), stmt)
if err != nil {
log.Fatal(err)
}
log.Println("task table created")
defer db.Close()
app := &application{
DB: db,
}
mux := http.NewServeMux()
mux.HandleFunc("/create", app.create)
log.Println("http server up at localhost:4444")
err = http.ListenAndServe(":4444", mux)
if err != nil {
log.Fatal(err)
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
TLDR:使用
R.Context()
在生产中工作正常,使用浏览器进行测试是一个问题。HTTP请求将获得其自己的上下文,该上下文在请求完成后将被取消。这是一个功能,而不是错误。期望开发人员使用它并在请求中断客户端或超时时优雅地关闭执行。例如,取消的请求可能意味着客户端从未看到响应(交易结果),而开发人员可以决定回滚该事务。
在生产中,对于通常设计/构建API的请求取消不经常发生。通常,流由服务器控制,服务器在取消请求之前返回结果。
多个客户端请求不会彼此影响,因为它们获得了独立的循环和上下文。同样,我们正在谈论通常设计/构建应用程序的快乐路径。您的示例应用程序看起来不错,应该很好。
问题是我们如何测试应用程序。我们使用浏览器并刷新单个浏览器会话,而不是创建多个独立的请求。我没有检查到底发生了什么,而是假设浏览器终止现有请求,以便在单击
ctrl-r
时运行新请求。服务器看到请求终止并将其传达给您的代码作为上下文取消。尝试使用
curl
或创建独立请求的其他脚本/实用程序来测试您的代码。我相信您不会在这种情况下看到取消。TLDR: Using
r.Context()
works fine in production, testing using Browser is a problem.An HTTP request gets its own context that is cancelled when the request is finished. That is a feature, not a bug. Developers are expected to use it and gracefully shutdown execution when the request is interrupted by client or timeout. For example, a cancelled request can mean that client never see the response (transaction result) and developer can decide to roll back that transaction.
In production, request cancelation does not happen very often for normally design/build APIs. Typically, flow is controlled by the server and the server returns the result before the request is cancelled.
Multiple Client requests does not affect each other because they get independent go-routine and context. Again, we are talking about happy path for normally designed/build applications. Your sample app looks good and should work fine.
The problem is how we test the app. Instead of creating multiple independent requests, we use Browser and refresh a single browser session. I did not check what exactly is going on, but assume that the Browser terminates the existing request in order to run a new one when you click
ctrl-R
. The server sees that request termination and communicates it to your code as context cancelation.Try to test your code using
curl
or some other script/utility that creates independent requests. I am sure you will not see cancelations in that case.