高性能 C++ HTTP 客户端原理与实现

一、什么是Http Client

Http协议,是全互联网共同的语言,而Http Client,可以说是我们需要从互联网世界获取数据的最基本方法,它本质上是一个URL到一个网页的转换过程。而有了基本的Http客户端功能,再搭配上我们想要的规则和策略,上至内容检索下至数据分析都可以实现了。

继上一次介绍用Workflow可以10行C++代码实现一个高性能Http服务器,今天继续给大家用C++实现一个高性能的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,以上代码即可以通过以下命令编译出一个简单的http_client:

g++ -o http_client http_client.cc --std=c++11 -lworkflow -lssl -lcrypto -lpthread

根据Http协议,我们执行这个可执行程序 ./http_client,就会得到以下内容:

HTTP/1.1 200 OK

同理,我们还可以通过其他api来获得返回的其他Http header和Http body,一切内容都在这个 WFHttpTask 中。而因为Workflow是个异步调度框架,因此这个任务发出之后,不会阻塞当前线程,外加内部自带的连接复用,从根本上保证了我们的Http Client的高性能。

接下来给大家详细讲解一下原理~

二、请求的过程

1. 创建Http任务

上述demo可以看到,请求是通过发起一个Workflow的Http异步任务来实现的,创建任务的接口如下:

WFHttpTask *create_http_task(const std::string& url,
                             int redirect_max, int retry_max,
                             http_callback_t callback);

第一个参数就是我们要请求的URL。对应的,在一开始的示例中,我们的重定向次数redirect_max是2次,而重试次数retry_max是3次。第四个参数是一个回调函数,示例中我们用了一个lambda,由于Workflow的任务都是异步的,因此我们处理结果这件事情是被动通知我们的,结果回来就会调起这个回调函数,格式如下:

using http_callback_t = std::function<void (WFHttpTask *)>;

2. 填写header并发出

我们的网络交互无非是请求-回复,对应到Http Client上,在我们创建好了task之后,我们有一些时机是处理请求的,在Http协议里,就是在header里填好协议相关的事情,比如我们可以通过Connection来指定希望得到建立Http的长连接,以节省下次建立连接的耗时,那么我们可以把Connection设置为Keep-Alive。示例如下:

protocol::HttpRequest *req = task->get_req();
req->add_header_pair("Connection", "Keep-Alive");
task->start();

最后我们会把设置好请求的任务,通过 task->start(); 发出。最开始的 http_client.cc 示例中,有一个 getchar(); 语句,是因为我们的异步任务发出后是非阻塞的,当前线程不暂时停住就会退出,而我们希望等到回调函数回来,因此我们可以用多种暂停的方式。

3. 处理返回结果

一个返回结果,根据Http协议,会包含三部分:消息行消息头header消息正文body。如果我们想要获取body,可以这样:

const void *body;
size_t body_len;
task->get_resp()->get_parsed_body(&body, &body_len); 

三、高性能的基本保证

我们使用C++来写Http Client,最香的就是可以利用其高性能。Workflow对高并发是如何保证的呢?其实就两点:

  • 纯异步;
  • 连接复用;

前者是对线程资源的重复利用、后者是对连接资源的重复利用,这些框架层级都为用户管理好了,充分减少开发者的心智负担。

1. 异步调度模式

同步和异步的模式直接决定了我们的Http Client可以有多大的并发度。为什么呢?通过下图可以先看看同步框架发起三个Http任务,线程模型是怎样的:

image

网络延迟往往非常大,如果我们在同步等待任务回来的话,线程就会一直被占用。这时候我们需要看看异步框架是如何实现的:

image

如图所示,只要任务发出之后,线程即可做其他事情,我们传入了一个回调函数做异步通知,因此等任务的网络回复收完之后,再让线程执行这个回调函数即可拿到Http请求的结果,期间多个任务并发出去的时候,线程是可以复用的,轻松达到几十万的QPS并发度。

2. 连接复用

我们刚才有提到,只要我们建立了长连接,即可提高效率。为什么呢?因为框架对连接有复用。我们先来看看如果一个请求就建立一个连接,会是什么样的情况:

image

很显然,占用大量的连接是对系统资源的浪费,而且每次都要做connect以及close是非常耗时的,除了TCP常见的握手以外,许多应用层协议建立连接的过程也会相对复杂。但使用Workflow就不会有这样的烦恼,Workflow会在任务发出的时候自动查找当前可以复用的连接,如果没有才会自动创建,完全不需要开发者关心连接如何复用的细节:

image

3. 解锁其他功能

当然,除了以上的高性能以外,一个高性能的Http Client往往还有许多其他的需求,这里可以结合实际情况与大家分享:

  1. 结合workflow的串并联任务流,实现超大规模并行抓取
  2. 按顺序或者按指定速度请求某个站点的内容,避免请求过猛被封禁;
  3. Http Client遇到redirect可以自动帮我做跳转,一步到位请求到最终结果;
  4. 希望通过proxy代理访问HTTPHTTPS资源;

以上这些需求,要求框架对于Http任务的编排有超高的灵活性,以及对实际需求(比如redirect、ssl代理等功能)有非常接地气的支持,这些Workflow都已经实现。

项目地址

https://github.com/sogou/workflow

欢迎使用 workflowstar 支持一下!

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,163评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,301评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,089评论 0 352
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,093评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,110评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,079评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,005评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,840评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,278评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,497评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,667评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,394评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,980评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,628评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,796评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,649评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,548评论 2 352

推荐阅读更多精彩内容