当发生http请求时(也就是在浏览器输入url然后回车的操作),服务器接收到数据后会触发一个kBroadcastHttpRequest事件,而该事件在一开始就已经加入了监听,下面简单描述为:
- 服务器是何时监听的kBroadcastHttpRequest
- 如何触发的kBroadcastHttpRequest
- 如何处理的kBroadcastHttpRequest
一:监听kBroadcastHttpRequest
从main函数开始就执行了installWebApi函数,定义如下:
void installWebApi() {
addHttpListener();
GET_CONFIG(string,api_secret,API::kSecret);
api_regist("/index/api/getThreadsLoad",[](API_ARGS_MAP_ASYNC){
});
......
}
在这里对kBroadcastHttpRequest事件进行监听,当事件触发时检测参数中的url是否在s_map_api中,在则调用s_map_api的值,其中保存了一个回调函数
static inline void addHttpListener(){
GET_CONFIG(bool, api_debug, API::kApiDebug);
//注册监听kBroadcastHttpRequest事件
NoticeCenter::Instance().addListener(nullptr, Broadcast::kBroadcastHttpRequest, [](BroadcastHttpRequestArgs) {
auto it = s_map_api.find(parser.Url());
if (it == s_map_api.end()) {
return;
}
//该api已被消费
consumed = true;
......
try {
it->second(parser, invoker, sender);
} catch (ApiRetException &ex) {
responseApi(ex.code(), ex.what(), invoker);
}
#ifdef ENABLE_MYSQL
catch(SqlException &ex){
responseApi(API::SqlFailed, StrPrinter << "操作数据库失败:" << ex.what() << ":" << ex.getSql(), invoker);
}
#endif// ENABLE_MYSQL
catch (std::exception &ex) {
responseApi(API::Exception, ex.what(), invoker);
}
});
}
二:触发kBroadcastHttpRequest
既然是http请求,那么肯定是由HttpSession发起的调用,监听http端口,接收到的数据都会到下面这个函数,然后调用HttpRequestSplitter的input函数,作用就是对数据进行分包处理(解决多包黏包的问题)
void HttpSession::onRecv(const Buffer::Ptr &pBuf) {
_ticker.resetTime();
input(pBuf->data(), pBuf->size());
}
分包后,会回调到HttpSession的onRecvHeader函数,如下面的函数,最终根据http请求的方法,调用下面4个函数
- Handle_Req_GET
- Handle_Req_POST
- Handle_Req_HEAD
- Handle_Req_OPTIONS
ssize_t HttpSession::onRecvHeader(const char *header, size_t len) {
typedef void (HttpSession::*HttpCMDHandle)(ssize_t &);
static unordered_map<string, HttpCMDHandle> s_func_map;
static onceToken token(
[]() {
s_func_map.emplace("GET", &HttpSession::Handle_Req_GET);
s_func_map.emplace("POST", &HttpSession::Handle_Req_POST);
s_func_map.emplace("HEAD", &HttpSession::Handle_Req_HEAD);
s_func_map.emplace("OPTIONS", &HttpSession::Handle_Req_OPTIONS);
},
nullptr);
......
(this->*(it->second))(content_len);
//清空解析器节省内存
_parser.Clear();
//返回content长度
return content_len;
}
由于是在浏览器输入url,那么就是get方式去请求,也就是调用的Handle_Req_GET_l 函数,在Handle_Req_GET_l中有一个
emitHttpEvent函数,这里就是发出kBroadcastHttpRequest事件的地方
void HttpSession::Handle_Req_GET(ssize_t &content_len) {
Handle_Req_GET_l(content_len, true);
}
void HttpSession::Handle_Req_GET_l(ssize_t &content_len, bool sendBody) {
if (emitHttpEvent(false)) {
//拦截http api事件
return;
}
三:处理kBroadcastHttpRequest
在对kBroadcastHttpRequest进行监听的时候,会根据url去s_map_api中找到对应的处理函数,而s_map_api数据的添加就在如下函数,所以在调用api_regist时就将webapi的处理函数也一并传入了
void api_regist(const string &api_path, const function<void(API_ARGS_MAP_ASYNC)> &func) {
s_map_api.emplace(api_path, toApi(func));
}
PS:对于事件的通知不了解的可以参考这个广播、通知中心