golang:使用timingwheel进行大量ticker的优化

原创 2014年01月16日 21:39:12

Ticker

最近的项目用go实现的服务器需要挂载大量的socket连接。如何判断连接是否还存活就是我们需要考虑的一个问题了。

通常情况下面,socket如果被客户端正常close,服务器是能检测到的,但是如果客户端突然拔掉网线,或者是断电,那么socket的状态在服务器看来可能仍然是established。而实际上该socket已经不可用了。

为了判断连接是否可用,通常我们会用timer机制来定时检测,在go里面,这非常容易实现,如下:

ticker := time.NewTicker(60 * time.Second)

for {
    select {
        case <-ticker.C:
            if err := ping(); err != nil {
                close()
            }
    }
}

上面我们使用一个60s的ticker,定时去ping,如果ping失败了,证明连接已经断开了,这时候就需要close了。

这套机制比较简单,也运行的很好,直到我们的服务器连上了10w+的连接。因为每一个连接都有一个ticker,所以同时会有大量的ticker运行,cpu一直在30%左右徘徊,性能不能让人接受。

其实,我们只需要的是一套高效的超时通知机制。

Close channel to broadcast

在go里面,channel是一个很不错的东西,我们可以通过close channel来进行broadcast。如下:

ch := make(bool)

for i := 0; i < 10; i++ {
    go func() {
        println("begin")
        <-ch
        println("end")
    }
}

time.Sleep(10 * time.Second)

close(ch)

上面,我们启动了10个goroutine,它们都会因为等待ch的数据而block,10s之后close这个channel,那么所有等待该channel的goroutine就会继续往下执行。

TimingWheel

通过channel这种close broadcast机制,我们可以非常方便的实现一个timer,timer有一个channel ch,所有需要在某一个时间 “T” 收到通知的goroutine都可以尝试读该ch,当T到达时候,close该ch,那么所有的goroutine都能收到该事件了。

timingwheel的使用很简单,首先我们创建一个wheel

//这里我们创建了一个timingwheel,精度是1s,最大的超时等待时间为3600s
w := timingwheel.NewTimingWheel(1 * time.Second, 3600)

//等待10s
<-w.After(10 * time.Second)

因为timingwheel只有一个1s的ticker,并且只创建了3600个channel,系统开销很小。当我们程序换上timingwheel之后,10w+连接cpu开销在10%以下,达到了优化效果。

timingwheel的代码在这里

golang 高效低精度定时器实现

golang默认定时器是通过time模块实现的,不管是golang,libev,libevent的定时器都是通过最小堆实现的,通过最小堆实现的定时器加入定时器时间复杂度为O(lgn),在需要大量定时器...
  • yueguanghaidao
  • yueguanghaidao
  • 2015年05月31日 18:56
  • 8470

golang中time包用法

time包中包括两类时间:时间点(某一时刻)和时常(某一段时间) 1时间常量(时间格式化) const ( ANSIC = "Mon Jan _2 15:04:05 2...
  • chenbaoke
  • chenbaoke
  • 2014年11月26日 17:47
  • 23135

golang的定时器

package mainimport ( "fmt" "time" )func main() { aChan := make(chan int, 1) ticker :=...
  • cabing2005
  • cabing2005
  • 2017年03月28日 14:24
  • 1236

Golang time包的定时器/断续器

定时器在time包中有两个函数可以帮助我们初始化time.Timertime.Newtimer函数初始化一个到期时间据此时的间隔为3小时30分的定时器t := time.Newtimer(3*time...
  • grassroots2011
  • grassroots2011
  • 2015年10月31日 01:51
  • 4008

golang 高效低精度定时器实现

golang默认定时器是通过time模块实现的,不管是golang,libev,libevent的定时器都是通过最小堆实现的,通过最小堆实现的定时器加入定时器时间复杂度为O(lgn),在需要大量定时器...
  • yueguanghaidao
  • yueguanghaidao
  • 2015年05月31日 18:56
  • 8470

[golang]time.After 在select中使用的正确姿势(解决超时逻辑未生效bug)

前言select 的语法如下所示每个case都必须是一个通信 所有channel表达式都会被求值 所有被发送的表达式都会被求值 如果任意某个通信可以进行,它就执行;其他被忽略。 如果有多个case都可...
  • ZongYinHu
  • ZongYinHu
  • 2017年01月23日 19:34
  • 4801

golang:一个高性能低精度timer实现

在go自带的timer实现中,采用的是通常的最小堆的方式,具体可以参见这里。 最小堆能够提供很好的定时精度,但是,在实际情况中,我们并不需要这样高精度的定时器,譬如对于一个连接,如果它在2分钟以...
  • siddontang
  • siddontang
  • 2014年04月16日 10:24
  • 5658

go里面select-case和time.Ticker的使用注意事项

上周末参加Go技术聚会,京东的美女工程师讲到一个select-case和time.tick的坑,觉得很有意思,记录一下。 package main import ( "fmt" "runtime...
  • wk3368
  • wk3368
  • 2015年01月13日 16:47
  • 1165

在android需要大量使用xml进行网络传输时,如何用面向对象的思路降低xml的耦合性并增加重用性

根据一个需求来就行分析。由于
  • jys1115
  • jys1115
  • 2014年10月10日 21:17
  • 531

使用ngx_log_if模块,对阿里云SLB健康检测产生的大量日志进行过滤处理

最近公司购买了阿里云的SLB服务,健康检测使用的是http和https协议,直接访问web。 后端的80端口是nginx。阿里云会按设置的健康检测时间使用get方法,去访问域名下的检查路径,判断we...
  • mayifan0
  • mayifan0
  • 2017年02月24日 15:11
  • 469
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:golang:使用timingwheel进行大量ticker的优化
举报原因:
原因补充:

(最多只允许输入30个字)