TeamTalk源码分析之http_msg_server对外提供API

来源:互联网 发布:手机淘宝卖话费充值 编辑:程序博客网 时间:2024/05/22 15:54

原文:www.bluefoxah.org/teamtalk/provide_api.html


1、如何提供一个接口

作为一个完整的平台,对外提供API是必不可少的,TT第二版跟第一版一个比较明显的变化,就是相比第一版多了一个http_msg_server这个模块,虽然这个模块暂时没有提供太多的功能,但是却也提供了一个参考。不过还是有很多朋友在群里咨询询问如果利用http_msg_server。今天就以提供一个发送消息给某个用户的接口为例子讲解利用http_msg_server。

这次相对于上一篇博客,会涉及到route_server,需要对TT整个架构有个比较明确的了解。

2、TT架构

Android/iOS/PC:各种客户端。


login_server:主要负责负载均衡的作用,当客户端来请求的时候,login_server会可以分配一个负载最小的msg_server给客户端。
msg_server:TT的主要服务端,负责维护各个客户端的链接,消息转发等功能。
route_server:负责消息路由的功能,当msg_server发现某个用户不在本服务器内,而又有消息需要发给他,就会将消息转发给route_server,route_server会将消息发给相应的msg_server,由此可知,route_server也维护了一定的用户状态。
db_proxy_server:在TT中负责了主要的业务逻辑,主要与存储层打交道。
msfs:小文件存储,负责存储聊天过程中的图片,语音信息。
http_msg_server:主要对外提供接口功能(平台与web之间)。


web:简单的管理功能。

3、前奏

在讲解如何增加消息API之前,我们先做好如下约束:
1、通过http_msg_server发送消息的url如下:http://ip:port/api/sendmsg
2、数据以post形式提交。
3、所有数据以json格式提交。{"appKey":"1234556","from_id":1,"to_id":2,"msg_content":"Hello World!"}
其中appKey是之前用来校验调用api权限使用的,这里大家根据自己的需要去处理。

我们首先观察下http_msg_server的目录结构:

lanhu:http_msg_server lanhu$ tree.├── AttachData.cpp├── AttachData.h├── CMakeLists.txt├── DBServConn.cpp├── DBServConn.h├── HttpConn.cpp├── HttpConn.h├── HttpPdu.cpp├── HttpPdu.h├── HttpQuery.cpp├── HttpQuery.h├── RouteServConn.cpp├── RouteServConn.h├── http_msg_server.cpp├── httpmsgserver.conf└── log4cxx.properties

我们来讲解各个文件的功能:
AttachData:老文件了,对需要放入协议中attach_data封装。
CMakeLists.txt:cmake文件
DBServConn:负责与db_proxy_server的链接。
HttpConn:负责解析外部传入的调用请求。
HttpPdu:主要是解析post数据类。
HttpQuery:主要解析业务逻辑,这里凡是http://ip:port/query/xxxx的请求都是这里处理的哦。
RouteServer:负责与route_server的链接。
http_msg_server:main函数入口,负责启动各种链接,启动监听等功能。
httpmsgserver.conf:配置文件
log4cxx.properties:log的配置文件。

4、实践

由上一节介绍我们大致了解了http_msg_server各个文件的作用,之前我们提到了HttpQuery主要负责/query/xxxx的请求,这里为了简便起见,我们/api/xxx的请求也由它来负责处理(大家完全可以模仿HttpQuery写出一个HttpApi这样的东西出来)。

由于时间关系,下面的我都尽量简单去处理,也没有经过测试,但是大致思路一定是对的。

首先我们在HttpConn.cpp的OnRead函数中修改:

if (m_HttpParser.IsReadAll()) {    string url =  m_HttpParser.GetUrl();    if (strncmp(url.c_str(), "/query/", 7) == 0) {        string content = m_HttpParser.GetBodyContent();        CHttpQuery* pQueryInstance = CHttpQuery::GetInstance();        pQueryInstance->DispatchQuery(url, content, this);    } else {        log("url unknown, url=%s ", url.c_str());        Close();    }}

为如下形式:

if (m_HttpParser.IsReadAll()) {    string url =  m_HttpParser.GetUrl();    if (strncmp(url.c_str(), "/query/", 7) == 0) {        string content = m_HttpParser.GetBodyContent();        CHttpQuery* pQueryInstance = CHttpQuery::GetInstance();        pQueryInstance->DispatchQuery(url, content, this);    } else if(strncmp(url.c_str(), "/api/", 5)) {        string content = m_HttpParser.GetBodyContent();        CHttpQuery::GetInstance()->DispatchQuery(url, content, this);    }        else {        log("url unknown, url=%s ", url.c_str());        Close();    }    }

这里代码很简单,我就不具体解释了,下面我们去HttpQuery.cpp查看DispatchQuery函数:

void CHttpQuery::DispatchQuery(std::string& url, std::string& post_data, CHttpConn* pHttpConn){    ++g_total_query;    log("DispatchQuery, url=%s, content=%s ", url.c_str(), post_data.c_str());    Json::Reader reader;    Json::Value value;    Json::Value root;    if ( !reader.parse(post_data, value) ) {        log("json parse failed, post_data=%s ", post_data.c_str());        pHttpConn->Close();        return;    }    string strErrorMsg;    string strAppKey;    HTTP_ERROR_CODE nRet = HTTP_ERROR_SUCCESS;    try    {        string strInterface(url.c_str() + strlen("/query/"));        strAppKey = value["app_key"].asString();        string strIp = pHttpConn->GetPeerIP();        uint32_t nUserId = value["req_user_id"].asUInt();        nRet = _CheckAuth(strAppKey, nUserId, strInterface, strIp);    }    catch ( std::runtime_error msg)    {        nRet = HTTP_ERROR_INTERFACE;    }    if(HTTP_ERROR_SUCCESS != nRet)    {        if(nRet < HTTP_ERROR_MAX)        {            root["error_code"] = nRet;            root["error_msg"] = HTTP_ERROR_MSG[nRet];        }        else        {            root["error_code"] = -1;            root["error_msg"] = "未知错误";        }        string strResponse = root.toStyledString();        pHttpConn->Send((void*)strResponse.c_str(), strResponse.length());        return;    }    // process post request with post content    if (strcmp(url.c_str(), "/query/CreateGroup") == 0)    {        _QueryCreateGroup(strAppKey, value, pHttpConn);    }    else if (strcmp(url.c_str(), "/query/ChangeMembers") == 0)    {        _QueryChangeMember(strAppKey, value, pHttpConn);    }    else {        log("url not support ");        pHttpConn->Close();        return;    }}

这个函数开始的主要功能就是解析post的数据,然后更具url调用不同的处理逻辑函数。

我们在这个类中增加一个处理发送消息的函数:

static void _ApiSendMsg(uint32_t nFromId, uint32_t nToId, const string& strMsg);

接着我们修改DispatchQuery,增加一个调用发送消息的。

if (strcmp(url.c_str(), "/query/CreateGroup") == 0){    _QueryCreateGroup(strAppKey, value, pHttpConn);}else if (strcmp(url.c_str(), "/query/ChangeMembers") == 0){    _QueryChangeMember(strAppKey, value, pHttpConn);}else if (strcmp(url.c_str(), "/api/sendmsg") == 0) {    uint32_t nFromId = value["req_id"].asUInt();    uint32_t nToId = value["to_id"].asUInt();    string strMsg = value["msg_content"].asString();    _ApiSendMsg(nFromId, nToId, strMsg);}else {    log("url not support ");    pHttpConn->Close();    return;}

接着我们去实现_ApiSendMsg函数:

void CHttpQuery::_ApiSendMsg(uint32_t nFromId, uint32_t nToId, const string &strMsg, CHttpConn* pHttpConn){    HTTP::CDBServConn* pDBConn = HTTP::get_db_serv_conn();    if(!pDBConn) {        log("no db server");        pHttpConn->Close();    }        IM::Message::IMMsgData msg;    msg.set_from_user_id(nFromId);    msg.set_msg_id(0);    msg.set_to_session_id(nToId);    msg.set_create_time(time(NULL));    msg.set_msg_type(::IM::BaseDefine::MSG_TYPE_SINGLE_TEXT);    msg.set_msg_data(strMsg);    CImPdu pdu;    pdu.SetPBMsg(&msg);    pdu.SetServiceId(IM::BaseDefine::SID_MSG);    pdu.SetCommandId(IM::BaseDefine::CID_MSG_DATA);    pDBConn->SendPdu(&pdu);    pHttpConn->Close();}

我们已经将消息发送到db_proxy_server中去了,db_proxy_server存储完成后会返回,我们需要在DBServConn中增加一个处理。

void _HandleSendMsg(CImPdu* pPdu);

去DBServConn.cpp中实现:

void CDBServConn::_HandleSendMsg(CImPdu *pPdu){    IM::Message::IMMsgData msg;    CHECK_PB_PARSE_MSG(msg.ParseFromArray(pPdu->GetBodyData(), pPdu->GetBodyLength()));        uint32_t from_user_id = msg.from_user_id();    uint32_t to_user_id = msg.to_session_id();    uint32_t msg_id = msg.msg_id();    if (msg_id == 0) {        log("_HandleSendMsg, write db failed, %u->%u.", from_user_id, to_user_id);        return;    }        log("_HandleSendMsg, from_user_id=%u, to_user_id=%u, msg_id=%u", from_user_id, to_user_id, msg_id);        CRouteServConn* pRouteConn = get_route_serv_conn();    if (pRouteConn) {        pRouteConn->SendPdu(pPdu);    }}

当db_proxy_server存储完毕返回后,http_msg_server将消息发送到route_server即可完成消息的发送了。

由于时间关系,本次讲解未涉及到route_server的修改,但是基本原理与这些类似,大家可以仿照已有的功能去添加。如果有必要,下次再进行补充。


0 0
原创粉丝点击