前言
Topic广播类似于我们b站的关注一样,当我们关注了了一个UP主,当这个UP主发了一个视频后,会将所有订阅他的用户都发送一个消息,表示他们订阅的UP已经发视频了,这就叫广播,将消息发送给订阅的多个用户
一、应用层:服务端广播管理
服务端的广播管理需要管理每个订阅者和服务端的连接,那么我们就需要将每个订阅者的信息描述起来
1-1 订阅者
struct subscriber {
using ptr = std::shared_ptr < subscriber > ;
std::mutex _mutex;
BaseConnection::ptr _conn;
// std::vector<std::string>topics;
std::unordered_set < std::string > _topics;
subscriber(const BaseConnection::ptr & conn): _conn(conn) {}
void AppendTopic(const std::string & newtopic) {
std::unique_lock < std::mutex > lock(_mutex);
_topics.insert(newtopic);
}
void RemoveTopic(const std::string & nowtopic) {
std::unique_lock < std::mutex > lock(_mutex);
if (_topics.find(nowtopic) != _topics.end()) {
_topics.erase(nowtopic);
}
}
};
_topics:这个数组存储了这个订阅者都订阅了哪些话题
_conn:和客户端的连接
AppendTopic():增加订阅的话题
RemoveTopic():删除订阅的话题
1-2 话题
为整个广播创建话题,并且unordered_set存储所有的订阅者
struct topic {
using ptr = std::shared_ptr < topic > ;
std::mutex _mutex;
std::string _topic_name;
std::unordered_set < subscriber::ptr > _subscribers;
topic(const std::string & topic_name): _topic_name(topic_name) {}
std::unordered_set < subscriber::ptr > GetSubscribers() {
std::unique_lock < std::mutex > lock(_mutex);
return _subscribers;
}
void AppendSubscriber(const subscriber::ptr & newsubscriber) {
std::unique_lock < std::mutex > lock(_mutex);
_subscribers.insert(newsubscriber);
}
void RemoveSubscriber(const subscriber::ptr & nowsubscriber) {
std::unique_lock < std::mutex > lock(_mutex);
if (_subscribers.find(nowsubscriber) != _subscribers.end()) {
_subscribers.erase(nowsubscriber);
}
}
void PublishMessage(const BaseMessage::ptr & msg) {
std::unique_lock < std::mutex > lock(_mutex);
for (auto & it: _subscribers) {
it -> _conn -> send(msg);
}
}
};
_topic_name:话题名称
_subscribers:有哪些订阅者订阅了该话题
AppendSubscriber():添加该话题的订阅者
RemoveSubscriber():删除该话题的订阅者
PublishMessage():对每个订阅者都发送报文
1-3 话题和订阅者管理
将订阅者和话题联系起来进行管理
class TopicManager {
public: using ptr = std::shared_ptr < TopicManager > ;
//success 用于topic接收消息的接口
void Ontopicrequest(const BaseConnection::ptr & conn,
const TopicRequest::ptr & msg) {
bool ret = true;
TopicOptype topicoptype = msg -> optype();
switch (topicoptype) {
case TopicOptype::TOPIC_CREATE:
Topiccreate(conn, msg);
break;
case TopicOptype::TOPIC_CANCEL:
Topiccancel(conn, msg);
break;
case TopicOptype::TOPIC_REMOVE:
Topicremove(conn, msg);
break;
case TopicOptype::TOPIC_PUBLISH:
ret = Topicpublish(conn, msg);
break;
case TopicOptype::TOPIC_SUBSCRIBE:
ret = Topicsubscribe(conn, msg);
break;
default:
return Errorresponse(conn, msg, Rcode::RCODE_INVALID_OPTYPE);
break;
}
if (ret) {
Topicresponse(conn, msg);
} else {
Errorresponse(conn, msg, Rcode::RCODE_NOT_FOUND_TOPIC);
}
// Topicresponse(conn,msg);
}
//success 用于topic断开订阅者的接口
void Onshutdown(const BaseConnection::ptr & conn) {
std::vector < topic::ptr > topics;
subscriber::ptr optsubscriber;
{
std::unique_lock < std::mutex > lock(_mutex);
auto Itsubscribe = ManagerSubscribes.find(conn);
if (Itsubscribe == ManagerSubscribes.end()) {
return;
}
optsubscriber = Itsubscribe -> second;
ManagerSubscribes.erase(conn);
// optsubscriber=Itsubscribe->second;
for (auto & item: optsubscriber -> _topics) {
auto it = ManagerTopics.find(item);
if (it != ManagerTopics.end()) {
topics.push_back(it -> second);
}
}
}
for (auto & singletopic: topics) {
singletopic -> RemoveSubscriber(optsubscriber);
}
}
private:
//success 错误操作回复
void Errorresponse(const BaseConnection::ptr & conn,
const TopicRequest::ptr & msg,
const Rcode & rcode) {
TopicResponse::ptr rep = MessageFactory::create < TopicResponse > ();
rep -> setId(msg -> rid());
rep -> setMType(MType::RSP_TOPIC);
rep -> setRCode(rcode);
conn -> send(rep);
}
//success 正确操作回复
void Topicresponse(const BaseConnection::ptr & conn,
const TopicRequest::ptr & msg) {
TopicResponse::ptr rep = MessageFactory::create < TopicResponse > ();
rep -> setId(msg -> rid());
rep -> setMType(MType::RSP_TOPIC);
rep -> setRCode(Rcode::RCODE_OK);
conn -> send(rep);
}
//success 订阅创建
void Topiccreate(const BaseConnection::ptr & conn,
const TopicRequest::ptr & msg) {
std::unique_lock < std::mutex > lock(_mutex);
auto it = std::make_shared < topic > (msg -> topicKey());
ManagerTopics.insert(std::make_pair(msg -> topicKey(), it));
}
//success 订阅删除
void Topicremove(const BaseConnection::ptr & conn,
const TopicRequest::ptr & msg) {
topic::ptr opttopic;
// topic::ptr topic_it;
std::unordered_set < subscriber::ptr > subscriberArr;
{
std::unique_lock < std::mutex > lock(_mutex);
auto topic_it = ManagerTopics.find(msg -> topicKey());
if (topic_it == ManagerTopics.end()) {
return;
}
opttopic = topic_it -> second;
ManagerTopics.erase(topic_it);
}
subscriberArr = opttopic -> GetSubscribers();
for (auto & it: subscriberArr) {
it -> RemoveTopic(msg -> topicKey());
}
// ManagerTopics.erase(msg->topicKey());
}
//success 订阅者发起订阅
bool Topicsubscribe(const BaseConnection::ptr & conn,
const TopicRequest::ptr & msg) {
topic::ptr opttopic;
subscriber::ptr optsub;
{
std::unique_lock < std::mutex > lock(_mutex);
auto topic_it = ManagerTopics.find(msg -> topicKey());
if (topic_it == ManagerTopics.end()) {
// opttopic=std::make_shared<topic>();
// ManagerTopics.insert(std::make_pair(msg->topicKey(),opttopic));
return false;
}
opttopic = topic_it -> second;
auto sub_it = ManagerSubscribes.find(conn);
if (sub_it == ManagerSubscribes.end()) {
optsub = std::make_shared < subscriber > (conn);
ManagerSubscribes.insert(std::make_pair(conn, optsub));
} else {
optsub = sub_it -> second;
}
}
optsub -> AppendTopic(msg -> topicKey());
opttopic -> AppendSubscriber(optsub);
return true;
}
//success 订阅者取消订阅
void Topiccancel(const BaseConnection::ptr & conn,
const TopicRequest::ptr & msg) {
subscriber::ptr client;
topic::ptr Topic;
{
std::unique_lock < std::mutex > lock(_mutex);
auto it1 = ManagerTopics.find(msg -> topicKey());
if (it1 != ManagerTopics.end()) {
Topic = it1 -> second;
}
auto it2 = ManagerSubscribes.find(conn);
if (it2 != ManagerSubscribes.end()) {
client = it2 -> second;
}
}
if (client) {
client -> RemoveTopic(msg -> topicKey());
}
if (client && Topic) {
Topic -> RemoveSubscriber(client);
}
}
//success 订阅者发布订阅消息
bool Topicpublish(const BaseConnection::ptr & conn,
const TopicRequest::ptr & msg) {
topic::ptr PublishTopic;
{
std::unique_lock < std::mutex > lock(_mutex);
auto it = ManagerTopics.find(msg -> topicKey());
if (it != ManagerTopics.end()) {
PublishTopic = it -> second;
} else {
return false;
}
}
PublishTopic -> PublishMessage(msg);
return true;
}
private: std::mutex _mutex;
std::unordered_map < std::string,
topic::ptr > ManagerTopics;
std::unordered_map < BaseConnection::ptr,
subscriber::ptr > ManagerSubscribes;
};
ManagerTopics:位图,话题名和话题的结构
ManagerSubscribes:位图,订阅者的连接和订阅的结构体
Errorresponse():回复给订阅者一个错误的RSP_TOPIC报文
Topicresponse() :订阅者创建了一个新的话题,回复他一个创建成功的报文
Topiccreate():订阅的创建根据订阅者的TopicRequest提供的话题名创建相关话题
Topicremove():订阅的删除,根据订阅者的TopicRequest提供的话题名删除相关话题
Topicsubscribe():订阅话题,根据订阅者的TopicRequest提供的话题名订阅相关话题,没有话题则创建,没有记载该订阅者也创建
Topiccancel():取消订阅,根据订阅者的TopicRequest提供的话题名取消订阅相关话题
Topicpublish():在话题上发布消息,让其它订阅该话题的订阅者都能接收到该消息
二、应用层:客户端广播管理
比起服务端,客户但的管理要简单许多,客户端要处理的事情就是发送和接收,发送用Requestor发送,接收也是用Requestor来接收
class TopicManager {
public: using SubCallBack = std:: function < void(const std::string & ,
const std::string & ) > ;
using ptr = std::shared_ptr < TopicManager > ;
TopicManager(const Requestor::ptr & requestor): _requestor(requestor) {}
bool createTopic(const BaseConnection::ptr & conn,
const std::string & key) {
return commentRequest(conn, "", TopicOptype::TOPIC_CREATE, key);
}
bool deleteTopic(const BaseConnection::ptr & conn,
const std::string & key) {
// delSubscribe(key);
if (commentRequest(conn, "", TopicOptype::TOPIC_REMOVE, key)) {
delSubscribe(key);
return true;
}
return false;
}
bool cancelTopic(const BaseConnection::ptr & conn,
const std::string & key) {
// delSubscribe(key);
if (commentRequest(conn, "", TopicOptype::TOPIC_CANCEL, key)) {
delSubscribe(key);
return true;
}
return false;
}
bool takeTopic(const BaseConnection::ptr & conn,
const std::string & key,
const SubCallBack & cb) {
addSubscribe(key, cb);
bool ret = commentRequest(conn, "", TopicOptype::TOPIC_SUBSCRIBE, key);
if (ret == false) {
delSubscribe(key);
return false;
}
// addSubscribe(key,cb);
return true;
}
bool publish(const BaseConnection::ptr & conn,
const std::string & msg,
const std::string & key) {
return commentRequest(conn, msg, TopicOptype::TOPIC_PUBLISH, key);
}
void onPublish(const BaseConnection::ptr & conn,
const TopicRequest::ptr & req) {
if (req -> optype() != TopicOptype::TOPIC_PUBLISH) {
ELOG("操作类型错误");
return;
}
std::string topic_key = req -> topicKey();
std::string msg = req -> topicMsg();
SubCallBack scb = getSubscribe(topic_key);
if (scb == nullptr) {
ELOG("主题类型无法处理%s", topic_key.c_str());
return;
}
scb(topic_key, msg);
}
private: void addSubscribe(const std::string & key,
const SubCallBack & cb) {
std::unique_lock < std::mutex > lock(_mutex);
_topic_callbacks.insert(std::make_pair(key, cb));
}
void delSubscribe(const std::string & key) {
std::unique_lock < std::mutex > lock(_mutex);
_topic_callbacks.erase(key);
}
SubCallBack getSubscribe(const std::string & key) {
std::unique_lock < std::mutex > lock(_mutex);
auto it = _topic_callbacks.find(key);
if (it == _topic_callbacks.end()) {
return SubCallBack();
}
return it -> second;
}
bool commentRequest(const BaseConnection::ptr & conn,
const std::string & msg,
const TopicOptype & optype,
const std::string & key) {
TopicRequest::ptr toq = MessageFactory::create < TopicRequest > ();
toq -> setId(UUID::uuid());
toq -> setOptype(optype);
toq -> setTopicKey(key);
if (optype == TopicOptype::TOPIC_PUBLISH) {
toq -> setTopicMsg(msg);
}
BaseMessage::ptr top;
bool ret = _requestor -> send(conn, toq, top);
if (ret == false) {
ELOG("传递请求失败");
return false;
}
TopicResponse::ptr response = std::dynamic_pointer_cast < TopicResponse > (top);
if (response == nullptr) {
ELOG("向下转化类型失败");
return false;
}
if (response -> rcode() != Rcode::RCODE_OK) {
ELOG("请求处理失败:%s", errReason(response -> rcode()).c_str());
return false;
}
return true;
}
std::mutex _mutex;
std::unordered_map < std::string,
SubCallBack > _topic_callbacks;
Requestor::ptr _requestor;
};
_requestor:客户端只需要接收处理和发送消息,所以需要_requestor
_topic_callbacks:在这个位图中,有一个SubCallBack回调函数,此回调函数传入话题和话题返回的string,其它的用户自定义处理
addSubscribe():添加话题,并为话题提供需要的回调函数
delSubscribe():删除话题
getSubscribe:获取相关话题的回调函数
commentRequest():向广播服务端发送TOPIC_REQ
createTopic():向广播服务端创建话题
deleteTopic():删除广播服务端对应的话题
cancelTopic():取消订阅的话题
takeTopic():订阅广播服务端的话题
publish():向广播服务端的话题发送消息
onPublish() :接收广播服务端发送的报文,并用话题相关的回调函数处理
三、应用接口层:服务广播端
封装广播的服务端
class TopicServer {
public: using ptr = std::shared_ptr < TopicServer > ;
TopicServer(const int & port): _topic_manager(std::make_shared < TopicManager > ()),
_dispatcher(std::make_shared < Dispatcher > ()) {
auto it1 = std::bind( & TopicManager::Ontopicrequest, _topic_manager.get(), std::placeholders::_1, std::placeholders::_2);
_dispatcher -> registerHandler < TopicRequest > (MType::REQ_TOPIC, it1);
_server = Serverfactory::create(port);
auto it2 = std::bind( & Dispatcher::OnMessage, _dispatcher.get(), std::placeholders::_1, std::placeholders::_2);
_server -> setMessageCallback(it2);
auto it3 = std::bind( & TopicServer::onConnectionShutdown, this, std::placeholders::_1);
_server -> setCloseCallback(it3);
}
void Start() {
_server -> start();
}
private: void onConnectionShutdown(const BaseConnection::ptr & conn) {
_topic_manager -> Onshutdown(conn);
}
private: TopicManager::ptr _topic_manager;
Dispatcher::ptr _dispatcher;
BaseServer::ptr _server;
};
topic_manager:之前介绍过了,服务广播话题管理
这里的Dispatcher的设计是接收REQ_TOPIC的报文,并将报文送给TopicManager::Ontopicrequest
onConnectionShutdown():断开和一个客户端的连接
四、应用接口层:客户广播端
封装客户端的接口
class TopicClient {
public: TopicClient(const std::string & ip,
const int & port): _requestor(std::make_shared < Requestor > ()),
_dispatcher(std::make_shared < Dispatcher > ()),
_topics(std::make_shared < TopicManager > (_requestor)) {
auto it1 = std::bind( & Requestor::onResponse, _requestor.get(), std::placeholders::_1, std::placeholders::_2);
_dispatcher -> registerHandler < TopicResponse > (MType::RSP_TOPIC, it1);
auto it2 = std::bind( & TopicManager::onPublish, _topics.get(), std::placeholders::_1, std::placeholders::_2);
_dispatcher -> registerHandler < TopicRequest > (MType::REQ_TOPIC, it2);
auto it3 = std::bind( & Dispatcher::OnMessage, _dispatcher.get(), std::placeholders::_1, std::placeholders::_2);
_client = Clientfactory::create(ip, port);
_client -> setMessageCallback(it3);
_client -> connect();
}
bool Create(const std::string & key) {
return _topics -> createTopic(_client -> connection(), key);
}
bool Delete(const std::string & key) {
return _topics -> deleteTopic(_client -> connection(), key);
}
bool Cancel(const std::string & key) {
return _topics -> cancelTopic(_client -> connection(), key);
}
bool Subscribe(const std::string & key,
const TopicManager::SubCallBack & scb) {
return _topics -> takeTopic(_client -> connection(), key, scb);
}
bool Publish(const std::string & key,
const std::string & msg) {
return _topics -> publish(_client -> connection(), msg, key);
}
void Shutdown() {
_client -> shutdown();
}
private: Requestor::ptr _requestor;
Dispatcher::ptr _dispatcher;
BaseClient::ptr _client;
TopicManager::ptr _topics;
};
这里的客户端的Dispatcher的设计是RSP_TOPIC让Requestor::onResponse来接收,REQ_TOPIC让TopicManager::onPublish来接收
Create():创建一个话题
Delete():删除一个话题
Cancel():取消订阅一个话题
Subscribe():订阅一个话题
Publish():向一个话题广播消息
Shutdown():断开和服务端的连接

被折叠的 条评论
为什么被折叠?



