ZLMediaKit 服务器源码解读---WebApi

当发生http请求时(也就是在浏览器输入url然后回车的操作),服务器接收到数据后会触发一个kBroadcastHttpRequest事件,而该事件在一开始就已经加入了监听,下面简单描述为:

  1. 服务器是何时监听的kBroadcastHttpRequest
  2. 如何触发的kBroadcastHttpRequest
  3. 如何处理的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:对于事件的通知不了解的可以参考这个广播、通知中心

猜你喜欢

转载自blog.csdn.net/dai1396734/article/details/123228043