搜狗 workflow异步调度框架(二)HTTP客户端

news2024/11/13 16:22:10

1.避免进程提前终止

由于任务的启动是异步的,所以任务的执行和主线程的执行是并行的,如果不加任何的控制,那么当主线程执行完所有操作以后直接退出,并且导致整个进程的终止。 WFFacilities::WaitGroup 可以根据情况阻塞线程或者恢复运行,可以用来控制主线程的运行情况。

#include <workflow/WFFacilities.h>
static WFFacilities::WaitGroup wait_group(1);
//wait_group的参数用来表示有多少个任务还没完成
void sig_handler(int signo){
  wait_group.done();
  //done方法表示完成了一个任务
}
int main(){
  signal(SIGINT, sig_handler);
  wait_group.wait();
  //wait方法当存在至少一个任务未完成时,线程阻塞。
  return 0;
}

2.http的客户端基本流程

利用workflow来实现一个http客户端。
实现一个http客户端的基本流程:

  • 使用工厂函数,根据任务类型HTTP,创建一个任务对象;
  • 设置任务的属性;
  • 为任务绑定一个回调函数;
  • 启动任务

在workflow当中,所有任务对象都是使用工厂函数来创建的。在创建任务的时候,还可以设置一些属性,比如要连接的服务端的url、最大重定向次数、连接失败的时候的重试次数和用户的回调函数(没有回调函数则传入nullptr)。

class WFTaskFactory
{
public:
	static WFHttpTask *create_http_task(const std::string& url,//要连接的服务端的url
										int redirect_max,//最大重定向次数
										int retry_max,//连接失败的时候的重试次数
										http_callback_t callback);//回调函数
};

在创建任务对象之后,启动任务对象之前,可以用访问任务对象的方法去修改任务的属性。

using WFHttpTask = WFNetworkTask<protocol::HttpRequest,
								 protocol::HttpResponse>;
REQ *get_req();//获取指向请求的指针
void set_callback(std::function<void (WFNetworkTask<REQ, RESP> *)> cb);//设置回调函数

关于HTTP的请求和响应,实际会存在更多相关的接口。

class HttpMessage : public ProtocolMessage
{
public:
	const char *get_http_version() const;
	
	bool set_http_version(const char *version);
	
	bool add_header_pair(const char *name, const char *value);
	
	bool set_header_pair(const char *name, const char *value);
	
	bool get_parsed_body(const void **body, size_t *size) const;
	
	/* Output body is for sending. Want to transfer a message received, maybe:
	* msg->get_parsed_body(&body, &size);
	* msg->append_output_body_nocopy(body, size); */
	bool append_output_body(const void *buf, size_t size);
	
	bool append_output_body_nocopy(const void *buf, size_t size);
	
	void clear_output_body();
	
	size_t get_output_body_size() const;
	//上述接口都有std::string版本
	//...
};
class HttpRequest : public HttpMessage
{
public:
	const char *get_method() const;
	
	const char *get_request_uri() const;
	
	bool set_method(const char *method);
	
	bool set_request_uri(const char *uri);
  	//上述接口都有std::string版本
	//...
};

class HttpResponse : public HttpMessage
{
public:
	const char *get_status_code() const;
	
	const char *get_reason_phrase() const;
	
	bool set_status_code(const char *code);
	
	bool set_reason_phrase(const char *phrase);
	
	/* Tell the parser, it is a HEAD response. */
	void parse_zero_body();
	//上述接口都有std::string版本
	//...
};

调用start方法可以异步启动任务。需要值得特别注意的是,只有客户端才可以调用start方法。通过观察得知,start方法的底层逻辑就是根据本任务对象创建一个序列,其中本任务是序列当中的第一个任务,随后启动该任务。

/* start(), dismiss() are for client tasks only. */
void start()
{
	assert(!series_of(this));
	Workflow::start_series_work(this, nullptr);
}

3.回调函数的设计

当任务的基本工作完成之后,就会执行用户设置的回调函数,在回调函数当中,可以获取本次任务的执行情况。
针对http任务,回调函数在执行过程中可以获取本次任务的执行状态和失败的原因。

template<class REQ, class RESP>
class WFNetworkTask : public CommRequest
{
public:
	// ...
	int get_state() const { return this->state; }
	int get_error() const { return this->error; }
	// ...
}

下面是使用状态码和错误码的例子。当http基本工作执行正常的时候,此时状态码为
WFT_STATE_SUCCESS ,当出现系统错误的时候,此时状态码为 WFT_STATE_SYS_ERROR ,可以使用strerror 获取报错信息。当出现url错误的使用,此时状态码为 WFT_STATE_DNS_ERROR ,可以使用gai_strerror 获取报错信息。

#include "unixHeader.h"
#include <workflow/HttpMessage.h>
#include <workflow/HttpUtil.h>
#include <workflow/WFTaskFactory.h>
#include <workflow/WFFacilities.h>
static WFFacilities::WaitGroup wait_group(1);
void sig_handler(int signo){
	wait_group.done();
}
void callback(WFHttpTask *httpTask){
	int state = httpTask->get_state();
	int error = httpTask->get_error();
	switch (state){
		case WFT_STATE_SYS_ERROR:
			fprintf(stderr, "system error: %s\n", strerror(error));
			break;
		case WFT_STATE_DNS_ERROR:
			fprintf(stderr, "DNS error: %s\n", gai_strerror(error));
			break;
		case WFT_STATE_SUCCESS:
			break;
	}
	if (state != WFT_STATE_SUCCESS){
		fprintf(stderr, "Failed. Press Ctrl-C to exit.\n");
		return;
	}
	fprintf(stderr, "success\n");
	wait_group.done();
}

int main(int argc, char *argv[]){
	std::string url = "http://";
	url.append(argv[1]);
	signal(SIGINT, sig_handler);
	auto httpTask = WFTaskFactory::create_http_task(url, 0, 0, callback);
	protocol::HttpRequest *req = httpTask->get_req();
	req->add_header_pair("Accept", "*/*");
	req->add_header_pair("User-Agent", "TestAgent");
	req->add_header_pair("Connection", "close");
	httpTask->start();
	wait_group.wait();
	return 0;
}

在使用回调函数的时候,还可以获取http请求报文和响应报文的内容。

template<class REQ, class RESP>
class WFNetworkTask : public CommRequest
{
	// ...
public:
	REQ *get_req() { return &this->req; }
	RESP *get_resp() { return &this->resp; }
 	// ...
}
//其中http任务的实例化版本
//REQ -> protocol::HttpRequest
//RESP -> protocol::HttpResponse

下面是样例代码:

void callback(WFHttpTask *task){
	protocol::HttpRequest *req = task->get_req();
	protocol::HttpResponse *resp = task->get_resp();
	// ...
	fprintf(stderr, "%s %s %s\r\n", req->get_method(),
	req->get_http_version(),
	req->get_request_uri());
	// ...
	fprintf(stderr, "%s %s %s\r\n", resp->get_http_version(),
	resp->get_status_code(),
	resp->get_reason_phrase());
	// ...
}

对于首部字段,workflow提供 protocol::HttpHeaderCursor 类型作为遍历所有首部字段的迭代器。next 方法负责找到下一对首部字段键值对,倘若已经解析完成,就会返回 false 。 find 会根据首部字段的键,找到对应的值,值得注意的是, find 方法会修改迭代器的位置。

class HttpHeaderCursor{
//...
public:
	bool next(std::string& name, std::string& value);
	bool find(const std::string& name, std::string& value);
	void rewind();
	//...
};

下面是样例:

void callback(WFHttpTask *task){
	protocol::HttpRequest *req = task->get_req();
	protocol::HttpResponse *resp = task->get_resp();
	//...
	std::string name;
	std::string value;
	// ....
	// 遍历请求报文的首部字段
	protocol::HttpHeaderCursor req_cursor(req);
	while (req_cursor.next(name, value)){
		fprintf(stderr, "%s: %s\r\n", name.c_str(), value.c_str());
 	}
	fprintf(stderr, "\r\n");
 	// 遍历响应报文的首部字段
	protocol::HttpHeaderCursor resp_cursor(resp);
	while (resp_cursor.next(name, value)){
		fprintf(stderr, "%s: %s\r\n", name.c_str(), value.c_str());
 	}
	fprintf(stderr, "\r\n");
 	//...
}

对于http报文的报文体,可以使用 get_parsed_body 方法获取报文的内容,需要注意的是它的用法。

	//...
	// 首先需要定义一个指针变量,该指针的基类型是const void
	const void *body;
	size_t body_len;
	// 将指针变量的地址传入get_parsed_body方法中,指针变量将要指向报文体
	resp->get_parsed_body(&body, &body_len);
	fwrite(body, 1, body_len, stdout);
	fflush(stdout);
	//...

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/134813.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

DDR3 数据传输 (六)

引言 前文链接: DDR3 数据传输 (一) DDR3 数据传输 (二) DDR3 数据传输 (三) DDR3 数据传输 (四) DDR3 数据传输 (五) 本文在前文设计的基础上,给出板级验证。<

Spring Boot MongoDB 入门

1. 概述 2. 快速入门 3. 基于方法名查询 4. 基于 Example 查询 5. MongoTemplate 6. 自增主键 666. 彩蛋 1. 概述 可能有一些胖友对 MongoDB 不是很了解&#xff0c;这里我们引用一段介绍&#xff1a; FROM 《分布式文档存储数据库 MongoDB》 MongoDB 是一个介于关系数据…

《计算机视觉》:角点检测与图像匹配

文章目录 任务一:基本处理-Harris角点检测原理代码结果与分析任务二:SIFT算法原理代码结果与分析任务一:基本处理-Harris角点检测 数据:棋盘图片 要求:自己写函数实现Harris角点检测子,设置不同参数,比较检测结果 边缘检测子:sobel检测子 响应函数参数alpha:0.05 参数…

【JavaScript】BOM 概念及相关操作

文章目录【JavaScript】BOM 概念及相关操作一. BOM概念BOM可以操作的内容二.window内置对象和属性(1) 获取浏览器窗口的尺寸(2) 获取文档窗口的尺寸(3) 浏览器的常见事件(4) 浏览器的历史记录(5) 浏览器的标签页(6) 浏览器卷去的尺寸(7) 浏览器滚动到的位置浏览器滚动到的位置案…

ARM 按键轮询编程实战

一、什么是按键 1、按键的物理特性 平时没人按的时候&#xff0c;弹簧把按键按钮弹开。此时内部断开的。有人按下的时候&#xff0c;手的力量克服弹簧的弹力&#xff0c;将按钮按下&#xff0c;此时内部保持接通&#xff08;闭合&#xff09;状态&#xff1b;如果手拿开&…

【应急响应】 - Windows 排查分析

Windows 分析排查1. 文件分析1.1 开机启动文件1.2 temp 临时异常文件1.3 浏览器信息分析1.4 文件时间属性分析1.5 最近打开文件分析2. 进程分析2.1 可疑进程发现与关闭3. 系统信息3.1 windows 计划任务3.2 隐藏账户与发现3.2.1 隐藏账号的建立3.2.2 隐藏账号的删除3.3 补丁查看…

Java开发的党员管理系统党员会议系统党务管理系统

简介 Java开发的大学生党员管理系统&#xff0c;主要功能会议&#xff0c;会议记录&#xff0c;会议主持&#xff0c;设置参会人员&#xff0c;请假申请&#xff0c;会议内容附件上传下载&#xff0c;党费管理&#xff0c;入党积极分子预备党员管理&#xff0c;人员变动&#…

hcip实验

1.搭建拓扑 2.配置IP R14&#xff1a; [r14]ip route-static 0.0.0.0 0 145.1.1.2 [r14]acl 2000 [r14-acl-basic-2000]rule permit source any [r14]int GigabitEthernet 0/0/1 [r14-GigabitEthernet0/0/1]nat outbound 2000 [r14]int Tunnel 0/0/0 [r14-Tunnel0/0/0…

【2 - 随机森林 - 原理部分】菜菜sklearn机器学习

课程地址&#xff1a;《菜菜的机器学习sklearn课堂》_哔哩哔哩_bilibili 第一期&#xff1a;sklearn入门 & 决策树在sklearn中的实现第二期&#xff1a;随机森林在sklearn中的实现第三期&#xff1a;sklearn中的数据预处理和特征工程第四期&#xff1a;sklearn中的降维算法…

DDR3 数据传输 (四)

目录 引言 AXI从侧接口参数 AXI从侧接口信号 参考说明 引言 前文链接

【数学思维】数理经济中一些基本概念

【数学思维】数理经济中一些基本概念开集 open set 与闭集 closed set紧集 compact set集合有界 bounded set度量空间 metric space欧式空间 euclidean space闭包 closure上包络 upper envelope、下包络 lower envelope上极限 limit superior、下极限 limit inferior左连续、右…

RabbitMQ第五个实操小案例——主题交换机(TopicExchange)

文章目录RabbitMQ第五个实操小案例——主题交换机&#xff08;TopicExchange&#xff09;RabbitMQ第五个实操小案例——主题交换机&#xff08;TopicExchange&#xff09; TopicExchange 和 DirectExchange 这两种交换机非常相似&#xff0c;Topic类型的Exchange与Direct相比&…

JavaScript 面向对象的编程 (Code with mosh学习笔记)

JavaScript OOP Getting Start - 1- What is OOP 面向对象的编程是一种编程范例围绕对象而不是函数一些OOP语言 C#JavaRubyPythonJavaScript Getting Start - 2- Four Pillars of OOP OOP的4个概念&#xff1a; 封装 使用封装重新组合相关的变量和函数减少复杂性增加代码…

jrtt 某头条网页版 _signature参数逆向

本文仅供参考学习&#xff0c;如有侵权可联系本人 目标网站 aHR0cHM6Ly93d3cudG91dGlhby5jb20vYy91c2VyL3Rva2VuL01TNHdMakFCQUFBQWE0alpUdzhvRlZnaUJIREprMTA1NDdBVFBUb050aHlsVDRqWndZMmlrMXcvPw接口分析 token&#xff1a;需要采集用户 _signature&#xff1a;加密参数 ai…

【每日一leetcode】Day2 链表(简单)

一、剑指 Offer 06. 从尾到头打印链表 输入一个链表的头节点&#xff0c;从尾到头反过来返回每个节点的值&#xff08;用数组返回&#xff09;。 示例 1&#xff1a; 输入&#xff1a;head [1,3,2] 输出&#xff1a;[2,3,1] 限制&#xff1a; 0 < 链表长度 < 10000…

SCI论文解读复现【NO.3】MSFT-YOLO:基于变压器的改进YOLOv5钢表面缺陷检测(代码已复现)

此前出了目标检测算法改进专栏&#xff0c;但是对于应用于什么场景&#xff0c;需要什么改进方法对应与自己的应用场景有效果&#xff0c;并且多少改进点能发什么水平的文章&#xff0c;为解决大家的困惑&#xff0c;此系列文章旨在给大家解读发表高水平学术期刊中的SCI论文&am…

HCIP第三天ospf星型和全连实验

HCIP文章目录 文章目录HCIP文章目录ospf实验实验要求拓扑图R1/4/5为全连的MGRE结构所有私有网段可以互相通讯ospf实验 实验要求 1、R6为ISP只能配置ip地址&#xff0c;R1-5的环回为私有网段 2、R1/4/5为全连的MGRE结构&#xff0c;R1/2/3为星型的拓扑结构&#xff0c;R1为中心…

stm32平衡小车(1)---蓝牙模块及其bug处理

基于stm32c8t6开发板 一&#xff0c;蓝牙模块HC-05 1.外观 2.接线方式 TX----->PB10 RX----->PB11 VCC----->3.3V GND---->GND 3.AT模式 不用烧录代码&#xff0c;直接将c8t6和HC-05相连接&#xff0c;通过XCOM或者SSCOM软件便可以进入调试模式&a…

MySQL常见深入优化

一、分页查询优化 1. SQL语句准备 CREATE TABLE employees (id INT ( 11 ) NOT NULL AUTO_INCREMENT,name VARCHAR ( 24 ) NOT NULL DEFAULT COMMENT 姓名,age INT ( 11 ) NOT NULL DEFAULT 0 COMMENT 年龄,position VARCHAR ( 20 ) NOT NULL DEFAULT COMMENT 职位,hire_ti…

(Django+redis双机配置)ubuntu虚拟机配置redis,window中django访问

目录 Ubuntu虚拟机配置redis 进入root用户 配置redis服务 开启端口 1.设置密码 2.关闭只允许本机访问 3.关闭保护模式 双向ping测试 ubuntu开启SSH服务 Django中 Django中settings配置redis Ubuntu虚拟机配置redis 进入root用户 首先要进入root用户 后续一定保证要…