在这里看到的。https://mzh.io/%E4%B8%80%E4%BA%9BGolang%E5%B0%8F%E6%8A%80%E5%B7%A7
在 nsq 中,需要读取之前磁盘上的,或者是从内存中直接读取,一般人都是先判断内存中有没有数据,然而,nsq 另辟蹊径使用了 select 语句,把 CSP 模式用到了极致。
select {
case msg = <-c.memoryMsgChan: //尝试从内存中读取
case buf = <-c.backend.ReadChan(): //如果内存中没有,直接从磁盘上读取
msg, err = decodeMessage(buf)
if err != nil {
c.ctx.nsqd.logf("ERROR: failed to decode message - %s", err)
continue
}
这个想法蛮好啊。
1
twm 2017-12-24 09:51:27 +08:00 via iPhone
go 是最好的语言,go 实现的网站
www.cshome.com |
2
Muninn 2017-12-24 10:13:58 +08:00
这样写确实挺简洁的,比我的 if else 短一些。
|
3
pathbox 2017-12-24 10:20:33 +08:00 via iPhone
你需要多开 channel 和 goroutine,这是占一些资源的 不过可以忽略。go 语言级别支持 csp,go 的 select 是在用户态中进行对 goroutine 的调度。
|
4
xrlin 2017-12-24 10:36:38 +08:00
但是从 channel 中取出后,channel 中的消息已经没有了,这样还需要在消费后再将消息推送进 channel,并且还要自己维护失效时间。
|
7
gamexg 2017-12-24 11:12:41 +08:00 1
go 并没有承诺 select 前面的高优先级,实测也发现并不是按照顺序确定优先级,而是乱序。
``` package main import ( "fmt" "time" ) func main() { c1 := make(chan int, 10) c2 := make(chan int, 10) go func() { for i := 0; i < 100; i++ { c1 <- 1 } }() go func() { for i := 0; i < 100; i++ { c2 <- 2 } }() time.Sleep(100 * time.Millisecond) for { select { case i := <-c1: fmt.Println(i) case i := <-c2: fmt.Println(i) } } } ``` 输出结果: 1 2 2 2 1 2 1 1 2 1 2 2 2 1 2 1 2 2 2 2 1 1 2 1 2 1 2 1 2 1 2 2 2 2 1 1 1 2 1 2 2 1 1 2 1 1 2 2 2 1 1 2 2 2 1 2 1 2 1 2 2 2 1 |
8
pubby 2017-12-24 11:15:53 +08:00
慎用,case 后面的都会被执行,本质上是在比哪个 case 更快返回
func TestSelectCase(t *testing.T) { var fromCache = func() chan int { t.Log("call fromCache()") c := make(chan int, 1) <-time.After(time.Second * 1) c <- 1 return c } var fromDB = func() chan int { t.Log("call fromDB()") c := make(chan int, 1) <-time.After(time.Second * 2) c <- 1 return c } select { case <-fromCache(): t.Log("got from cache") case <-fromDB(): t.Log("got from db") } } select_case_test.go:11: call fromCache() select_case_test.go:21: call fromDB() select_case_test.go:34: got from db ///////////////////////// func TestSelectCase(t *testing.T) { var fromCache = func() chan int { t.Log("call fromCache()") c := make(chan int, 1) go func() { <-time.After(time.Second * 1) c <- 1 }() return c } var fromDB = func() chan int { t.Log("call fromDB()") c := make(chan int, 1) go func() { <-time.After(time.Second * 2) c <- 1 }() return c } select { case <-fromCache(): t.Log("got from cache") case <-fromDB(): t.Log("got from db") } } select_case_test.go:11: call fromCache() select_case_test.go:23: call fromDB() select_case_test.go:36: got from cache |
9
sonyxperia 2017-12-24 11:25:45 +08:00
@twm 你给一个页面怎么看出来是 go 实现的
|
10
pathbox 2017-12-24 11:42:04 +08:00 via iPhone
@pubby 所以 select 还要考虑 switch 的条件,case 的条件同一时刻只满足一个,如果同时满足多个 调度器选择执行哪一个都是有可能的
|
13
pathbox 2017-12-24 12:53:25 +08:00
@jpmorn 有弊端。 当缓存和硬盘中都有数据的时候, 理论上是想从缓存读取,这样速度快,才起到缓存的作用,而却从硬盘取数据了,这样即使有缓存数据也没有起到作用。 所以 这种写法只适合非常简单的一种缓存机制,即使从硬盘取也不会很慢。 真要做大规模的缓存,不适合吧,当高并发的时候,会太多缓存失效的情况会发生,而实际中这些缓存数据都是有的
|
14
pubby 2017-12-24 13:01:52 +08:00 1
|
15
wweir 2017-12-24 13:22:07 +08:00
这样做的前提是读压力小,读压力大的话,磁盘 io 直接就爆了
|
16
cholerae 2017-12-24 13:30:26 +08:00
这代码逻辑有问题啊,有可能在有缓存的时候读盘,吹 go 不是这么吹的
|
20
Reset 2017-12-24 14:46:46 +08:00 via iPhone
这个是看哪个 case 运气好被执行了
|
22
dumplinger 2017-12-25 09:39:59 +08:00
select 的一个神坑就是多个 case 之间是无序的,当都有数值的时候会随机的返回其中一个 case,巨恶心。
|
23
zhqy 2017-12-25 11:03:07 +08:00 1
代码逻辑没问题,建议看完整的代码。你贴的代码里那段中文注释错了,这里不是 if else,是 or 的关系。select 本身是无序的。
|
24
jameshuazhou 2017-12-25 11:17:55 +08:00
说 select-case 无序是坑的,官方文档早就说明得很详细了,而且这特性可以实现一些比较有意思的功能。
敢问坑在哪里? |
25
sorra 2017-12-25 17:47:52 +08:00
两个都是 channel,应该是谁刚好有数据就选谁。channel 不可能主动去查磁盘吧?应该要别的 goroutine 从磁盘查数据放进 channel。
|