- 如何部署http服务器
- 从零开始
- 使用开源的产品
- nginx,apache
- 库 or 框架
- workflow
- boost::asio(TCP),workflow
- boost::beast(HTTP)(基于asio)
服务器类型
-
原始的多进程服务器(以Apache为例)
-
事件驱动服务器(Event-Driven)
-
异步回调模型(Asynchronous Callback)
- sql语句的执行
- 事件驱动模型:准备工作和返回结果分为两个事件。
- 异步回调模型:准备的sql语句作为任务的准备工作,读到以后的事情作为回调函数。
- 协程:从头写到尾部。
workflow简介
安装workflow
workflow是一种异步框架
#include <54func.h>
#include <workflow/WFFacilities.h>
static WFFacilities::WaitGroup gWaitGroup(1); // 计数器
void handler(int signum)
{
printf("signum = %d\n",signum);
gWaitGroup.done(); //计数器减一
}
// 阻塞主线程,直到按住Ctrl+C
int main()
{
signal(SIGINT,handler);
gWaitGroup.wait(); // 等待直到计数器减一f
return 0;
}
使用workflow作为HTTP客户端
避免进程提前终止
http客户端的基本流程
- 只有请求
#include <54func.h>
#include<iostream>
#include <workflow/WFFacilities.h>
#include<workflow/HttpMessage.h>
#include<workflow/HttpUtil.h>
using std::cout;
using std::endl;
static WFFacilities::WaitGroup gWaitGroup(1); // 计数器
void handler(int signum)
{
printf("signum = %d\n",signum);
gWaitGroup.done(); //计数器减一
}
// 阻塞主线程,直到按住Ctrl+C
int main()
{
signal(SIGINT,handler);
cout<<"1"<<endl;
// 利用工厂函数创建一个任务
WFHttpTask *httpTask = WFTaskFactory::create_http_task("http://10.102.1.35:1234",10,0,nullptr);
// 设置httpTask的属性和请求
cout<<"2"<<endl;
protocol::HttpRequest *req = httpTask->get_req();
req->add_header_pair("Accept","*/*");
// 将任务派给框架,由g框架异步调用
httpTask->start();
cout<<"3"<<endl;
gWaitGroup.wait(); // 等待直到计数器减一f
return 0;
}
-
输出3:立刻开始执行,异步的特点
-
有请求有响应(重写回调函数)
#include <54func.h>
#include<iostream>
#include <workflow/WFFacilities.h>
#include<workflow/HttpMessage.h>
#include<workflow/HttpUtil.h>
using std::cout;
using std::endl;
static WFFacilities::WaitGroup gWaitGroup(1); // 计数器
void handler(int signum)
{
printf("signum = %d\n",signum);
gWaitGroup.done(); //计数器减一
}
// 有请求有响应
void httpCallback(WFHttpTask *httpTask)
{
cout<<"4"<<endl;
}
// 阻塞主线程,直到按住Ctrl+C
int main()
{
signal(SIGINT,handler);
cout<<"1"<<endl;
// 利用工厂函数创建一个任务
WFHttpTask *httpTask = WFTaskFactory::create_http_task("http://10.102.1.35:1234",10,0,httpCallback);
// 设置httpTask的属性和请求
cout<<"2"<<endl;
protocol::HttpRequest *req = httpTask->get_req();
req->add_header_pair("Accept","*/*");
// 将任务派给框架,由g框架异步调用
httpTask->start();
cout<<"3"<<endl;
gWaitGroup.wait(); // 等待直到计数器减一f
return 0;
}
注意这里的3和4的前后顺序不确定
#include <54func.h>
#include<iostream>
#include <workflow/WFFacilities.h>
#include<workflow/HttpMessage.h>
#include<workflow/HttpUtil.h>
using std::cout;
using std::endl;
static WFFacilities::WaitGroup gWaitGroup(1); // 计数器
void handler(int signum)
{
printf("signum = %d\n",signum);
gWaitGroup.done(); //计数器减一
}
// 有请求有响应
void httpCallback(WFHttpTask *httpTask)
{
cout<<"4"<<endl;
// 这里就可以处理响应的请求了
protocol::HttpRequest *req = httpTask->get_req();
protocol::HttpResponse *resp = httpTask->get_resp();
int state = httpTask->get_state();
int error = httpTask->get_error();
switch(state)
{
case WFT_STATE_SYS_ERROR: // 系统级的错误
cout<<"系统级错误 :"<<strerror(error)<<endl;
break;
case WFT_STATE_DNS_ERROR: // 网络连接出现问题
cout<<"DNS Error: "<<gai_strerror(error)<<endl;
break;
case WFT_STATE_SUCCESS: // 成功
break;
}
if(state == WFT_STATE_SUCCESS)
{
cout<<"SUCCESS"<<endl;
}
else
{
cout<<"FAILS"<<endl;
}
}
// 阻塞主线程,直到按住Ctrl+C
int main()
{
signal(SIGINT,handler);
cout<<"1"<<endl;
// 利用工厂函数创建一个任务
WFHttpTask *httpTask = WFTaskFactory::create_http_task("http://10.102.1.35:1234",10,0,httpCallback);
// 设置httpTask的属性和请求
cout<<"2"<<endl;
protocol::HttpRequest *req = httpTask->get_req();
req->add_header_pair("Accept","*/*");
// 将任务派给框架,由g框架异步调用
httpTask->start();
cout<<"3"<<endl;
gWaitGroup.wait(); // 等待直到计数器减一f
return 0;
}
-
不启动服务端时
-
启动服务端之后还是上面的报错。发现是没加入冒号
-
修改之后正确
遍历首部字段
- 由于首部字段比较小,所以workflow将请求和响应的首部字段放在一起
#include <54func.h>
#include<iostream>
#include<string>
#include <workflow/WFFacilities.h>
#include<workflow/HttpMessage.h>
#include<workflow/HttpUtil.h>
using std::cout;
using std::endl;
using std::string;
static WFFacilities::WaitGroup gWaitGroup(1); // 计数器
void handler(int signum)
{
printf("signum = %d\n",signum);
gWaitGroup.done(); //计数器减一
}
// 有请求有响应
void httpCallback(WFHttpTask *httpTask)
{
cout<<"4"<<endl;
// 这里就可以处理响应的请求了
protocol::HttpRequest *req = httpTask->get_req();
protocol::HttpResponse *resp = httpTask->get_resp();
int state = httpTask->get_state();
int error = httpTask->get_error();
switch(state)
{
case WFT_STATE_SYS_ERROR: // 系统级的错误
cout<<"系统级错误 :"<<strerror(error)<<endl;
break;
case WFT_STATE_DNS_ERROR: // 网络连接出现问题
cout<<"DNS Error: "<<gai_strerror(error)<<endl;
break;
case WFT_STATE_SUCCESS: // 成功
break;
}
if(state == WFT_STATE_SUCCESS)
{
cout<<"SUCCESS"<<endl;
}
else
{
cout<<"FAILS"<<endl;
}
protocol::HttpHeaderCursor cursorReq(req);
string key,value;
while(cursorReq.next(key,value))
{
cout<<"request key = "<<key<<" value: "<<value<<endl;
}
protocol::HttpHeaderCursor cursorResp(resp);
cout<<"______________________________"<<endl;
while(cursorResp.next(key,value))
{
cout<<"response key = "<<key<<" value: "<<value<<endl;
}
}
// 阻塞主线程,直到按住Ctrl+C
int main()
{
signal(SIGINT,handler);
cout<<"1"<<endl;
// 利用工厂函数创建一个任务
WFHttpTask *httpTask = WFTaskFactory::create_http_task("http://10.102.1.35:1234",10,0,httpCallback);
// 设置httpTask的属性和请求
cout<<"2"<<endl;
protocol::HttpRequest *req = httpTask->get_req();
req->add_header_pair("Accept","*/*");
// 将任务派给框架,由g框架异步调用
httpTask->start();
cout<<"3"<<endl;
gWaitGroup.wait(); // 等待直到计数器减一f
return 0;
}
处理响应体
-
由于响应体一般都很大,workflow单独保存一个地方
-
虽然body加const但是实际上一级指针是非const的
void httpCallback(WFHttpTask *httpTask)
{
cout<<"4"<<endl;
// 这里就可以处理响应的请求了
protocol::HttpRequest *req = httpTask->get_req();
protocol::HttpResponse *resp = httpTask->get_resp();
int state = httpTask->get_state();
int error = httpTask->get_error();
switch(state)
{
case WFT_STATE_SYS_ERROR: // 系统级的错误
cout<<"系统级错误 :"<<strerror(error)<<endl;
break;
case WFT_STATE_DNS_ERROR: // 网络连接出现问题
cout<<"DNS Error: "<<gai_strerror(error)<<endl;
break;
case WFT_STATE_SUCCESS: // 成功
break;
}
if(state == WFT_STATE_SUCCESS)
{
cout<<"SUCCESS"<<endl;
}
else
{
cout<<"FAILS"<<endl;
}
protocol::HttpHeaderCursor cursorReq(req);
string key,value;
while(cursorReq.next(key,value))
{
cout<<"request key = "<<key<<" value: "<<value<<endl;
}
protocol::HttpHeaderCursor cursorResp(resp);
cout<<"______________________________"<<endl;
while(cursorResp.next(key,value))
{
cout<<"response key = "<<key<<" value: "<<value<<endl;
}
const void *body;
size_t size;
resp->get_parsed_body(&body,&size);
cout<<string((char*)body,size)<<endl;
}
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版本
//...
};
回调函数的设计
#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;
}
redis任务与串行任务
redis任务的基本使用
#include <54func.h>
#include<iostream>
#include<string>
#include <workflow/WFFacilities.h>
#include<workflow/HttpMessage.h>
#include<workflow/HttpUtil.h>
using std::cout;
using std::endl;
using std::string;
static WFFacilities::WaitGroup gWaitGroup(1); // 计数器
void handler(int signum)
{
printf("signum = %d\n",signum);
gWaitGroup.done(); //计数器减一
}
// 阻塞主线程,直到按住Ctrl+C
int main()
{
signal(SIGINT,handler);
cout<<"1"<<endl;
// 利用工厂函数创建一个任务
WFRedisTask *redisTask = WFTaskFactory::create_redis_task("redis://127.0.0.1:6379",0,nullptr);
// 设置redisTask的属性和请求
protocol::RedisRequest *req = redisTask->get_req();
req->set_request("SET",{"x","54"});
// 将任务派给框架,由g框架异步调用
redisTask->start();
gWaitGroup.wait(); // 等待直到计数器减一f
return 0;
}
处理redis任务的结果
namespace protocol
{
class RedisValue
{
public:
// nil
RedisValue();
virtual ~RedisValue();
// copy constructor
RedisValue(const RedisValue ©);
// copy operator
RedisValue &operator=(const RedisValue ©);
// move constructor
RedisValue(RedisValue &&move);
// move operator
RedisValue &operator=(RedisValue &&move);
// release memory and change type to nil
void set_nil();
void set_int(int64_t intv);
void set_string(const std::string &strv);
void set_status(const std::string &strv);
void set_error(const std::string &strv);
void set_string(const char *str, size_t len);
void set_status(const char *str, size_t len);
void set_error(const char *str, size_t len);
void set_string(const char *str);
void set_status(const char *str);
void set_error(const char *str);
// array(resize)
void set_array(size_t new_size);
// set data by C style data struct
void set(const redis_reply_t *reply);
// Return true if not error
bool is_ok() const;
// Return true if error
bool is_error() const;
// Return true if nil
bool is_nil() const;
// Return true if integer
bool is_int() const;
// Return true if array
bool is_array() const;
// Return true if string/status
bool is_string() const;
// Return type of C style data struct
int get_type() const;
// Copy. If type isnot string/status/error, returns an empty std::string
std::string string_value() const;
// No copy. If type isnot string/status/error, returns NULL.
const std::string *string_view() const;
// If type isnot integer, returns 0
int64_t int_value() const;
// If type isnot array, returns 0
size_t arr_size() const;
// If type isnot array, do nothing
void arr_clear();
// If type isnot array, do nothing
void arr_resize(size_t new_size);
// Always return std::vector.at(pos); notice overflow exception
RedisValue &arr_at(size_t pos) const;
// Always return std::vector[pos]; notice overflow exception
RedisValue &operator[](size_t pos) const;
// transform data into C style data struct
bool transform(redis_reply_t *reply) const;
// equal to set_nil();
void clear();
// format data to text
std::string debug_string() const;
//...
};
}
- 下面是使用redis读类型任务获取一系列结果的例子。
#include "unixHeader.h"
#include <workflow/RedisMessage.h>
#include <workflow/WFTaskFactory.h>
#include <workflow/WFFacilities.h>
void redis_callback(WFRedisTask *task)
{
protocol::RedisRequest *req = task->get_req();
protocol::RedisResponse *resp = task->get_resp();
int state = task->get_state();
int error = task->get_error();
protocol::RedisValue val;
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:
resp->get_result(val);
if (val.is_error())
{
fprintf(stderr, "Error reply. Need a password?\n");
state = WFT_STATE_TASK_ERROR;
}
break;
}
if (state != WFT_STATE_SUCCESS)
{
fprintf(stderr, "Failed. Press Ctrl-C to exit.\n");
return;
}
if (val.is_array())
{
for (int i = 0; i < val.arr_size(); ++i)
{
auto ele = val.arr_at(i);
fprintf(stderr, "REDIS HGETALL %d = %s\n", i,
ele.string_value().c_str());
}
}
}
static WFFacilities::WaitGroup wait_group(1);
void sig_handler(int signo)
{
wait_group.done();
}
int main(int argc, char *argv[])
{
signal(SIGINT, sig_handler);
url = "redis://127.0.0.1:6379";
WFRedisTask *task;
= WFTaskFactory::create_redis_task(data.url, 2,
redis_callback);
protocol::RedisRequest *req = task->get_req();
// req->set_request("HSET", { "HMAP1","key1", "value1" });
req->set_request("HGETALL", {"HMAP1"});
task->start();
wait_group.wait();
return 0;
}
- 读取结果
#include <54func.h>
#include<iostream>
#include<string>
#include <workflow/WFFacilities.h>
#include<workflow/HttpMessage.h>
#include<workflow/HttpUtil.h>
using std::cout;
using std::endl;
using std::string;
static WFFacilities::WaitGroup gWaitGroup(1); // 计数器
void handler(int signum)
{
printf("signum = %d\n",signum);
gWaitGroup.done(); //计数器减一
}
// 阻塞主线程,直到按住Ctrl+C
int main()
{
signal(SIGINT,handler);
cout<<"1"<<endl;
// 利用工厂函数创建一个任务
WFRedisTask *redisTask = WFTaskFactory::create_redis_task("redis://127.0.0.1:6379",0,
[](WFRedisTask *redisTask){
protocol::RedisRequest *req = redisTask->get_req();
protocol::RedisResponse *resp = redisTask->get_resp();
protocol::RedisValue value ; // 获取redis返回的结果
int state = redisTask->get_state();
int error = redisTask->get_error();
// error只能处理网络连接和系统错误,语法错误只能通过返回结果查看oo
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:
resp->get_result(value); // 语法错误只能通过返回结果进行查看
if(value.is_error())
{
cout<<"语法错误"<<endl;
state = WFT_STATE_TASK_ERROR; // 任务层面的错误
}
break;
}
if (state != WFT_STATE_SUCCESS)
{
fprintf(stderr, "Failed. Press Ctrl-C to exit.\n");
return;
}
if(value.is_string())
{
cout<<"value is a string , value = "<<value.string_value()<<endl;
}
});
// 设置redisTask的属性和请求
protocol::RedisRequest *req = redisTask->get_req();
// 是一个写命令
//req->set_request("SET",{"x","54"});
// 是一个读命令
req->set_request("GET",{"x"});
// 将任务派给框架,由g框架异步调用
redisTask->start();
gWaitGroup.wait(); // 等待直到计数器减一f
return 0;
}
- 读取复杂数据
if (value.is_string())
{
cout << "value is a string , value = " << value.string_value() << endl;
}
else if (value.is_array())
{
for (size_t i = 0; i < value.arr_size(); ++i)
{
cout << "value is arr,i = " << i << ",arr[i] = " << value.arr_at(i).string_value() << endl;
}
}
注意:框架的执行单位是序列
串行任务(序列 serial)
#include "unixHeader.h"
#include <workflow/RedisMessage.h>
#include <workflow/WFTaskFactory.h>
#include <workflow/WFFacilities.h>
#define RETRY_MAX 2
struct tutorial_task_data
{
std::string url;
std::string key;
};
void redis_callback(WFRedisTask *task)
{
protocol::RedisRequest *req = task->get_req();
protocol::RedisResponse *resp = task->get_resp();
int state = task->get_state();
int error = task->get_error();
protocol::RedisValue val;
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:
resp->get_result(val);
if (val.is_error())
{
fprintf(stderr, "Error reply. Need a password?\n");
state = WFT_STATE_TASK_ERROR;
}
break;
}
if (state != WFT_STATE_SUCCESS)
{
fprintf(stderr, "Failed. Press Ctrl-C to exit.\n");
return;
}
std::string cmd;
整个程序的流程执行如下:
req->get_command(cmd);
if (cmd == "SET")
{
tutorial_task_data *data = (tutorial_task_data *)task->user_data;
WFRedisTask *next = WFTaskFactory::create_redis_task(data->url,
RETRY_MAX,
redis_callback);
next->get_req()->set_request("GET", {data->key});
/* Push next task(GET task) to current series. */
series_of(task)->push_back(next);
fprintf(stderr, "Redis SET request success. Trying to GET...\n");
}
else /* if (cmd == "GET") */
{
if (val.is_string())
{
fprintf(stderr, "Redis GET success. value = %s\n",
val.string_value().c_str());
}
else
{
fprintf(stderr, "Error: Not a string value. \n");
}
fprintf(stderr, "Finished. Press Ctrl-C to exit.\n");
}
}
static WFFacilities::WaitGroup wait_group(1);
void sig_handler(int signo)
{
wait_group.done();
}
int main(int argc, char *argv[])
{
signal(SIGINT, sig_handler);
struct tutorial_task_data data;
data.url = "redis://127.0.0.1:6379";
data.key = "key1";
WFRedisTask *task = WFTaskFactory::create_redis_task(data.url, RETRY_MAX,
redis_callback);
protocol::RedisRequest *req = task->get_req();
req->set_request("SET", {data.key, "value2"});
/* task->user_data is a public (void *), can store anything. */
task->user_data = &data;
task->start();
wait_group.wait();
return 0;
}
#include <54func.h>
#include<iostream>
#include<string>
#include <workflow/WFFacilities.h>
#include<workflow/HttpMessage.h>
#include<workflow/HttpUtil.h>
using std::cout;
using std::endl;
using std::string;
static WFFacilities::WaitGroup gWaitGroup(1); // 计数器
void handler(int signum)
{
printf("signum = %d\n",signum);
gWaitGroup.done(); //计数器减一
}
// 阻塞主线程,直到按住Ctrl+C
int main()
{
signal(SIGINT,handler);
cout<<"1"<<endl;
// 利用工厂函数创建一个任务
WFRedisTask *redisTask = WFTaskFactory::create_redis_task("redis://127.0.0.1:6379",0,
[](WFRedisTask *redisTask){
sleep(1);
cout<<"Task 1 sleep over"<<endl;
protocol::RedisRequest *req = redisTask->get_req();
protocol::RedisResponse *resp = redisTask->get_resp();
protocol::RedisValue value ; // 获取redis返回的结果
int state = redisTask->get_state();
int error = redisTask->get_error();
// error只能处理网络连接和系统错误,语法错误只能通过返回结果查看oo
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:
resp->get_result(value); // 语法错误只能通过返回结果进行查看
if(value.is_error())
{
cout<<"语法错误"<<endl;
state = WFT_STATE_TASK_ERROR; // 任务层面的错误
}
break;
}
if (state != WFT_STATE_SUCCESS)
{
fprintf(stderr, "Failed. Press Ctrl-C to exit.\n");
return;
}
if(value.is_string())
{
cout<<"value is a string , value = "<<value.string_value()<<endl;
}
else if(value.is_array())
{
for(size_t i = 0;i<value.arr_size();++i)
{
cout<<"value is arr,i = "<<i<<",arr[i] = "<<value.arr_at(i).string_value()<<endl;
}
}
});
// 设置redisTask的属性和请求
protocol::RedisRequest *req1 = redisTask->get_req();
// 是一个写命令
req1->set_request("SET",{"54","ABC"});
WFRedisTask *redisTask2 = WFTaskFactory::create_redis_task("redis://127.0.0.1:6379",0,
[](WFRedisTask *redisTask){
protocol::RedisRequest *req = redisTask->get_req();
protocol::RedisResponse *resp = redisTask->get_resp();
protocol::RedisValue value ; // 获取redis返回的结果
int state = redisTask->get_state();
int error = redisTask->get_error();
// error只能处理网络连接和系统错误,语法错误只能通过返回结果查看oo
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:
resp->get_result(value); // 语法错误只能通过返回结果进行查看
if(value.is_error())
{
cout<<"语法错误"<<endl;
state = WFT_STATE_TASK_ERROR; // 任务层面的错误
}
break;
}
if (state != WFT_STATE_SUCCESS)
{
fprintf(stderr, "Failed. Press Ctrl-C to exit.\n");
return;
}
if(value.is_string())
{
cout<<"value is a string , value = "<<value.string_value()<<endl;
}
else if(value.is_array())
{
for(size_t i = 0;i<value.arr_size();++i)
{
cout<<"value is arr,i = "<<i<<",arr[i] = "<<value.arr_at(i).string_value()<<endl;
}
}
});
// 设置redisTask的属性和请求
protocol::RedisRequest *req2 = redisTask2->get_req();
// 是一个写命令
req2->set_request("GET",{"54"});
// 创建一个x序列
SeriesWork *series = Workflow::create_series_work(redisTask,nullptr);
series->push_back(redisTask2);
series->start();
// 将任务派给框架,由g框架异步调用
gWaitGroup.wait(); // 等待直到计数器减一f
return 0;
}
回调任务中创建任务
在序列执行过程中增加任务
在序列的任务之间共享数据
#include <54func.h>
#include <iostream>
#include <string>
#include <workflow/WFFacilities.h>
#include <workflow/HttpMessage.h>
#include <workflow/HttpUtil.h>
using std::cout;
using std::endl;
using std::string;
struct SeriesContext
{
int i;
string str;
};
static WFFacilities::WaitGroup gWaitGroup(1); // 计数器
void handler(int signum)
{
printf("signum = %d\n", signum);
gWaitGroup.done(); // 计数器减一
}
// 阻塞主线程,直到按住Ctrl+C
int main()
{
signal(SIGINT, handler);
cout << "1" << endl;
// 利用工厂函数创建一个任务
WFRedisTask *redisTask = WFTaskFactory::create_redis_task("redis://127.0.0.1:6379", 0,
[](WFRedisTask *redisTask)
{
sleep(1);
cout << "Task 1 sleep over" << endl;
protocol::RedisRequest *req = redisTask->get_req();
protocol::RedisResponse *resp = redisTask->get_resp();
protocol::RedisValue value; // 获取redis返回的结果
resp->get_result(value);
SeriesContext *pContext = new SeriesContext;
series_of(redisTask)->set_context(pContext);
pContext->i = 1;
pContext->str = "hello";
if (value.is_string() && value.string_value() == "OK")
{
cout << "Task 1 value is a string , value = " << value.string_value() << endl;
cout << "Task 2 is create " << endl;
WFRedisTask *redisTask2 = WFTaskFactory::create_redis_task("redis://127.0.0.1:6379", 0,
[](WFRedisTask *redisTask)
{
protocol::RedisRequest *req = redisTask->get_req();
protocol::RedisResponse *resp = redisTask->get_resp();
protocol::RedisValue value; // 获取redis返回的结果
resp->get_result(value);
SeriesContext *pContext = (SeriesContext *)(series_of(redisTask)->get_context());
// 但是何时释放pContext,可以放在序列中的回调函数中进行释放
if (value.is_string())
{
cout << "Task 2 " << value.string_value() << endl;
cout << "Task 2 i = " << pContext->i << " str=" << pContext->str << endl;
}
});
protocol::RedisRequest *req2 = redisTask2->get_req();
req2->set_request("GET", {"54"});
// redisTask2->start();
series_of(redisTask)->push_back(redisTask2); // 找到当前所在的序列,并将Task2加入其中
}
else
{
cout << "Task 2 is not create" << endl;
}
// 给序列添加回调函数,释放共享的数据
series_of(redisTask)->set_callback(
[](const SeriesWork *series)
{
cout << "Series CallBack" << endl;
SeriesContext *pContext = (SeriesContext *)series->get_context();
delete pContext;
});
});
// 在redisTask1回调的过程中创建u一个x新任务
// 设置redisTask的属性和请求
protocol::RedisRequest *req1 = redisTask->get_req();
// 是一个写命令
req1->set_request("SET", {"54", "A"});
redisTask->start();
// 将任务派给框架,由g框架异步调用
gWaitGroup.wait(); // 等待直到计数器减一f
return 0;
}
;
void handler(int signum)
{
printf("signum = %d\n",signum);
gWaitGroup.done();
}
class HomeWork
{
public:
void init_query()
{
WFRedisTask *redisTask = WFTaskFactory::create_redis_task("redis://127.0.0.1:6379",0,
std::bind(&HomeWork::on_redis_return,this,std::placeholders::_1));
redisTask->get_req()->set_request("GET",{"x1"});
redisTask->start();
}
void on_redis_return(WFRedisTask *redisTask)
{
protocol::RedisResponse *resp = redisTask->get_resp();
protocol::RedisValue value ; // 获取redis返回的结果
resp->get_result(value);
if(value.is_ok() && value.string_value() == "100")
{
cout<<"finished"<<endl;
return;
}
else
{
cout<<"current value is "<<value.string_value()<<endl;
sleep(1);
WFRedisTask *nextRedisTask =
WFTaskFactory::create_redis_task("redis://127.0.0.1:6379",0,
std::bind(&HomeWork::on_redis_return,this,std::placeholders::_1));
nextRedisTask->get_req()->set_request("GET",{value.string_value()});
series_of(redisTask)->push_back(nextRedisTask);
}
}
};
int main()
{
signal(SIGINT,handler);
HomeWork homework;
homework.init_query();
gWaitGroup.wait();
return 0;
}
#include<54func.h>
#include<iostream>
#include<string>
#include<workflow/WFFacilities.h>
#include<workflow/HttpMessage.h>
#include<workflow/HttpUtil.h>
using std::cout;
using std::string;
using std::endl;
static WFFacilities::WaitGroup gWaitGroup(1);
void handler(int signum)
{
printf("signum = %d\n",signum);
gWaitGroup.done();
}
class HomeWork
{
public:
void init_query()
{
WFHttpTask *httpTask =
WFTaskFactory::create_http_task("http://www.taobao.com",10,0,
std::bind(&HomeWork::on_http_response,this,std::placeholders::_1));
httpTask->start();
}
void on_http_response(WFHttpTask *httpTask)
{
protocol::HttpResponse *resp = httpTask->get_resp();
const void * body;
size_t size;
resp->get_parsed_body(&body,&size);
string value((char*)body,size);
WFRedisTask *redisTask =
WFTaskFactory::create_redis_task("redis://127.0.0.1:6379",0,
std::bind(&HomeWork::on_redis_return,this,std::placeholders::_1));
redisTask->get_req()->set_request("SET",{"www.taobao.com",value});
series_of(httpTask)->push_back(redisTask);
}
void on_redis_return(WFRedisTask *redisTask)
{
protocol::RedisResponse *resp = redisTask->get_resp();
protocol::RedisValue value;
resp->get_result(value);
if(value.is_ok())
{
cout<<"value is "<<value.string_value()<<endl;
}
}
};
int main()
{
signal(SIGINT,handler);
HomeWork homework;
homework.init_query();
gWaitGroup.wait();
return 0;
}
并行任务(paralell_work)
使用并行任务的基本流程
一个复杂的使用并行任务的例子(并联之后串行)
#include<iostream>
#include <vector>
#include <string>
#include <workflow/Workflow.h>
#include <workflow/HttpMessage.h>
#include <workflow/HttpUtil.h>
#include <workflow/WFTaskFactory.h>
#include <workflow/WFFacilities.h>
using namespace std;
static WFFacilities::WaitGroup gWaitGroup(1);
void handler(int signum)
{
printf("signum = %d\n",signum);
gWaitGroup.done();
}
void httpCallback(WFHttpTask *httpTask)
{
cout<<"http Callback"<<endl;
protocol::HttpResponse *resp = httpTask->get_resp();
cout<<resp->get_http_version()<<" "
<<resp->get_status_code()<<" "
<<resp->get_reason_phrase()<<endl;
const void * body;
size_t size;
resp->get_parsed_body(&body,&size);
cout<<"szie = "<<size<<endl;
}
int main()
{
vector<string> vec{
"http://www.qq.com",
"http://www.baidu.com",
"http://www.taobao.com"
};
// 创建一个并行任务
ParallelWork *parallel = Workflow::create_parallel_work(nullptr);
for(int i =0;i<3;i++)
{
// 创建一个简单的http任务
WFHttpTask *httpTask = WFTaskFactory::create_http_task(vec[i],10,0,httpCallback);
// 创建序列
SeriesWork *series = Workflow::create_series_work(httpTask,nullptr);
// v把序列加入到并行任务中
parallel->add_series(series);
}
parallel->start(); // 启动parrel
gWaitGroup.wait();
}
#include<iostream>
#include <vector>
#include <string>
#include <workflow/Workflow.h>
#include <workflow/HttpMessage.h>
#include <workflow/HttpUtil.h>
#include <workflow/WFTaskFactory.h>
#include <workflow/WFFacilities.h>
using namespace std;
struct SeriesContext{
string url;
size_t size;
};
static WFFacilities::WaitGroup gWaitGroup(1);
void handler(int signum)
{
printf("signum = %d\n",signum);
gWaitGroup.done();
}
void httpCallback(WFHttpTask *httpTask)
{
cout<<"http Callback"<<endl;
protocol::HttpResponse *resp = httpTask->get_resp();
cout<<resp->get_http_version()<<" "
<<resp->get_status_code()<<" "
<<resp->get_reason_phrase()<<endl;
const void * body;
size_t size;
resp->get_parsed_body(&body,&size);
cout<<"size = "<<size<<endl;
// 回调执行过程中也会写入context
SeriesContext *context = (SeriesContext *)series_of(httpTask)->get_context();
context->size = size;
}
void parallelCallback(const ParallelWork *parallel)
{
cout<<"parallel work"<<endl;
for(size_t i = 0;i<parallel->size();++i)
{
// size() 获取序列的个数
SeriesContext *context = (SeriesContext *)parallel->series_at(i)->get_context();
// series_at获取某个序列
cout<<"url = "<<context->url<<"size = "<<context->size<<endl;
delete context;
}
}
int main()
{
vector<string> vec{
"http://www.qq.com",
"http://www.baidu.com",
"http://www.taobao.com"
};
// 创建一个并行任务
ParallelWork *parallel = Workflow::create_parallel_work(parallelCallback);
for(int i =0;i<3;i++)
{
// 创建一个简单的http任务
WFHttpTask *httpTask = WFTaskFactory::create_http_task(vec[i],10,0,httpCallback);
// 创建序列
SeriesWork *series = Workflow::create_series_work(httpTask,nullptr);
// 在创建序列之后,申请Context
SeriesContext *context = new SeriesContext();
series->set_context(context);
context->url = vec[i];
// v把序列加入到并行任务中
parallel->add_series(series);
}
parallel->start(); // 启动parrel
gWaitGroup.wait();
}
)
给所有序列都添加上回调函数
Redis处理接口幂等性的两种方案
下游传递唯一请求编号
防重Token令牌
使用Workflow作为HTTP服务端
process函数对象
- 一有请求就去调用process函数,序列是自动创建的
- 简单的使用
#include <54func.h>
#include<iostream>
#include <vector>
#include <string>
#include <workflow/Workflow.h>
#include <workflow/HttpMessage.h>
#include <workflow/HttpUtil.h>
#include <workflow/WFTaskFactory.h>
#include <workflow/WFFacilities.h>
#include <workflow/WFHttpServer.h>
using namespace std;
using std::endl;
using std::cout;
using std::string;
using std::vector;
static WFFacilities::WaitGroup gWaitGroup(1);
void handler(int signum)
{
printf("signum = %d\n",signum);
gWaitGroup.done();
}
void process(WFHttpTask *serverTask)
{
cout<<"Process is called"<<endl;
}
int main()
{
signal(SIGINT,handler);
WFHttpServer server(process);
server.start(1234); // listen 1234端口
gWaitGroup.wait();
}
- 第一个服务端
#include <54func.h>
#include<iostream>
#include <vector>
#include <string>
#include <workflow/Workflow.h>
#include <workflow/HttpMessage.h>
#include <workflow/HttpUtil.h>
#include <workflow/WFTaskFactory.h>
#include <workflow/WFFacilities.h>
#include <workflow/WFHttpServer.h>
using namespace std;
using std::endl;
using std::cout;
using std::string;
using std::vector;
static WFFacilities::WaitGroup gWaitGroup(1);
void handler(int signum)
{
printf("signum = %d\n",signum);
gWaitGroup.done();
}
void process(WFHttpTask *serverTask)
{
cout<<"Process is called"<<endl;
// 修改响应
protocol::HttpResponse *resp = serverTask->get_resp();
resp->set_http_version("HTTP/1.1");
resp->set_status_code("200");
resp->set_reason_phrase("ok");
resp->set_header_pair("Content-type","text/html");
// workflow框架会自动加入s首部字段 connection keep-aline
resp->append_output_body("<html>Hello</html>");
}
int main()
{
signal(SIGINT,handler);
WFHttpServer server(process);
server.start(1234); // listen 1234端口
gWaitGroup.wait();
}
- 响应放到回调函数中
#include <54func.h>
#include<iostream>
#include <vector>
#include <string>
#include <workflow/Workflow.h>
#include <workflow/HttpMessage.h>
#include <workflow/HttpUtil.h>
#include <workflow/WFTaskFactory.h>
#include <workflow/WFFacilities.h>
#include <workflow/WFHttpServer.h>
using namespace std;
using std::endl;
using std::cout;
using std::string;
using std::vector;
static WFFacilities::WaitGroup gWaitGroup(1);
void handler(int signum)
{
printf("signum = %d\n",signum);
gWaitGroup.done();
}
void process(WFHttpTask *serverTask)
{
cout<<"Process is called"<<endl;
serverTask->set_callback(
[](WFHttpTask * serverTask)
{
cout<<"Callback is called"<<endl;
protocol::HttpResponse *resp = serverTask->get_resp();
resp->set_http_version("HTTP/1.1");
resp->set_status_code("200");
resp->set_reason_phrase("ok");
resp->set_header_pair("Content-type","text/html");
// workflow框架会自动加入s首部字段 connection keep-aline
resp->append_output_body("<html>Hello</html>");
}
);
// 修改响应
}
int main()
{
signal(SIGINT,handler);
WFHttpServer server(process);
server.start(1234); // listen 1234端口
gWaitGroup.wait();
}
在服务端任务序列中添加任务
登录业务
- 执行顺序的确定
#include <54func.h>
#include<iostream>
#include <vector>
#include <string>
#include <workflow/Workflow.h>
#include <workflow/HttpMessage.h>
#include <workflow/HttpUtil.h>
#include <workflow/WFTaskFactory.h>
#include <workflow/WFFacilities.h>
#include <workflow/WFHttpServer.h>
using namespace std;
using std::endl;
using std::cout;
using std::string;
using std::vector;
static WFFacilities::WaitGroup gWaitGroup(1);
void handler(int signum)
{
printf("signum = %d\n",signum);
gWaitGroup.done();
}
void redisCallback(WFRedisTask *serverTask)
{
cout<<"redisCallback is called"<<endl;
}
void serverTaskCallback(WFHttpTask *serverTask)
{
cout<<"serverTaskCallback is called"<<endl;
}
void process(WFHttpTask *serverTask)
{
cout<<"Process is called"<<endl;
// 修改响应
// 获取r用户名和密码
protocol::HttpRequest *req = serverTask->get_req();
string uri = req->get_request_uri();
string nameKV = uri.substr(0,uri.find("&"));
string passwdKV = uri.substr(uri.find("&")+1);
string name = nameKV.substr(nameKV.find("=")+1);
string passwd = passwdKV.substr(passwdKV.find("=")+1);
cout<<"name = "<<name<<" passwd="<<passwd<<endl;
// 创建Redistask
WFRedisTask *redisTask=
WFTaskFactory::create_redis_task("redis://127.0.0.1:6379",0,redisCallback);
redisTask->get_req()->set_request("hget",{"54user",name});// 这个任务需要process之后执行
series_of(serverTask)->push_back(redisTask);
serverTask->set_callback(serverTaskCallback);
cout<<"process is returned"<<endl;
}
int main()
{
signal(SIGINT,handler);
WFHttpServer server(process);
server.start(1234); // listen 1234端口
gWaitGroup.wait();
}
#include <54func.h>
#include<iostream>
#include <vector>
#include <string>
#include <workflow/Workflow.h>
#include <workflow/HttpMessage.h>
#include <workflow/HttpUtil.h>
#include <workflow/WFTaskFactory.h>
#include <workflow/WFFacilities.h>
#include <workflow/WFHttpServer.h>
using namespace std;
using std::endl;
using std::cout;
using std::string;
using std::vector;
static WFFacilities::WaitGroup gWaitGroup(1);
struct SeriesContext
{
string password;
protocol::HttpResponse *resp;
};
void handler(int signum)
{
printf("signum = %d\n",signum);
gWaitGroup.done();
}
void redisCallback(WFRedisTask *redisTask)
{
cout<<"redisCallback is called"<<endl;
protocol::RedisResponse *resp = redisTask->get_resp();
protocol::RedisValue value;
resp->get_result(value);
// 提取共享的数据
SeriesContext *context = (SeriesContext*)series_of(redisTask)->get_context();
if(value.string_value() == context->password)
{
context->resp->append_output_body("login success");
}
else
{
context->resp->append_output_body("login failed");
}
delete context;
}
void serverTaskCallback(WFHttpTask *serverTask)
{
cout<<"serverTaskCallback is called"<<endl;
}
void process(WFHttpTask *serverTask)
{
cout<<"Process is called"<<endl;
// 修改响应
// 获取r用户名和密码
protocol::HttpRequest *req = serverTask->get_req();
string uri = req->get_request_uri();
string nameKV = uri.substr(0,uri.find("&"));
string passwdKV = uri.substr(uri.find("&")+1);
string name = nameKV.substr(nameKV.find("=")+1);
string passwd = passwdKV.substr(passwdKV.find("=")+1);
cout<<"name = "<<name<<" passwd="<<passwd<<endl;
// 设置要共享的数据
SeriesContext *context = new SeriesContext;
context->password = passwd;
context->resp = serverTask->get_resp();
series_of(serverTask)->set_context(context);
// 创建Redistask
WFRedisTask *redisTask=
WFTaskFactory::create_redis_task("redis://127.0.0.1:6379",0,redisCallback);
redisTask->get_req()->set_request("hget",{"54user",name});// 这个任务需要process之后执行
series_of(serverTask)->push_back(redisTask);
serverTask->set_callback(serverTaskCallback);
cout<<"process is returned"<<endl;
}
int main()
{
signal(SIGINT,handler);
WFHttpServer server(process);
server.start(1234); // listen 1234端口
gWaitGroup.wait();
}
部署静态资源
pread任务和pwrite任务
#include <54func.h>
#include<iostream>
#include <vector>
#include <string>
#include <workflow/Workflow.h>
#include <workflow/HttpMessage.h>
#include <workflow/HttpUtil.h>
#include <workflow/WFTaskFactory.h>
#include <workflow/WFFacilities.h>
#include <workflow/WFHttpServer.h>
using namespace std;
using std::endl;
using std::cout;
using std::string;
using std::vector;
struct SeriesContext
{
protocol::HttpResponse *resp;
char buf[4096];
};
static WFFacilities::WaitGroup gWaitGroup(1);
void handler(int signum)
{
printf("signum = %d\n",signum);
gWaitGroup.done();
}
void fileIOCallback(WFFileIOTask *preadTask)
{
cout<<"fileIOCallback is called"<<endl;
SeriesContext *context = (SeriesContext*)series_of(preadTask)->get_context();
cout<<"buf = "<<context->buf<<endl;
context->resp->append_output_body(context->buf,strlen(context->buf));
}
void process(WFHttpTask *serverTask)
{
cout<<"Process is called"<<endl;
// 修改响应
protocol::HttpResponse *resp = serverTask->get_resp();
resp->set_http_version("HTTP/1.1");
resp->set_status_code("200");
resp->set_reason_phrase("ok");
resp->set_header_pair("Content-type","text/html");
// workflow框架会自动加入s首部字段 connection keep-aline
resp->append_output_body("<html>Hello</html>");
protocol::HttpRequest *req = serverTask->get_req();
string method = req->get_method();
if(method == "GET")
{
int fd = open("postform.html",O_RDONLY);
SeriesContext *context = new SeriesContext();
context->resp = resp;
series_of(serverTask)->set_context(context);
WFFileIOTask *preadTask = WFTaskFactory::create_pread_task(fd,context->buf,4096,0,fileIOCallback);
series_of(serverTask)->push_back(preadTask);
}
else
{
resp->append_output_body("<html>Yes</html>");
}
}
int main()
{
signal(SIGINT,handler);
WFHttpServer server(process);
server.start(1234); // listen 1234端口
gWaitGroup.wait();
}
wfrest库
- 目的是简化workflow的使用
- 按照这样写,uri路由匹配太多了
项目实战:分块上传功能实现
安装wfrest
分块上传功能的实现方案
接口1:POST /file/mupload/init
上传ID生成
分块信息处理
接口2:POST /file/mupload/uppart
接口3:/file/mupload/complete
#include <signal.h>
#include <workflow/WFFacilities.h>
#include <workflow/WFHttpServer.h>
#include <workflow/HttpUtil.h>
#include <wfrest/json.hpp>
#include "UnixHeader.h"
using Json = nlohmann::json;
static WFFacilities::WaitGroup waitGroup(1);
void sighandler(int signum)
{
printf("finish 1 work!\n");
waitGroup.done();
}
void process(WFHttpTask *serverTask)
{
// http://192.168.118.128:1234/file/mupload/init
// http://192.168.118.128:1234/file/mupload/uppart?uploadID=liao1122&chkidx=2
// http://192.168.118.128:1234/file/mupload/complete?uploadID=liao1122
auto req = serverTask->get_req();
auto resp = serverTask->get_resp();
std::string uri = req->get_request_uri();
std::string path = uri.substr(0, uri.find("?"));
std::string query = uri.substr(uri.find("?") + 1);
std::string method = req->get_method();
if (path == "/file/mupload/init" && method == "POST")
{
// 1 解析请求报文
const void *body;
size_t size;
req->get_parsed_body(&body, &size);
// 2 从字符串解析成json
Json fileInfo = Json::parse(static_cast<const char *>(body));
std::string filename = fileInfo["filename"];
int filesize = fileInfo["filesize"];
std::string filehash = fileInfo["filehash"];
// fprintf(stderr,"%s %s %d\n", filename.c_str(), filehash.c_str(), filesize);
// 3 初始化分块操作
// 生成uploadID
std::string uploadID = "liao";
time_t now = time(NULL);
struct tm *ptm = localtime(&now);
char timeStamp[30] = {0};
sprintf(timeStamp, "%02d%02d%02d", ptm->tm_hour, ptm->tm_min, ptm->tm_sec);
uploadID = uploadID + timeStamp;
// fprintf(stderr,"%s\n", uploadID.c_str());
// 生成分块信息
int chunkcount;
int chunksize = 1024 * 1024;
if (filesize % chunksize != 0)
{
chunkcount = filesize / chunksize + 1;
}
else
{
chunkcount = filesize / chunksize;
}
// fprintf(stderr,"chunksize = %d, chunkcount = %d\n", chunksize, chunkcount);
// 初始化redis
// HSET uploadID chunkcount 6
// HSET uploadID filehash xxx
// HSET uploadID filesize xxx
std::vector<std::vector<std::string>> argsVec = {
{uploadID, "chunkcount", std::to_string(chunkcount)},
{uploadID, "filehash", filehash},
{uploadID, "filesize", std::to_string(filesize)}};
for (int i = 0; i < 3; ++i)
{
auto redisTask = WFTaskFactory::create_redis_task("redis://localhost", 0, nullptr);
redisTask->get_req()->set_request("HSET", argsVec[i]);
redisTask->start();
}
// 生成响应给客户端
Json uppartInfo;
uppartInfo["status"] = "OK";
uppartInfo["uploadID"] = uploadID;
uppartInfo["chunkcount"] = chunkcount;
uppartInfo["filesize"] = filesize;
uppartInfo["chunksize"] = chunksize;
uppartInfo["filehash"] = filehash;
resp->append_output_body(uppartInfo.dump());
}
else if (path == "/file/mupload/uppart" && method == "POST")
{
// fprintf(stderr,"uppart\n");
// 解析uri的查询参数,提取出uploadID和chkidx
// query = "uploadID=liao101335&chkidx=2"
std::string uploadIDKV = query.substr(0, query.find("&"));
std::string uploadID = uploadIDKV.substr(uploadIDKV.find("=") + 1);
std::string chkidxKV = query.substr(query.find("&") + 1);
std::string chkidx = chkidxKV.substr(chkidxKV.find("=") + 1);
// 保存单个分片 ./filehash/1 filehash需要从redis当中获取 HGET uploadID filehash
auto redisTaskHGET = WFTaskFactory::create_redis_task("redis://localhost", 0, [chkidx, req](WFRedisTask *redisTaskHGET)
{
protocol::RedisValue value;
redisTaskHGET->get_resp()->get_result(value);
std::string filehash = value.string_value();
mkdir(filehash.c_str(),0777);
std::string filepath = filehash + "/" + chkidx;
// 文件IO任务
int fd = open(filepath.c_str(),O_RDWR|O_CREAT,0666);
const void *body;
size_t size;
req->get_parsed_body(&body,&size);
write(fd,body,size);
close(fd); });
redisTaskHGET->get_req()->set_request("HGET", {uploadID, "filehash"});
series_of(serverTask)->push_back(redisTaskHGET);
// 记录下载进度 HSET uploadID chkidx 1
auto redisTaskHSET = WFTaskFactory::create_redis_task("redis://localhost", 0, nullptr);
redisTaskHSET->get_req()->set_request("HSET", {uploadID, "chkidx_" + chkidx, "1"});
series_of(serverTask)->push_back(redisTaskHSET);
// 设置响应的内容
resp->append_output_body("OK");
}
else if (path == "/file/mupload/complete" && method == "POST")
{
// fprintf(stderr,"complete\n");
// 解析请求,提取出uploadID
std::string uploadID = query.substr(query.find("=") + 1);
// 查询上传进度
// HGETALL uploadID
// chunkcount键所对应的值-->下载的总进度
// 前缀为chkidx_的键的数量-->下载的当前进度
auto redisTask = WFTaskFactory::create_redis_task("redis://localhost", 0, [resp](WFRedisTask *redisTask)
{
protocol::RedisValue value;
redisTask->get_resp()->get_result(value);
int chunkcount;
int chunknow = 0;
for(int i = 0; i < value.arr_size(); i+=2){
std::string key = value.arr_at(i).string_value();
std::string val = value.arr_at(i+1).string_value();
if(key == "chunkcount"){
chunkcount = std::stoi(val);
}
else if(key.substr(0,7) == "chkidx_"){
++chunknow;
}
}
//fprintf(stderr,"chunkcount = %d, chunknow = %d\n", chunkcount, chunknow);
if(chunkcount == chunknow){
//分块合并操作
resp->append_output_body("SUCCESS");
}
else{
resp->append_output_body("FAIL");
} });
redisTask->get_req()->set_request("HGETALL", {uploadID});
series_of(serverTask)->push_back(redisTask);
}
}
int main()
{
signal(SIGINT, sighandler);
WFHttpServer server(process);
if (server.start(1234) == 0)
{
waitGroup.wait();
server.stop();
}
else
{
perror("server start error!\n");
exit(-1);
}
return 0;
}
MySQL任务
workflow去操作表的时候,表的名字前面一定要加 数据库名.
- 注意是在做客户端
#include <54func.h>
#include <iostream>
#include <vector>
#include <workflow/WFTaskFactory.h>
#include <workflow/WFFacilities.h>
#include <workflow/MySQLMessage.h>
#include <workflow/MySQLResult.h>
#include <workflow/MySQLUtil.h>
static WFFacilities::WaitGroup waitGroup(1);
void sigHandler(int num)
{
waitGroup.done();
printf("waitGroup is done\n");
}
void mysqlCallback(WFMySQLTask *mysqlTask)
{
// step 1 检查连接的状态
if (mysqlTask->get_state() != WFT_STATE_SUCCESS)
{
std::cerr << "error msg :" << WFGlobal::get_error_string(mysqlTask->get_state(), mysqlTask->get_error());
return;
}
protocol::MySQLResponse *resp = mysqlTask->get_resp();
protocol::MySQLResultCursor cursor(mysqlTask->get_resp());
// step 2 检查mysql任务的状态
if (resp->get_packet_type() == MYSQL_PACKET_ERROR)
{
std::cerr << "ERROR, error code = " << resp->get_error_code()
<< " msg = " << resp->get_error_msg();
return;
}
do
{ // step 3 遍历结果集合
// step 4 判断指令的类型是 读 select 写 update insert delete from alter
if (cursor.get_cursor_status() == MYSQL_STATUS_OK)
{
// 说明是写操作
std::cerr << "OK, " << cursor.get_affected_rows() << " rows affected."
<< cursor.get_warnings() << " warnings. insert_id = " << cursor.get_insert_id() << ".\n";
}
else if (cursor.get_cursor_status() == MYSQL_STATUS_GET_RESULT)
{
// 说明sql语句是读操作
std::cerr << "field count = " << cursor.get_field_count()
<< " rows count = " << cursor.get_rows_count() << "\n";
// 找到所有域的信息
const protocol::MySQLField *const *fields = cursor.fetch_fields();
for (int i = 0; i < cursor.get_field_count(); ++i)
{
std::cerr << "database = " << fields[i]->get_db()
<< " table = " << fields[i]->get_table()
<< " fieldname = " << fields[i]->get_name()
<< " fieldtype = " << datatype2str(fields[i]->get_data_type())
<< "\n";
}
// 取出表格当中的所有内容
std::vector<std::vector<protocol::MySQLCell>> rows;
cursor.fetch_all(rows);
for (unsigned int j = 0; j < rows.size(); ++j)
{
for (unsigned int i = 0; i < rows[j].size(); ++i)
{
if (rows[j][i].is_int())
{
std::cerr << rows[j][i].as_int() << " ";
}
else if (rows[j][i].is_string())
{
std::cerr << rows[j][i].as_string() << " ";
}
else if (rows[j][i].is_datetime())
{
std::cerr << rows[j][i].as_datetime() << " ";
}
}
std::cerr << "\n";
}
}
} while (cursor.next_result_set()); // 多个sql指令合在一起的情况
//cursor.next_result_set(); // 当执行多个sql语句时
}
int main()
{
signal(SIGINT, sigHandler);
WFMySQLTask *mysqlTask =
WFTaskFactory::create_mysql_task("mysql://root:123@127.0.0.1:3306", 0, mysqlCallback);
// std::string sql = "update cloudisk.tbl_user set email_validated = 1 where
// std::string sql = "show databases;";
//std::string sql = "select * from cloudisk.tbl_user_token;";
//std::string sql = "INSERT INTO cloudisk.tbl_user_token VALUES(1, 'liao', 'abcdef');";
// workflow去操作表的时候,表的名字前面一定要加 数据库名.
std::string sql = "select * from cloudisk.tbl_user_token;";
sql += "INSERT INTO cloudisk.tbl_user_token VALUES(3, 'liao3', 'abcdef');";
auto req = mysqlTask->get_req();
req->set_query(sql);
mysqlTask->start();
waitGroup.wait();
}
Mysql可以执行多个语句,redis一次只能执行一个任务
定时任务
#include <54func.h>
#include <iostream>
#include <map>
#include <workflow/WFTaskFactory.h>
#include <workflow/WFFacilities.h>
#include <wfrest/HttpServer.h>
static WFFacilities::WaitGroup waitGroup(1);
void sigHandler(int num)
{
waitGroup.done();
printf("waitGroup is done\n");
}
void timerCallback(WFTimerTask *timerTask)
{
std::cerr << "timer callback " << time(NULL) << std::endl;
WFTimerTask *nextTask =
WFTaskFactory::create_timer_task(5000000, timerCallback);
series_of(timerTask)->push_back(nextTask);
}
int main()
{
signal(SIGINT, sigHandler);
WFTimerTask *timerTask =
WFTaskFactory::create_timer_task(5000000, timerCallback);
timerTask->start();
std::cerr << "timer start " << time(NULL) << std::endl;
waitGroup.wait();
return 0;
}
文件IO任务
文件IO任务
示例:静态资源服务器
pread任务和pwrite任务
#include <54func.h>
#include<iostream>
#include <vector>
#include <string>
#include <workflow/Workflow.h>
#include <workflow/HttpMessage.h>
#include <workflow/HttpUtil.h>
#include <workflow/WFTaskFactory.h>
#include <workflow/WFFacilities.h>
#include <workflow/WFHttpServer.h>
using namespace std;
using std::endl;
using std::cout;
using std::string;
using std::vector;
struct SeriesContext
{
protocol::HttpResponse *resp;
char buf[4096];
};
static WFFacilities::WaitGroup gWaitGroup(1);
void handler(int signum)
{
printf("signum = %d\n",signum);
gWaitGroup.done();
}
void fileIOCallback(WFFileIOTask *preadTask)
{
cout<<"fileIOCallback is called"<<endl;
SeriesContext *context = (SeriesContext*)series_of(preadTask)->get_context();
cout<<"buf = "<<context->buf<<endl;
context->resp->append_output_body(context->buf,strlen(context->buf));
}
void process(WFHttpTask *serverTask)
{
cout<<"Process is called"<<endl;
// 修改响应
protocol::HttpResponse *resp = serverTask->get_resp();
resp->set_http_version("HTTP/1.1");
resp->set_status_code("200");
resp->set_reason_phrase("ok");
resp->set_header_pair("Content-type","text/html");
// workflow框架会自动加入s首部字段 connection keep-aline
resp->append_output_body("<html>Hello</html>");
protocol::HttpRequest *req = serverTask->get_req();
string method = req->get_method();
if(method == "GET")
{
int fd = open("postform.html",O_RDONLY);
SeriesContext *context = new SeriesContext();
context->resp = resp;
series_of(serverTask)->set_context(context);
WFFileIOTask *preadTask = WFTaskFactory::create_pread_task(fd,context->buf,4096,0,fileIOCallback);
series_of(serverTask)->push_back(preadTask);
}
else
{
resp->append_output_body("<html>Yes</html>");
}
}
int main()
{
signal(SIGINT,handler);
WFHttpServer server(process);
server.start(1234); // listen 1234端口
gWaitGroup.wait();
}
wfrest的使用
#include <54func.h>
#include <iostream>
using std::cout;
using std::endl;
#include <workflow/WFFacilities.h>
#include <wfrest/HttpServer.h>
static WFFacilities::WaitGroup waitGroup(1);
void sighandler(int num)
{
printf("sig %d is coming\n", num);
// 将计数器的值减1
waitGroup.done();
}
int main()
{
using std::string;
signal(SIGINT, sighandler);
using namespace wfrest;
HttpServer httpserver;
httpserver.GET("/wfrest/test0",
[](const HttpReq *, HttpResp *resp)
{
// 使用workflow的方式设置响应
resp->append_output_body("hello, wfrest client");
});
httpserver.GET("/wfrest/query",
[](const HttpReq *req, HttpResp *resp)
{
// uri的查询参数部分
cout << "username:" << req->query("username") << endl;
auto queryList = req->query_list();
for (auto &query : queryList)
{
cout << query.first << ": " << query.second << endl;
}
resp->append_output_body("hello, wfrest client1");
});
httpserver.POST("/wfrest/body",
[](const HttpReq *req, HttpResp *resp)
{
// 请求体
string body = req->body();
cout << "body:" << body << endl;
resp->append_output_body("hello, wfrest client");
});
httpserver.POST("/wfrest/urlencoded",
[](const HttpReq *req, HttpResp *resp)
{
// URLENCODED类型的请求体
if (req->content_type() == APPLICATION_URLENCODED)
{
auto formKV = req->form_kv();
for (auto &elem : formKV)
{
cout << elem.first << ": " << elem.second << endl;
}
}
resp->append_output_body("hello, wfrest client");
});
httpserver.POST("/wfrest/formdata",
[](const HttpReq *req, HttpResp *resp)
{
// form_data类型的请求体
if (req->content_type() == MULTIPART_FORM_DATA)
{
auto form = req->form();
for (auto &elem : form)
{
cout << elem.first << ": " << elem.second.first
<< endl;
cout << elem.second.second << endl;
}
}
resp->append_output_body("hello, wfrest client");
});
if (httpserver.track().start(1234) == 0)
{
// 列出当前服务器上部署的服务
httpserver.list_routes();
waitGroup.wait();
httpserver.stop();
}
else
{
printf("HttpServer start failed.\n");
}
return 0;
}
- 更多功能
简化首部字段操作
resp->add_header_pair() // 添加首部字段
resp_set_header_pair() // 首部字段已经存在
resp->headers["Location"] = ""; // 没有就添加有就修改
简化查询参数操作
- 简化登录业务
#include <54func.h>
#include <iostream>
using std::cout;
using std::endl;
#include <workflow/WFFacilities.h>
#include <workflow/MySQLResult.h>
#include <workflow/MySQLUtil.h>
#include <wfrest/HttpServer.h>
#include <wfrest/json.hpp>
using std::vector;
static WFFacilities::WaitGroup waitGroup(1);
void sighandler(int num)
{
printf("sig %d is coming\n", num);
// 将计数器的值减1
waitGroup.done();
}
int main()
{
using std::string;
signal(SIGINT, sighandler);
using namespace wfrest;
HttpServer httpserver;
// String可以自动设置首部字段
httpserver.GET("/wfrest/test0",
[](const HttpReq *, HttpResp *resp)
{
resp->String("hello,wfrest client2");
});
// 部署静态资源
httpserver.GET("/wfrest/file",
[](const HttpReq *, HttpResp *resp)
{
resp->File("postform.html");
});
// 响应JSON
httpserver.GET("/wfrest/json",
[](const HttpReq *, HttpResp *resp)
{
Json data;
data["username"] = "liubei";
data["password"] = "123";
resp->Json(data);
});
// MySQL默认结果
httpserver.GET("/wfrest/mysql0",
[](const HttpReq *, HttpResp *resp)
{
string url("mysql://root:123@localhost");
string sql("select * from mycloud.tbl_user_token");
resp->MySQL(url, sql);
});
// MySQL的Json结果
httpserver.GET("/wfrest/mysql1",
[](const HttpReq *, HttpResp *resp)
{
string url("mysql://root:123@localhost");
string sql("select * from mycloud.tbl_user_token");
// json指针指向的就是MySQL的结果集
resp->MySQL(url, sql, [resp](Json *json)
{
string msg = (*json)["result_set"][0]["rows"][0][1];
resp->String(msg); });
});
// MySQL cursor
httpserver.GET("/wfrest/mysql2",
[](const HttpReq *, HttpResp *resp)
{
string url("mysql://root:123@localhost");
string sql("select * from mycloud.tbl_user_token");
using namespace protocol;
// pcursor指针指向的就是MySQL的结果集的迭代器
resp->MySQL(url, sql, [resp](MySQLResultCursor *pcursor)
{
std::vector<std::vector<MySQLCell>> matrix;
pcursor->fetch_all(matrix);
string msg = matrix[0][2].as_string();
resp->String(msg); });
});
// 在wfrest中显式使用序列
httpserver.GET("/wfrest/series",
[](const HttpReq *, HttpResp *resp, SeriesWork *series)
{
auto timerTask = WFTaskFactory::create_timer_task(1000 *
1000,
[resp](WFTimerTask *)
{
printf("timerCallback is running.\n");
resp->String("timer callback");
});
series->push_back(timerTask);
});
// 序列配合mysql任务
httpserver.GET("/wfrest/mysql3",
[](const HttpReq *, HttpResp *httpResp, SeriesWork *series)
{
string url("mysql://root:123@localhost");
auto mysqlTask =
WFTaskFactory::create_mysql_task(url, 1,
[httpResp](WFMySQLTask *mysqltask)
{
// 0. 对任务的状态进行检测
int state = mysqltask->get_state();
int error = mysqltask->get_error();
if (state != WFT_STATE_SUCCESS)
{
printf("%s\n", WFGlobal::get_error_string(state,
error));
return;
}
// 1. 检测SQL语句是否存在语法错误
auto resp = mysqltask->get_resp();
if (resp->get_packet_type() == MYSQL_PACKET_ERROR)
{
printf("ERROR %d: %s\n", resp->get_error_code(),
resp->get_error_msg().c_str());
return;
}
printf("sql sentence is ok.\n");
using namespace protocol;
MySQLResultCursor cursor(resp);
if (cursor.get_cursor_status() == MYSQL_STATUS_OK)
{
printf("Query OK. %llu row affected.\n",
cursor.get_affected_rows());
}
else if (cursor.get_cursor_status() ==
MYSQL_STATUS_GET_RESULT)
{
// 读操作
vector<vector<MySQLCell>> matrix;
cursor.fetch_all(matrix);
string msg = matrix[1][1].as_string();
httpResp->String(msg);
}
});
string sql("select * from mycloud.tbl_user_token");
mysqlTask->get_req()->set_query(sql);
series->push_back(mysqlTask);
});
if (httpserver.track().start(1234) == 0)
{
// 列出当前服务器上部署的服务
httpserver.list_routes();
waitGroup.wait();
httpserver.stop();
}
else
{
printf("HttpServer start failed.\n");
}
return 0;
}
wfrest解析报文体
解析x-www-form-urlencoded(传简单消息)
#include <54func.h>
#include <iostream>
#include<map>
using std::cout;
using std::endl;
using std::cerr;
using std::map;
#include <workflow/WFFacilities.h>
#include <wfrest/HttpServer.h>
static WFFacilities::WaitGroup waitGroup(1);
void sighandler(int num)
{
printf("sig %d is coming\n", num);
// 将计数器的值减1
waitGroup.done();
}
int main()
{
using std::string;
using namespace wfrest;
signal(SIGINT, sighandler);
wfrest::HttpServer server;
server.GET("/abc",[](const wfrest::HttpReq *req,wfrest::HttpResp *resp){
resp->headers["key"] = "value";
});
server.POST("/def",[](const HttpReq *req,HttpResp *resp){
// 头体: a=b&c=d&e=f
if(req->content_type() == APPLICATION_URLENCODED)
{
map<string,string> body = req->form_kv();
for(auto &myPair:body)
{
cerr<<"key = "<<myPair.first<<",value = "<<myPair.second<<endl;
}
}
});
if (server.start(1234) == 0)
{
// 列出当前服务器上部署的服务
server.list_routes();
waitGroup.wait();
server.stop();
}
else
{
printf("HttpServer start failed.\n");
}
return 0;
}
解析from-data(传文件)
// 注意框架,要求单个文件不能上传超过128M
// 会下载
server.POST("/upload",[](const HttpReq * req,HttpResp *resp){
resp->File("./upload.html");
if(req->content_type() == MULTIPART_FORM_DATA)
{
map<string,pair<string,string>> &formdata = req->form();
for(auto &mypair : formdata)
{
cerr<<"key = "<<mypair.first
<<",value1 = "<<mypair.second.first
<<",value2 = "<<mypair.second.second<<endl;
}
}
});
// z只会展示
server.GET("/upload",[](const HttpReq * req,HttpResp *resp){
resp->File("./upload.html");
});
server.Static("/static","./static_res");
部署静态资源
server.GET("/postform.html",[](const HttpReq *req,HttpResp *resp){
resp->File("postform.html");
});
- 一次性部署多个文件
int main()
{
using std::string;
using namespace wfrest;
signal(SIGINT, sighandler);
wfrest::HttpServer server;
server.GET("/abc",[](const wfrest::HttpReq *req,wfrest::HttpResp *resp){
resp->headers["key"] = "value";
});
server.POST("/def",[](const HttpReq *req,HttpResp *resp){
// 头体: a=b&c=d&e=f
if(req->content_type() == APPLICATION_URLENCODED)
{
map<string,string> body = req->form_kv();
for(auto &myPair:body)
{
cerr<<"key = "<<myPair.first<<",value = "<<myPair.second<<endl;
}
}
});
server.GET("/postform.html",[](const HttpReq *req,HttpResp *resp){
resp->File("postform.html");
});
server.Static("/static","./static_res");
if (server.start(1234) == 0)
{
// 列出当前服务器上部署的服务
server.list_routes();
waitGroup.wait();
server.stop();
}
else
{
printf("HttpServer start failed.\n");
}
return 0;
}
解析json报文体格式
#include<wfrest/json.hpp>
using Json = nlohmann::json;
server.POST("/json", [](const HttpReq *req, HttpResp *resp) {
try {
string body = req->body();
Json info = Json::parse(body);
string filename = info["filename"];
string filecontent = info["filecontent"];
int size = info["filesize"];
cerr << filename << filecontent << size << endl;
resp->String("OK"); // 添加响应,以便客户端知道请求成功
} catch (const Json::parse_error &e) {
cerr << "JSON parse error: " << e.what() << endl;
resp->String("Invalid JSON format");
} catch (const Json::out_of_range &e) {
cerr << "JSON key error: " << e.what() << endl;
resp->String("Missing required fields in JSON");
}
});// 删除其中一个定义
发送SQL
直接将结果返给前端
server.GET("/mysql",[](const HttpReq *res,HttpResp *resp){
resp->MySQL("mysql://root:123@localhost","select * from cloudisk.tbl_user_token");
});
使用三参数版本,调用序列进行回调
server.GET("/mysql1", [](const HttpReq *req, HttpResp *resp, SeriesWork *series)
{ WFMySQLTask *mysqlTask =
WFTaskFactory::create_mysql_task("mysql://root:123@localhost", 0,
[](WFMySQLTask *mysqlTask)
{
// step 1 检查连接的状态
if (mysqlTask->get_state() != WFT_STATE_SUCCESS)
{
std::cerr << "error msg :" << WFGlobal::get_error_string(mysqlTask->get_state(), mysqlTask->get_error());
return;
}
protocol::MySQLResponse *resp = mysqlTask->get_resp();
protocol::MySQLResultCursor cursor(mysqlTask->get_resp());
// step 2 检查mysql任务的状态
if (resp->get_packet_type() == MYSQL_PACKET_ERROR)
{
std::cerr << "ERROR, error code = " << resp->get_error_code()
<< " msg = " << resp->get_error_msg();
return;
}
do
{ // step 3 遍历结果集合
// step 4 判断指令的类型是 读 select 写 update insert delete from alter
if (cursor.get_cursor_status() == MYSQL_STATUS_OK)
{
// 说明是写操作
std::cerr << "OK, " << cursor.get_affected_rows() << " rows affected."
<< cursor.get_warnings() << " warnings. insert_id = " << cursor.get_insert_id() << ".\n";
}
else if (cursor.get_cursor_status() == MYSQL_STATUS_GET_RESULT)
{
// 说明sql语句是读操作
std::cerr << "field count = " << cursor.get_field_count()
<< " rows count = " << cursor.get_rows_count() << "\n";
// 找到所有域的信息
const protocol::MySQLField *const *fields = cursor.fetch_fields();
for (int i = 0; i < cursor.get_field_count(); ++i)
{
std::cerr << "database = " << fields[i]->get_db()
<< " table = " << fields[i]->get_table()
<< " fieldname = " << fields[i]->get_name()
<< " fieldtype = " << datatype2str(fields[i]->get_data_type())
<< "\n";
}
// 取出表格当中的所有内容
std::vector<std::vector<protocol::MySQLCell>> rows;
cursor.fetch_all(rows);
for (unsigned int j = 0; j < rows.size(); ++j)
{
for (unsigned int i = 0; i < rows[j].size(); ++i)
{
if (rows[j][i].is_int())
{
std::cerr << rows[j][i].as_int() << " ";
}
else if (rows[j][i].is_string())
{
std::cerr << rows[j][i].as_string() << " ";
}
else if (rows[j][i].is_datetime())
{
std::cerr << rows[j][i].as_datetime() << " ";
}
}
std::cerr << "\n";
}
}
} while (cursor.next_result_set()); // 多个sql指令合在一起的情况
// cursor.next_result_set(); // 当执行多个sql语句时
});
mysqlTask->get_req()->set_query("select * from cloudisk.tbl_user_token");
series->push_back(mysqlTask); });