围棋

围棋的英文名有三种:中国读 Weiqi,西方读 Go,韩国读 baduk(发音:怕度)。

围棋对局一般分为三个阶段:布局、中盘、收官。收官阶段也称为官子阶段。然而,这三个阶段之间并没有明显的界限,何时进入中盘,何时进入收官,完全取决于棋局的发展和对局者的决断。

Go之GORM

GORM

  • 设置某一字段为NULL:db.Model(&foo).Where(“id=xxx”).Updates(map[string]interface{}{“created_at”: gorm.Expr(“NULL”)})
  • FindInBatches():用于批量查询并处理记录,如果有使用select(),必须 select 主键(如id),否则会陷入无限循环
  • Attrs(User{Age: 20}).FirstOrCreate(&user): 如果记录未找到,将使用参数创建 struct 和记录.
  • Assign(User{Age: 30}).FirstOrCreate(&user): 不管记录是否找到,都将参数赋值给 struct 并保存至数据库.

数据类型建议

脚本跑数

MySQL数据表更新数据

  1. 对于100万条记录、200M占用空间的数据表,执行全量update语句,速度很快,1分钟可执行完毕
  2. 对于100万条记录、200M占用空间的数据表,使用脚本循环执行一条条记录的update,大概5分钟内可执行完毕

你为什么会感觉人生无聊?

"我为什么要像现在这样活着?"

"我应该怎么样活着?"

"好无聊,我能干什么?"

"为什么没有能够勾起我兴趣的东西?"

……这样的话正逐渐成为当代人的口头禅,这样思考也让我们感受到了前所未有的空虚。

AppleScript

什么是 AppleScript

点击查看官方文档

脚本语言有很多种,你可能听说过的 Shell Script、Python 和 JavaScript,都是其中的代表。而 AppleScript 则是 macOS 下提供的系统级别的脚本语言。

讲话技巧

善用不同连接词

善用不同连接词,才不会一直「然后、然后」下去⋯⋯

说话时使用的连接词非常重要,如果只有不断的重复然后……然后……,会让整段表达死板无趣,而在写作时如果只有一个连接词从头用到尾,那只会了无新意、阐述不明。

Go语言环境安装及如何使用Go多版本(Mac)

go环境的卸载

  1. pkg方式安装go?

    删除/usr/local下的go目录即可

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    
    sudo rm -rf /usr/local/go
    
    sudo rm -rf /etc/paths.d/go
    
    vim ~/.bash_profile 或 vim ~/.zshrc # 删除go的环境变量
    
    # go环境变量
    export GOROOT=/usr/local/go  # Go的安装目录
    export GOPATH=$HOME/go  # 你的Go代码工作目录
    export PATH=$PATH:$GOPATH/bin  # 添加go命令所在路径
    

    如果你是通过安装包的形式安装的go语言编译器,那么更新的方式也会非常简单,那就是直接下载新的go安装包。在安装新的时候,会自动删除掉旧的编译器。

网约车和顺风车

网约车

从事合规的网约车业务必须办理网约车相关证件,个人办理的话主要是网约车驾驶员和网约车运输证。

网约车驾驶员证

这个驾驶员证其实就是我们所说的网约车从业资格证。司机必须取得网约车从业资格证,才能开网约车。没有从业资格证就开网约车的话,那就属于无证驾驶。

Go之项目程序结构

项目程序结构

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
.
├── Dockerfile                // 镜像配置
├── docker-compose-local.yaml // docker-compose文件,方便开发环境的启动
├── Makefile                  // 自动化命令文件
├── README.md                 // 项目 readme
├── config.json               // 本地配置文件
├── config.json.example       // 本地配置文件示例
├── go.mod
├── go.sum
├── internal        // 代码实现
│   ├── config      // 配置信息目录
│   │   └── db.go   // 数据库配置
│   ├── cron        // 定时任务
│   ├── global      // 全局常量定义
│   ├── form        // 结构体定义
│   ├── lib         // 第三方库
│   ├── logic       // logic服务
│   │   ├── controller  // 控制器
│   │   ├── middleware  // 中间件
│   │   └── repository  // 业务层
│   ├── logic_admin     // logic_admin服务
│   │   ├── controller  // 控制器
│   │   ├── middleware  // 中间件
│   │   └── repository  // 业务层
│   ├── business        // 业务层共用代码,业务逻辑层的进一步抽象,主要目的是提供共用代码给各个服务的repository层调用,此层不允许直接调用各个服务的repository层方法
│   │   └── customer.go // 业务逻辑代码
│   ├── model           // 数据模型
│   │   └── customer.go // 客户模型定义
│   ├── dao             // data access object,model层对应的数据处理层
│   │   └── customer.go // 客户模型对应的数据处理层,比如增删改查
│   ├── mq              // mq
│   ├── socket          // socket
│   └── rpc             // rpc代码
├── log // 日志存放目录
├── main // 程序运行入口
│   ├── crontab.go // 定时任务
│   ├── logic.go   // 业务逻辑
│   ├── migrate.go // 迁移文件
│   ├── message.go // 消息处理
│   ├── newapi.go  // 生成API脚手架
│   ├── socket.go  // socket
│   └── script.go  // 临时跑数据脚本
└── vendor // 第三方扩展
    ├── github.com
    └── modules.txt

Go之分布式

在使用分布式并发原语时,除了需要考虑可用性和数据一致性,还需要考虑分布式设计带来的性能损耗问题。所以,在使用之前,你一定要做好性能的评估。

分布式互斥锁

基于redis的分布式互斥锁

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
package tool

import (
    "context"
    "errors"
    "github.com/go-redis/redis/v8"
    "time"
)

// 分布式互斥锁

// 默认重试次数
var retryTimes = 20

// 默认重试频率
var retryInterval = time.Millisecond * 50

var ctx = context.Background()

type DcsLock struct {
    rdb           *redis.Client
    RetryTimes    int
    RetryInterval time.Duration
}

func (m *DcsLock) SetRdb(rdb *redis.Client) *DcsLock {
    m.rdb = rdb
    m.RetryTimes = retryTimes
    m.RetryInterval = retryInterval
    return m
}

func (m *DcsLock) SetRetryTimes(i int) *DcsLock {
    m.RetryTimes = i
    return m
}

func (m *DcsLock) SetRetryInterval(t time.Duration) *DcsLock {
    m.RetryInterval = t
    return m
}

func (m *DcsLock) Lock(cacheKey string, ttl time.Duration) error {
    resp := false
    for i := 0; i < m.RetryTimes; i++ {
        if m.SetNX(cacheKey, ttl) {
            resp = true
            break
        }
        time.Sleep(m.RetryInterval)
    }
    if !resp {
        return errors.New("抢锁失败,请稍后重试")
    }
    return nil
}

func (m *DcsLock) Unlock(cacheKey string) {
    m.DelSetNX(cacheKey)
}

func (m *DcsLock) SetNX(cacheKey string, ttl time.Duration) bool {
    if m.rdb.SetNX(ctx, cacheKey, 1, ttl).Val() {
        return true
    }
    return false
}

func (m *DcsLock) DelSetNX(cacheKey string) {
    m.rdb.Del(ctx, cacheKey)
}

基于etcd的分布式互斥锁

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
package main

import (
    "context"
    "go.etcd.io/etcd/client/v3"
    "go.etcd.io/etcd/client/v3/concurrency"
    "log"
    "math/rand"
    "strings"
    "time"
)

// etcd实现分布式互斥锁
var (
    addr     = "http://127.0.0.1:23791,http://127.0.0.1:23792,http://127.0.0.1:23793"
    lockName = "my-test-lock"
)

func main() {
    rand.Seed(time.Now().UnixNano())

    // etcd地址
    endpoints := strings.Split(addr, ",")

    // 生成一个etcd client
    cli, err := clientv3.New(clientv3.Config{Endpoints: endpoints})
    if err != nil {
        log.Fatal(err)
    }
    defer cli.Close()

    useLock(cli) // 测试锁
}

func useLock(cli *clientv3.Client) {
    // 为锁生成session「节点宕机对应 session 销毁,持有的锁会被释放」
    s1, err := concurrency.NewSession(cli)
    if err != nil {
        log.Fatal(err)
    }
    defer s1.Close()

    // 得到一个分布式锁
    locker := concurrency.NewMutex(s1, lockName)

    // 请求锁
    log.Println("acquiring lock")
    if err := locker.Lock(context.TODO()); err != nil {
        log.Fatal(err)
    }
    log.Println("acquired lock")

    // 等待一段时间
    time.Sleep(time.Duration(rand.Intn(30)+3) * time.Second)

    // 释放锁
    if err := locker.Unlock(context.TODO()); err != nil {
        log.Fatal(err)
    }

    log.Println("released lock")
}

分布式读写锁

基于etcd的分布式读写锁

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
package main

import (
    "bufio"
    "flag"
    "fmt"
    "go.etcd.io/etcd/client/v3"
    "go.etcd.io/etcd/client/v3/concurrency"
    "go.etcd.io/etcd/client/v3/experimental/recipes"
    "log"
    "math/rand"
    "os"
    "strings"
    "time"
)

// etcd实现分布式读写锁
var (
    rwAddr     = "http://127.0.0.1:23791,http://127.0.0.1:23792,http://127.0.0.1:23793"
    rwLockName = "my-test-lock"
)

func main() {
    rand.Seed(time.Now().UnixNano())

    // 解析etcd地址
    endpoints := strings.Split(rwAddr, ",")

    // 创建etcd的client
    cli, err := clientv3.New(clientv3.Config{Endpoints: endpoints})
    if err != nil {
        log.Fatal(err)
    }

    defer cli.Close()

    // 创建session
    s1, err := concurrency.NewSession(cli) // 节点宕机对应 session 销毁,持有的锁会被释放
    if err != nil {
        log.Fatal(err)
    }

    defer s1.Close()

    m1 := recipe.NewRWMutex(s1, rwLockName)

    // 从命令行读取命令
    consolescanner := bufio.NewScanner(os.Stdin)
    log.Println("请输入指令w/r:")
    for consolescanner.Scan() {
        action := consolescanner.Text()
        switch action {
        case "w": // 请求写锁
            testWriteLocker(m1)
        case "r": // 请求读锁
            testReadLocker(m1)
        default:
            fmt.Println("unknown action")
        }
        log.Println("请输入指令w/r:")
    }
}

func testWriteLocker(m1 *recipe.RWMutex) {
    // 请求写锁
    log.Println("acquiring write lock")
    if err := m1.Lock(); err != nil {
        log.Fatal(err)
    }

    log.Println("acquired write lock")

    // 等待一段时间
    time.Sleep(time.Duration(rand.Intn(10)+3) * time.Second)

    // 释放写锁
    if err := m1.Unlock(); err != nil {
        log.Fatal(err)
    }

    log.Println("released write lock")
}

func testReadLocker(m1 *recipe.RWMutex) {
    // 请求读锁
    log.Println("acquiring read lock")
    if err := m1.RLock(); err != nil {
        log.Fatal(err)
    }

    log.Println("acquired read lock")

    // 等待一段时间
    time.Sleep(time.Duration(rand.Intn(10)+3) * time.Second)

    // 释放写锁
    if err := m1.RUnlock(); err != nil {
        log.Fatal(err)
    }

    log.Println("released read lock")
}

分布式队列

基于etcd的分布式队列

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61

package main

import (
    "bufio"
    "fmt"
    "go.etcd.io/etcd/client/v3"
    recipe "go.etcd.io/etcd/client/v3/experimental/recipes"
    "log"
    "os"
    "strings"
)

// etcd实现分布式队列
var (
    etcdAddrQ     = "http://127.0.0.1:23791,http://127.0.0.1:23792,http://127.0.0.1:23793"
    queueName = "my-test-queue"
)

func main() {
    // 解析etcd地址
    endpoints := strings.Split(etcdAddrQ, ",")

    // 创建etcd的client
    cli, err := clientv3.New(clientv3.Config{Endpoints: endpoints})
    if err != nil {
        log.Fatal(err)
    }
    defer cli.Close()

    // 创建/获取队列
    q := recipe.NewQueue(cli, queueName)

    // 从命令行读取命令
    fmt.Println("请输入指令:push/pop,多参数用空格间隔")
    consolescanner := bufio.NewScanner(os.Stdin)
    for consolescanner.Scan() {
        action := consolescanner.Text()
        items := strings.Split(action, " ")
        switch items[0] {
        case "push": // 加入队列
            if len(items) != 2 {
                fmt.Println("must set value to push")
                continue
            }
            q.Enqueue(items[1]) // 入队
        case "pop": // 从队列弹出
            v, err := q.Dequeue() // 出队
            if err != nil {
                log.Fatal(err)
            }
            fmt.Println(v) // 输出出队的元素
        case "quit", "exit": //退出
            return
        default:
            fmt.Println("unknown action")
        }

        fmt.Println("请输入指令:push/pop,多参数用空格间隔")
    }
}

分布式栅栏

基于etcd的分布式栅栏

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
package main

import (
    "bufio"
    "fmt"
    "go.etcd.io/etcd/client/v3"
    recipe "go.etcd.io/etcd/client/v3/experimental/recipes"
    "log"
    "os"
    "strings"
)

// 分布式栅栏
var (
    etcdAddrB   = "http://127.0.0.1:23791,http://127.0.0.1:23792,http://127.0.0.1:23793"
    barrierName = "my-test-queue"
)

func main() {

    // 解析etcd地址
    endpoints := strings.Split(etcdAddrB, ",")

    // 创建etcd的client
    cli, err := clientv3.New(clientv3.Config{Endpoints: endpoints})
    if err != nil {
        log.Fatal(err)
    }
    defer cli.Close()

    // 创建/获取栅栏
    b := recipe.NewBarrier(cli, barrierName)

    // 从命令行读取命令
    fmt.Println("请输入指令hold/release/wait:")
    consolescanner := bufio.NewScanner(os.Stdin)
    for consolescanner.Scan() {
        action := consolescanner.Text()
        items := strings.Split(action, " ")
        switch items[0] {
        case "hold": // 持有这个barrier
            if err := b.Hold(); err != nil {
                fmt.Printf("hold fail: %v\n", err)
            } else {
                fmt.Println("hold success")
            }
        case "release": // 释放这个barrier
            if err := b.Release(); err != nil {
                fmt.Printf("released fail: %v\n", err)
            } else {
                fmt.Println("released success")
            }
        case "wait": // 等待barrier被释放
            if err := b.Wait(); err != nil {
                fmt.Printf("wait fail: %v\n", err)
            } else {
                fmt.Println("after wait")
            }
        case "quit", "exit": // 退出
            return
        default:
            fmt.Println("unknown action")
        }
        fmt.Println("请输入指令hold/release/wait:")
    }
}

分布式事务

todo