V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
The Go Programming Language
http://golang.org/
Go Playground
Go Projects
Revel Web Framework
jpmorn
V2EX  ›  Go 编程语言

go 的 select 方法很适合 cache 啊

  •  
  •   jpmorn · 2017-12-24 09:41:35 +08:00 · 3375 次点击
    这是一个创建于 2551 天前的主题,其中的信息可能已经有所发展或是发生改变。

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

    这个想法蛮好啊。

    25 条回复    2017-12-25 17:47:52 +08:00
    twm
        1
    twm  
       2017-12-24 09:51:27 +08:00 via iPhone
    go 是最好的语言,go 实现的网站
    www.cshome.com
    Muninn
        2
    Muninn  
       2017-12-24 10:13:58 +08:00
    这样写确实挺简洁的,比我的 if else 短一些。
    pathbox
        3
    pathbox  
       2017-12-24 10:20:33 +08:00 via iPhone
    你需要多开 channel 和 goroutine,这是占一些资源的 不过可以忽略。go 语言级别支持 csp,go 的 select 是在用户态中进行对 goroutine 的调度。
    xrlin
        4
    xrlin  
       2017-12-24 10:36:38 +08:00
    但是从 channel 中取出后,channel 中的消息已经没有了,这样还需要在消费后再将消息推送进 channel,并且还要自己维护失效时间。
    HXM
        5
    HXM  
       2017-12-24 10:37:04 +08:00 via Android
    @twm 乍一看域名以为是游戏相关的网站。。。
    jpmorn
        6
    jpmorn  
    OP
       2017-12-24 11:08:12 +08:00
    @xrlin 对的 这个是要自己再搞下。
    gamexg
        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
    pubby
        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
    sonyxperia
        9
    sonyxperia  
       2017-12-24 11:25:45 +08:00
    @twm 你给一个页面怎么看出来是 go 实现的
    pathbox
        10
    pathbox  
       2017-12-24 11:42:04 +08:00 via iPhone
    @pubby 所以 select 还要考虑 switch 的条件,case 的条件同一时刻只满足一个,如果同时满足多个 调度器选择执行哪一个都是有可能的
    jpmorn
        11
    jpmorn  
    OP
       2017-12-24 12:06:07 +08:00
    @pathbox 所以我说感觉就是这个场景的(缓存)下,这种设计还是很精妙的。
    TangMonk
        12
    TangMonk  
       2017-12-24 12:24:17 +08:00 via Android
    @gamexg 学到了,golang 文档好像没有写明
    pathbox
        13
    pathbox  
       2017-12-24 12:53:25 +08:00
    @jpmorn 有弊端。 当缓存和硬盘中都有数据的时候, 理论上是想从缓存读取,这样速度快,才起到缓存的作用,而却从硬盘取数据了,这样即使有缓存数据也没有起到作用。 所以 这种写法只适合非常简单的一种缓存机制,即使从硬盘取也不会很慢。 真要做大规模的缓存,不适合吧,当高并发的时候,会太多缓存失效的情况会发生,而实际中这些缓存数据都是有的
    pubby
        14
    pubby  
       2017-12-24 13:01:52 +08:00   ❤️ 1
    wweir
        15
    wweir  
       2017-12-24 13:22:07 +08:00
    这样做的前提是读压力小,读压力大的话,磁盘 io 直接就爆了
    cholerae
        16
    cholerae  
       2017-12-24 13:30:26 +08:00
    这代码逻辑有问题啊,有可能在有缓存的时候读盘,吹 go 不是这么吹的
    jpmorn
        17
    jpmorn  
    OP
       2017-12-24 13:32:25 +08:00
    @TangMonk @pubby 这里也有讲的。
    jpmorn
        18
    jpmorn  
    OP
       2017-12-24 13:33:13 +08:00
    @pathbox 唔~ 有道理~
    jpmorn
        19
    jpmorn  
    OP
       2017-12-24 13:34:05 +08:00
    @cholerae 读盘理论上应该比返回慢吧~读缓存应该先返回了
    Reset
        20
    Reset  
       2017-12-24 14:46:46 +08:00 via iPhone
    这个是看哪个 case 运气好被执行了
    stabc
        21
    stabc  
       2017-12-24 18:18:32 +08:00
    @twm 这个域名不做 CSGO 社区可以了……
    dumplinger
        22
    dumplinger  
       2017-12-25 09:39:59 +08:00
    select 的一个神坑就是多个 case 之间是无序的,当都有数值的时候会随机的返回其中一个 case,巨恶心。
    zhqy
        23
    zhqy  
       2017-12-25 11:03:07 +08:00   ❤️ 1
    代码逻辑没问题,建议看完整的代码。你贴的代码里那段中文注释错了,这里不是 if else,是 or 的关系。select 本身是无序的。
    jameshuazhou
        24
    jameshuazhou  
       2017-12-25 11:17:55 +08:00
    说 select-case 无序是坑的,官方文档早就说明得很详细了,而且这特性可以实现一些比较有意思的功能。
    敢问坑在哪里?
    sorra
        25
    sorra  
       2017-12-25 17:47:52 +08:00
    两个都是 channel,应该是谁刚好有数据就选谁。channel 不可能主动去查磁盘吧?应该要别的 goroutine 从磁盘查数据放进 channel。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   3011 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 26ms · UTC 14:22 · PVG 22:22 · LAX 06:22 · JFK 09:22
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.