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

2024-04-11 03:32

本文主要是介绍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的修改,但是基本原理与这些类似,大家可以仿照已有的功能去添加。如果有必要,下次再进行补充。

这篇关于TeamTalk源码分析之http_msg_server对外提供API的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/892964

相关文章

Java汇编源码如何查看环境搭建

《Java汇编源码如何查看环境搭建》:本文主要介绍如何在IntelliJIDEA开发环境中搭建字节码和汇编环境,以便更好地进行代码调优和JVM学习,首先,介绍了如何配置IntelliJIDEA以方... 目录一、简介二、在IDEA开发环境中搭建汇编环境2.1 在IDEA中搭建字节码查看环境2.1.1 搭建步

Redis主从复制实现原理分析

《Redis主从复制实现原理分析》Redis主从复制通过Sync和CommandPropagate阶段实现数据同步,2.8版本后引入Psync指令,根据复制偏移量进行全量或部分同步,优化了数据传输效率... 目录Redis主DodMIK从复制实现原理实现原理Psync: 2.8版本后总结Redis主从复制实

Node.js 中 http 模块的深度剖析与实战应用小结

《Node.js中http模块的深度剖析与实战应用小结》本文详细介绍了Node.js中的http模块,从创建HTTP服务器、处理请求与响应,到获取请求参数,每个环节都通过代码示例进行解析,旨在帮... 目录Node.js 中 http 模块的深度剖析与实战应用一、引言二、创建 HTTP 服务器:基石搭建(一

锐捷和腾达哪个好? 两个品牌路由器对比分析

《锐捷和腾达哪个好?两个品牌路由器对比分析》在选择路由器时,Tenda和锐捷都是备受关注的品牌,各自有独特的产品特点和市场定位,选择哪个品牌的路由器更合适,实际上取决于你的具体需求和使用场景,我们从... 在选购路由器时,锐捷和腾达都是市场上备受关注的品牌,但它们的定位和特点却有所不同。锐捷更偏向企业级和专

Python如何实现 HTTP echo 服务器

《Python如何实现HTTPecho服务器》本文介绍了如何使用Python实现一个简单的HTTPecho服务器,该服务器支持GET和POST请求,并返回JSON格式的响应,GET请求返回请求路... 一个用来做测试的简单的 HTTP echo 服务器。from http.server import HT

SQL Server数据库磁盘满了的解决办法

《SQLServer数据库磁盘满了的解决办法》系统再正常运行,我还在操作中,突然发现接口报错,后续所有接口都报错了,一查日志发现说是数据库磁盘满了,所以本文记录了SQLServer数据库磁盘满了的解... 目录问题解决方法删除数据库日志设置数据库日志大小问题今http://www.chinasem.cn天发

Spring中Bean有关NullPointerException异常的原因分析

《Spring中Bean有关NullPointerException异常的原因分析》在Spring中使用@Autowired注解注入的bean不能在静态上下文中访问,否则会导致NullPointerE... 目录Spring中Bean有关NullPointerException异常的原因问题描述解决方案总结

python中的与时间相关的模块应用场景分析

《python中的与时间相关的模块应用场景分析》本文介绍了Python中与时间相关的几个重要模块:`time`、`datetime`、`calendar`、`timeit`、`pytz`和`dateu... 目录1. time 模块2. datetime 模块3. calendar 模块4. timeit

python-nmap实现python利用nmap进行扫描分析

《python-nmap实现python利用nmap进行扫描分析》Nmap是一个非常用的网络/端口扫描工具,如果想将nmap集成进你的工具里,可以使用python-nmap这个python库,它提供了... 目录前言python-nmap的基本使用PortScanner扫描PortScannerAsync异

Oracle数据库执行计划的查看与分析技巧

《Oracle数据库执行计划的查看与分析技巧》在Oracle数据库中,执行计划能够帮助我们深入了解SQL语句在数据库内部的执行细节,进而优化查询性能、提升系统效率,执行计划是Oracle数据库优化器为... 目录一、什么是执行计划二、查看执行计划的方法(一)使用 EXPLAIN PLAN 命令(二)通过 S