go语言Fasthttp实践系列(1)

https://juejin.im/post/5d446c15e51d4561fd6cb46f

fasthttp 文章系列:

  • fasthttp web client 客户端(本文)
  • fasthttp web server 服务端(及路由)
  • fasthttp web server 中间件( 简单认证/ uber-go的zap日志/session会话…)
  • fasthttp RESTful (处理JSON数据)
  • fasthttp 处理 JWT (及 JWT安全性)
  • fasthttp 对接非标准 web client (作为AAA, 数据加解密)
  • fasthttp 部署

[简述] github.com/valyala/fas… 是 golang 中一个标志性的高性能 HTTP库, 主要用于 webserver 开发, 以及 web client / proxy 等. fasthttp 的高性能开发思路, 启发了很多开发者.

fasthttp 自己的介绍如下:

Fast HTTP package for Go. Tuned for high performance. Zero memory allocations in hot paths. Up to 10x faster than net/http

Fast HTTP implementation for Go.

Currently fasthttp is successfully used by VertaMedia in a production serving up to 200K rps from more than 1.5M concurrent keep-alive connections per physical server.

事实上, 这有点小夸张, 但在一定场景下经过优化部署, 确是有很高的性能.

近3年来, fasthttp 被我用在几个重大项目(对我而言, 项目有多重大, 与收钱的多少成正比) 中, 这里, 就写一个小系列, 介绍 fasthttp 的实际使用与经验得失.


想直接看代码的朋友, 请访问 我写的 fasthttp-example


0. 关于 fasthttp 的优点介绍

以下文字来自 傅小黑 原创文章: Go 开发 HTTP 的另一个选择 fasthttp 写于2016/09/30 :

fasthttp 是 Go 的一款不同于标准库 net/http 的 HTTP 实现。fasthttp 的性能可以达到标准库的 10 倍,说明他魔性的实现方式。主要的点在于四个方面:

  • net/http 的实现是一个连接新建一个 goroutine;fasthttp 是利用一个 worker 复用 goroutine,减轻 runtime 调度 goroutine 的压力
  • net/http 解析的请求数据很多放在 map[string]string(http.Header) 或 map[string][]string(http.Request.Form),有不必要的 []byte 到 string 的转换,是可以规避的
  • net/http 解析 HTTP 请求每次生成新的 http.Request 和 http.ResponseWriter; fasthttp 解析 HTTP 数据到 fasthttp.RequestCtx,然后使用 sync.Pool 复用结构实例,减少对象的数量
  • fasthttp 会延迟解析 HTTP 请求中的数据,尤其是 Body 部分。这样节省了很多不直接操作 Body 的情况的消耗

但是因为 fasthttp 的实现与标准库差距较大,所以 API 的设计完全不同。使用时既需要理解 HTTP 的处理过程,又需要注意和标准库的差别。

这段文字非常精练的总结了 fasthttp 的特点, 我摘录了这部分放在这里, 感谢 傅小黑 —- 另外, 傅小黑 的技术文章非常棒, 欢迎大家去围观他….

1. 从 HTTP 1.x 协议说起

想要使用 fasthttp 的朋友, 请尽量对 http 1.x 协议要很熟悉, 很熟悉.

1.1 HTTP 1.x 协议简述

简单来说, HTTP 1.x 协议, 是一个被动式短连接的 client (请求 request ) - server ( 响应 response) 交互的规范:

  1. 协议一般来说, 以 TCP 通讯协议为基础 ( 不谈 QUIC 这个以 udp 为底层实现的变异)

    web client 通过 DNS 把域名转换成 IP 地址后, 与 web server 握手连接, 连接成功后, web client 客户端向 web server 服务端发出请求, 服务端收到请求后, 向 client 客户端应答

  2. 通过 URL / URI 进行导址, 同时, URL/URI 中包含部分数据

    URL 形式如 http://192.168.1.1:8088/rpc/schedule 其中 http://192.168.1.1:8080 这部分是通讯协议, 服务器 IP 地址与端口号, 这是前面 TCP 通讯的依据

    1. web 服务器端在 http://192.168.1.1:8080 这个地址上监听, 随时准备接收 web client 请求并应答
    2. web 客户端通过 http://192.168.1.1:8080 这个地址所指定的 web 服务器进行 tcp 连接, 连接成功后, web 客户端向服务器发出 请求数据, web 服务端应答 响应数据
    3. 特别注意, 请求数据, 与响应数据, 遵从 HTTP 协议规定的统一格式
  3. 在 HTTP 1.x 协议中规定的传输( 请求/应答) 数据格式, 一般称为 HyperText, 是一种文本数据格式, 当然了, 在 TCP 传输时还是二进制数据块 ( 这是使用 fasthttp 的关键点) . 具体数据格式见 1.2 小节

  4. HTTP 协议规定了一些信令, 如下描述, 来区分不同的交互操作

    根据HTTP标准,HTTP请求可以使用多种请求方法:

    • HTTP1.0定义了三种请求方法: GET, POST 和 HEAD方法。
    • HTTP1.1新增了五种请求方法:OPTIONS, PUT, DELETE, TRACE 和 CONNECT 方法。
  5. 由于 HTTP 协议相关的 MIME 规范, HTTP 1.x 也可以传输图像/音乐/视频等其他数据格式,但这些被传输的真正有效数据都被封装在 http payload 这一部分里, http header 还保留( 只是字段多少, 以及字段中的值不同) ————-这是另一个与 fasthttp 关联的另一个要点

1.2 HTTP 1.x 中的请求/响应共同遵从的数据格式

下面看一个 POST 请求











请求数据如下, 响应是一样的格式. 在下面的数据中:

  1. 1\. 在下面的数据格式中, 特别注意, 中间有一个空行
  2. 1\. 空行上半部分, http header , 下半部分, http payload 或叫 http body
  3. 1\. 在上半部分的 http header 中, 请注意第1,2
  4. 1\. 请对比一下, 下方同时列出的 GET 请求数据

POST 请求数据示例

  1. POST /rpc/schedule HTTP/1.1
  2. Host: 192.168.1.1:3001
  3. Content-Type: application/json
  4. Accept: application/vnd.pgrst.object+json
  5. User-Agent: PostmanRuntime/7.15.2
  6. Host: 192.168.1.1:3001
  7. Accept-Encoding: gzip, deflate
  8. Content-Length: 208
  9. Connection: keep-alive
  10. {
  11. "actual_start_date": "2019-07-29",
  12. "actual_end_date": "2019-07-29",
  13. "plan_start_date": "2019-07-29",
  14. "plan_end_date": "2019-02-12",
  15. "title": "00002",
  16. "user_id": 2098735545843717147
  17. }

GET 请求示例

  1. GET /schedule?user_id=eq.2098735545843717147 HTTP/1.1
  2. Host: 192.168.1.1:3001
  3. Content-Type: application/json
  4. User-Agent: PostmanRuntime/7.15.2
  5. Accept: */*
  6. Host: 192.168.1.1:3001
  7. Accept-Encoding: gzip, deflate
  8. Content-Length: 208
  9. Connection: keep-alive
  10. {
  11. "actual_start_date": "2019-07-29",
  12. "actual_end_date": "2019-07-29",
  13. "plan_start_date": "2019-07-29",
  14. "plan_end_date": "2019-02-12",
  15. "title": "00002",
  16. "user_id": 2098735545843717147
  17. }

1.3 http 1.x 协议小结与开发关联点

这里几句很重要, 所以,

HTTP 1.x 几个基础点:

  1. HTTP 1.x 通过 tcp 进行通讯
  2. 请求与响应的格式, 数据数据的格式是一样的

    特别注意请求数据中的第一行,第二行 特别注意 HTTP header 与 HTTP payload 的那空行分隔

  3. 注意 URL/URI 中也包含有数据, 换个话说,在 http://192.168.1.1:3001/schedule?user_id=eq.2098735545843717147 中, 其他部分 /schedule?user_id=eq.2098735545843717147 看做请求数据的一部分


从 HTTP 1.x 协议, 可以总结 web 开发的要点

  1. 处理 tcp 通讯, 包括:

    • 通过 dns 转化域名得到 IP 地址, 包括 ip4 / ip6 地址
    • 对 tcp 进行通讯重写或优化, 长连接或短连接, 都在这里了
    • 或对 tcp 进行转发 ( 这是 proxy ) 或劫持, 在 tcp 通讯最底层进行一些特殊操作
  2. 对 URL /URI 进行处理, 这是路由寻址

    • 按 URI 及相关数据特征进行拦截处理, 这是反向代理与缓存
    • 进行一些 URI 转换, 例如 302 的重定向
    • 在 URI 中携带小部分数据的组装与处理
  3. HTTP 数据处理

    • 对 HTTP header / HTTP payload 进行处理, 这是变化最多的部分, 按业务/功能的不同, 即简单也复杂

fasthttp 的性能优化思路

  1. 重写了在 tcp 之上进行 HTTP 握手/连接/通讯的 goroutine pool 实现
  2. 对 http 数据基本按传输时的二进制进行延迟处理, 交由开发者按需决定
  3. 对二进制的数据进行了缓存池处理, 需要开发者手工处理以达到零内存分配

_

_


好, HTTP 1.x 就简述到这了, 后面会大量引用到这一章节说的内容.


_

_

2. fasthttp 非”标准”的争议, 及为什么选择fasthttp

3. fasthttp 中的两种类型web client

4. fasthttp 客户端实践

5. 小结

_

关于我

网名 tsingson (三明智, 江湖人称3爷)

原 ustarcom IPTV/OTT 事业部播控产品线技术架构湿/解决方案工程湿角色(8年), 自由职业者,

喜欢音乐(口琴,是第三/四/五届广东国际口琴嘉年华的主策划人之一), 摄影与越野,

喜欢 golang 语言 (商用项目中主要用 postgres + golang )

_

_ tsingson 写于中国深圳 小罗号口琴音乐中心, 2019/08/02

ft_authoradmin  ft_create_time2019-08-03 16:17
 ft_update_time2019-08-03 16:17