为避免Go Web应用程序处理程序中多个数据库调用相互阻塞,goroutines + syncGroup是唯一方法吗? [英] To avoid multiple database calls blocking each other in a Go web app handler, are goroutines + syncGroup the only way?

查看:37
本文介绍了为避免Go Web应用程序处理程序中多个数据库调用相互阻塞,goroutines + syncGroup是唯一方法吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

看了几个Web应用程序示例和样板之后,它们采用的方法往往是这种形式(我在这里以Gin处理程序为例,虚构的User和Billing存储库"结构从数据库或外部API提取数据.我省略了错误处理以简化示例):

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) :

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库都以同步"方式返回数据(例如:作为返回值,而不是通过渠道).因此,在我继续执行"BillingInfo.FindById"之前,对"User.FindById"的调用将一直阻塞到完成为止,这根本不是理想的选择,因为它们都可以并行工作.

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 (e.g: as return values, not via channels). 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.

因此,我认为最好的主意是利用go例程+ syncGroup解决该问题.像这样:

So I figured that the best idea was to make use of go routines + syncGroup to solve the problem. 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
}

现在,这大概可以完成工作了.但是这对我来说似乎不必要地冗长,并且可能容易出错(如果我在执行go例程之前忘记将添加"到waitGroup,或者如果我忘记了等待",那么一切都将分崩离析).这是唯一的方法吗?还是有更简单的东西让我错过了?

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). Is this the only way to do this? Or is there something simpler that I'm missing out?

推荐答案

也许是这样的


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
}

这篇关于为避免Go Web应用程序处理程序中多个数据库调用相互阻塞,goroutines + syncGroup是唯一方法吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆