C++ 實(shí)現(xiàn)高性能HTTP客戶端
一、什么是Http Client
Http協(xié)議,是全互聯(lián)網(wǎng)共同的語言,而Http Client,可以說是我們需要從互聯(lián)網(wǎng)世界獲取數(shù)據(jù)的最基本方法,它本質(zhì)上是一個(gè)URL到一個(gè)網(wǎng)頁的轉(zhuǎn)換過程。而有了基本的Http客戶端功能,再搭配上我們想要的規(guī)則和策略,上至內(nèi)容檢索下至數(shù)據(jù)分析都可以實(shí)現(xiàn)了。
繼上一次介紹用Workflow可以10行C++代碼實(shí)現(xiàn)高性能HTTP服務(wù),今天繼續(xù)給大家用C++實(shí)現(xiàn)一個(gè)高性能的Http客戶端也同樣很簡單!
// [http_client.cc]
#include "stdio.h"
#include "workflow/HttpMessage.h"
#include "workflow/WFTaskFactory.h"
int main (int argc, char *argv[])
{
const char *url = "https://github.com/sogou/workflow";
WFHttpTask *task = WFTaskFactory::create_http_task (url, 2, 3,
[](WFHttpTask * task) {
fprintf(stderr, "%s %s %s\r\n",
task->get_resp()->get_http_version(),
task->get_resp()->get_status_code(),
task->get_resp()->get_reason_phrase());
});
task->start();
getchar(); // press "Enter" to end.
return 0;
}
只要安裝好了Workflow,以上代碼即可以通過以下命令編譯出一個(gè)簡單的http_client:
g++ -o http_client http_client.cc --std=c++11 -lworkflow -lssl -lcrypto -lpthread
根據(jù)Http協(xié)議,我們執(zhí)行這個(gè)可執(zhí)行程序 ./http_client,就會(huì)得到以下內(nèi)容:
HTTP/1.1 200 OK
同理,我們還可以通過其他api來獲得返回的其他Http header和Http body,一切內(nèi)容都在這個(gè) WFHttpTask 中。而因?yàn)?strong>Workflow是個(gè)異步調(diào)度框架,因此這個(gè)任務(wù)發(fā)出之后,不會(huì)阻塞當(dāng)前線程,外加內(nèi)部自帶的連接復(fù)用,從根本上保證了我們的Http Client的高性能。
接下來給大家詳細(xì)講解一下原理~
二、請求的過程
1. 創(chuàng)建Http任務(wù)
上述demo可以看到,請求是通過發(fā)起一個(gè)Workflow的Http異步任務(wù)來實(shí)現(xiàn)的,創(chuàng)建任務(wù)的接口如下:
WFHttpTask *create_http_task(const std::string& url,
int redirect_max, int retry_max,
http_callback_t callback);
第一個(gè)參數(shù)就是我們要請求的URL。對應(yīng)的,在一開始的示例中,我們的重定向次數(shù)redirect_max是2次,而重試次數(shù)retry_max是3次。第四個(gè)參數(shù)是一個(gè)回調(diào)函數(shù),示例中我們用了一個(gè)lambda,由于Workflow的任務(wù)都是異步的,因此我們處理結(jié)果這件事情是被動(dòng)通知我們的,結(jié)果回來就會(huì)調(diào)起這個(gè)回調(diào)函數(shù),格式如下:
using http_callback_t = std::function<void (WFHttpTask *)>;
2. 填寫header并發(fā)出
我們的網(wǎng)絡(luò)交互無非是請求-回復(fù),對應(yīng)到Http Client上,在我們創(chuàng)建好了task之后,我們有一些時(shí)機(jī)是處理請求的,在Http協(xié)議里,就是在header里填好協(xié)議相關(guān)的事情,比如我們可以通過Connection來指定希望得到建立Http的長連接,以節(jié)省下次建立連接的耗時(shí),那么我們可以把Connection設(shè)置為Keep-Alive。示例如下:
protocol::HttpRequest *req = task->get_req();
req->add_header_pair("Connection", "Keep-Alive");
task->start();
最后我們會(huì)把設(shè)置好請求的任務(wù),通過 task->start(); 發(fā)出。最開始的 http_client.cc 示例中,有一個(gè) getchar(); 語句,是因?yàn)槲覀兊漠惒饺蝿?wù)發(fā)出后是非阻塞的,當(dāng)前線程不暫時(shí)停住就會(huì)退出,而我們希望等到回調(diào)函數(shù)回來,因此我們可以用多種暫停的方式。
3. 處理返回結(jié)果
一個(gè)返回結(jié)果,根據(jù)Http協(xié)議,會(huì)包含三部分:消息行、消息頭header、消息正文body。如果我們想要獲取body,可以這樣:
const void *body; size_t body_len; task->get_resp()->get_parsed_body(&body, &body_len);
三、高性能的基本保證
我們使用C++來寫Http Client,最香的就是可以利用其高性能。Workflow對高并發(fā)是如何保證的呢?其實(shí)就兩點(diǎn):
純異步;
連接復(fù)用;
前者是對線程資源的重復(fù)利用、后者是對連接資源的重復(fù)利用,這些框架層級(jí)都為用戶管理好了,充分減少開發(fā)者的心智負(fù)擔(dān)。
1. 異步調(diào)度模式
同步和異步的模式直接決定了我們的Http Client可以有多大的并發(fā)度。為什么呢?通過下圖可以先看看同步框架發(fā)起三個(gè)Http任務(wù),線程模型是怎樣的:

網(wǎng)絡(luò)延遲往往非常大,如果我們在同步等待任務(wù)回來的話,線程就會(huì)一直被占用。這時(shí)候我們需要看看異步框架是如何實(shí)現(xiàn)的:

如圖所示,只要任務(wù)發(fā)出之后,線程即可做其他事情,我們傳入了一個(gè)回調(diào)函數(shù)做異步通知,因此等任務(wù)的網(wǎng)絡(luò)回復(fù)收完之后,再讓線程執(zhí)行這個(gè)回調(diào)函數(shù)即可拿到Http請求的結(jié)果,期間多個(gè)任務(wù)并發(fā)出去的時(shí)候,線程是可以復(fù)用的,輕松達(dá)到幾十萬的QPS并發(fā)度。
2. 連接復(fù)用
我們剛才有提到,只要我們建立了長連接,即可提高效率。為什么呢?因?yàn)榭蚣軐B接有復(fù)用。我們先來看看如果一個(gè)請求就建立一個(gè)連接,會(huì)是什么樣的情況:

很顯然,占用大量的連接是對系統(tǒng)資源的浪費(fèi),而且每次都要做connect以及close是非常耗時(shí)的,除了TCP常見的握手以外,許多應(yīng)用層協(xié)議建立連接的過程也會(huì)相對復(fù)雜。但使用Workflow就不會(huì)有這樣的煩惱,Workflow會(huì)在任務(wù)發(fā)出的時(shí)候自動(dòng)查找當(dāng)前可以復(fù)用的連接,如果沒有才會(huì)自動(dòng)創(chuàng)建,完全不需要開發(fā)者關(guān)心連接如何復(fù)用的細(xì)節(jié):

3. 解鎖其他功能
當(dāng)然,除了以上的高性能以外,一個(gè)高性能的Http Client往往還有許多其他的需求,這里可以結(jié)合實(shí)際情況與大家分享:
- 1.結(jié)合workflow的串并聯(lián)任務(wù)流,實(shí)現(xiàn)超大規(guī)模并行抓?。?/li>
- 2.按順序或者按指定速度請求某個(gè)站點(diǎn)的內(nèi)容,避免請求過猛被封禁;
- 3.Http Client遇到redirect可以自動(dòng)幫我做跳轉(zhuǎn),一步到位請求到最終結(jié)果;
- 4.希望通過proxy代理訪問
HTTP與HTTPS資源;
以上這些需求,要求框架對于Http任務(wù)的編排有超高的靈活性,以及對實(shí)際需求(比如redirect、ssl代理等功能)有非常接地氣的支持,這些Workflow都已經(jīng)實(shí)現(xiàn)。
項(xiàng)目地址
https://github.com/sogou/workflow
到此這篇關(guān)于C++ 實(shí)現(xiàn)高性能HTTP客戶端的文章就介紹到這了,更多相關(guān)C++ 實(shí)現(xiàn)HTTP客戶端內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C++?自增自減運(yùn)算符的實(shí)現(xiàn)示例
本文主要介紹了C++?自增自減運(yùn)算符的實(shí)現(xiàn)示例,自增和自減運(yùn)算符在C++中主要用于循環(huán)語句中,使循環(huán)變量的值自動(dòng)+1或者-1,具有一定的參考價(jià)值,感興趣的可以了解一下2023-08-08
一文搞懂C語言static關(guān)鍵字的三個(gè)作用
這篇文章主要介紹了C語言static關(guān)鍵字的三個(gè)作用,本文通過實(shí)例代碼圖文相結(jié)合給大家介紹的非常詳細(xì),需要的朋友可以參考下2022-04-04
VC程序設(shè)計(jì)中CreateProcess用法注意事項(xiàng)
這篇文章主要介紹了VC程序設(shè)計(jì)中CreateProcess用法注意事項(xiàng),需要的朋友可以參考下2014-07-07
C++控制臺(tái)實(shí)現(xiàn)簡單人機(jī)對弈井字棋
這篇文章主要為大家詳細(xì)介紹了C++控制臺(tái)實(shí)現(xiàn)簡單人機(jī)對弈井字棋,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-05-05
c++?qt自定義搜索編輯框的實(shí)現(xiàn)方法
這篇文章主要介紹了c++?qt自定義搜索編輯框,通過自定義QLineEdit,在編輯框里添加布局,將按鈕設(shè)置在右邊,當(dāng)點(diǎn)擊按鈕搜索按鈕時(shí)發(fā)送信號(hào)到主界面做相應(yīng)的操作,需要的朋友可以參考下2022-03-03
C++ 標(biāo)準(zhǔn)模板庫 STL 順序容器詳解
這篇文章主要介紹了C++ 標(biāo)準(zhǔn)模板庫 STL 順序容器詳解,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-05-05

