golang入门学习笔记(三)

作者: 一字马胡
转载标志 【2017-11-23】

更新日志

日期 更新内容 备注
2017-11-23 新建文章 go语言入门学习笔记(三)

golang入门学习笔记系列

golang入门学习笔记(一)
golang入门学习笔记(二)

Select for golang

Go’s select lets you wait on multiple channel
operations. Combining goroutines and channels
with select is a powerful feature of Go.

go中的select类似于Linux中的select I/O,事件驱动,select等待在多个Channel,当某个Channel的数据准备好了的时候就会执行相应的动作,select保证会阻塞等待在一组Channel上,直到某个Channel完成(包括default语句),下面是一个使用select的例子:


//  delay "delay" s then push the msg to channel "ch"
//   just test the select of goLang
//   difference msg and delay is needed
func selectFunc(msg string, delay int, ch chan string)  {
    if delay == 0 {
        delay = 1 // default delay 1s
    } else if delay > 5 {
        delay = 5 // the max delay time
    }

    time.Sleep(time.Second * time.Duration(delay))

    if msg == "" {
        ch <- "default msg from selectFunc";
    } else {
        ch <- msg
    }
}

    ch1 := make(chan string)
    ch2 := make(chan string)

    go selectFunc("msg from channel 1", 1, ch1)
    go selectFunc("msg from channel 2", 2, ch2)

    //at least revive 2 msg
    //
    for i := 0; i < 2; i ++ {
        select {
        case msg := <- ch1:
            log.Printf("%s\n", msg)
        case msg := <- ch2:
            log.Printf("%s\n", msg)
        }
    }

上面的例子展示了go语言中select的使用方法,它用于等待Channel的数据准备完成,当有一个被select监听的Channel完成I/O之后就会进行相应的操作,上面的例子中没有涉及default,default代表的意思是如果没有Channel完成了I/O,那么就默认执行default分支。上面的例子好像比较无聊,下面一个例子使用select来实现timeout的功能:


// using a goroutine to run this function.
// you can set the timeout value, then the function
// will wait some time, then set the channel to true
// means timeout
//
func timeoutFunc(timeout int, flag chan bool)  {
    if timeout == 0 {
        flag <- true // timeout now.
    }

    time.Sleep(time.Second * time.Duration(timeout))

    flag <- true
}

    ch1 := make(chan string)
    ch2 := make(chan string)
    timeoutCh := make(chan bool)

    go selectFunc("msg from channel 1", 1, ch1)
    go selectFunc("msg from channel 2", 4, ch2)
    go timeoutFunc(2, timeoutCh)

    //at least revive 2 msg
    //
    for i := 0; i < 2; i ++ {
        select {
        case <- timeoutCh:
            log.Printf("Time out !")
        case msg := <- ch1:
            log.Printf("%s\n", msg)
        case msg := <- ch2:
            log.Printf("%s\n", msg)
        }
    }
    

运行上面的代码,你将会发现输出一条msg之后就会输出超时了,这还是非常有趣并且有用的。有些时候,我们需要显示的关闭一个select,让它不再阻塞监听它所关联着的Channel,下面是一个使用close功能的select例子:


//check if we have more job to do.
//
func moreJobCheck(jobs chan int, done chan bool)  {
    for {
        j , more := <- jobs
        if more {
            log.Printf("Receive job: %d\n", j)
        } else {
            done <- true
            log.Printf("No more Jobs\n")
            return
        }
    }
}


    jobCh := make(chan int)
    done := make(chan  bool)

    go moreJobCheck(jobCh, done)

    for i := 0; i < 4; i ++ {
        jobCh <- i
    }

    //close the job.
    close(jobCh)

    <- done
    

Timers for golang

We often want to execute Go code at some point in
the future, or repeatedly at some interval.
Go’s built-in timer and ticker features
make both of these tasks easy

Timers represent a single event in the future.
You tell the timer how long you want to wait,
and it provides a channel that will be notified at that time

If you just wanted to wait, you could have
used time.Sleep. One reason a timer may be
useful is that you can cancel the timer before it expires.

下面是关于timer的一个例子,上面提到,如果你仅仅是想要休眠一段时间,使用time.Sleep就可以了,但是使用timer的一个原因是你可以在timer超时之前取消它。


    timeout1 := time.NewTimer(time.Second * time.Duration(1)) // timeout := 1s
    timeout2 := time.NewTimer(time.Second * time.Duration(2)) // timeout := 2s

    <- timeout1.C

    log.Printf("timeout1 expired")

    go func() {
        <- timeout2.C
        log.Printf("timeout2 expired")
    }()

    timeout2Stop := timeout2.Stop() // stop the timer

    if timeout2Stop {
        log.Printf("timeout2 was stoped")
    }

tickers for golang

Timers are for when you want to do something
once in the future - tickers are for when
you want to do something repeatedly at regular intervals.

tickers和timer的区别在于,timer类似于计时器,会等待一段时间,而ticker类似于定时器,会周期性的超时,下面是一个使用ticker的例子:


    ticker := time.NewTicker(time.Second * time.Duration(1)) // schedule 1s

    go func() {
        for t := range ticker.C {
            log.Printf("Tocker at:%s\n", t)
        }
    } ()

    time.Sleep(time.Second * time.Duration(2))

    ticker.Stop()

Worker Pools

到目前为止已经学习了goroutine、Channel、select,那如何使用这些组件来实现Worker Pools呢?下面是一个实现:


//simulate the work,receive job ob the jobs channel,
// then do the job and get the result and send the
// corresponding results on results.
func wokrer(jobId int, jobs <- chan int, result chan <- int)  {
    for job := range jobs {
        log.Printf("worker #%d start to do the job #%d", jobId, job)
        time.Sleep(time.Millisecond * time.Duration(rand.Intn(1000)))
        log.Printf("worker #%d finished to do the job #%d", jobId, job)

        //corresponding results on results.
        result <- job * jobId
    }
}

    jobs := make(chan int, 10)
    result := make(chan int, 10)

    for w := 1; w < 5; w ++ {
        go wokrer(w, jobs, result)
    }

    for j := 1; j < 10; j ++ {
        jobs <- j
    }

    close(jobs)

    for r := 1; r < 5; r ++ {
        <- result
    }
    

上面的例子简单实现了一个worker pool,其实和java中的线程池是类似的,个中原委,还得自己慢慢体会啊!

Rate Limiting

Rate limiting is an important mechanism for controlling resource
utilization and maintaining quality of service. Go elegantly
supports rate limiting with >goroutines, channels, and tickers

下面是实现Rate Limiting的一个小例子:


    mockRequest := make(chan int, 10)

    for i := 1; i <= 10; i ++ {
        mockRequest <- i
    }

    close(mockRequest)

    limiter := time.Tick(time.Millisecond * time.Duration( 100 )) // 100 ms

    for request := range mockRequest {
        <- limiter
        log.Printf("Request inCome:%d At %s", request, time.Now())
    }

limiter的职责就是做限流,每过100ms再尝试去获取一个request来执行,这样就可以保护我们的server可以稳定运行了。但是这个例子中使用了timer来做Rate Limiting,下面的例子使用ticker来实现更复杂的Rate Limiting:


    burstyLimiter := make(chan time.Time, 5)

    for i := 0; i < 5; i ++ {
        burstyLimiter <- time.Now()
    }

    go func() {
        for t := range time.Tick(time.Millisecond * time.Duration(100)) {
            burstyLimiter <- t
        }
    } ()

    burstyRequest := make(chan int, 10)
    for i := 1; i <= 10; i ++ {
        burstyRequest <- i
    }

    close(burstyRequest)

    for request := range burstyRequest {
        <- burstyLimiter
        log.Printf("Request inCome: %d At %s", request, time.Now())
    }

上面的例子感觉就优点复杂了,我们使用了5个ticker来实现Rate Limiting,每个Rate Limiting过100ms接收一个请求来处理,当然Rate Limiting的需求是否那么紧迫还不得而知,我们总是希望我们的服务能跑得越快越好,QPS越高越好,但是同时我们也需要考虑是否需要做一些Rate Limiting的工作,这一点也需要仔细体会才能得到结论啊,但是golang提供了实现Rate Limiting的思路,日后可以借鉴一下。

文/一字马胡
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 195,898评论 5 462
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,401评论 2 373
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 143,058评论 0 325
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,539评论 1 267
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,382评论 5 358
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,319评论 1 273
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,706评论 3 386
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,370评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,664评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,715评论 2 312
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,476评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,326评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,730评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,003评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,275评论 1 251
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,683评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,877评论 2 335

推荐阅读更多精彩内容