Go语言最方便的地方在于可以自由自在的起routine,并且不用自己维护队列。
一个很简单的处理模型,针对于长连接活动平凡的链接独立routine进行处理,方便同一连接上下文关联,read routine A讲接收到的消息解包生成消息丢到对应socket的routine B channel中进行处理,routine B在根据不同的任务丢到对应的routine B1或者 routine B2中进行处理,
我们需要一个channel回写routine B1 routine B2的退出信号给你 B 以便B进行响应处理。
那么这么想的话 B 和B1 B2 需要两个channel进行消息通信
伪代码如下,未做友好处理。
var B1 B1Consumer var B2 B2Consumer //statch 回写子routine的状态 statch := make(chan StatMsg,1) B1Ch := make(chan Msg,100) B2Ch := make(chan Msg,100) go B1(B1ch,statch) go B2(B2ch,statch) for{ select{ case cmsg, ok := <-BCh: if cmsg.type == B1{ B1ch<-cmsg }else{ B2ch<-cmsg } case StatMsg := <-statch: deal_state(msg) } }
乍一看没什么问题,后面再测试过程中 偶尔发现调用接口没有响应超时。
仔细排查后发现,B1 routine 在退出时回写了一个Done的StatMsg,然后就退出了。
此事Bch还在写数据如果此时B1ch写满了,那么写channel就会卡死,而B1退出的信号已经发送了 不在处理新数据了,那么B routine就会一直卡死在
B1ch<-cmsg
此时已经B routine 已经无法进行下一次select的操作,进而等Bch channel写满 卡死读取routine,等多个读取routine卡死,客户端表现就是无响应了 超时了。
找到问题解决办法就简单了。
增加写入channel超时 并且增加statch 的大小
statch := make(chan StatMsg,MAXCHANNELSIZE) select { case B1ch <- cmsg: case <-time.After(time.Duration(CLOSETIMEOUT) * time.Second): }
加大channel大小 避免多次触发超时,如果一旦出现超时,将超时的任务释放掉。
有疑问加站长微信联系(非本文作者)