第 45 期:杭州的周末能不下雨吗?

@Author : Lewis Tian (taseikyo@gmail.com)

@Link : github.com/taseikyo

@Range : 2021-09-05 - 2021-09-11

Weekly #45

readme | previous | next

本文总字数 2165 个,阅读时长约:4 分 16 秒,统计数据来自:算筹字数统计

*Photo by Janine Robinson on Unsplash

她彷徨在这寂寥的雨巷,撑着油纸伞像我一样,像我一样地默默彳亍着,冷漠,凄清,又惆怅。 —— 戴望舒《雨巷》

Table of Contents

  • review

    • NetBSD Explained: The Unix System That Can Run on Anything

  • tip

    • golang-redis 判断 key 不存在还是读取出错

    • golang-redis 分布式自旋锁

    • 使用 GitHub Actions 将仓库里的 Markdown 文件定时发到推特

    • 本地运行 GitHub Actions

  • share

    • 杭州的周末能不下雨吗?

algorithm 🔝

review 🔝

什么是 NetBSD?

NetBSD 是一个开源的操作系统,与 Linux 一样,NetBSD 致力于与 Unix 的广泛兼容性,提供类似的实用程序和行为。

NetBSD 是基于 BSD 版本(Berkeley Software Distribution)的 Unix,因此名称中有 “BSD”。它是 20 世纪 90 年代早期支持 pc 的 386/BSD 版本的一个分支。

FreeBSD 侧重于 PC 平台,OpenBSD 侧重于安全性,NetBSD 侧重于不同平台的可移植性。虽然 NetBSD 看起来像另一个 Linux 发行版,但是整个系统,包括内核和用户实用程序,是作为一个整体一起开发的。这与 Linux 发行版拼凑来自多个源的组件的方式形成了鲜明的对比。

BSD 最初是基于贝尔实验室的 Unix 系统,但多年来与其母公司 AT&T 的版本分道扬镳,因此可以发布一个不包含 AT&T 代码的版本。

虽然它不是一个完整的操作系统,但这个“网络版”之所以这么命名,是因为它包含了几个公司用来在他们的产品中实现网络的 TCP/IP 网络代码。它甚至最终进入了微软的 Windows 系统。

随着英特尔 80386 处理器的出现,个人电脑变得更加强大,William joritz 将 BSD 移植到 386 处理器,使用网络版本作为起点,他发布了 386BSD。Jolitz 很难跟上其他开发人员为改进系统而发送给他的所有补丁,所以项目的分支立即出现了。

一个小组希望继续改进 PC 版本,而另一个小组则希望关注不同体系结构之间的可移植性。前者成为 FreeBSD,后者成为 NetBSD。

当开发人员 Theo de Raadt 被要求从 NetBSD 项目中辞职后,NetBSD 又被迫离开,随后他创建了一个变体 OpenBSD,专注于安全性和代码正确性。

tip 🔝

redis 中有一个特殊值(redis.Ni)用来判断读取时 key 不存在的情况:

// 判断 key 是否存在
val, err := client.Get("user:zhangyunfeiVir").Result()
if err == redis.Nil {
    fmt.Println("key2 does not exist")
} else if err != nil {
    panic(err)
} else {
    fmt.Println("读取:", val)
}

1、使用 redis 的 setnx 实现了一个自选锁,有 key 超时,同时也有我们调用 redis 链接时的超时。

spin_lock_setnx.go

type Lock struct {
    resource string
    value    interface{}
    timeout  time.Duration
    redisCli *redis.ClusterClient //这个是链接redis集群的cli,可以自行修改
}

func NewRedis(redisCli *redis.ClusterClient, resource string, value interface{}, timeOut time.Duration) *Lock {
    return &Lock{
        resource: resource,
        value:    value,
        timeout:  timeOut,
        redisCli: redisCli,
    }
}

func (lock *Lock) TryLock() (ok bool, err error) {
    ok, err = lock.redisCli.SetNX(lock.resource, lock.value, lock.timeout).Result()
    //log.Printf("resource:%s, timeout:%v, ok:%v, err:%v\n", lock.resource, lock.timeout, ok, err)

    return
}

func (lock *Lock) Unlock() (err error) {
    err = lock.redisCli.Del(lock.resource).Err()
    return
}

func (lock *Lock) SpinLockUntilTimeOut(ctx context.Context, d time.Duration) (timeOut bool, err error) {
    var (
        now time.Time
        ok  bool
    )

    endTime := time.Now().Add(d)
    for {
        select {
        case <-ctx.Done():
            timeOut = true
            //log.Printf("SpinLockUntilTimeOut at ctx.Done()")
            return

        default:
            now = time.Now()
            if now.After(endTime) {
                timeOut = true
                //log.Printf("SpinLockUntilTimeOut at d")
                return
            }

            ok, err = lock.TryLock()
            if err != nil {
                return
            }

            if ok {
                return
            } else {
                runtime.Gosched()
            }

        }

    }

}

2、内存版

spin_lock_memory.go

var lockers map[string]map[uint64]chan interface{}
var addLock chan lockImpl
var delLock chan lockImpl
var tranceId uint64

type lockImpl struct {
    delId uint64
    addId chan uint64
    wait  chan interface{}
    key   string
}

func init() {
    lockers = make(map[string]map[uint64]chan interface{})
    addLock = make(chan lockImpl)
    delLock = make(chan lockImpl)
    run()
}

func run() {
    go func() {
        var (
            ok1, ok2     bool
            addLs, delLs map[uint64]chan interface{}
        )

        for {
            select {
            case add := <-addLock:
                if addLs, ok1 = lockers[add.key]; !ok1 {
                    addLs = make(map[uint64]chan interface{})
                }
                tranceId++
                addLs[tranceId] = add.wait
                lockers[add.key] = addLs
                add.addId <- tranceId
                close(add.addId)
                if len(addLs) == 1 {
                    add.wait <- struct{}{}
                    close(add.wait)
                }

            case del := <-delLock:
                if delLs, ok2 = lockers[del.key]; ok2 {
                    delete(delLs, del.delId)
                    for _, v := range delLs {
                        v <- struct{}{}
                        close(v)
                        break
                    }
                }

            }
        }

    }()
}

type Memory struct {
    key string
    id  uint64
}

func (m *Memory) SLock(ctx context.Context) (timeOut bool, err error) {

    l := lockImpl{
        addId: make(chan uint64, 1),
        wait:  make(chan interface{}, 1),
        key:   m.key,
    }
    addLock <- l
    m.id = <-l.addId

    select {
    case <-l.wait:
        return
    case <-ctx.Done():
        timeOut = true
        return
    }

}

func (m *Memory) UnSLock() (err error) {
    l := lockImpl{
        delId: m.id,
        key:   m.key,
    }
    delLock <- l

    return
}

func NewMemory(path string) *Memory {
    return &Memory{
        key: path,
    }
}

3. 使用 GitHub Actions 将仓库里的 Markdown 文件定时发到推特

拿到 twitter 的各个 key,然后利用 GitHub Actions 读取 README 来发送 twitter,感觉本质上是一种爬虫。

4. 本地运行 GitHub Actions

挺棒的一个东西,在本地运行就可以确定是否写对了,可以不必像之前一次次推送来验证。

share 🔝

1. 杭州的周末能不下雨吗?

来了杭州两个多月,几乎每周末都会下雨,好不容易到周末,本以为可以约两三好友出来吃饭,结果来这么一出,谁下雨愿意出门啊,下雨不是更适合睡觉吗?

这周末又与往常一般下雨了,还好今天中午雨停了,于是跟同学一起去大运河逛了圈,之后去武林广场吃了烤肉,虽然中途下雨了,但是吃完就没下了。

烤肉还不错,虽然走了一下午很累,但是吃的很快乐。

吃完回来的时候路过一家店,第一眼看错了名字,感觉老板也太嚣张了,旁边就是警察叔叔啊,再看一眼原来是我看错了,主要是这名字有点怪(

另外,手机掉电太离谱了,出地铁站还剩 4%,差点以为要自动关机了,搞得我在地铁上都不敢玩了,果然该换电(手)池(机)了!

readme | previous | next

最后更新于

这有帮助吗?