CS110L-Lab/proj-2/balancebeam/implement_notes.md
2023-03-10 17:39:47 +08:00

5.0 KiB
Raw Blame History

Project-2 Balancebeam 实现记录

综述

这东西本质上是一个类似于反向代理之类的东西,

每个 Milestone 的说明

Milestone 0

依然是传统的看代码环节。

首先是 main.rs 里面的东西。

  1. CmdOption 用的是 clap 这个 crate用非常奇葩的 macro 语法来定义命令行参数并解析。
  2. ProxyState 是执行线程的共享参数,不知道有啥特别的意义,可能是为后面的东西预留的
  3. TCPListener 是标准库提供的东西,提供了一个简单的 TCP server。它通过 incoming 迭代器来返回构造的 TCP stream 抽象,这种字节流抽象最终会被分发给执行线程。
  4. 执行线程:
    1. 首先它随机选择一个上游服务器并打开与该服务器的连接。如果服务器无了返回502。
    2. 然后循环从客户端读取请求,客户端可以在一个连接上发送任何数量的请求,而 balancebeam 会将每个请求转发到上游服务器。如果客户端发送了一个错误的请求,会有一些错误处理。
    3. 一旦请求被读取,balancebeam 就会添加一个 X-Forwarded-For HTTP以便上游服务器能够知道客户端的IP地址。这对理解并不特别重要它只是一个事实上的标准头。
    4. 接下来,balancebeam 将请求转发给上游服务器。如果在传输请求时有错误,它就会通知客户端。
    5. 接下来,balancebeam 试图从上游服务器读取响应。如果响应被破坏或在接收时有其他问题,它将通知客户端。
    6. 最后,balancebeam 将响应转发给客户。

然后是封装库 request.rs response.rs,就像 project 1 里面的 gimili wrapper 一样,又臭又长。分别用来基于 TCP 字节流来处理 HTTP 请求和响应。分别提供了 read_from_streamwrite_to_stream 这两个接口供 main 处理两个 TCP 链接( Client HTTP req -> Proxy HTTP req -> Server, Server HTTP resp -> Proxy HTTP resp -> Client。具体的里面的接口我也没细看等到 Milestone 2 需要修改的时候再说吧。

Milestone 1

讲义上没有提出明确的实现目标,简单的把代码改成多线程而已。主要就是把 stateArc 给共享掉(反正是不可变借用,十分的安全)。

ThreadPoolstd::thread 没啥太大区别,都封装的很好了,直接看一下 doc.rs 里面的例子就行了。

Milestone 2

又是一个奇怪的 task point要求把所有的 IO 换成异步的。但是依然没有任何的难度,根据 IDE 提示加 asyncawait 就可以了。

Milestone 3

实现故障转移failover如果一个 upstream 挂了,将连接选一个别的(一开始有一堆可用的 upstream同时还要标记这个挂掉的 upstream 防止后面的连接继续选择它;如果所有的 upstream 都挂了,这时候才返回错误。实现方法是修改 main.rs::connect_to_upstream,因为这个过程仅在建立连接的时候执行。

实现了一个最简单的,在 ProxyState 里面加了一个 Arc<Mutex<Vec<usize>>>,用来存放可用的 upstream 的 index。
选择的时候就在这个加了锁的 Vec 里面选,然后用这个里面的 index 来取对应的 upstream 的地址。如果一开始就发现这个可用列表为空,那么直接返回错误;否则尝试连接。如果连接失败,需要将这个 index 从可用列表里面删除。唯一需要注意的就是,可以在 TcpStream::connect 之前释放一次锁,然后需要修改可用列表的时候再加锁,因为 connect 比较耗时。

感觉 RwLock 和这个东西差不多,但是可能在某些情况下的并发性能会更好一些。至于它说的用 channel感觉挺麻烦的所以没写。

Milestone 4

实现周期性的主动连接测试active health check就是我们的代理程序隔一段时间向每个 upstream 的某一个特定地址(参数里面的 active_health_check_path)发一个 HTTP 请求,如果返回 200 就说明没问题;如果之前有问题现在没问题,就可以继续用;出现问题则需要标记为下线。

由于上个 Milestone 里面用到的极简数据结构,导致实现起来有点生草。这里我是用 tokio::spawn 来生成了一个 task根据文档的说明这是一个 green thread鬼知道是什么东西。然后一个大循环先 sleep 一波(不能放到后面,因为他的测试比较智障,放到后面会出错);遍历所有的 upstream不管有没有被 disable建立 tcpstream 构造空请求然后接受响应最后判断状态码是不是200。以上任何一步出错都必须从可用列表中删除注意有可能已经删除了如果是 200则需要将它重新加入到可用列表中如果之前被删除了的话

一个坑:不要使用前面实现的 connect_to_upstream,因为它无法连接到已经被移出可用列表的 upstream不如说它根本就不让自己选 upstream但是我们这里需要测试每个 upstream。

附加任务?