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

相关文章

Nginx部署HTTP/3的实现步骤

《Nginx部署HTTP/3的实现步骤》本文介绍了在Nginx中部署HTTP/3的详细步骤,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学... 目录前提条件第一步:安装必要的依赖库第二步:获取并构建 BoringSSL第三步:获取 Nginx

PHP应用中处理限流和API节流的最佳实践

《PHP应用中处理限流和API节流的最佳实践》限流和API节流对于确保Web应用程序的可靠性、安全性和可扩展性至关重要,本文将详细介绍PHP应用中处理限流和API节流的最佳实践,下面就来和小编一起学习... 目录限流的重要性在 php 中实施限流的最佳实践使用集中式存储进行状态管理(如 Redis)采用滑动

HTTP 与 SpringBoot 参数提交与接收协议方式

《HTTP与SpringBoot参数提交与接收协议方式》HTTP参数提交方式包括URL查询、表单、JSON/XML、路径变量、头部、Cookie、GraphQL、WebSocket和SSE,依据... 目录HTTP 协议支持多种参数提交方式,主要取决于请求方法(Method)和内容类型(Content-Ty

Python 基于http.server模块实现简单http服务的代码举例

《Python基于http.server模块实现简单http服务的代码举例》Pythonhttp.server模块通过继承BaseHTTPRequestHandler处理HTTP请求,使用Threa... 目录测试环境代码实现相关介绍模块简介类及相关函数简介参考链接测试环境win11专业版python

使用Python的requests库来发送HTTP请求的操作指南

《使用Python的requests库来发送HTTP请求的操作指南》使用Python的requests库发送HTTP请求是非常简单和直观的,requests库提供了丰富的API,可以发送各种类型的HT... 目录前言1. 安装 requests 库2. 发送 GET 请求3. 发送 POST 请求4. 发送

SQL Server 查询数据库及数据文件大小的方法

《SQLServer查询数据库及数据文件大小的方法》文章介绍了查询数据库大小的SQL方法及存储过程实现,涵盖当前数据库、所有数据库的总大小及文件明细,本文结合实例代码给大家介绍的非常详细,感兴趣的... 目录1. 直接使用SQL1.1 查询当前数据库大小1.2 查询所有数据库的大小1.3 查询每个数据库的详

Spring Boot 整合 SSE(Server-Sent Events)实战案例(全网最全)

《SpringBoot整合SSE(Server-SentEvents)实战案例(全网最全)》本文通过实战案例讲解SpringBoot整合SSE技术,涵盖实现原理、代码配置、异常处理及前端交互,... 目录Spring Boot 整合 SSE(Server-Sent Events)1、简述SSE与其他技术的对

Go语言使用net/http构建一个RESTful API的示例代码

《Go语言使用net/http构建一个RESTfulAPI的示例代码》Go的标准库net/http提供了构建Web服务所需的强大功能,虽然众多第三方框架(如Gin、Echo)已经封装了很多功能,但... 目录引言一、什么是 RESTful API?二、实战目标:用户信息管理 API三、代码实现1. 用户数据

Python用Flask封装API及调用详解

《Python用Flask封装API及调用详解》本文介绍Flask的优势(轻量、灵活、易扩展),对比GET/POST表单/JSON请求方式,涵盖错误处理、开发建议及生产环境部署注意事项... 目录一、Flask的优势一、基础设置二、GET请求方式服务端代码客户端调用三、POST表单方式服务端代码客户端调用四

Python WSGI HTTP服务器Gunicorn使用详解

《PythonWSGIHTTP服务器Gunicorn使用详解》Gunicorn是Python的WSGI服务器,用于部署Flask/Django应用,性能高且稳定,支持多Worker类型与配置,可处... 目录一、什么是 Gunicorn?二、为什么需要Gunicorn?三、安装Gunicorn四、基本使用启