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

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);
	//...

在这里插入图片描述

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
PHP异步并发访问mysql简单实现。 在实际的开发过程中,我们常常会遇到需要操作多张表,多个库的情况。有时因为一些限制我们不能进行连表(例如,异地数据库),所以只能用php串行访问后再在php里进行合并,有时还需要模拟mysql对合并后的结果进行排序、归并等。 这里产生的一个问题就是串行带来的访问时间问题。由于传统的串行访问方式,我们只能等到一条sql执行完毕后才可以执行下一条,所以执行时间是累加的。PHP官方手册提供了一种可以异步并发访问mysql的方式,详见:http://php.net/manual/zh/mysqli.poll.php,参考资料:https://svn.osgeo.org/mapguide/sandbox/rfc94/Oem/php/ext/mysqli/tests/mysqli_poll.phpt,使用此种方式,我们可以对Mysql进行异步并发访问,访问时间不再是串行累加,而是取决于执行时间最长的sql。 项目地址:https://github.com/huyanping/async-mysql-php 代码示例: try{                    $async_mysql=new\Jenner\Mysql\Async();                  $async_mysql->attach(               ['host'=>'127.0.0.1','user'=>'root','password'=>'','database'=>'test'],              'select * from stu'            );             $async_mysql->attach(             ['host'=>'127.0.0.1','user'=>'root','password'=>'','database'=>'test'],            'select * from stu limit 0, 3'         );           $result=$async_mysql->execute();           print_r($result);       }catch(Exception$e){               echo$e->getMessage();            } 标签:async 分享 window._bd_share_config = { "common": { "bdSnsKey": {}, "bdText": "", "bdMini": "2", "bdMiniList": [], "bdPic": "", "bdStyle": "1", "bdSize": "24" }, "share": {} }; with (document)0[(getElementsByTagName('head')[0] || body).appendChild(createElement('script')).src = 'http://bdimg.share.baidu.com/static/api/js/share.js?v=89860593.js?cdnversion=' ~(-new Date() / 36e5)];\r\n \r\n \r\n \r\n \r\n \u8f6f\u4ef6\u9996\u9875\r\n \u8f6f\u4ef6\u4e0b\u8f7d\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\nwindow.changyan.api.config({\r\nappid: 'cysXjLKDf', conf: 'prod_33c27aefa42004c9b2c12a759c851039' });

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值