同时调用应该只获取一次数据。

huangapple go评论77阅读模式
英文:

Simultaneous calls should fetch data only once

问题

我遇到了一个不寻常的情况:

  • 一个外部工具同时调用了我的API的多个端点,
  • 所有端点都依赖于托管在S3上的同一个配置文件。

目前虽然能正常工作,但是它在并发时会同时获取相同的配置文件,而实际上只需要获取一次。为了尝试解决这个问题,我在这里提供了一个最简化的版本:https://go.dev/play/p/Nx-kidmprQx,它返回随机整数而不是进行HTTP调用。

当前输出结果为:

#2 开始
#1 开始
#1 结果 5577006791947779410
#2 结果 8674665223082153551
#3 开始
#3 结果 6129484611666145821

但是我希望前两个调用返回相同的值,因为它们是并发执行的:

#2 开始
#1 开始
#1 结果 5577006791947779410
#2 结果 5577006791947779410
#3 开始
#3 结果 6129484611666145821

我对如何解决这个问题感到困惑。多个goroutine等待同一个结果的情况让人感到困惑。有什么解决办法吗?

英文:

I'm running into an unusual scenario where:

  • an external tool calls multiple endpoints of my API at the same time,
  • all endpoints rely on the same config file hosted somewhere on S3.

That works but it's fetching the same config file many times concurrently when it could be fetched only once. To experiment with this I have a minimal version here https://go.dev/play/p/Nx-kidmprQx that gives back random ints rather than doing HTTP calls.

Currently it prints:

#2 start
#1 start
#1 result 5577006791947779410
#2 result 8674665223082153551
#3 start
#3 result 6129484611666145821

But I would like the first 2 calls to return the same value because they are done concurrently:

#2 start
#1 start
#1 result 5577006791947779410
#2 result 5577006791947779410
#3 start
#3 result 6129484611666145821

I'm struggling to imagine a solution for this. The fact that multiple goroutines should wait on a single result is confusing. How could it be done?

答案1

得分: 3

你可以使用golang.org/x/sync/singleflight中的Group.Do实现。

// Do执行并返回给定函数的结果,确保同一时间只有一个执行正在进行。
// 如果有重复的调用者,重复的调用者会等待原始调用完成并接收相同的结果。
// 返回值shared指示v是否被多个调用者使用。

修改你的示例,将函数的同步部分与通道操作分开:

func httpCall(run int) int {
	fmt.Printf("#%d start\n", run)
	time.Sleep(time.Second)
	return rand.Int()
}

你可以使用一个简单的包装函数来调用singleflight.Group

go func() {
	res, _, _ := g.Do("httpCall", func() (any, error) {
		return httpCall(1), nil
	})
	done <- res.(int)
}()

请参阅https://go.dev/play/p/ET0jNKEyg1_B

英文:

You can use the Group.Do implementation from golang.org/x/sync/singleflight

// Do executes and returns the results of the given function, making
// sure that only one execution is in-flight for a given key at a
// time. If a duplicate comes in, the duplicate caller waits for the
// original to complete and receives the same results.
// The return value shared indicates whether v was given to multiple callers.

Modifying your example to separate the synchronous part of the function from the channel operations:

func httpCall(run int) int {
	fmt.Printf(&quot;#%d start\n&quot;, run)
	time.Sleep(time.Second)
	return rand.Int()
}

You can call that with a simple wrapper function to handle the singleflight.Group call:

go func() {
	res, _, _ := g.Do(&quot;httpCall&quot;, func() (any, error) {
		return httpCall(1), nil
	})
	done &lt;- res.(int)
}()

See https://go.dev/play/p/ET0jNKEyg1_B

huangapple
  • 本文由 发表于 2022年8月6日 00:59:25
  • 转载请务必保留本文链接:https://go.coder-hub.com/73253115.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定