Go 实现同步请求的思路
- 1、背景
- 2、思路
1、背景
在实际生产中,我们或多或少的需要对接第三方的同步接口,那我们如何实现高并发下go的同步请求方法呢,下面我就讲一下我最近在生产中运用的一种方法。
我们最近遇到的需求是对接第三方的接口,需要实时的获取到对方的相应数据,超过三秒没有拿到则判断请求失败,返回对应的错误信息。
2、思路
我的做法是,首先定义一个全局的channel
var ReqChannel = make(chan *Packet, 20)
然后在请求的业务里,定义一个请求唯一的id并往这个channel里塞值,同时起一个协程循环的读取这个channel
for {
req := <-model.ReqChannel
packet := &Packet{
Sign: SignPubwin,
CompanyId: CompanyId,
Type: req.Type,
Id: req.Id,
Data: req.Data,
}
Logger.Debugf("send packet to pubwin, id:%d, type:%#x, body:%s", req.Id, req.Type, string(req.Data))
err := ptr.SendPacket2Pubwin(packet)
if err != nil {
Logger.Errorf("send packet to pubwin failed, id:%d, type:%#x, body:%s, err:%v", err, req.Id, req.Type, string(req.Data))
}
}
读取完以后,再定义一个全局的map,key是刚读取的请求的唯一ID,value定义成需要返回类型的channel
// 我的请求是返回一个byte数组
var ResMapChannel map[uint32]chan []byte
请求返回后,将请求的ID和返回的值塞到定义的map里,同是在请求的地方循环的读取key为请求ID的channel,
并设置对应的超时时间,记得处理结束移除对应map里的key,防止循环处理
// 初始化channel
var respChannel []byte
// 设置超时时间
ticker := time.NewTicker(3 * time.Second)
// 阻塞读取channel
for {
select {
case respChannel = <-model.ResMapChannel[id]:
if respChannel != nil {
// 处理返回的channel []byte
err := json.Unmarshal(respChannel, &checkoutInfo)
if err != nil {
Logger.Errorf("response change failed! id %d, response channel %+v, err %s \n", id, respChannel, err.Error())
return nil, err
}
// 处理结束移除map
delete(model.ResMapChannel, id)
return checkoutInfo, nil
}
case <-ticker.C:
Logger.Errorf("response timeout, id %d", id)
// 处理结束移除map
delete(model.ResMapChannel, id)
return nil, nil
}
}
这样就可以保证高并发下的请求的返回的唯一正确性,以上仅仅是个人思路,仅供参考,对于有更好方法的小伙伴,或者是我的方法有不正确的地方,欢迎大家在评论区留言。