乐趣区

关于分布式:MIT6824分布式系统课程-翻译学习笔记二基础架构RPC和线程持续更新中

阐明

本系列文章是对赫赫有名的 MIT6.824 分布式系统课程 的翻译补充和学习总结,算是本人一边学习一边记录了。

如有疏漏谬误,还请斧正:)

继续更新 ing。。。

翻译 & 补充

内容

Go 中的线程和 RPC,看一下试验

为什么抉择 Go?

对线程良好的反对
不便的 RPC
类型平安
垃圾回收(没有内存开释后再被应用的问题)
线程 + 垃圾回收十分棒
绝对简略
在教程学习后,请浏览 effective go

线程

很有用的结构工具,然而有时候会很辣手
在 Go 里叫做“goroutines”;其余中央叫做线程

线程 =“执行线程”

线程容许一个程序同时做很多事件
每一个线程依序执行,就像没有线程的一般程序一样
线程之间共享内存
每个线程包含本人的状态:程序计数器,寄存器和栈

为什么抉择线程?

它们可能解决并发,在分布式系统中很有用
I/ O 并发

  • 客户端并行地发送申请到许多服务器,并期待回复;
  • 服务端解决多个客户端的申请,每个申请会阻塞;
  • 在期待客户端 X 从磁盘读取数据的申请时,会解决客户端 Y 的申请。

多核的性能:在多个核上并行地执行代码
方便性:在后盾,每隔 1 秒,查看每个 worker 是否仍旧存活。

有没有线程的替代品?

有的:在一个独自的线程里,用代码明确地退出流动;通常称为“事件驱动”
维持每个流动的状态信息,比方每个客户端申请
一个“事件”的过程如下:

  • 查看每个流动的新输出(比方,服务器的回应)
  • 执行每个流动的下个步骤
  • 更新状态

事件驱动实现 I / O 并发:

  • 打消线程的耗费(可能会比拟大)
  • 然而并没有应用到多核带来的减速
  • 对代码来说很苦楚
线程带来的挑战:

共享数据:

  • 比方,如果两个线程同时执行 n = n + 1 会怎么样?或者一个线程减少一个线程读数据?
  • –> 应用锁(Go 里的 sync.Mutex)
  • –> 防止共享会批改的数据

线程中的协同工作:

  • 比方,一个线程生产数据,另一个线程生产。怎么让消费者期待(开释 CPU)?怎么让生产者告诉消费者?
  • –> 应用 go 中的 channel,或 sync.Cond,或 WaitGroup

死锁:

  • 加锁有环形依赖,或通信有环形依赖(比方:RPC 或 Go 的 channel)

让咱们看教程中的网络爬虫,把它作为一个线程示例

为什么是网络爬虫?

目前是获取所有的网页,比方:用于建设索引
网页和链接组成图
多个链接指向雷同的页面
图中有环形

爬虫带来的挑战:

须要应用 I / O 并发:

  • 网络提早比网络容量更受限
  • 须要同时获取多个 URL。为了减少每秒的 URL 获取数目,须要应用并发的多线程

每个 URL 仅“获取一次”

  • 避免浪费网络带宽
  • 对近程服务器更敌对
  • 须要记住每个拜访过的 URL

晓得何时完结

咱们看下两种解决形式【课程表页面的 crawler.go】
Serial 爬虫:

通过递归 Serial 调用实现深度遍历
“fetched”map 防止了反复,突破环形;一个简略的 map,通过传递援用,调用者能够看到并调用者的更新
然而,一个时刻只能获取一个页面;咱们能够间接在 Serial()函数前加“go”吗?咱们能够试下,看看会产生什么?

ConcurrentMutex 爬虫:

创立一个线程用于获取每个页面,启动很多并发的获取,放慢获取速度
“go func”创立并执行协程,func 能够是匿名办法
多个线程共享“fetched”map,所以同一时刻只有一个线程能够获取任意的指定页面
为什么应用 Mutex(Lock()和 Unlock())?

  • 起因一:

    * 两个不同的 web 页面可能蕴含雷同的 URL 链接
    * 两个线程可能会同步获取这两个页面
    * T1 读取 fetched[url],T2 也读取一样内容
    * 它们会同时发现这个 url 没有获取(already==false)* 它们会同时获取,这是谬误的
    * 锁会让检查和更新操作原子化,所以只有一个线程看到 already==false
  • 起因二:

    * 从外部来看,map 是个简单的数据结构(tree?就还是可扩大的 hash?)* 并发更新会毁坏外部变量
    * 并发读写会导致读失败
  • 如果我正文掉 Lock()/Unlock(),会怎么样?

    * go run crawler.go 会工作吗?* go run -race crawler.go 会检测出资源竞争,即便输入是正确的

ConcurrentMutex 爬虫怎么判断完结?

  • sync.WaitGroup
  • Wait()会期待所有的 Add()数目和 Done()始终,即,期待所有的子线程完结[图表:环形 URL 图上的协程树];每个树的节点上都有一个 WaitGroup

这个爬虫会创立多少个并发的线程?

ConcurrentChannel 爬虫:

一个 Go channel:

  • 一个 channel 是一个对象,ch := make(chan int)
  • 一个 channel 能够实现一个线程向另个一个线程发送数据,ch <- x,发送者期待,直到有协程接管,y := <- ch,for y := range ch,接受者期待,直到有协程发送
  • channel 既反对通信又反对同步
  • 多个线程能够在一个 channel 上通信
  • channel 很便宜
  • 记住:发送者会阻塞,直到接受者收到,“synchronnous”,小心死锁

ConcurrentChannel master()

  • master() 创立一个 worker 协程用来获取每个页面
  • worker() 发送页面的 URL 切片到 channel 上,多个 wroker 在同一个 channel 上发送
  • master() 从 channel 上读取 URL 切片

什么状况下 master 在期待?master 期待的时候占用 CPU 工夫了吗?
不须要对 fetched map 加锁,因为它并没有被共享!
master 怎么晓得完结了?

  • 放弃 worker 的数目在 n 以内
  • 每个 worker 只在 channel 上发送一条数据
为什么多个线程应用雷同的 channel 不会资源竞争呢?
worker 线程写入 URL 切片,master 线程读取切片,却不应用锁,会有资源竞争吗?
  • worker 只在发送“前”写入切片
  • master 只在收到“后”读取切片

所以他们不会同时应用切片

什么时候应用共享和锁,什么时候应用 channel?

大多数问题用两种形式都能够解决
应用哪个取决于程序员的思考:

* 状态 -- 共享和锁
* 通信 -- channel

对于 6.824 的试验,倡议应用共享和锁存储状态,应用 sync.Cond 或 channel 或 time.Sleep()用来期待和告诉

近程过程调用(RPC)

分布式系统机制会要害的一块,所有试验都在应用 RPC
指标:客户端能和服务器的通信更易于编程
暗藏网络协议的细节
转换 string、数组、map、指针等各种数据格式为能够传输的格局

【未完待续。。。】

退出移动版