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

原文: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的目录结构:

 

 
  1. lanhu:http_msg_server lanhu$ tree

  2. .

  3. ├── AttachData.cpp

  4. ├── AttachData.h

  5. ├── CMakeLists.txt

  6. ├── DBServConn.cpp

  7. ├── DBServConn.h

  8. ├── HttpConn.cpp

  9. ├── HttpConn.h

  10. ├── HttpPdu.cpp

  11. ├── HttpPdu.h

  12. ├── HttpQuery.cpp

  13. ├── HttpQuery.h

  14. ├── RouteServConn.cpp

  15. ├── RouteServConn.h

  16. ├── http_msg_server.cpp

  17. ├── httpmsgserver.conf

  18. └── 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函数中修改:

 

 
  1. if (m_HttpParser.IsReadAll()) {

  2. string url = m_HttpParser.GetUrl();

  3. if (strncmp(url.c_str(), "/query/", 7) == 0) {

  4. string content = m_HttpParser.GetBodyContent();

  5. CHttpQuery* pQueryInstance = CHttpQuery::GetInstance();

  6. pQueryInstance->DispatchQuery(url, content, this);

  7. } else {

  8. log("url unknown, url=%s ", url.c_str());

  9. Close();

  10. }

  11. }

 

为如下形式:

 

 
  1. if (m_HttpParser.IsReadAll()) {

  2. string url = m_HttpParser.GetUrl();

  3. if (strncmp(url.c_str(), "/query/", 7) == 0) {

  4. string content = m_HttpParser.GetBodyContent();

  5. CHttpQuery* pQueryInstance = CHttpQuery::GetInstance();

  6. pQueryInstance->DispatchQuery(url, content, this);

  7. } else if(strncmp(url.c_str(), "/api/", 5)) {

  8. string content = m_HttpParser.GetBodyContent();

  9. CHttpQuery::GetInstance()->DispatchQuery(url, content, this);

  10. }

  11.  
  12. else {

  13. log("url unknown, url=%s ", url.c_str());

  14. Close();

  15. }

  16.  
  17. }

 

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

 

 
  1. void CHttpQuery::DispatchQuery(std::string& url, std::string& post_data, CHttpConn* pHttpConn)

  2. {

  3. ++g_total_query;

  4.  
  5. log("DispatchQuery, url=%s, content=%s ", url.c_str(), post_data.c_str());

  6.  
  7. Json::Reader reader;

  8. Json::Value value;

  9. Json::Value root;

  10.  
  11. if ( !reader.parse(post_data, value) ) {

  12. log("json parse failed, post_data=%s ", post_data.c_str());

  13. pHttpConn->Close();

  14. return;

  15. }

  16.  
  17. string strErrorMsg;

  18. string strAppKey;

  19. HTTP_ERROR_CODE nRet = HTTP_ERROR_SUCCESS;

  20. try

  21. {

  22. string strInterface(url.c_str() + strlen("/query/"));

  23. strAppKey = value["app_key"].asString();

  24. string strIp = pHttpConn->GetPeerIP();

  25. uint32_t nUserId = value["req_user_id"].asUInt();

  26. nRet = _CheckAuth(strAppKey, nUserId, strInterface, strIp);

  27. }

  28. catch ( std::runtime_error msg)

  29. {

  30. nRet = HTTP_ERROR_INTERFACE;

  31. }

  32.  
  33. if(HTTP_ERROR_SUCCESS != nRet)

  34. {

  35. if(nRet < HTTP_ERROR_MAX)

  36. {

  37. root["error_code"] = nRet;

  38. root["error_msg"] = HTTP_ERROR_MSG[nRet];

  39. }

  40. else

  41. {

  42. root["error_code"] = -1;

  43. root["error_msg"] = "未知错误";

  44. }

  45. string strResponse = root.toStyledString();

  46. pHttpConn->Send((void*)strResponse.c_str(), strResponse.length());

  47. return;

  48. }

  49.  
  50. // process post request with post content

  51. if (strcmp(url.c_str(), "/query/CreateGroup") == 0)

  52. {

  53. _QueryCreateGroup(strAppKey, value, pHttpConn);

  54. }

  55. else if (strcmp(url.c_str(), "/query/ChangeMembers") == 0)

  56. {

  57. _QueryChangeMember(strAppKey, value, pHttpConn);

  58. }

  59. else {

  60. log("url not support ");

  61. pHttpConn->Close();

  62. return;

  63. }

  64. }

 

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

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

 

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

 

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

 

 
  1. if (strcmp(url.c_str(), "/query/CreateGroup") == 0)

  2. {

  3. _QueryCreateGroup(strAppKey, value, pHttpConn);

  4. }

  5. else if (strcmp(url.c_str(), "/query/ChangeMembers") == 0)

  6. {

  7. _QueryChangeMember(strAppKey, value, pHttpConn);

  8. }

  9. else if (strcmp(url.c_str(), "/api/sendmsg") == 0) {

  10. uint32_t nFromId = value["req_id"].asUInt();

  11. uint32_t nToId = value["to_id"].asUInt();

  12. string strMsg = value["msg_content"].asString();

  13. _ApiSendMsg(nFromId, nToId, strMsg);

  14. }

  15. else {

  16. log("url not support ");

  17. pHttpConn->Close();

  18. return;

  19. }

 

接着我们去实现_ApiSendMsg函数:

 

 
  1. void CHttpQuery::_ApiSendMsg(uint32_t nFromId, uint32_t nToId, const string &strMsg, CHttpConn* pHttpConn)

  2. {

  3. HTTP::CDBServConn* pDBConn = HTTP::get_db_serv_conn();

  4. if(!pDBConn) {

  5. log("no db server");

  6. pHttpConn->Close();

  7. }

  8.  
  9. IM::Message::IMMsgData msg;

  10. msg.set_from_user_id(nFromId);

  11. msg.set_msg_id(0);

  12. msg.set_to_session_id(nToId);

  13. msg.set_create_time(time(NULL));

  14. msg.set_msg_type(::IM::BaseDefine::MSG_TYPE_SINGLE_TEXT);

  15. msg.set_msg_data(strMsg);

  16. CImPdu pdu;

  17. pdu.SetPBMsg(&msg);

  18. pdu.SetServiceId(IM::BaseDefine::SID_MSG);

  19. pdu.SetCommandId(IM::BaseDefine::CID_MSG_DATA);

  20. pDBConn->SendPdu(&pdu);

  21. pHttpConn->Close();

  22. }

 

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

 

void _HandleSendMsg(CImPdu* pPdu);

 

去DBServConn.cpp中实现:

 

 
  1. void CDBServConn::_HandleSendMsg(CImPdu *pPdu)

  2. {

  3. IM::Message::IMMsgData msg;

  4. CHECK_PB_PARSE_MSG(msg.ParseFromArray(pPdu->GetBodyData(), pPdu->GetBodyLength()));

  5.  
  6. uint32_t from_user_id = msg.from_user_id();

  7. uint32_t to_user_id = msg.to_session_id();

  8. uint32_t msg_id = msg.msg_id();

  9. if (msg_id == 0) {

  10. log("_HandleSendMsg, write db failed, %u->%u.", from_user_id, to_user_id);

  11. return;

  12. }

  13.  
  14. log("_HandleSendMsg, from_user_id=%u, to_user_id=%u, msg_id=%u", from_user_id, to_user_id, msg_id);

  15.  
  16. CRouteServConn* pRouteConn = get_route_serv_conn();

  17. if (pRouteConn) {

  18. pRouteConn->SendPdu(pPdu);

  19. }

  20. }

 

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

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值