天天看點

TrafficServer http2 代碼分析

TrafficServer 是Apache基金會的 HTTP/HTTP2 代理伺服器。

TrafficServer 的 HTTP2 部分主要的代碼在 :

  1. trafficserver/proxy/http2/HTTP2.h, HTTP2.cc
  2. trafficserver/proxy/http2/Http2Stream.h, Http2Stream.cc
  3. trafficserver/proxy/http2/Http2ClientSession.h, Http2ClientSession.cc
  4. trafficserver/proxy/http2/Http2ConnectionState.h, Http2ConnectionState.cc
  5. trafficserver/proxy/http2/ Http2DependencyTree.h, Http2DependencyTree.cc
  6. trafficserver/proxy/http2/HPACK.h, HPACK.cc
  7. trafficserver/proxy/http2/HuffmanCodec.h, HuffmanCodec.cc

1. trafficserver/proxy/http2/HTTP2.h, HTTP2.cc

10種Frame 的struct定義,解析/序列化函數,HeaderList轉換函數,一些常量定義

2. trafficserver/proxy/http2/Http2ClientSession.h, Http2ClientSession.cc

Http2ClientSession 處理 io event 事件,解析 FrameHeader,

調用順序:

1

2

3

4

5

6

7

8

9

10

11

12

Http2ClientSession::main_event_handler()

--> state_read_connection_preface()

--> state_start_frame_read()

--> state_process_frame_read() 解析出一個完整的Frame

--> do_complete_frame_read()

--> Http2ConnectionState::main_event_handler(HTTP2_SESSION_EVENT_RECV )

3. trafficserver/proxy/http2/Http2ConnectionState.h, Http2ConnectionState.cc

Http2ConnectionState,對應一個 HTTP2 連接配接,代碼基本等價于 proxygen 的 HTTPSession,主要的成員變量:

1

2

3

4

5

6

7

8

9

10

11

{

雙連結清單<Http2Stream> //略挫,有個find_stream 用的周遊,改map好點。

Http2ClientSession,

HpackHandle 2個,

DependencyTree,

Http2ConnectionSettingsclient_settings server_settings,連接配接的幾個 Settings 配置項,一個int數組

}

主要的方法:各個 rcv_xxx_frame() 和 send_xxx_frame(0 Http2ConnectionState::main_event_handler(HTTP2_SESSION_EVENT_RECV )裡面,解析輸入的各種 Frame

  • rcv_data_frame, // HTTP2_FRAME_TYPE_DATA
  • rcv_headers_frame, // HTTP2_FRAME_TYPE_HEADERS
  • rcv_priority_frame, // HTTP2_FRAME_TYPE_PRIORITY
  • rcv_rst_stream_frame, // HTTP2_FRAME_TYPE_RST_STREAM
  • rcv_settings_frame, // HTTP2_FRAME_TYPE_SETTINGS
  • rcv_push_promise_frame, // HTTP2_FRAME_TYPE_PUSH_PROMISE
  • rcv_ping_frame, // HTTP2_FRAME_TYPE_PING
  • rcv_goaway_frame, // HTTP2_FRAME_TYPE_GOAWAY
  • rcv_window_update_frame, // HTTP2_FRAME_TYPE_WINDOW_UPDATE
  • rcv_continuation_frame, // HTTP2_FRAME_TYPE_CONTINUATION

還有發送各類 frame 的接口,send_data_frames()/send_headers_frame() / send_push_promise_frame() / send_rst_stream_frame() 等。

具體地:

rcv_data_frame :

find_stream,然後檢查stream的state是不是OPEN/HALF_CLOSE_LOCAL,去除 padding,處理END_STREAM flag,檢查本端流量控制 window_size ,儲存buffer,更新本地的window,發回 Connection 和 Stream Level 的WindowUpdate Frame。

rcv_headers_frame:

find_stream/create_stream,處理padding,處理PRIORITY flag添加到 PriorityTree 裡面,如果 Header Block 結束了(即有END_HEADERS FLAG),那就http2_decode_header_blocks() 做HPACK 解壓縮,調用 Http2Stream 處了解析出的HeaderList

rcv_priority_frame:

find_stream,reprioritize,重新調整樹。

rcv_rst_stream_frame

rcv_settings_frame:

parse檢查收到的各個配置項,更新到client_settings中,并發送SETTINGS_ACK

rcv_push_promise_frame 等

包括 rcv_ping_frame

rcv_goaway_frame:

rcv_window_update_frame

比較簡單,略。

rcv_continuation_frame

類似 rcv_headers_frame

send_data_frames_depends_on_priority

取了 DependencyTree的top(),

send_data_frames 等

send_a_data_frame

send_headers_frame

send_push_promise_frame

send_rst_stream_frame

send_settings_frame

send_ping_frame

send_goaway_frame

send_window_update_frame

比較簡單,就是檢查字段合法性,序列化,然後發送。

4. trafficserver/proxy/http2/Http2Stream.h, Http2Stream.cc

對應實作HTTP2 的Stream,重要的成員變量有:

Http2StreamState _state;//實作IDLE,OPEN,RESERVED,HALF_CLOSE等的切換。

DependencyTree::Node *priority_node;// Priority Tree中的節點指針。

5. trafficserver/proxy/http2/Http2DependencyTree.h, Http2DependencyTree.cc

Http2DependencyTree::Node 表示一個Stream,成員變量有:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

{

bool actived; //是否有輸出資料

bool queued; //是否在 actived 隊列中

uint32_t id;

uint32_t weight;

uint32_t point; //queue中的節點按照這個 point 做優先級排序

Node *parent; //父節點

DLL<Node> children; //子節點的雙清單

PriorityQueueEntry<Node *> *entry; //

PriorityQueue<Node *> *queue; //有輸出資料的所有子節點的隊列,按point 優先級降序排列

Http2Stream *t;

}

Http2DependencyTree 中有個 Node * root 指向 樹的根節點。

Http2DependencyTree的主要方法有:

  1. Node *find(uint32_t id);

    DFS 遞歸周遊樹,查找一個Stream。

  2. Node *add(uint32_t parent_id, uint32_t id, uint32_t weight, bool exclusive, T t); 就是插入 parent 的 children 連結清單中,兼做 exclusive 處理。
  3. void remove(Node *node); 從parent 的 queue 中移除自己,把自己的 queue 全轉給 parent,把自己的 children 全轉給 parent
  4. void reprioritize(uint32_t new_parent_id, uint32_t id, bool exclusive); 重排優先級,就是移動節點
  5. Node *top(); 傳回整個樹中,point 最高的葉子節點。遞歸實作。
  6. void activate(Node *node); 往上周遊,如果目前節點沒有 在 parent 的 queue中,加入
  7. void deactivate(Node *node, uint32_t sent); 從parent 的queue中去除自己。
  8. void update(Node *node, uint32_t sent);

這裡使用 Weighted Fair Queue (WFQ) Scheduling 來排程 Stream 之間的優先級。

Http2ConnectionState::send_data_frames_depends_on_priority() 中,是直接取了 樹的 top() 節點,

6. trafficserver/proxy/http2/HPACK.h, HPACK.cc

實作 HPACK 壓縮/解壓縮

7. trafficserver/proxy/http2/HuffmanCodec.h, HuffmanCodec.cc

實作靜态的 Huffman 表,接口簡單,隻有4個函數,,

是實作成 二叉樹,相比proxygen估計效率會低一些。