共有两台服务器A跟B,它们使用一条grpc长链接进行交互,A是grpc服务端,在公网;B是客户端,在内网。
服务器A的代码里有两个协程,分别监听http和grpc,用一个同步channel进行通信。
我的业务流程:
1、用户通过http接口访问A,A需要调用B的程序,此时使用Send(*Response) error把必要的参数发送过去;
2、B通过Recv()监听到A发来的请示,把运算结果通过Send()发送回A;
3、A通过Recv()收到运算结果,把结果输入到同步channel,http服务结束同步阻塞,返回http response给用户。
现在我要在步骤1加上30秒超时的限制,却出现了奇怪的问题,伪代码如下:
```
A.Send("data...")
go func() {
time.Sleep(30e9)
timeoutChan <- true
}()
select {
case <-timeoutChan:
// 返回超时错误信息
case resp := <-respChan:
// 正常返回数据
}
```
按照我的想法,应该是用户访问A的http接口时,如果超过30秒,会得到个超时错误。但却产生以下问题,有并发请示时(假设恰好请示1和请示2),请示1得到的http response是请示2的运算结果,请示2得到的http response是请示1的运算结果。
我回滚到原代码,(即去掉了超时限制功能,没有上边的go func(){time.Sleep()}和select... case...),只使用同步channel一直阻塞等待返回结果,进行压力测试,并不会出上以上问题,http request跟response都能对应。所以现在想不通,为何select... case...会出现以上问题,我需要做什么改进。
对于chan的timeout 参考下面的例子
```go
package main
import (
"fmt"
"os"
"time"
)
func main() {
notify := make(chan int64, 1)
timeoutCnt := 0
go func() {
counter := 0
for {
time.Sleep(1 * time.Second)
notify <- time.Now().UnixNano()
counter++
if counter > 30 {
break
}
}
}()
for {
select {
case currentTime := <-notify:
fmt.Println(currentTime)
case <-time.After(5 * time.Second):
fmt.Println("time out")
timeoutCnt++
if timeoutCnt > 3 {
os.Exit(0)
}
}
}
}
```
#1