go 的 select 方法很适合 cache 啊

2017-12-24 09:41:35 +08:00
 jpmorn

在这里看到的。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
            }

这个想法蛮好啊。

3388 次点击
所在节点    Go 编程语言
25 条回复
twm
2017-12-24 09:51:27 +08:00
go 是最好的语言,go 实现的网站
www.cshome.com
Muninn
2017-12-24 10:13:58 +08:00
这样写确实挺简洁的,比我的 if else 短一些。
pathbox
2017-12-24 10:20:33 +08:00
你需要多开 channel 和 goroutine,这是占一些资源的 不过可以忽略。go 语言级别支持 csp,go 的 select 是在用户态中进行对 goroutine 的调度。
xrlin
2017-12-24 10:36:38 +08:00
但是从 channel 中取出后,channel 中的消息已经没有了,这样还需要在消费后再将消息推送进 channel,并且还要自己维护失效时间。
HXM
2017-12-24 10:37:04 +08:00
@twm 乍一看域名以为是游戏相关的网站。。。
jpmorn
2017-12-24 11:08:12 +08:00
@xrlin 对的 这个是要自己再搞下。
gamexg
2017-12-24 11:12:41 +08:00
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
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
sonyxperia
2017-12-24 11:25:45 +08:00
@twm 你给一个页面怎么看出来是 go 实现的
pathbox
2017-12-24 11:42:04 +08:00
@pubby 所以 select 还要考虑 switch 的条件,case 的条件同一时刻只满足一个,如果同时满足多个 调度器选择执行哪一个都是有可能的
jpmorn
2017-12-24 12:06:07 +08:00
@pathbox 所以我说感觉就是这个场景的(缓存)下,这种设计还是很精妙的。
TangMonk
2017-12-24 12:24:17 +08:00
@gamexg 学到了,golang 文档好像没有写明
pathbox
2017-12-24 12:53:25 +08:00
@jpmorn 有弊端。 当缓存和硬盘中都有数据的时候, 理论上是想从缓存读取,这样速度快,才起到缓存的作用,而却从硬盘取数据了,这样即使有缓存数据也没有起到作用。 所以 这种写法只适合非常简单的一种缓存机制,即使从硬盘取也不会很慢。 真要做大规模的缓存,不适合吧,当高并发的时候,会太多缓存失效的情况会发生,而实际中这些缓存数据都是有的
pubby
2017-12-24 13:01:52 +08:00
wweir
2017-12-24 13:22:07 +08:00
这样做的前提是读压力小,读压力大的话,磁盘 io 直接就爆了
cholerae
2017-12-24 13:30:26 +08:00
这代码逻辑有问题啊,有可能在有缓存的时候读盘,吹 go 不是这么吹的
jpmorn
2017-12-24 13:32:25 +08:00
@TangMonk @pubby 这里也有讲的。
jpmorn
2017-12-24 13:33:13 +08:00
@pathbox 唔~ 有道理~
jpmorn
2017-12-24 13:34:05 +08:00
@cholerae 读盘理论上应该比返回慢吧~读缓存应该先返回了
Reset
2017-12-24 14:46:46 +08:00
这个是看哪个 case 运气好被执行了

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/417179

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX