这一期全是干货。干得你口渴想喝水。
环境搭建
- 安装 Python。你可以选择官网安装、Anaconda安装或者你已经有了 Python3.5 以上的版本。PyPy也可以的。
- 可选:创建一个 Python 虚拟环境(不知所云的直接忽略这一步)
- 创建我们的项目文件夹
1 | 复制代码# bash shell |
在 Windows 上的同学不用担心,本教程的一切操作都是可以在 Windows、Linux 和 Mac 上完成的。
4. 创建测试路径和源代码路径
1 | 复制代码mkdir gethy # Python 界的约定俗成是在项目根目录下创建一个同名的路径来放源代码 |
- 安装依赖
1 | 复制代码pip install h2 |
我们要用到 Lukasa 大神写的 hyper-h2 库:https://github.com/python-hyper/hyper-h2
这个库实现了 h2 协议的底层部分,包括:编码解码 TCP 层字节串(hpack),建立并管理 HTTP 连接。
但是,这个库并没有实现 HTTP 应用层的方法(GET、POST)和语义(Requset & Response),也没有实现 Flow Control(流量管理)和 Server Push(服务器推送)。这些也是我们要实现的部分(除了 Server Push)
我们可以看到,HTTP协议是5、6、7层的协议,但是 hyper-h2 只实现了5、6层的功能。Web 应用是没有办法直接使用 hyper-h2 。所以我们要在 hyper-h2 的基础上,实现完整的 h2 协议。
关于网络协议和架构,请参考 What’s The Difference Between The OSI Seven-Layer Network Model And TCP/IP?
开始编程
定义 API
我们遵循自上而下的设计。先设计API,再实现函数。
1 | 复制代码touch http2protocol.py event.py |
用你最喜欢的编辑器打开http2protocol.py
,加入以下代码
1 | 复制代码class HTTP2Protocol: |
我们的库只有 2 个公开API,receive
和send
。
receive
用来从 TCP 层获取数据。send
将一个完整的 Stream
编码为 TCP 可以直接接收的数据。
值得强调的是,这个库不做任何 I/O。这种开发范式叫做 I/O 独立范式。库的使用者应该自己决定使用哪一种 IO 方式。这给予了开发者最大的灵活性。也符合 Clean Architecture 的原则。
hyper-h2 本身也是不做任何 IO 的,所以我们保留这个优良传统。
英文里叫 sans-IO model,请参考:http://sans-io.readthedocs.io
定义 Stream
除了HTTP2Protocol
类,Stream
类也是用户会直接使用的类。
1 | 复制代码class Stream: |
看到这里大家可能就会觉得很亲切了。一个 Stream 其实就代表了一个常规的 HTTP Request 或者 Response。我们有常规的 headers,常规的 data(有些人叫 body)。与 HTTP/1.x 时代唯一不同的是,多了一个 stream id。
写测试 TDD
Test Driven Development 与自上而下得到设计模式是密不可分的。现在我们有了API,写测试同时也交代了 API 的使用方法。
1 | 复制代码cd ../test |
我们的库很小,一个测试文件就够了。我们还需要一个帮助模组
1 | 复制代码wget https://raw.githubusercontent.com/CreatCodeBuild/gethy/master/test/helpers.py |
这个帮助模组是 Lusaka 大神在 hyper-h2 的测试中提供的。
我们现在来想象一下 gethy 的用法
1 | 复制代码# 伪代码 |
大家可以看到,我在这里写了一个伪代码的单线程阻塞式同步服务器。我们的库是完全不做 IO 的。一切IO都直接交给 Server 去完成。gethy 仅仅是在内存里处理数据而已。上面的代码例子也清楚地展示了API的使用方式。
测试网络协议的实现的一大难点就在于 IO。如果类库没有 IO,那么测试其实变得简单了。那么,我们来看看具体的测试怎么写吧。
1 | 复制代码# test_all.py |
六个测试案例,测试了发送接收与回复请求。最后两个测试使用巨大数据量,是为了测试 Flow Control 的正确性。我们目前可以不管。
先实现第一个
1 | 复制代码# test_all.py |
阅读上面的测试,大家可以基本上知道 gethy 的用法和 http2 的基本语义。大家可以发现,http2 的语义和 http1 基本没有变化。唯一需要注意的就是 headers 里4个:xxx
字样的 header。:
冒号是协议使用的 header 符号。应用自定义的 header 不应该使用冒号。然后,虽然 http2 协议本身是允许大写字母,并且是大小写敏感的,但是 gethy 的依赖库 hyper-h2 只允许小写。
现在来实现
1 | 复制代码def test_receive_headers_and_data(): |
带数据的请求也很简单,加上DATA
frame即可。
好的,我们再来看看如何发送回复。
1 | 复制代码def test_send_headers_only(): |
只发送 Headers 很简单,创建一个Stream
,然后发送就行了。目前大家可以忽略MoreDataToSendEvent
。我会在视频和后续文章中娓娓道来。
1 | 复制代码def test_send_headers_and_data(): |
如果要发送数据,只需要将stream.data
赋值。注意,一定要是bytes
类型。以上测试也涉及到了 Flow Control(流量控制),我会在视频和后续文章中讲解。
结语
好啦,想必到这里你一定对 GetHy 有了大局观的认识,也熟悉了 API 及应用场景。接下来就是要实现它了。我们下一期再见!
资源
代码
B站
油腻的管子
文章
本文转载自: 掘金