管理Go中每个请求的连接

管理Go中每个请求的连接,go,connection,mgo,Go,Connection,Mgo,假设地说,为每个请求连接到数据库并在请求完成时关闭是一种良好的做法吗 我正在对数据库使用mongodb和mgo 在我的项目中,我希望通过从请求头获取数据库名称来连接到某个数据库(当然,这与身份验证机制相结合,例如我的应用程序中的JWT)。流程如下所示: func main() { ... // Configure connection and set in global var model.DBSession, err = mgo.DialWithInfo(mongoDB

假设地说,为每个请求连接到数据库并在请求完成时关闭是一种良好的做法吗

我正在对数据库使用
mongodb
mgo

在我的项目中,我希望通过从请求头获取数据库名称来连接到某个数据库(当然,这与身份验证机制相结合,例如我的应用程序中的JWT)。流程如下所示:

func main() {
    ...
    // Configure connection and set in global var
    model.DBSession, err = mgo.DialWithInfo(mongoDBDialInfo)
    defer model.DBSession.Close()
    ...

    n := negroni.Classic()
    n.Use(negroni.HandlerFunc(Middleware))

    ...
}

func Middleware(res http.ResponseWriter, req *http.Request, next http.HandlerFunc) {

    ...
    db := NewDataStore(clientDomain)
    // db.Close() is an alias for ds.session.Close(), code for this function is not included in this post
    // Im still experimenting with this, I need to make sure the session is only closed after a request has completed, currently it does not always do so
    defer db.Close()

    ctx := req.Context()
    ctx = context.WithValue(ctx, auth.DataStore, db)
    req = req.WithContext(ctx)
    ...
}

func NewDataStore(db string) *DataStore {
    store := &DataStore{
        db: DBSession.Copy().DB(db),
        session: DBSession.Copy(),
    }
    return store
}
  • 用户身份验证:

    POST to http://api.app.com/authenticate
    // which checks the user in a "global" database,
    // authenticates them and returns a signed JWT token
    // The token is stored in bolt.db for the authentication mechanism
    
  • 一些RESTful操作

    POST to http://api.app.com/v1/blog/posts
    // JWT middleware for each request to /v1* is set up
    // `Client-Domain` in header is set to a database's name, e.g 'app-com'
    // so we open a connection to that database and close when
    // request finishes
    
  • 因此,我的问题是:

  • 这是否可行我已经读过关于连接池和重用它们的书,但是我还没有读到太多关于它们的书
  • 是否有更好的方法实现所需的功能
  • 如何确保会话仅在请求完成时关闭
  • 我之所以需要这样做,是因为我们有多个供应商拥有相同的数据库集合,具有不同的条目,并且对自己的数据库的访问受到限制

    更新/解决方案 我最终使用了
    Go
    内置的
    Context
    ,复制了一个会话,并在任何需要执行CRUD操作的地方使用它

    比如:

    func main() {
        ...
        // Configure connection and set in global var
        model.DBSession, err = mgo.DialWithInfo(mongoDBDialInfo)
        defer model.DBSession.Close()
        ...
    
        n := negroni.Classic()
        n.Use(negroni.HandlerFunc(Middleware))
    
        ...
    }
    
    func Middleware(res http.ResponseWriter, req *http.Request, next http.HandlerFunc) {
    
        ...
        db := NewDataStore(clientDomain)
        // db.Close() is an alias for ds.session.Close(), code for this function is not included in this post
        // Im still experimenting with this, I need to make sure the session is only closed after a request has completed, currently it does not always do so
        defer db.Close()
    
        ctx := req.Context()
        ctx = context.WithValue(ctx, auth.DataStore, db)
        req = req.WithContext(ctx)
        ...
    }
    
    func NewDataStore(db string) *DataStore {
        store := &DataStore{
            db: DBSession.Copy().DB(db),
            session: DBSession.Copy(),
        }
        return store
    }
    
    然后在HandlerFunc中使用它,例如
    /v1/system/users

    func getUsers(res http.ResponseWriter, req *http.Request) {
        db := req.Context().Value(auth.DataStore).(*model.DataStore)
        users := make([]SystemUser{}, 0)
        // db.C() is an alias for ds.db.C(), code for this function is not included in this post
        db.C("system_users").Find(nil).All(&users)
    }
    

    与我试验的原始方法相比,响应时间减少了40%。

    假设这不是一个好的做法,因为:

  • 数据库逻辑分散在几个包中
  • 这很难测试
  • 您不能应用DI(主要是很难维护代码)
  • 答复你的问题:

  • Yes是可行的,但您不会在go包中使用连接池(如果您想了解更多关于连接池的信息,请查看代码)
  • 更好的方法是创建一个包含数据库连接的全局变量,并在应用程序停止时关闭(而不是每次请求都关闭连接)

  • 如何确保会话仅在请求完成时关闭?假设这不是一个好的做法,因为:

  • 数据库逻辑分散在几个包中
  • 这很难测试
  • 您不能应用DI(主要是很难维护代码)
  • 答复你的问题:

  • Yes是可行的,但您不会在go包中使用连接池(如果您想了解更多关于连接池的信息,请查看代码)
  • 更好的方法是创建一个包含数据库连接的全局变量,并在应用程序停止时关闭(而不是每次请求都关闭连接)

  • 我如何确保只有当请求完成时会话才会关闭在您的第二个回答中,您说了“创建包含数据库连接的全局变量”,但这将限制我一次只能连接一个数据库,对吗?我可以创建一个包含多个连接的全局slice变量并在应用程序死机时关闭所有连接吗?这取决于:主要是因为通过拨号,您可以一次连接到多个主机,然后您可以使用更改数据库的命令来更改Mongo数据库。这取决于你。但是,imho,每个db单独连接更干净。通过这样做,您还可以拆分每个db逻辑。您所说的db逻辑是什么意思?每个数据库都有相同的集合。假设您正在为SaaS网站托管/编辑复制shopify或类似服务,每当新用户注册并创建网站时,我们都会为他们的“专用”网站启动一个新的
    docker
    容器,该网站拥有自己的
    mongodb
    服务器。我们跟踪每个
    docker
    容器的所有者以及如何从“main”
    Go
    服务器访问它们。因此,区别实际上只是谁可以从
    main
    Go
    服务器中访问哪个数据库。并非所有情况下,相同的集合都意味着相同的逻辑(可能查询应该不同)。无论如何,对于这种情况,您可以尝试创建一个连接到更多主机的全局变量,请看一看,这已经解释得很清楚了。然后,如果您发现查询速度很慢,您可以随时添加连接,谢谢您的帮助。我将对此进行一些研究,并尝试为我的应用程序提出最佳解决方案。在您的第二个回答中,您说“创建包含数据库连接的全局变量”,但这将限制我一次只能连接一个数据库,对吗?我可以创建一个包含多个连接的全局slice变量并在应用程序死机时关闭所有连接吗?这取决于:主要是因为通过拨号,您可以一次连接到多个主机,然后您可以使用更改数据库的命令来更改Mongo数据库。这取决于你。但是,imho,每个db单独连接更干净。通过这样做,您还可以拆分每个db逻辑。您所说的db逻辑是什么意思?每个数据库都有相同的集合。假设您正在为SaaS网站托管/编辑复制shopify或类似服务,每当新用户注册并创建网站时,我们都会为他们的“专用”网站启动一个新的
    docker
    容器,该网站拥有自己的
    mongodb
    服务器。我们跟踪每个
    docker
    容器的所有者以及如何从“main”
    Go
    服务器访问它们。因此,区别实际上只是谁可以从
    main
    Go
    服务器中访问哪个数据库。并非所有情况下,相同的集合都意味着相同的逻辑(可能查询应该不同)。无论如何,对于这种情况,您可以尝试创建一个连接到更多主机的全局变量,请看一看,这已经解释得很清楚了。然后,如果您发现查询速度很慢,您可以随时添加连接,谢谢您的帮助。我将对此进行一些研究,并尝试为我的应用程序提出最佳解决方案