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

相关文章

python panda库从基础到高级操作分析

《pythonpanda库从基础到高级操作分析》本文介绍了Pandas库的核心功能,包括处理结构化数据的Series和DataFrame数据结构,数据读取、清洗、分组聚合、合并、时间序列分析及大数据... 目录1. Pandas 概述2. 基本操作:数据读取与查看3. 索引操作:精准定位数据4. Group

MySQL中EXISTS与IN用法使用与对比分析

《MySQL中EXISTS与IN用法使用与对比分析》在MySQL中,EXISTS和IN都用于子查询中根据另一个查询的结果来过滤主查询的记录,本文将基于工作原理、效率和应用场景进行全面对比... 目录一、基本用法详解1. IN 运算符2. EXISTS 运算符二、EXISTS 与 IN 的选择策略三、性能对比

SQL Server跟踪自动统计信息更新实战指南

《SQLServer跟踪自动统计信息更新实战指南》本文详解SQLServer自动统计信息更新的跟踪方法,推荐使用扩展事件实时捕获更新操作及详细信息,同时结合系统视图快速检查统计信息状态,重点强调修... 目录SQL Server 如何跟踪自动统计信息更新:深入解析与实战指南 核心跟踪方法1️⃣ 利用系统目录

MySQL 内存使用率常用分析语句

《MySQL内存使用率常用分析语句》用户整理了MySQL内存占用过高的分析方法,涵盖操作系统层确认及数据库层bufferpool、内存模块差值、线程状态、performance_schema性能数据... 目录一、 OS层二、 DB层1. 全局情况2. 内存占js用详情最近连续遇到mysql内存占用过高导致

深度解析Nginx日志分析与499状态码问题解决

《深度解析Nginx日志分析与499状态码问题解决》在Web服务器运维和性能优化过程中,Nginx日志是排查问题的重要依据,本文将围绕Nginx日志分析、499状态码的成因、排查方法及解决方案展开讨论... 目录前言1. Nginx日志基础1.1 Nginx日志存放位置1.2 Nginx日志格式2. 499

SpringBoot监控API请求耗时的6中解决解决方案

《SpringBoot监控API请求耗时的6中解决解决方案》本文介绍SpringBoot中记录API请求耗时的6种方案,包括手动埋点、AOP切面、拦截器、Filter、事件监听、Micrometer+... 目录1. 简介2.实战案例2.1 手动记录2.2 自定义AOP记录2.3 拦截器技术2.4 使用Fi

SQL Server 中的 WITH (NOLOCK) 示例详解

《SQLServer中的WITH(NOLOCK)示例详解》SQLServer中的WITH(NOLOCK)是一种表提示,等同于READUNCOMMITTED隔离级别,允许查询在不获取共享锁的情... 目录SQL Server 中的 WITH (NOLOCK) 详解一、WITH (NOLOCK) 的本质二、工作

SQL Server安装时候没有中文选项的解决方法

《SQLServer安装时候没有中文选项的解决方法》用户安装SQLServer时界面全英文,无中文选项,通过修改安装设置中的国家或地区为中文中国,重启安装程序后界面恢复中文,解决了问题,对SQLSe... 你是不是在安装SQL Server时候发现安装界面和别人不同,并且无论如何都没有中文选项?这个问题也

Olingo分析和实践之EDM 辅助序列化器详解(最佳实践)

《Olingo分析和实践之EDM辅助序列化器详解(最佳实践)》EDM辅助序列化器是ApacheOlingoOData框架中无需完整EDM模型的智能序列化工具,通过运行时类型推断实现灵活数据转换,适用... 目录概念与定义什么是 EDM 辅助序列化器?核心概念设计目标核心特点1. EDM 信息可选2. 智能类

Olingo分析和实践之OData框架核心组件初始化(关键步骤)

《Olingo分析和实践之OData框架核心组件初始化(关键步骤)》ODataSpringBootService通过初始化OData实例和服务元数据,构建框架核心能力与数据模型结构,实现序列化、URI... 目录概述第一步:OData实例创建1.1 OData.newInstance() 详细分析1.1.1