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

时间:2017-09-08 13:41:10

标签: go goroutine

看了几个Web应用程序示例和样板,他们采取的方法往往采用这种形式(我在这里使用Gin处理程序作为示例,并假想用户和计费&#34 ; repository"从数据库或外部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
}

在上面的场景中,调用" User.FindById"可能会使用某种数据库驱动程序,但据我所知,所有可用的Golang数据库/ ORM库都在"同步"中返回数据。时尚(例如:作为返回值,而不是通过渠道)。因此,调用" User.FindById"在我继续执行" BillingInfo.FindById"之前,它会阻止它完成,因为它们可以并行工作,所以它们并不理想。

所以我认为最好的想法是利用go例程+ syncGroup来解决问题。像这样:

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
}

现在,这可能就是这个工作。但是对我来说似乎不必要的冗长,并且可能容易出错(如果我忘记了#34;在任何常规程序之前添加&#34;或者如果我忘记&#34;等待&#34;,那么这一切碎成片)。这是唯一的方法吗?或者是否有一些我错过的更简单的东西?

1 个答案:

答案 0 :(得分:1)

也许是这样的

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)
    for _, d := range do {
        go func(do func() error) {
            rcverr <- do()
        }(d)
    }
    for range do {
        e := <-rcverr
        if e != nil {
            err = e // return here for fast path
        }
    }
    close(rcverr)
    return err
}