#include "precompile.h" #include #include #include #include #include #include #include #include #include #include "framework/cpp/httpclientpool.h" #include "framework/cpp/msgqueue.h" #include "framework/cpp/utils.h" namespace f8 { enum AsyncHttpError { AHE_NO_ERROR = 0, AHE_NO_CONN = 1, }; struct AsyncHttpRequest { long long context_id = 0; a8::XParams param; time_t add_time = 0; AsyncHttpOnOkFunc on_ok = nullptr; AsyncHttpOnErrorFunc on_error = nullptr; a8::TimerAttacher timer_attacher; }; struct AsyncHttpNode { int socket_handle = 0; long long context_id = 0; int method = 0; std::string url; std::string url_params; std::string content; a8::XObject headers; AsyncHttpNode* nextnode = nullptr; }; class HttpThread { public: void Init() { loop_mutex_ = new std::mutex(); loop_cond_ = new std::condition_variable(); top_node_ = nullptr; bot_node_ = nullptr; work_node_ = nullptr; msg_mutex_ = new std::mutex(); work_thread_ = new std::thread(&HttpThread::WorkThreadProc, this); } void AddAsyncHttp(AsyncHttpNode* p) { std::unique_lock lk(*loop_mutex_); msg_mutex_->lock(); if (bot_node_) { bot_node_->nextnode = p; bot_node_ = p; } else { top_node_ = p; bot_node_ = p; } msg_mutex_->unlock(); loop_cond_->notify_all(); } private: void WorkThreadProc() { while (true) { ProcessMsg(); WaitLoopCond(); } } void ProcessMsg() { if (!work_node_ && top_node_) { msg_mutex_->lock(); work_node_ = top_node_; top_node_ = nullptr; bot_node_ = nullptr; msg_mutex_->unlock(); } while (work_node_) { AsyncHttpNode *pdelnode = work_node_; work_node_ = work_node_->nextnode; ProcAsyncHttp(pdelnode); delete pdelnode; } } void WaitLoopCond() { std::unique_lock lk(*loop_mutex_); { msg_mutex_->lock(); if (!work_node_ && top_node_) { work_node_ = top_node_; top_node_ = nullptr; bot_node_ = nullptr; } msg_mutex_->unlock(); } if (!work_node_) { loop_cond_->wait_for(lk, std::chrono::seconds(10)); } } void ProcAsyncHttp(AsyncHttpNode* node) { std::string finally_url; if (node->url.find('?') != std::string::npos) { finally_url = node->url + node->url_params; } else { finally_url = node->url + "?" + node->url_params; } std::string response; bool ret = false; switch (node->method) { case 1: { long long begin_tick = a8::XGetTickCount(); ret = a8::http::Get(finally_url, response, &node->headers, 10); long long end_tick = a8::XGetTickCount(); if (end_tick - begin_tick > f8::HttpClientPool::Instance()->max_request_delay) { f8::HttpClientPool::Instance()->max_request_delay = end_tick - begin_tick; } break; } case 2: { long long begin_tick = a8::XGetTickCount(); ret = a8::http::Post(finally_url.c_str(), node->content, response, &node->headers, 10); long long end_tick = a8::XGetTickCount(); if (end_tick - begin_tick > f8::HttpClientPool::Instance()->max_request_delay) { f8::HttpClientPool::Instance()->max_request_delay = end_tick - begin_tick; } break; } default: { break; } } if (ret) { a8::XObject* xobj = new a8::XObject(); if (xobj->ReadFromJsonString(response)) { f8::MsgQueue::Instance()->PostMsg_r(exec_async_http_msgid, a8::XParams() .SetSender(node->context_id) .SetParam1(AHE_NO_ERROR) .SetParam2((void*)xobj) ); } else { f8::MsgQueue::Instance()->PostMsg_r(exec_async_http_msgid, a8::XParams() .SetSender(node->context_id) .SetParam1(AHE_NO_CONN) .SetParam2(response) ); delete xobj; } } else { f8::MsgQueue::Instance()->PostMsg_r(exec_async_http_msgid, a8::XParams() .SetSender(node->context_id) .SetParam1(AHE_NO_CONN) ); } } public: int exec_async_http_msgid = 0; private: std::mutex *loop_mutex_ = nullptr; std::condition_variable *loop_cond_ = nullptr; std::thread *work_thread_ = nullptr; AsyncHttpNode *top_node_ = nullptr; AsyncHttpNode *bot_node_ = nullptr; AsyncHttpNode *work_node_ = nullptr; std::mutex *msg_mutex_ = nullptr; }; class HttpClientPoolImpl { public: void Init() { curr_seqid = 1000001; exec_async_http_msgid = MsgQueue::Instance()->AllocIMMsgId(); } void SetThreadNum(int thread_num) { assert(thread_num > 0); for (int i = 0; i < thread_num; i++) { HttpThread *http_thread = new HttpThread(); http_thread->exec_async_http_msgid = exec_async_http_msgid; http_thread->Init(); http_thread_pool.push_back(http_thread); } } AsyncHttpRequest* GetAsyncHttpRequest(long long seqid) { auto itr = async_http_hash.find(seqid); return itr != async_http_hash.end() ? itr->second : nullptr; } void AsyncHttpOnOk(long long seqid, a8::XObject& data) { AsyncHttpRequest* request = GetAsyncHttpRequest(seqid); if (!request) { return; } if (request->on_ok) { request->on_ok(request->param, data); } async_http_hash.erase(seqid); delete request; } void AsyncHttpOnError(long long seqid, const std::string& response) { AsyncHttpRequest* request = GetAsyncHttpRequest(seqid); if (!request) { return; } if (request->on_error) { request->on_error(request->param, response); } async_http_hash.erase(seqid); delete request; } void InternalExecAsyncHttp(int method, const char* url, a8::XObject& url_params, const char* content, a8::XObject* headers, a8::XParams param, AsyncHttpOnOkFunc on_ok, AsyncHttpOnErrorFunc on_error, long long hash_code) { AsyncHttpRequest* p = new AsyncHttpRequest(); { p->context_id = ++curr_seqid; p->param = param; p->add_time = time(nullptr); p->on_ok = on_ok; p->on_error = on_error; async_http_hash[p->context_id] = p; } HttpThread* http_thread = GetHttpThread(hash_code); if (!http_thread) { abort(); return; } { AsyncHttpNode* node = new AsyncHttpNode(); node->socket_handle = 0; node->context_id = p->context_id; node->method = method; node->url = url; url_params.ToUrlEncodeStr(node->url_params); node->content = std::string(content); if (headers) { headers->DeepCopy(node->headers); } http_thread->AddAsyncHttp(node); } a8::Timer::Instance()->AddDeadLineTimerAndAttach(1000 * 10, a8::XParams() .SetSender(this) .SetParam1(p->context_id), [] (const a8::XParams& param) { }, &p->timer_attacher.timer_list_); } HttpThread* GetHttpThread(long long hash_code) { if (http_thread_pool.empty() || hash_code < 0) { return nullptr; } return http_thread_pool[hash_code % http_thread_pool.size()]; } public: long long curr_seqid = 0; std::map async_http_hash; unsigned short exec_async_http_msgid = 0; std::vector http_thread_pool; }; void HttpClientPool::Init(int thread_num) { impl_ = new HttpClientPoolImpl(); impl_->Init(); MsgQueue::Instance()->RegisterCallBack(impl_->exec_async_http_msgid, [] (const a8::XParams& param) { if (param.param1.GetInt() == AHE_NO_ERROR) { a8::XObject* xobj = (a8::XObject*)param.param2.GetUserData(); HttpClientPool::Instance()->impl_->AsyncHttpOnOk(param.sender, *xobj); delete xobj; } else { HttpClientPool::Instance()->impl_->AsyncHttpOnError(param.sender, param.param2.GetString()); } } ); impl_->SetThreadNum(thread_num); } void HttpClientPool::UnInit() { delete impl_; impl_ = nullptr; } void HttpClientPool::HttpGet(a8::XParams param, AsyncHttpOnOkFunc on_ok, AsyncHttpOnErrorFunc on_error, const char* url, a8::XObject url_params, long long hash_code, a8::XObject* headers) { impl_->InternalExecAsyncHttp(1, url, url_params, "", headers, param, on_ok, on_error, hash_code); } void HttpClientPool::HttpPost(a8::XParams param, AsyncHttpOnOkFunc on_ok, AsyncHttpOnErrorFunc on_error, const char* url, a8::XObject url_params, const std::string& content, long long hash_code, a8::XObject* headers) { impl_->InternalExecAsyncHttp(2, url, url_params, content.c_str(), headers, param, on_ok, on_error, hash_code); } }