什么是分布式系统?

一个分布式系统, 从物理上说是 用户 -network- 多台服务器主机 形成的一个特殊的系统

如果对这个模型进行抽象, 其实就是 单个terminal 和 多个 process 之间的通信,
其中, 这些 process 空间上分离, 通过网络进行通信, 不应该存在明显延迟, 并且命运不同(一台主机挂了不应该影响其他主机)

从上面的抽象系统目标, 我们还可以更加抽象出分布式系统的设计目标:

  1. High availability: 整体的分布式系统应当永远可用
  2. Low latency: 用户请求的响应时间应当足够低
  3. Scalability: 系统支持同时服务多个用户

银行后端为例

分布式系统设计的困难可以分为以下几个难点:

  1. concurrency: 多个 process 之间的并发控制, 但是之间的网络不可靠,
    主机状态不可靠
  2. inconsistency: 数据交换一定会存在可见延迟
  3. ambiguity: 一个存款请求需要等待下游主机反馈
    ACK, 但是如果没有收到, 如何判断是哪一步出了问题? 存款是否成功?

channel 管道实现的同步

rendezvous 汇合同步

管道天生就具有其堵塞的属性, 也就是说, 管道的设置会要求管道双方在管道传输处建立一个握手, 只有完成了握手 (双方同时到达) 才能继续执行

for-select 语法中, 往往会用三种方式来使用管道

1
2
3
4
5
6
// 1. 读取: 看到 =<- 这样的写法就说明这里是一个读取操作, 也就是等待另一端有数据发送过来
case var:= <- ch
// 2. 发送: 没有 = 的出现, 只有一个 ch <- var
case ch <- var // 这个一般是等待下游接受方已就绪
// 3. 等待信号
case <- ch: // 类似于 cv.wait() 等待对应的 signal()

for-select 中央引擎

对于一个类封装的时候, 如果这个类要支持并发且有同步操作, 往往会在一个中央函数中
(很有可能在初始化函数中直接开一个 goroutine 来运行) 加入这个 for-select
引擎来支持并发, 这有以下几个好处:

  1. 单一线程同时监听多个 channel, 比如设计了一个 manager 其手下有多个
    worker, 那么这个 manager 可以 select 多个 worker.ch 来判断工人是否完成了任务, 或者查看是否有下游的 worker 在等待任务的分发
  2. 支持随时收到 term 信号来实现程序的优雅退出
  3. confinement: 由于单一 goroutine 线程能通过这个 for-select
    引擎同时监管多个 channel 的资源, 所以这里实际上已经要求将所有的资源集中化管理, 也就是这里强制这个 select 所在的环境函数对这些资源具有所有权, 而为了减少对应 chan 的随便乱传 (串子司马) 程序设计的时候更倾向于直接将对应 channel 写成当前类的资源

不过注意这里的 term 终止 channel 一般是每个对应有 for-select
的类都会具有的一个自身 channel

分布式语言的 代码约束 (Confinement)