微服务即时通信系统(十二)---入口网关子服务

目录

功能设计

模块划分

业务接口/功能示意图

服务实现流程

网关HTTP接口

网关WebSocket接口

总体流程

服务代码实现

客户端长连接管理封装(connectionManage.hpp)

proto文件的编写

身份鉴权proto

事件通知proto

各项请求的URL的确定

服务端完成入口网关服务类(GatewayServer),进行其中的各项功能函数(请求处理函数)的实现

宏定义个功能请求的URL

GatewayServer类的成员变量和构造函数

WebSocketServer的三个回调函数实现

Speech::SpeechRecognize语音识别请求处理

File::SingleFileUp单文件上传/发送单文件请求处理

File::MultiFileUp多文件上传/发送多文件请求处理

File::SingleFileDonw单文件下载/获取单文件数据请求处理

File::MultiFileDonw多文件下载/获取多文件数据请求处理

User::UserNicknameRegistr用户名注册请求处理

User::UserNicknameLogin用户名登陆请求处理

User::GetPhoneVerificationCode获取手机验证码请求处理

User::UserPhoneRegister手机号注册请求处理

User::UserPhoneLogin手机号登陆请求处理

User::GetOneUserInfo获取用户信息请求处理

User::SetUserAvatar设置用户头像请求处理

User::SetUserNickname设置用户昵称请求处理

User::SetUserDescription设置用户签名请求处理

User::SetUserPhone设置用户手机号请求处理

Transmite::SendNewMessage发送新消息请求处理

Store::GetRecentNMessage获取最近N条消息请求处理

Store::GetTimeRangeMessage获取时间段消息请求处理

Store::GetMessageByKeyword消息关键字搜索请求处理

Friend::GetFriendList获取好友列表请求处理

Friend::UserSearch用户搜索请求处理

Friend::ApplyAddFriend申请添加好友请求处理

Friend::GetFriendApplyList获取好友申请列表请求处理

Friend::FriendApplyEventHandle处理好友申请事件请求处理

Friend::RemoveFriend删除好友请求处理

Friend::GetChatSessionList获取聊天会话列表请求处理

Friend::CreateGroupChatSession创建群聊会话请求处理

Friend::GetChatSessionMember获取会话成员请求处理

完成入口网关服务类的构造者(GatewayServerBuilder)

实例化服务对象,启动服务

工程系统构建配置文件(CMakeLists.txt)


本章节,主要对项目中入口网关子服务模块进行分析、开发与测试。

功能设计

入口网关子服务在设计中,最重要的两个功能:

1、接收客户端的所有请求,并解析,然后进行请求的子服务分发,得到响应后,再响应给客户端。(其中这些请求再细化为具体的各项请求,一共28个)。

2、对客户端进行事件通知:好友申请、申请处理、删除好友、聊天会话的创建、新消息。

基于上述两个功能,入口网关子服务包含两种通信:

1、HTTP通信:进行接收请求,业务处理。

2、WebSocket通信:进行事件通知。

模块划分

参数/配置文件解析模块基于gflags框架直接使用,进行参数/配置文件的解析。
日志模块基于spdlog封装的logger 直接进行日志输出。
服务发现与调用模块

基于etcd框架封装的服务发现与brpc框架封装的服务调用模块。

因为要分发处理所有请求,需要发现所有的子服务模块。

redis客户端模块

基于redis++封装的客户端进行内存数据库的操作。

根据用户管理子服务添加的登录会话信息,进行用户的身份鉴权和识别。

HTTP通信服务器模块基于httplib搭建HTTP服务器,接收HTTP请求,进行业务处理。
WebSocket服务器模块基于WebSocketpp,搭建WebSocket服务器,进行事件通知。
客户端长连接Connection管理模块通过用户ID 和 长连接句柄 建立映射关系,对长连接进行封装,便于后续管理。

业务接口/功能示意图

服务实现流程

网关HTTP接口

HTTP通信,分为首行、头部和正文三部分。

首行中的URL明确了业务请求目标(目的地)。

头部进行正文或连接描述。

正文中包含请求或响应的内容。

因此,之后实现的时候,首先就要确定各项URL。

其次,在HTTP请求正文中,将采用protobuf协议作为正文的序列化方式。不同的请求正文与后台的请求基本上吻合,因此,请求正文结构 将与 后台服务之间复用同一套接口。

网关WebSocket接口

WebSocket通信接口中,包含两个方面:

1、长连接的身份识别。

当用户登陆成功之后,向服务器发送WebSocket长连接请求,建立长连接。

长连接建立成功之后,向服务器发送身份鉴权请求,请求内容为protobuf结构数据,包含内容:请求ID、登录会话ID。

该请求不需要服务端进行回复,鉴权成功则长连接保持,否则断开长连接即可。

2、事件通知的内容。

因为事件通知在长连接中进行,因此,只需要定义出事件通知的消息结构即可。

总体流程

0、完成客户端长连接管理的封装。
1、编写服务所需的proto文件,利用protoc工具生成RPC服务器所需的.pb.h 和 .pb.cc 项目文件。
2、完成各项功能函数(请求)的URL的编写。
3、服务端完成入口网关服务类,进行其中的各项功能函数(请求处理函数)的实现。
4、实例化服务器对象,启动服务。

服务代码实现

客户端长连接管理封装(connectionManage.hpp)

在该封装中,提供了4个API:

1、新增长连接管理。

2、移除长连接。

3、获取长连接。

4、通过长连接获取对应用户(客户端)信息。

#pragma once
#include <websocketpp/config/asio_no_tls.hpp>
#include <websocketpp/server.hpp>
#include <iostream>
#include "logger.hpp"namespace yangz
{typedef websocketpp::server<websocketpp::config::asio> websocket_server; // 简单重定义websocket_server类型// 此时, 长连接的类型: websocket_server::connection_ptrclass Connection{public:// 长连接客户端 -> user_id, login_session_idstruct Client{Client(const std::string &uid, const std::string &lssid) : user_id(uid), login_session_id(lssid) {}std::string user_id;std::string login_session_id;};using ptr = std::shared_ptr<Connection>;Connection() {}~Connection() {}public:// 新增长连接管理void append(const websocket_server::connection_ptr &connection, const std::string &user_id, const std::string &login_session_id){// connection_ptr是weak_ptr, 需要get获取shared_ptrstd::unique_lock<std::mutex> lock(_mutex);_userid_connections.insert({user_id, connection});_connection_clients.insert({connection, Client(user_id, login_session_id)});LOG_DEBUG("新增长连接用户信息, connection_ptr: {}, user_id: {}, login_session_id: {}", (size_t)connection.get(), user_id, login_session_id);}// 移除长连接void remove(const websocket_server::connection_ptr &connection){std::unique_lock<std::mutex> lock(_mutex);auto it = _connection_clients.find(connection);if (it == _connection_clients.end()){LOG_ERROR("删除长连接时, 未找到该长连接, connection_ptr: {}", (size_t)connection.get());return;}_userid_connections.erase(it->second.user_id);_connection_clients.erase(it);LOG_DEBUG("移除长连接成功");}// 获取该长连接websocket_server::connection_ptr get_connection(const std::string &user_id){std::unique_lock<std::mutex> lock(_mutex);auto it = _userid_connections.find(user_id);if (it == _userid_connections.end()){LOG_ERROR("获取长连接时, 没有找到该长连接, user_id: {}", user_id);return websocket_server::connection_ptr();}return it->second;}// 判断该用户是否已经建立了长连接, 同时获取user_id 和 login_session_idbool client_is_connect(const websocket_server::connection_ptr &connection, std::string &user_id, std::string &login_session_id){std::unique_lock<std::mutex> lock(_mutex);auto it = _connection_clients.find(connection);if (it == _connection_clients.end()){LOG_ERROR("获取用户信息时, 该用户并没有建立长连接");return false;}user_id = it->second.user_id;login_session_id = it->second.login_session_id;return true;}private:std::mutex _mutex;std::unordered_map<std::string, websocket_server::connection_ptr> _userid_connections; // <user_id, 长连接>std::unordered_map<websocket_server::connection_ptr, Client> _connection_clients;      // <长连接, [user_id, login_session_id]>};
}

proto文件的编写

身份鉴权proto

入口网关子服务首先根据用户请求中携带信息,通过长连接获取到客户端信息,然后根据redis数据库中的信息进行审核。

因此,只需要一个简单的protobuf结构的信息:user_id, login_session_id即可:

syntax = "proto3";
package yangz;
option cc_generic_services = true;// 用户身份鉴权请求
message ClientIdentityAuthenticationReq
{string req_id = 1;string login_session_id = 2; // 登录会话ID, 网关根据该字段识别长连接客户端的身份
};

事件通知proto

在该proto文件中,要定义出来需要被通知的事件的protobuf结构数据:申请添加好友、处理申请结构、删除好友、创建群聊、发送新消息。

syntax = "proto3";
package yangz;
import "base.proto";option cc_generic_services = true;enum MessageNotifyType
{APPLY_ADD_FRIEND_NOTIFY = 0; // 申请添加好友FRIEND_APPLY_EVENT_HANDLE_NOTIFY = 1; // 处理好友申请REMOVE_FRIEND_NOTIFY = 2; // 删除好友CREATE_GROUP_CHAT_SESSION_NOTIFY = 3; // 创建群聊NEW_MESSAGE_NOTIFY = 4; // 新消息
}// 申请添加好友
message ApplyAddFriendNotify
{UserInfo user_info = 1; // 申请人信息
};// 好友申请请求处理
message FriendApplyEventHandleNotify
{bool agree = 1;UserInfo user_info = 2; // 处理人信息
};// 删除好友
message RemoveFriendNotify
{string user_id = 1; // 主动删除者的用户ID
};// 创建群聊
message CreateGroupChatSessionNotify
{ChatSessionInfo chat_session_info = 1; // 聊天会话信息
};// 新聊天消息
message NewMessageNotify
{MessageInfo message_info = 1; // 消息元信息
};message NotifyMessage
{optional string notify_event_id = 1; // 通知事件IDMessageNotifyType notify_type = 2;oneof notify_remarks // 事件备注信息{ApplyAddFriendNotify apply_add_friend = 3;FriendApplyEventHandleNotify friend_apply_event_handle = 4;RemoveFriendNotify remove_friend = 5;CreateGroupChatSessionNotify create_group_chat_session = 6;NewMessageNotify new_message = 7;}
};

各项请求的URL的确定

对于客户端的请求,该项目中一共提供28个请求功能,分别对应到各个子服务中,因此,需要提供28个URL,来对应子服务下的对应功能。

并且客户端的请求采用HTTP模式进行通信,通信时采用Post作为请求方法。

SERVICE HHTP PATH:// 语音识别子服务
/service/speech_recognition/speech_recognize        语音识别// 文件管理子服务
/service/file_manage/up_single_file                 发送/上传单个文件
/service/file_manage/up_multi_file                  发送/上传多个文件
/service/file_manage/donw_single_file               获取/下载单个文件
/service/file_manage/donw_single_file               获取/下载多个文件// 用户管理子服务
/service/user_manage/user_nickname_registr          用户名注册
/service/user_manage/user_nickname_login            用户名登陆
/service/user_manage/get_phone_verification_code    获取手机验证码
/service/user_manage/user_phone_registr             手机号注册
/service/user_manage/user_phone_login               手机号登陆
/service/user_manage/get_one_user_info              获取单个用户信息
/service/user_manage/set_user_avatar                修改头像
/service/user_manage/set_user_nickname              修改昵称
/service/user_manage/set_user_description           修改签名
/service/user_manage/set_user_phone                 修改手机号// 消息转发子服务
/service/message_transmite/send_new_message         发送新消息// 消息存储子服务
/service/message_store/get_recent_n_message         获取最近N条消息
/service/message_store/get_time_range_message       获取指定时间段消息
/service/message_store/get_message_by_keyword       消息关键字搜索// 好友管理子服务
/service/friend_manage/get_friend_list              获取好友列表
/service/friend_manage/user_search                  用户搜索
/service/friend_manage/apply_add_friend             申请添加好友
/service/friend_manage/get_friend_apply_list        获取好友申请列表
/service/friend_manage/friend_apply_event_handle    处理好友申请
/service/friend_manage/remove_friend                删除好友
/service/friend_manage/get_chat_session_list        获取聊天会话列表
/service/friend_manage/create_group_chat_session    创建群聊会话
/service/friend_manage/get_chat_session_member      获取聊天会话成员

服务端完成入口网关服务类(GatewayServer),进行其中的各项功能函数(请求处理函数)的实现

宏定义个功能请求的URL

// 各功能请求的URL
// speech_recognition_service
#define SPEECH_RECOGNITION "/service/speech_recognition/speech_recognize"// file_manage_service
#define UP_SINGLE_FILE "/service/file_manage/up_single_file"
#define UP_MULTI_FILE "/service/file_manage/up_multi_file"
#define DONW_SINGLE_FILE "/service/file_manage/donw_single_file"
#define DONW_MULTI_FILE "/service/file_manage/donw_multi_file"// user_manage_service
#define USER_NICKNAME_REGISTR "/service/user_manage/user_nickname_registr"
#define USER_NICKNAME_LOGIN "/service/user_manage/user_nickname_login"
#define GET_PHONE_VERIFICATION_CODE "/service/user_manage/get_phone_verification_code"
#define USER_PHONE_REGISTR "/service/user_manage/user_phone_registr"
#define USER_PHONE_LOGIN "/service/user_manage/user_phone_login"
#define GET_ONE_USER_INFO "/service/user_manage/get_one_user_info"
#define SET_USER_AVATAR "/service/user_manage/set_user_avatar"
#define SET_USER_NICKNAME "/service/user_manage/set_user_nickname"
#define SET_USER_DESCRIPTION "/service/user_manage/set_user_description"
#define SET_USER_PHONE "/service/user_manage/set_user_phone"// message_transmite_service
#define SEND_NEW_MESSAGE "/service/message_transmite/send_new_message"// message_store_service
#define GET_RECENT_N_MESSAGE "/service/message_store/get_recent_n_message"
#define GET_TIME_RANGE_MESSAGE "/service/message_store/get_time_range_message"
#define GET_MESSAGE_BY_KEYWORD "/service/message_store/get_message_by_keyword"// friend_manage_service
#define GET_FRIEND_LIST "/service/friend_manage/get_friend_list"
#define USER_SEARCH "/service/friend_manage/user_search"
#define APPLY_ADD_FRIEND "/service/friend_manage/apply_add_friend"
#define GET_FRIEND_APPLY_LIST "/service/friend_manage/get_friend_apply_list"
#define FRIEND_APPLY_EVENT_HANDLE "/service/friend_manage/friend_apply_event_handle"
#define REMOVE_FRIEND "/service/friend_manage/remove_friend"
#define GET_CHAT_SESSION_LIST "/service/friend_manage/get_chat_session_list"
#define CREATE_GROUP_CHAT_SESSION "/service/friend_manage/create_group_chat_session"
#define GET_CHAT_SESSION_MEMBER "/service/friend_manage/get_chat_session_member"

GatewayServer类的成员变量和构造函数

先定义各成员变量,然后进行列表初始化。

随后在构造函数中,对websocket服务器进行初始化:设置建立长连接/关闭长连接/新消息到来的回调处理函数。

再对http服务器进行初始化:设置客户端的Post请求处理方法,一共28个,之后再具体实现28个请求处理方法函数。

    class GatewayServer{public:using ptr = std::shared_ptr<GatewayServer>;GatewayServer(const std::shared_ptr<sw::redis::Redis> &redis_client,const std::string &speech_recognition_service_name,const std::string &file_manage_service_name,const std::string &user_manage_service_name,const std::string &message_transmite_service_name,const std::string &message_store_service_name,const std::string &friend_manage_service_name,const ServiceDiscovery::ptr &sd_client,const ServiceChannelManager::ptr &channel_manager,int websocket_port,int http_port): _redis_login_session_client(std::make_shared<LoginSessionManage>(redis_client)),_redis_login_status_client(std::make_shared<LoginStatusManage>(redis_client)),_speech_recognition_service_name(speech_recognition_service_name),_file_manage_service_name(file_manage_service_name),_user_manage_service_name(user_manage_service_name),_message_transmite_service_name(message_transmite_service_name),_message_store_service_name(message_store_service_name),_friend_manage_service_name(friend_manage_service_name),_sd_client(sd_client),_channel_manager(channel_manager),_connections(std::make_shared<Connection>()){// init websocket server_websocket_server.set_access_channels(websocketpp::log::alevel::none);                                                                     // 关闭日志输出_websocket_server.init_asio();                                                                                                             // 初始化websocket的异步IO_websocket_server.set_open_handler(std::bind(&GatewayServer::open_callback, this, std::placeholders::_1));                                 // 设置websocket握手成功后的回调函数_websocket_server.set_close_handler(std::bind(&GatewayServer::close_callback, this, std::placeholders::_1));                               // 设置websocket连接关闭的回调函数_websocket_server.set_message_handler(std::bind(&GatewayServer::newmessage_callback, this, std::placeholders::_1, std::placeholders::_2)); // 设置websocket新消息的回到处理函数_websocket_server.set_reuse_addr(true);                                                                                                    // 启动地址重用_websocket_server.listen(websocket_port);                                                                                                  // 开始监听_websocket_server.start_accept();                                                                                                          // 开始接收连接请求// init http server// 即设置各种客户端Post请求的回调处理函数, 即28各功能请求处理函数// Port(URL, (httplib::Server::Handler)call_back)// speech_recognition_service_http_server.Post(SPEECH_RECOGNITION, (httplib::Server::Handler)std::bind(&GatewayServer::SpeechRecognize, this, std::placeholders::_1, std::placeholders::_2));// file_manage_service_http_server.Post(UP_SINGLE_FILE, (httplib::Server::Handler)std::bind(&GatewayServer::SingleFileUp, this, std::placeholders::_1, std::placeholders::_2));_http_server.Post(UP_MULTI_FILE, (httplib::Server::Handler)std::bind(&GatewayServer::MultiFileUp, this, std::placeholders::_1, std::placeholders::_2));_http_server.Post(DONW_SINGLE_FILE, (httplib::Server::Handler)std::bind(&GatewayServer::SingleFileDown, this, std::placeholders::_1, std::placeholders::_2));_http_server.Post(DONW_MULTI_FILE, (httplib::Server::Handler)std::bind(&GatewayServer::MultiFileDown, this, std::placeholders::_1, std::placeholders::_2));// user_manage_service_http_server.Post(USER_NICKNAME_REGISTR, (httplib::Server::Handler)std::bind(&GatewayServer::UserNicknameRegistr, this, std::placeholders::_1, std::placeholders::_2));_http_server.Post(USER_NICKNAME_LOGIN, (httplib::Server::Handler)std::bind(&GatewayServer::UserNicknameLogin, this, std::placeholders::_1, std::placeholders::_2));_http_server.Post(GET_PHONE_VERIFICATION_CODE, (httplib::Server::Handler)std::bind(&GatewayServer::GetPhoneVerificationCode, this, std::placeholders::_1, std::placeholders::_2));_http_server.Post(USER_PHONE_REGISTR, (httplib::Server::Handler)std::bind(&GatewayServer::UserPhoneRegister, this, std::placeholders::_1, std::placeholders::_2));_http_server.Post(USER_PHONE_LOGIN, (httplib::Server::Handler)std::bind(&GatewayServer::UserPhoneLogin, this, std::placeholders::_1, std::placeholders::_2));_http_server.Post(GET_ONE_USER_INFO, (httplib::Server::Handler)std::bind(&GatewayServer::GetOneUserInfo, this, std::placeholders::_1, std::placeholders::_2));_http_server.Post(SET_USER_AVATAR, (httplib::Server::Handler)std::bind(&GatewayServer::SetUserAvatar, this, std::placeholders::_1, std::placeholders::_2));_http_server.Post(SET_USER_NICKNAME, (httplib::Server::Handler)std::bind(&GatewayServer::SetUserNickname, this, std::placeholders::_1, std::placeholders::_2));_http_server.Post(SET_USER_DESCRIPTION, (httplib::Server::Handler)std::bind(&GatewayServer::SetUserDescription, this, std::placeholders::_1, std::placeholders::_2));_http_server.Post(SET_USER_PHONE, (httplib::Server::Handler)std::bind(&GatewayServer::SetUserPhone, this, std::placeholders::_1, std::placeholders::_2));// message_transmite_service_http_server.Post(SEND_NEW_MESSAGE, (httplib::Server::Handler)std::bind(&GatewayServer::SendNewMessage, this, std::placeholders::_1, std::placeholders::_2));// message_store_service_http_server.Post(GET_RECENT_N_MESSAGE, (httplib::Server::Handler)std::bind(&GatewayServer::GetRecentNMessage, this, std::placeholders::_1, std::placeholders::_2));_http_server.Post(GET_TIME_RANGE_MESSAGE, (httplib::Server::Handler)std::bind(&GatewayServer::GetTimeRangeMessage, this, std::placeholders::_1, std::placeholders::_2));_http_server.Post(GET_MESSAGE_BY_KEYWORD, (httplib::Server::Handler)std::bind(&GatewayServer::GetMessageByKeyword, this, std::placeholders::_1, std::placeholders::_2));// friend_manage_service_http_server.Post(GET_FRIEND_LIST, (httplib::Server::Handler)std::bind(&GatewayServer::GetFriendList, this, std::placeholders::_1, std::placeholders::_2));_http_server.Post(USER_SEARCH, (httplib::Server::Handler)std::bind(&GatewayServer::UserSearch, this, std::placeholders::_1, std::placeholders::_2));_http_server.Post(APPLY_ADD_FRIEND, (httplib::Server::Handler)std::bind(&GatewayServer::ApplyAddFriend, this, std::placeholders::_1, std::placeholders::_2));_http_server.Post(GET_FRIEND_APPLY_LIST, (httplib::Server::Handler)std::bind(&GatewayServer::GetFriendApplyList, this, std::placeholders::_1, std::placeholders::_2));_http_server.Post(FRIEND_APPLY_EVENT_HANDLE, (httplib::Server::Handler)std::bind(&GatewayServer::FriendApplyEventHandle, this, std::placeholders::_1, std::placeholders::_2));_http_server.Post(REMOVE_FRIEND, (httplib::Server::Handler)std::bind(&GatewayServer::RemoveFriend, this, std::placeholders::_1, std::placeholders::_2));_http_server.Post(GET_CHAT_SESSION_LIST, (httplib::Server::Handler)std::bind(&GatewayServer::GetChatSessionList, this, std::placeholders::_1, std::placeholders::_2));_http_server.Post(CREATE_GROUP_CHAT_SESSION, (httplib::Server::Handler)std::bind(&GatewayServer::CreateGroupChatSession, this, std::placeholders::_1, std::placeholders::_2));_http_server.Post(GET_CHAT_SESSION_MEMBER, (httplib::Server::Handler)std::bind(&GatewayServer::GetChatSessionMember, this, std::placeholders::_1, std::placeholders::_2));// http 新启线程, 放置阻塞等待消息, 并分离_http_thread = std::thread([this, http_port](){ _http_server.listen("0.0.0.0", http_port); });_http_thread.detach();}// 服务器启动void start(){_websocket_server.run();}private:// redisLoginSessionManage::ptr _redis_login_session_client; // 登录会话操作句柄LoginStatusManage::ptr _redis_login_status_client;   // 登陆状态操作句柄// service namestd::string _speech_recognition_service_name;std::string _file_manage_service_name;std::string _user_manage_service_name;std::string _message_transmite_service_name;std::string _message_store_service_name;std::string _friend_manage_service_name;// service discoverServiceDiscovery::ptr _sd_client; // etcd.hpp中服务发现的操作句柄// channel managerServiceChannelManager::ptr _channel_manager; // 通信信道, 用于和子服务建立连接// connectionConnection::ptr _connections;// wevsocket & httpwebsocket_server _websocket_server;httplib::Server _http_server;std::thread _http_thread; // http新线程, 异步执行, 放置阻塞等候};

WebSocketServer的三个回调函数实现

握手成功回调函数:

简单打印日志提示。

        // websocket握手成功后的回调函数void open_callback(websocketpp::connection_hdl hdl){LOG_DEBUG("websocket长连接建立成功, connection: {}", (size_t)_websocket_server.get_con_from_hdl(hdl).get());}

连接关闭回调函数:

1、清理对应的connection缓存。

2、日志打印提示。

        // websocket连接关闭的回调函数void close_callback(websocketpp::connection_hdl hdl){// 1. 获取到connectionauto connection = _websocket_server.get_con_from_hdl(hdl);// 2. 通过connection获取到客户端信息std::string user_id, login_session_id;bool res = _connections->client_is_connect(connection, user_id, login_session_id);if (res == false){LOG_WARN("长连接断开时, 未找到客户端信息");return;}// 3. 移除相关管理信息_redis_login_session_client->remove(login_session_id);_redis_login_status_client->remove(user_id);_connections->remove(connection);LOG_DEBUG("长连接断开, 清理缓存完成");}

新消息到来回调函数:

1、获取其中的connection。

2、将消息正文反序列化。

3、提取login_session_id。

4、根据login_session_id在redis中进行身份查找。

5、添加长连接管理。

6、长连接进行保活。

        // 设置websocket新消息的回到处理函数void newmessage_callback(websocketpp::connection_hdl hdl, websocket_server::message_ptr message){// 1. 获取connectionauto connection = _websocket_server.get_con_from_hdl(hdl);// 2. 将消息中的ClientIdentityAuthenticationReq进行反序列化获取ClientIdentityAuthenticationReq req

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/78103.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

存储器层次结构:理解计算机记忆的金字塔

存储器层次结构&#xff1a;理解计算机记忆的金字塔 在计算机系统中&#xff0c;“速度”与“成本”常常处于对立面。为了在速度与成本之间取得平衡&#xff0c;计算机体系结构采用了一种名为“存储器层次结构&#xff08;Memory Hierarchy&#xff09;”的设计思想。本文将通…

HTTP 错误 500.19 - Internal Server Error

1.HTTP 错误 500.19 - Internal Server Error NetCore项目托管到IIS后&#xff0c;报错如下&#xff1a; 原因是因为IIS中没有安装AspNetCoreModuleV2导致的&#xff0c; 1.打开IIS 2.选中服务器根节点&#xff0c;找到模块&#xff0c;双击进入&#xff0c;确认模块中是否存…

【c++】【STL】stack详解

目录 stack类的作用什么是容器适配器stack的接口构造函数emptysizetoppushpopswap关系运算符重载 stack类的实现 stack类的作用 stack是stl库提供的一种容器适配器&#xff0c;也就是我们数据结构中学到的栈&#xff0c;是非常常用的数据结构&#xff0c;特点是遵循LIFO&#…

K8s学习与实践

一、Kubernetes 核心原理 1. Kubernetes 设计哲学 Kubernetes&#xff08;k8s&#xff09;是一个开源的容器编排平台&#xff0c;旨在自动化容器化应用的部署、扩展和管理。其核心设计围绕以下目标&#xff1a; 声明式配置&#xff1a;用户描述期望状态&#xff08;如 YAML …

Umi-OCR项目(1)

最近接触到了一个项目&#xff0c;我在想能不能做出点东西出来。 目标&#xff1a;识别一张带表格的图片&#xff0c;要求非表格内容和表格内容都要识别得很好&#xff0c;并且可视化输出为word文档。 下面是第一步的测试代码&#xff0c;测试是否能够调用ocr能力。 import re…

Mioty|采用报文分割(Telegram Splitting)以提高抗干扰能力的无线通信技术【无线通信小百科】

1、什么是Mioty 在物联网&#xff08;IoT&#xff09;快速发展的背景下&#xff0c;低功耗广域网&#xff08;LPWAN&#xff09;技术成为连接海量设备的关键。LPWAN具有低功耗、低成本、广覆盖和强抗干扰能力等特点&#xff0c;使其特别适用于大规模、远距离、低数据速率的IoT…

TCP三次握手、四次挥手+多线程并发处理

目录 一、三次握手建立连接 1.1 标记位 1.2 三次握手的过程 二、四次挥手断开连接 三、模拟服务器和客户端收发数据 四、多线程并发处理 五、TCP粘包问题 5.1 什么是TCP粘包&#xff1f; 5.2 TCP粘包会有什么问题&#xff1f; 5.3 TCP粘包的解决方法&#xff1f; 一、三…

使用HunyuanVideo搭建文本生视频大模型

1.摘要 HunyuanVideo是一个全新的开源视频基础模型&#xff0c;其视频生成性能堪比领先的闭源模型&#xff0c;甚至超越它们。我们采用了多项模型学习的关键技术&#xff0c;通过有效的模型架构和数据集扩展策略&#xff0c;我们成功训练了一个拥有超过 130 亿个参数的视频生成…

LabVIEW圆锥滚子视觉检测系统

基于LabVIEW平台的视觉检测系统提高圆锥滚子内组件的生产质量和效率。通过集成高分辨率摄像头和先进的图像处理算法&#xff0c;系统能够自动识别和分类产品缺陷&#xff0c;从而减少人工检查需求&#xff0c;提高检测的准确性和速度。 ​​ ​ 项目背景 随着制造业对产品质…

mac 基于Docker安装minio服务器

在 macOS 上基于 Docker 安装 MinIO 是一个高效且灵活的方案&#xff0c;尤其适合本地开发或测试环境。以下是详细的安装与配置步骤&#xff0c;结合了最佳实践和常见问题的解决方案&#xff1a; 一、安装 Docker Desktop 下载安装包 访问 Docker 官网&#xff0c;下载适用于 …

EchoMimicV2 部署记录

在这里插入代码片# 虚拟环境配置 pip install pip -U pip install torch2.5.1 torchvision0.20.1 torchaudio2.5.1 xformers0.0.28.post3 --index-url https://download.pytorch.org/whl/cu124 pip install torchao --index-url https://download.pytorch.org/whl/nightly/cu1…

数据升降级:医疗数据的“时空穿梭“系统工程(分析与架构篇)

一、核心挑战与量化分析 1. 版本演化困境的深度解析 (1) 格式断层的结构化危机 数据转换黑洞:某医疗信息平台(2021-2023)统计显示: 数据类型CDA R1→R2转换失败率R2→FHIR转换失败率关键失败点诊断记录28.4%19.7%ICD编码版本冲突(18.7%)用药记录15.2%12.3%剂量单位标准化…

个人开发免费好用

聊一聊 现在输入法非常多&#xff0c;有时候都不知道哪个更好用。 其实&#xff0c;只有多尝试&#xff0c;才能找到适合自己的。 今天给大家分享一款输入法&#xff0c;用起来比较顺手&#xff0c;大家可以试试。 软件介绍 BL输入法 这是一款绿色纯净&#xff0c;安全放心…

Windows查看和修改IP,IP互相ping通

Windows系统 查看IP地址 winr 输入cmd 打开终端使用 ipconfig 或 ipconfig -all 命令查看当前网络 IPV4地址 Windows系统 修改IP地址 自动获取IP&#xff08;DHCP&#xff09;&#xff1a; 打开 控制面板&#xff0c;点击 网络和Internet。点击 网络和共享中心。选择 更改适配…

【IP101】图像处理基础:从零开始学习颜色操作(RGB、灰度化、二值化、HSV变换)

&#x1f3a8; 颜色操作详解 &#x1f31f; 在图像处理的世界里&#xff0c;颜色操作就像是一个魔术师的基本功。今天&#xff0c;让我们一起来解锁这些有趣又实用的"魔法"吧&#xff01; &#x1f4da; 目录 通道替换 - RGB与BGR的"调包"游戏灰度化 - 让…

windows系统搭建自己的ftp服务器,保姆级教程(用户验证+无验证)

前言 最近在搭建环境时&#xff0c;我发现每次都需要在网上下载依赖包和软件&#xff0c;这不仅耗时&#xff0c;而且有时还会遇到网络不稳定的问题&#xff0c;导致下载速度慢或者中断&#xff0c;实在不太方便。于是&#xff0c;我产生了搭建一个FTP服务器的想法。通过搭建FT…

蓝桥杯 7. 晚会节目单

晚会节目单 原题目链接 题目描述 小明要组织一台晚会&#xff0c;总共准备了 n 个节目。然而晚会时间有限&#xff0c;他只能从中选择 m 个节目。 这 n 个节目是按照小明设想的顺序给定的&#xff0c;顺序不能改变。 小明发现观众对于晚会的喜欢程度与前几个节目的好看程度…

JavaScript如何实现类型判断?

判断一个数据的类型&#xff0c;常用的方法有以下几种&#xff1a; typeofinstanceofObject.prototype.toString.call(xxx) 下面来分别分析一下这三种方法各自的优缺点 typeof typeof的本意是用来判断一个数据的数据类型&#xff0c;所以返回的也是一个数据类型。但是会遇到下…

哈希表笔记(四)Redis对比Java总结

文章目录 一、基础结构对比数据结构定义Java HashMapRedis字典 主要区别与设计思路 二、关键操作API对比初始化Java HashMapRedis字典 添加元素Java HashMapRedis字典 查找元素Java HashMapRedis字典 删除元素Java HashMapRedis字典 扩容/重哈希操作Java HashMapRedis字典 三、…

docker拉取国内镜像

1. 场景 最近整了一个tencent云服务器&#xff0c;想要玩一下docker&#xff0c;结果发现拉不下来&#xff0c;镜像根本拉不下来。 2. 原因 1.云服务器无法访问外网&#xff1b; 2. 国内的很多公有镜像仓库都被封了&#xff1b; 3. 推荐 https://zhuanlan.zhihu.com/p/713…