简体   繁体   English

为了避免在 Go 网络应用程序处理程序中多个数据库调用相互阻塞,goroutines + syncGroup 是唯一的方法吗?

[英]To avoid multiple database calls blocking each other in a Go web app handler, are goroutines + syncGroup the only way?

Having taken a look at several web application examples and boilerplates, the approach they take tends to be in the form of this (I'm using a Gin handler here as an example, and imaginary User and Billing "repository" structs that fetch data from either a database or an external API. I omitted error handling to make the example shorter) :查看了几个 Web 应用程序示例和样板后,他们采用的方法往往是这种形式(我在这里使用 Gin 处理程序作为示例,以及从数据库或外部 API。我省略了错误处理以使示例更短):

func GetUserDetailsHandler(c *gin.Context) {
    //this result presumably comes from the app's database
    var userResult = UserRepository.FindById( c.getInt("user_id") )

    //assume that this result comes from a different data source (e.g: a different database) all together, hence why we're not just doing a join query with "User"
    var billingInfo = BillingRepository.FindById(  c.getInt("user_id")  )

    c.JSON(http.StatusOK, gin.H {
        user_data : userResult,
        billing_data : billingInfo,
    })

    return
}

In the above scenario, the call to "User.FindById" might use some kind of database driver, but as far as I'm aware, all available Golang database/ORM libraries return data in a "synchronous" fashion (eg: as return values, not via channels).在上述场景中,对“User.FindById”的调用可能会使用某种数据库驱动程序,但据我所知,所有可用的 Golang 数据库/ORM 库都以“同步”方式返回数据(例如:作为返回值,而不是通过渠道)。 As such, the call to "User.FindById" will block until it's complete, before I can move on to executing "BillingInfo.FindById", which is not at all ideal since they can both work in parallel.因此,对“User.FindById”的调用将阻塞,直到它完成,然后我才能继续执行“BillingInfo.FindById”,这一点都不理想,因为它们可以并行工作。

So I figured that the best idea was to make use of go routines + syncGroup to solve the problem.所以我认为最好的想法是利用 goroutines + syncGroup 来解决问题。 Something like this:像这样的东西:

func GetUserDetailsHandler(c *gin.Context) {
    var waitGroup sync.WaitGroup

    userChannel := make(chan User);
    billingChannel := make(chan Billing)

    waitGroup.Add(1)
    go func() {
            defer waitGroup.Done()
            userChannel <- UserRepository.FindById( c.getInt("user_id") )               
    }()

    waitGroup.Add(1)
    go func(){
            defer waitGroup.Done()
            billingChannel <- BillingRepository.FindById(  c.getInt("user_id") )
    }()

    waitGroup.Wait()

    userInfo := <- userChannel
    billingInfo = <- billingChannel

    c.JSON(http.StatusOK, gin.H {
        user_data : userResult,
        billing_data : billingInfo,
    })

    return
}

Now, this presumably does the job.现在,这大概可以完成工作。 But it seems unnecessarily verbose to me, and potentially error prone (if I forget to "Add" to the waitGroup before any go routine, or if I forget to "Wait", then it all falls apart).但这对我来说似乎是不必要的冗长,并且可能容易出错(如果我忘记在任何 go 例程之前“添加”到 waitGroup,或者如果我忘记“等待”,那么一切都会分崩离析)。 Is this the only way to do this?这是唯一的方法吗? Or is there something simpler that I'm missing out?或者有什么更简单的东西我错过了?

maybe something like this也许像这样


package main

import (
    "fmt"
)

func GetUserDetailsHander(c *gin.Context) {
    var userInfo USERINlFO
    var billingInfo BILLL

    err := parallel(
        func() (e error) {
            userInfo, e = UserRepository.FindById(c.getInt("user_id"))
            return
        },
        func() (e error) {
            billingInfo, e = BillingRepository.FindById(c.getInt("user_id"))
            return
        },
    )
    fmt.Println(err)

    c.JSON(http.StatusOK, gin.H{
        user_data:    userResult,
        billing_data: billingInfo,
    })

    return
}
func parallel(do ...func() error) error {
    var err error
    rcverr := make(chan error)
    var wg sync.WaitGroup
    for _, d := range do {
        wg.Add(1)
        go func(do func() error) {
            rcverr <- do()
            wg.Done()
        }(d)
    }
    go func() {
        wg.Wait()
        close(rcverr)
    }()
    for range do {
        e := <-rcverr
        if e != nil {
            err = e // return here for fast path
        }
    }
    return err
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM