作者: 一字馬胡
轉(zhuǎn)載標志 【2017-11-23】
更新日志
日期 | 更新內(nèi)容 | 備注 |
---|---|---|
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,事件驅(qū)動,select等待在多個Channel反镇,當某個Channel的數(shù)據(jù)準備好了的時候就會執(zhí)行相應的動作,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的數(shù)據(jù)準備完成坤按,當有一個被select監(jiān)聽的Channel完成I/O之后就會進行相應的操作棵磷,上面的例子中沒有涉及default,default代表的意思是如果沒有Channel完成了I/O晋涣,那么就默認執(zhí)行default分支仪媒。上面的例子好像比較無聊,下面一個例子使用select來實現(xiàn)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)
}
}
運行上面的代碼谢鹊,你將會發(fā)現(xiàn)輸出一條msg之后就會輸出超時了算吩,這還是非常有趣并且有用的。有些時候佃扼,我們需要顯示的關(guān)閉一個select偎巢,讓它不再阻塞監(jiān)聽它所關(guān)聯(lián)著的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.
下面是關(guān)于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的區(qū)別在于,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
到目前為止已經(jīng)學習了goroutine冷溃、Channel、select梦裂,那如何使用這些組件來實現(xiàn)Worker Pools呢似枕?下面是一個實現(xiàn):
//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
}
上面的例子簡單實現(xiàn)了一個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
下面是實現(xiàn)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來執(zhí)行牵咙,這樣就可以保護我們的server可以穩(wěn)定運行了派近。但是這個例子中使用了timer來做Rate Limiting,下面的例子使用ticker來實現(xiàn)更復雜的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())
}
上面的例子感覺就優(yōu)點復雜了洁桌,我們使用了5個ticker來實現(xiàn)Rate Limiting渴丸,每個Rate Limiting過100ms接收一個請求來處理,當然Rate Limiting的需求是否那么緊迫還不得而知,我們總是希望我們的服務能跑得越快越好谱轨,QPS越高越好戒幔,但是同時我們也需要考慮是否需要做一些Rate Limiting的工作,這一點也需要仔細體會才能得到結(jié)論啊土童,但是golang提供了實現(xiàn)Rate Limiting的思路诗茎,日后可以借鑒一下。