# RPC and Threads

## 线程

线程的存在使得程序可以在逻辑上同时做许多事情。程序中的线程共享内存，同时也保存着自己的局部信息，包括程序指针 (PC)，寄存器，栈等。

### **程序中线程的数量**

* 由程序的功能结构决定：如 Web 服务器
* 由 CPU 的核数决定：最大化并行
* 由 I/O 决定：最大化吞吐量

### 线程的挑战

* 数据共享：竞争条件 (Race Condition)
* 线程之间合作：
* 并发的粒度：
  * 粗粒度：并发程度低，但容易处理
  * 细粒度：并发程度高，但容易产生更多的竞争和死锁

### 应用举例：[爬虫](http://nil.csail.mit.edu/6.824/2018/notes/crawler.go)

## RPC

RPC 的目的在于：让远程调用看起来就像本地方法调用一样。但实践起来并不简单。

### RPC 架构

![图1 - RPC 架构](/files/-LNz2BAfTG919cVweKwV)

### 执行细节

* Marshalling: 将 RPC 调用的函数名、参数放到数据包 (packets) 中
* Binding: 客户端绑定 RPC 服务端的过程
  * 使用服务器的域名和端口
  * 服务发现配置

实现举例：[Toy RPC](http://nil.csail.mit.edu/6.824/2015/notes/l-rpc.go)

### 问题与解决方案

#### RPC 调用过程可能出现哪些问题？

* 丢包
* 断网
* RPC 服务器响应慢
* RPC 服务器宕机

#### 这些问题在客户端上的 RPC Lib 眼中的样子

* 永远得不到请求的结果
* 甚至不知道究竟是服务器的问题还是网络的问题

#### 解决方案1：At Least Once/Best Effort

**做法**：在服务器未响应后重试几次，若仍如此则返回错误。\
**缺点**：有写操作存在时可能出现竞争条件\
**结论**：只能在读或幂等操作的情况下使用

思考实验：假设客户端执行

```bash
Put("k", 10);
Put("k", 20);
Get("k");
```

可能会出现哪些结果？

#### 解决方案2：At Most Once

**做法**：RPC 客户端发送请求时为每个请求附加唯一的 xid, 服务端根据 xid 检测重复请求，检测成功后直接返回之前缓存的结果，伪代码如下所示：

```python
if seen[xid]:
  r = old[xid]
else:
  r = handler()
  old[xid] = r
  seen[xid] = true
```

思考实验：

* 如何保证 XID 的唯一性？
  * 大随机数
  * ip + sequence number
* 服务端如何清理不必要的缓存？
  * TCP
* 当前请求还在执行时，又接收到相同的请求？
  * pending flag
  * 等待或者忽略
* RPC 服务器崩溃重启？
  * 持久化数据

例子：Go RPC

* 建立 TCP 连接，调用请求通过 TCP 传输
* Go RPC 不重发请求，因此服务器不会看到重复请求
* Go RPC 得不到回复直接返回错误

#### 解决方案3：Exactly Once

At Most Once + 无限重试 + 能够容错的 RPC 服务

### 应用举例：[KV](http://nil.csail.mit.edu/6.824/2018/notes/kv.go)

## 参考

course note [2015](http://nil.csail.mit.edu/6.824/2015/notes/l-rpc.txt), [2018](http://nil.csail.mit.edu/6.824/2018/notes/l-rpc.txt), [toy RPC](http://nil.csail.mit.edu/6.824/2015/notes/l-rpc.go), [crawler](http://nil.csail.mit.edu/6.824/2018/notes/crawler.go), [kv](http://nil.csail.mit.edu/6.824/2018/notes/kv.go)\
Youtube video [2015](https://www.youtube.com/watch?v=6NXUYWLYTp4\&t=0s\&list=PLpl804R-ZwjKCOwWpTZ21eeaBS3uBrMfV\&index=3)


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://zhenghe.gitbook.io/open-courses/mit-6.824/di-er-ke-rpc-yu-xian-cheng.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
