记一次Golang routine卡死

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大小 避免多次触发超时,如果一旦出现超时,将超时的任务释放掉。

时间: 2024-07-30 10:04:32

记一次Golang routine卡死的相关文章

个人犯的一个golang routine错误

认识golang也不少时间了,也做过几个项目.最近发现之前用golang写的一个服务,内存涨得比较快,一直没找出来原因来.今天把疑惑发到群里,经过golang学习班的童鞋的指点,发现我一个常用的错误. 在不少golang入门的文章上,用并发的例子一般是这样写的: package main import ( "fmt" "time" ) func main() { messages := make(chan int) go func() { time.Sleep(ti

记一次sql执行卡死的问题

这几天一直在进行aix环境下10.2.0.1数据库升级为10.2.0.5的实验 但是在跑数据库脚本的时候总是跑到某一步数据库就没反应了,然后检查数据库等待事件一直显示为空闲等待 日志中报错 Errors in file /u01/app/oracle/admin/test/bdump/test_arc1_23310.trc: ORA-16038: log 3 sequence# 90 cannot be archived ORA-19809: limit exceeded for recover

golang在多个go routine中进行map或者slice操作应该注意的对象。

因为golang的map和列表切片都是引用类型,且非线程安全的,所以在多个go routine中进行读写操作的时候,会产生"map read and map write"的panic错误. 某一些类型的对象,会有这种类似的set方法来写数据,或者get方法来返回一个map: func (this *object) Set(name, val) { this.Lock() defer this.Unlock() this.m[name] = val } func (this *objec

golang for thread channel routine consumer and producer

package main import "time" func testThread(){ i:=3 go func(a int){ println(a) println("this is go func!") }(i) time.Sleep(1*time.Second) println("hello main thread!") } func testChannel(){ //read write channel ch:=make(chan i

golang 多个routine之间的同步

本文以一个例子的方式介绍channel在同步中的使用. 下面的例子中,主task首先阻塞,直到两个task完成后,再继续执行. package main import ( "log" "time" ) func main() { ch := make(chan int) go task1(ch) go task2(ch) for i:=0; i<2; i++ { v := <-ch log.Println("one task done:&quo

golang修仙记之gorm(一)

学习了如何连接数据库.简单的错误处理.关闭数据库.创建表.创建表中的一条记录.读取表的记录.更新表的记录.删除标的记录 package main import ( "github.com/jinzhu/gorm" _ "github.com/jinzhu/gorm/dialects/mysql" "time" ) type User struct { gorm.Model Name string Age int Birthday time.Tim

足记--大家一起来吐槽!(二)

今天我们来继续来吐槽足记,我将从简单,极致这两个方面展开论述. 四.简单 首先,我们先来分析一下大片模式的简单化操作,给足记带来的优势. 足记正是凭借着三步操作完成了电影触感的戏剧化照片,以其简单粗暴的方式,深深博得用户的喜爱,使得用户量突飞猛进: 下面我们在来分析一下"发现"这个模块,是足记的一个瑕疵,给足记带来的不足.          足记中"发现"这个模块的内容虽然做的很精彩,但是由于其排版密集,导致视觉疲劳,不得不说反而影响了这内容的精彩啊,删繁就简,才是

golang并发编程

golang并发编程 引子 golang提供了goroutine快速实现并发编程,在实际环境中,如果goroutine中的代码要消耗大量资源时(CPU.内存.带宽等),我们就需要对程序限速,以防止goroutine将资源耗尽.以下面伪代码为例,看看goroutine如何拖垮一台DB.假设userList长度为10000,先从数据库中查询userList中的user是否在数据库中存在,存在则忽略,不存在则创建. //不使用goroutine,程序运行时间长,但数据库压力不大 for _,v:=ra

记一次公司仓库服务器死锁过程

记一次公司仓库服务器死锁过程 仓库拣货卡死,排查了数据库的很多地方,都没有头绪,最后到SQL Server 错误日志里查看,终于发现了蛛丝马迹 EXEC xp_readerrorlog 0,1,NULL,NULL,'2015-09-21','2015-10-10','DESC' waiter id=process5c30e08 mode=U requestType=wait waiter-list owner id=process5c26988 mode=X owner-list keylock