JsonRpc:手搓一个高性能Rpc服务(广播篇)

2025博客之星年度评选已开启 10w+人浏览 2.5k人参与


前言

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_TOPICRequestor::onResponse来接收,REQ_TOPICTopicManager::onPublish来接收

Create():创建一个话题

Delete():删除一个话题

Cancel():取消订阅一个话题

Subscribe():订阅一个话题

Publish():向一个话题广播消息

Shutdown():断开和服务端的连接

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值