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

文章目录


前言

Topic广播类似于我们b站的关注一样,当我们关注了了一个UP主,当这个UP主发了一个视频后,会将所有订阅他的用户都发送一个消息,表示他们订阅的UP已经发视频了,这就叫广播,将消息发送给订阅的多个用户


一、应用层:服务端广播管理

服务端的广播管理需要管理每个订阅者和服务端的连接,那么我们就需要将每个订阅者的信息描述起来


1-1 订阅者

cpp 复制代码
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存储所有的订阅者

cpp 复制代码
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 话题和订阅者管理

将订阅者和话题联系起来进行管理

cpp 复制代码
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来接收

cpp 复制代码
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() :接收广播服务端发送的报文,并用话题相关的回调函数处理


三、应用接口层:服务广播端

封装广播的服务端

cpp 复制代码
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():断开和一个客户端的连接


四、应用接口层:客户广播端

封装客户端的接口

cpp 复制代码
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():断开和服务端的连接

相关推荐
一条咸鱼_SaltyFish8 小时前
远程鉴权中心设计:HTTP 与 gRPC 的技术决策与实践
开发语言·网络·网络协议·程序人生·http·开源软件·个人开发
北京耐用通信9 小时前
耐达讯自动化Profibus总线光纤中继器:光伏逆变器通讯的“稳定纽带”
人工智能·物联网·网络协议·自动化·信息与通信
上海云盾安全满满12 小时前
选择高防IP时需要重点关注哪些因素
网络·网络协议·tcp/ip
智在碧得12 小时前
碧服打造DataOps全链路闭环,定义大数据工程化发布新标杆
大数据·网络·数据库
孟无岐12 小时前
【Laya】Byte 二进制数据处理
网络·typescript·游戏引擎·游戏程序·laya
负二代0.012 小时前
Linux下的网络管理
linux·网络
小快说网安13 小时前
AI 短剧平台的 “保命符”:高防 IP 如何抵御流量攻击与业务中断风险
人工智能·网络协议·tcp/ip
欧洵.13 小时前
深入理解TCP/IP协议栈:数据链路层核心知识点解析
网络
光影少年14 小时前
http和https区别、令牌、三次握手流程
网络协议·http·https
雨声不在14 小时前
udp穿透的方法V2
网络·网络协议·udp