JsonRpc:手搓一个高性能Rpc服务(底座篇)

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

项目链接:https://gitee.com/gan2729583308/json-rpc-framework

一、什么是JsonRpc?

Rpc的意思就是Remote Procedure Call,指的是一种远程过程调用,类似于我们使用AI的过程就是一种远程过程调用。

在这里插入图片描述

这里我们使用AI,但是问题的计算过程并不在我们的计算机当中,我们只是提出了问题就得到了结果,这就是一种Rpc,我们只是发送一个指令给远程的服务器,消耗远程服务器的资源来计算内容,然后将结果返回到我们自己的计算机就叫RpcJson指的是两台计算机之间通信需要一种协议才能通信,那么我们这里就采用Json的形式

二、JsonRpc第一步具体需要实现什么功能?

在这里插入图片描述

为了便于初步的理解,大概需要实现的内容就是如上图所示,但是我们在后面需要引入微服务功能所以不会这么简单,但是我们首先需要简历初步的理解,否则直接理解会感觉很抽象。

三、第一层抽象接口层

3-1 抽象层消息类

既然Rpc是要发送消息来解决任务,那我们就需要定义发送消息的格式,也就是将消息给封装起来

    class BaseMessage{
        public:
        using ptr=std::shared_ptr<BaseMessage>;
        virtual ~BaseMessage(){}
        virtual void setId(const std::string &id)
        {
            _rid=id;
        }
        virtual std::string rid()
        {
            return _rid;
        }
        virtual void setMType(MType mtype)
        {
            _mtype=mtype;
        }
        virtual MType mtype()
        {
            return _mtype;
        }
        virtual std::string serialize()=0;
        virtual bool unserialize(std::string& msg)=0;
        virtual bool check()=0;
        private:
        MType _mtype;
        std::string _rid;
    };
  • rid:我们需要保证不管什么类型的消息都需要有一个唯一标识符,不然会导致消息的处理错乱,毕竟是在多态主机中进行流通的消息,有时我们需要让一个消息有唯一的属性才方便管理

  • mtype:我们后续的主机消息交换会涉及到多种数据类型,每种数据类型有不同的处理方法,所以需要在每个消息中加上这个消息属于那种消息类型的参数,以确保后续处理对应类型的数据有相关的方法

  • serializeunserialize:序列化和反序列化方法,将消息以string字符串形式呈现,或者以Json格式呈现

  • check:则是检查消息中必要的参数是否存在

3-2 抽象层连接类

连接类和muduo::net::TcpConnectionPtr联系程度很高,因为所有的消息都需要序列化后通过muduo::net::TcpConnectionPtr这根线后才能抵达对方,

    class BaseConnection
    {
        public:
        using ptr=std::shared_ptr<BaseConnection>;
        virtual void send(const BaseMessage::ptr& msg)=0;
        virtual void shutdown()=0;
        virtual bool connected()=0;
    };

send:直接将BaseMessage通过muduo::net::TcpConnectionPtr来发送出去,当然BaseMessage需要先序列化成string才能发送出去

connected():用于判断这个BaseConnection是否已经建立了连接

connected():用于断开连接

3-3 抽象层缓冲区类

每个客户端或者服务端都需要一个接收消息数据的缓冲区,发送过来的数据需要先存入缓冲区中再取出来使用

    class BaseBuffer{
        public:
        using ptr=std::shared_ptr<BaseBuffer>;
        virtual size_t readableSize()=0;
        virtual int32_t peekInt32()=0;
        virtual void retrieveInt32()=0;
        virtual int32_t readInt32()=0;
        virtual std::string retrieveAsString(size_t len)=0;
    };

这五个接口全都是muduo::net::Buffer的接口,只是我们在这里封装了一层

  • readableSize:查看缓冲区中还有多少字节可读
  • peekInt32:返回前4个字节的整型值但不读取
  • retrieveInt32:丢弃前面四个字节的数据,配合peekInt32使用读取
  • readInt32:读取前面4个字节的数据并移动读取指针
  • retrieveAsString:读取前len个字节的数据,并移动指针,以string的形式返回

3-4 抽象层协议类

协议类是为了解决粘包半包问题的,在TCP通信中可能会存在数据包的丢失,而TCP是通过字节流的方式来读取数据的,如果有数据丢失会导致半包问题,如果不设置协议,那么所有的数据包黏在一起我们不知道怎么分开

    class BaseProtocol{
        public:
        using ptr=std::shared_ptr<BaseProtocol>;
        virtual bool canProcessed(const BaseBuffer::ptr &buf)=0;
        virtual bool onMessage(const BaseBuffer::ptr &buf,BaseMessage::ptr &msg)=0;
        virtual std::string serialize(const BaseMessage::ptr &msg)=0;
    };
  • canProcessed:检验这个缓冲区,第一个数据包的完整性
  • onMessage:执行完canProcessed,将第一个报文拆解出来转化成BaseMessage
  • serialize:将BaseMessage使用协议的规则序列化

3-5 抽象层服务类

协议,缓冲区,消息,连接都有了,那么最底层就差通信双方了,就是客户端和服务端

  • 服务端
    using ConnectionCallback=std::function<void(const BaseConnection::ptr&)>;
    using CloseCallback=std::function<void(const BaseConnection::ptr&)>;
    using MessageCallback=std::function<void(const BaseConnection::ptr&,BaseMessage::ptr&)>;
    class BaseServer
    {
        public:
        using ptr=std::shared_ptr<BaseServer>;
        virtual void setConnectionCallback(const ConnectionCallback& cb)
        {
            _cb_connection=cb;
        }
        virtual void setCloseCallback(const CloseCallback& cb)
        {
            _cb_close=cb;
        }
        virtual void setMessageCallback(const MessageCallback& cb)
        {
            _cb_message=cb;
        }
        virtual void start()=0;
        protected:
        ConnectionCallback _cb_connection;
        CloseCallback _cb_close;
        MessageCallback _cb_message;
    };
  • 客户端
    class BaseClient{
        public:
        using ptr=std::shared_ptr<BaseClient>;
        virtual void setConnectionCallback(const ConnectionCallback& cb)
        {
            _cb_connection=cb;
        }
        virtual void setCloseCallback(const CloseCallback& cb)
        {
            _cb_close=cb;
        }
        virtual void setMessageCallback(const MessageCallback& cb)
        {
            _cb_message=cb;
        }
        virtual void connect()=0;
        virtual void shutdown()=0;
        virtual bool send(const BaseMessage::ptr&)=0;
        virtual BaseConnection::ptr connection()=0;
        virtual bool connected()=0;
        protected:
        ConnectionCallback _cb_connection;
        CloseCallback _cb_close;
        MessageCallback _cb_message;
    };

四、第二层网络实现层

上述的抽象层属于地基,而网络层就是在这个地基上负责施工的工人

我们先来看工人施工用的工具有哪些

4-1 网络层工具

4-1-1 日志工具

在程序运行时,我们需要打印一些信息,所以我们需要一个日志的工具,而且为了方便调试,这个日志工具带有时间,并且需要分级

    #define LOG(level,format,...){\
        if(level>=LDEFAULT){\
            time_t t=time(NULL);\
            struct tm* lt=localtime(&t);\
            char time_tmp[32]={0};\
            strftime(time_tmp,31,"%m-%d %T",lt);\
            fprintf(stdout,"[%s][%s:%d]" format "\n",time_tmp,__FILE__,__LINE__,##__VA_ARGS__);\
        }\
    }
    #define DLOG(format,...) LOG(LDBG,format,##__VA_ARGS__);
    #define ILOG(format,...) LOG(LINF,format,##__VA_ARGS__);
    #define ELOG(format,...) LOG(LERR,format,##__VA_ARGS__);

4-1-2 Json序列化和反序列化工具

定义一个结构,序列化输入一个结构体对象,返回一个字符串,反序列化输入一个字符串返回一个结构体对象

    class JSON{
        public:

        //Json::Value----->String
        static bool serialize(const Json::Value& val,std::string& body)
        {
            
            std::stringstream ss;
            Json::StreamWriterBuilder swb;
            swb["emitUTF8"]=true;
            std::unique_ptr<Json::StreamWriter>sw(swb.newStreamWriter());
            int ret=sw->write(val,&ss);
            if(ret!=0)
            {
                ELOG("JSON UNSERIALIZE FAILED");
                return false;
            }
            body=ss.str();
            return true;
        }

        //String----->Json::value
        static bool unserialize(const std::string &body,Json::Value&val)
        {
            Json::CharReaderBuilder crb;
            std::string errs;
            std::unique_ptr<Json::CharReader>cr(crb.newCharReader());
            bool ret=cr->parse(body.c_str(),body.c_str()+body.size(),&val,&errs);
            if(ret==false)
            {
                ELOG("JSON UNSERIALIZE FAILED:%s",errs.c_str());
                return false;
            }
            return true;
        }

    };

4-1-3 UUID工具

因为每个报文都要有唯一的ID,所以需要一个工具为我们生成一个32位的ID,比如:ad2af789-d53c-154f-0000-000000000001

    class UUID{
        public:
        static std::string uuid()
        {
            std::stringstream ss;
            
            std::random_device rd;

            std::mt19937 generator(rd());

            std::uniform_int_distribution<int>distribution(0,255);
            for(int i=0;i<8;i++)
            {
                if(i==4||i==6)
                {
                    ss<<"-";
                }
                ss<<std::setw(2)<<std::setfill('0')<<std::hex<<distribution(generator);
            }
            ss<<"-";
            static std::atomic<size_t>seq(1);
            size_t cur=seq.fetch_add(1);
            for(int i=7;i>=0;i--)
            {
                if(i==5)
                {
                    ss<<"-";
                }
                ss<<std::setw(2)<<std::setfill('0')<<std::hex<<((cur>>(i*8))&0xFF);
            }
            return ss.str();
        }
    };

4-2 网络层缓冲区类

    class MuduoBuffer:public BaseBuffer
    {
        public:
        using ptr=std::shared_ptr<MuduoBuffer>;
        MuduoBuffer(muduo::net::Buffer*buf)
        {
            _buf=buf;
        }
        //查看缓冲区中还有多少字节可读
        virtual size_t readableSize()override{
            return _buf->readableBytes();
        }

        //返回前4个字节的整型值但不读取
        virtual int32_t peekInt32() override{
            return _buf->peekInt32();
        }

        //丢弃前面四个字节的数据,配合peekInt32使用读取
        virtual void retrieveInt32()override{
            return _buf->retrieveInt32();
        }

        //读取前面4个字节的数据并移动读取指针
        virtual int32_t readInt32()override{
            return _buf->readInt32();
        }

        //读取前len个字节的数据,并移动指针,以string的形式返回
        virtual std::string retrieveAsString(size_t len)override{
            return _buf->retrieveAsString(len);
        }
        private:
        muduo::net::Buffer *_buf;
    };

这里的缓冲区和我们上面所述的差不多,就是简单封装muduo::net::Buffer的接口

4-3 网络层连接类

    class MuduoConnection:public BaseConnection
    {
        public:
        using ptr=std::shared_ptr<MuduoConnection>;

        //TcpConnectionPtr是用来控制一个端口和另一个端口的连接,传入一个协议一个连接即可,每个客户端只要一个协议即可
        MuduoConnection(const muduo::net::TcpConnectionPtr &conn
            ,const BaseProtocol::ptr& protocol):_protocol(protocol),_conn(conn)
        {}
        virtual void send(const BaseMessage::ptr& msg) override
        {
            //将BaseMessage的对象转化成string
            std::string body=_protocol->serialize(msg);
            //用指针控制的连接发送数据
            _conn->send(body);
        }
        virtual void shutdown()override
        {
            //关闭连接
            _conn->shutdown();
        }
        virtual bool connected() override
        {
            //检查连接是否进行
            return _conn->connected();
        }
        private:
        BaseProtocol::ptr _protocol;//连接的协议
        muduo::net::TcpConnectionPtr _conn;//muduo库中的连接
    };

对于抽象类的功能具体实现,因为数据发送走的直接对象是连接,所以我们需要用BaseProtocol协议先序列化一下然后发送

4-4 网络层服务端类

网络层的服务,用于接收muduo::net::TcpConnectionPtrmuduo::net::Buffer,设置回调函数

    class MuduoServer:public BaseServer{
        public:
        using ptr=std::shared_ptr<MuduoServer>;

        //初始化_server,设置端口
        MuduoServer(int port)
        :_server(&_baseloop,muduo::net::InetAddress("0.0.0.0",port)
        ,"MuduoServer"
        ,muduo::net::TcpServer::kReusePort)//kReusePort表示端口复用,可以多个进程绑定同一个端口
        ,_protocol(Protocolfactory::create())
        {
            _server.setConnectionCallback(std::bind(&MuduoServer::onConnection,this,std::placeholders::_1));
            _server.setMessageCallback(std::bind(&MuduoServer::onMessage,this
                ,std::placeholders::_1
                ,std::placeholders::_2
                ,std::placeholders::_3));//bind函数将函数和参数绑定起来,使得函数可以接受参数
        }
        virtual void start()override{
            _server.start();//启动服务
            _baseloop.loop();//开始监听套接字
        }
        private:
        void onConnection(const muduo::net::TcpConnectionPtr& conn)
        {
            ILOG("连接有新的情况,开始处理,可能是建立或者断开");
            if(conn->connected())
            {
                //如果连接建立成功将连接存放到_conns中   
                // std::cout<<"连接建立"<<std::endl;
                ILOG("MuduoServer中连接建立");
                auto muduo_conn=Connectionfactory::create(conn,_protocol);
                {
                    std::unique_lock<std::mutex>lock(_mutex);
                    _conns.insert(std::make_pair(conn,muduo_conn));
                }
                if(_cb_connection)
                //对该连接进行回调
                _cb_connection(muduo_conn);
            }
            else
            {
                //如果连接断开,检查连接是否存在conns中存在则删除,并进行回调
                // std::cout<<"连接断开"<<std::endl;
                ILOG("连接断开");
                BaseConnection::ptr muduo_conn;
                {
                    std::unique_lock<std::mutex>lock(_mutex);
                    auto it=_conns.find(conn);
                    if(it==_conns.end())
                    {
                        return;
                    }
                    muduo_conn=it->second;
                    _conns.erase(it->first);
                }
                if(_cb_close)
                _cb_close(muduo_conn);
            }
        }
        void onMessage(const muduo::net::TcpConnectionPtr&conn,muduo::net::Buffer*buf,muduo::Timestamp)
        {
            ILOG("连接有数据的到来,开始处理");
            auto base_buf=BufferFactory::create(buf);
            while(1)
            {
                ILOG("检查缓冲区报文是否完全")
                if(_protocol->canProcessed(base_buf)==false)
                {
                    if(base_buf->readableSize()>maxDataSize)
                    {
                        conn->shutdown();
                        ELOG("缓冲区中数据过大");
                        return;
                    }
                    break;
                }
                BaseMessage::ptr msg;
                ILOG("将缓冲区中的一个报文转化为BaseMessage的对象")
                bool ret=_protocol->onMessage(base_buf,msg);
                if(ret==false)
                {
                    conn->shutdown();
                    ELOG("缓冲区中数据错误");
                    return;
                }

                ILOG("根据连接找到对应的BaseConnection对象")
                BaseConnection::ptr base_conn;
                {
                    std::unique_lock<std::mutex>lock(_mutex);
                    auto it=_conns.find(conn);
                    if(it==_conns.end())
                    {
                        conn->shutdown();
                        return;
                    }
                    base_conn=it->second;
                }
                //将BaseMessage的对象转化成string
                if(_cb_message)
                {
                    ILOG("MuduoServer中使用绑定的函数处理")
                    _cb_message(base_conn,msg);
                }
            }
        }
        private:
        const size_t maxDataSize=(1<<16);
        BaseProtocol::ptr _protocol;
        //用于监听设置在服务端中的套接字包括连接和消息监听的
        muduo::net::EventLoop _baseloop;
        //用于建立服务的,内部封装listen和bind和accept,并且设置callback函数
        muduo::net::TcpServer _server;
        //锁住对_conns资源的访问操作
        std::mutex _mutex;
        //BaseConnection里面的TcpConnectionPtr和BaseMessage::ptr的映射关系
        std::unordered_map<muduo::net::TcpConnectionPtr,BaseConnection::ptr>_conns;
    };

这里的_baseloop就是muduo::net::TcpServer中用于监听套接字的,当对应的套接字返回const muduo::net::TcpConnectionPtr&conn,muduo::net::Buffer*buf,muduo::Timestamp会顺着bind的绑定到onConnectiononMessage

这里的回调函数很关键:

  • _cb_connection:用于建立连接后,我们该如何继续接下来的操作

  • _cb_close:用于断开连接后,我们该如何继续接下来的操作

  • _cb_message:用于收到消息后,我们该如何继续接下来的操作

4-5 网络层客户端类

客户端类和服务端类会有所不同,客户端有两个线程,主线程负责生产数据,IO 子线程负责搬运数据,而服务端唯一使命就是处理网络请求,所以直接使用一个线程就可以了

    class MuduoClient:public BaseClient
    {
        public:
        using ptr=std::shared_ptr<MuduoClient>;
        //初始化_client,设置ip和端口
        MuduoClient(const std::string&sip,int sport):
        _protocol(Protocolfactory::create()),
        //创建一个线程,用于监听套接字
        _baseloop(_loopthread.startLoop()),
        //用户客户端等待连接
        _downlatch(1),
        _client(_baseloop,muduo::net::InetAddress(sip,sport),"MuduoClient")
        {}
        // virtual void connect()override{
        //     DLOG("设置回调函数,连接服务器");
        //     _client.setConnectionCallback(std::bind())
        // }
        virtual void connect()override{
            _client.setConnectionCallback(std::bind(&MuduoClient::onConnection,this,std::placeholders::_1));
            _client.setMessageCallback(std::bind(&MuduoClient::onMessage,this,std::placeholders::_1,
            std::placeholders::_2,std::placeholders::_3));
            _client.connect();//连接服务端
            _downlatch.wait();//等待连接
            DLOG("服务端连接成功");
        }

        virtual void shutdown()override{
            return _client.disconnect();//断开连接
        }
        //发送Message给服务端
        virtual bool send(const BaseMessage::ptr&msg)override{
            if(connected()==false)
            {
                ELOG("连接已断开");
                return false;
            }
            _conn->send(msg);
            return true;
        }

        //返回和服务端的BaseConnection对象
        virtual BaseConnection::ptr connection()override{
            return _conn;
        }

        //检查与服务端的连接
        virtual bool connected() override
        {
            return(_conn&&_conn->connected());
        }
        private:

        //连接回调函数如果建立连接则让_client停止阻塞
        void onConnection(const muduo::net::TcpConnectionPtr& conn)
        {
            if(conn->connected())
            {
                std::cout<<"连接建立"<<std::endl;
                _downlatch.countDown();
                _conn=Connectionfactory::create(conn,_protocol);
            }
            else
            {
                std::cout<<"连接断开"<<std::endl;
                _conn.reset();
            }
        }

        //将数据写入到缓冲区当中
        void onMessage(const muduo::net::TcpConnectionPtr&conn,
            muduo::net::Buffer* buf,muduo::Timestamp)
        {
            DLOG("链接有数据的到来,开始处理!");
            MuduoBuffer::ptr base_buf=BufferFactory::create(buf);
            while(1)
            {
                if(_protocol->canProcessed(base_buf)==false)
                {
                    if(buf->readableBytes()>_maxBufferSize)
                    {
                        conn->shutdown();
                        ELOG("可读字节超过限制");
                        return;
                    }
                    break;
                }

                //将缓冲区的数据写入BaseMessage
                BaseMessage::ptr msg;
                bool ret=_protocol->onMessage(base_buf,msg);
                if(ret==false)
                {
                    conn->shutdown();
                    ELOG("缓冲区中数据错误!");
                    return;
                }

                //处理回调
                if(_cb_message)
                {
                    _cb_message(_conn,msg);
                }
            }
        }

        private:
        static const int _maxBufferSize=(1<<16);
        BaseProtocol::ptr _protocol; //协议,用于将缓冲区中的数据转化为BaseMessage
        BaseConnection::ptr _conn; //和服务端的连接
        muduo::CountDownLatch _downlatch; //用于等待连接建立
        muduo::net::EventLoopThread _loopthread; //用于创建新的线程监听套接字
        muduo::net::EventLoop *_baseloop; //用于监听套接字的线程
        muduo::net::TcpClient _client; //用于建立连接的客户端
        
    };

这里有很多知识,我们分开来讲述

  • _loopthread有什么用?

客户端和服务端不一样,客户端需要根据用户的需求生产任务,而服务端不需要,服务端只要设置好任务的处理条件就可以了,处理完之后的结果直接发送给客户端就可以了,所以服务端只需要一个线程一直不断处理任务和发送数据,而客户端需要有两个工作台,一个工作台给客户端生产任务,一个工作台负责自动化接收数据和发送数据

  • _downlatch是用来做什么的

就像是条件变量一样,当我们使用_downlatch.wait()时,如果_downlatch的值为1时,这个_downlatch.wait()会阻塞进程,防止进程执行得太快,在onConnectionTcpConnectionPtr还没传过来的时候,我们不能往后执行程序send,所以这个是一个保险,确认当客户端和服务端连接完成后才执行数据传输的任务

connect() -> _downlatch.wait() -> onConnection() -> countDown() -> send()

保证连接完成后执行任务

  • conn->send()并不是在主线程中执行

它这里只是将任务和资源传给了子线程来执行,当主线程执行到了这一块的时候发现conn并不是主线程的,所以会将这份任务传给子线程执行,这里非常复杂,我们来慢慢讲,会比较抽象

_loopthread.startLoop()

这里子线程返回了一个muduo::net::EventLoop给主线程,当主线程使用这个来构造muduo::net::TcpClient后,返回的TcpConnection会带上子线程的ID,所以使用TcpConnection后会识别出不是主线程的,所以会让子线程来执行任务

五、第三层协议与序列化层

5-1 协议类

到这一层其实我们应该明白了,网络层其实就是对于抽象层的一个具体功能实现

    class LVProtocol:public BaseProtocol
    {
        public:
        using ptr=std::shared_ptr<LVProtocol>;
        virtual bool canProcessed(const BaseBuffer::ptr &buf)override
        {
            //检查缓冲区中是否有4个字节,如果4个字节都没有那连报文都不知道多长
            if(buf->readableSize()<lenFieldsLength)
            {
                return false;
            }
            //将前4个字节表示的报文长度返回给total_len,表示这个报文主体的大小
            int32_t total_len=buf->peekInt32();

            //不仅报文的主体,还需要前面表示大小的前4个字节
            if(buf->readableSize()<(total_len+lenFieldsLength))
            {
                return false;
            }
            return true;
        }
        virtual bool onMessage(const BaseBuffer::ptr& buf,BaseMessage::ptr& msg)override
        {
            int total_len=buf->readInt32();//获取报文总长度
            MType mtype=(MType)buf->readInt32();//获取消息类型
            int32_t idlen=buf->readInt32();//获取消息id的长度
            int32_t body_len=total_len-idlen-idlenFieldsLength-mtypeFieldsLength;//获取消息主体的长度
            std::string id=buf->retrieveAsString(idlen);//获取消息id
            std::string body=buf->retrieveAsString(body_len);//获取消息主体
            msg=MessageFactory::create(mtype);//创建一个消息对象
            if(!msg)
            {
                ELOG("消息类型错误,构造消息对象失败!");
                return false;
            }
            bool ret=msg->unserialize(body);//将body用于构建消息对象
            if(!ret)
            {
                ELOG("消息正文反序列化失败!");
                return false;
            }
            msg->setId(id);//设置消息id
            msg->setMType(mtype);//设置消息类型,虽然这个类已经是消息类型,但还是要明确一下的
            return true;
        }

        virtual std::string serialize(const BaseMessage::ptr& msg)override{
            std::string body=msg->serialize();//主体消息的大小
            std::string id=msg->rid();//取出id
            int32_t mtype=htonl((int32_t)msg->mtype());//将mtype转化为网络字节序
            int32_t alllen=idlenFieldsLength+mtypeFieldsLength+id.size()+body.size();//计算全部的大小
            int32_t nall=htonl(alllen);//将全部的大小转化为网络字节序

            int32_t idlen=htonl(id.size());//将id长度的大小计算并转化为网络字节序
            std::string result;
            result.reserve(alllen+lenFieldsLength);
            result.append((char*)&nall,lenFieldsLength);//第一个加全部的大小
            result.append((char*)&mtype,mtypeFieldsLength);//第二个家mtype的大小
            result.append((char*)&idlen,idlenFieldsLength);//第三个加id的大小
            result.append(id);//string无需转化为网络字节序,因为字符只占一个字节
            result.append(body);
            return result;
        }
        private:
        //消息头部,存储了整个报文的总长度值
        const size_t lenFieldsLength=4;
        //消息类型放在第二位
        const size_t mtypeFieldsLength=4;
        //消息ID的长度
        const size_t idlenFieldsLength=4;
        //只有消息类型和消息ID是不在body中的所以要放到外面来
    };

lenFieldsLength存储了除lenFieldsLength以外一个报文的总长度

5-2 消息类

#pragma once
#include"detail.hpp"
#include"fields.hpp"
#include"abstract.hpp"

namespace gchrpc
{
    
    typedef std::pair<std::string,int> Address;


    //用于存储Json格式的Message,也可以将保留的Json格式的对象序列化返回
    class JsonMessage:public BaseMessage{
        public:
        using ptr=std::shared_ptr<JsonMessage>;

        //Success
        virtual std::string serialize() override{
            std::string body;
            bool ret=JSON::serialize(_body,body);
            if(ret==false)
            {
                return std::string();
            }
            return body;
        }

        //Success
        virtual bool unserialize(std::string& msg)override
        {
            return JSON::unserialize(msg,_body);
        }
        protected:
        Json::Value _body;
    };


    //Json格式的请求,只有Json::Value _body和MType _mtype和std::string _rid;
    class JsonRequest:public JsonMessage{
        public:
        using ptr=std::shared_ptr<JsonRequest>;
    };


    //返回的消息,返回的消息和请求消息的区别是返回的消息有返回状态码,而请求消息只有请求码
    class JsonResponse:public JsonMessage{
        public:
        using ptr=std::shared_ptr<JsonResponse>;

        //Success
        virtual bool check()override{
            if(_body[KEY_RCODE].isNull()==true)
            {
                ELOG("响应中无状态码");
                return false;
            }
            if(_body[KEY_RCODE].isIntegral()==false)
            {
                ELOG("相应状态码类型错误");
            }
            return true;
        }

        //Success
        virtual Rcode rcode()
        {
            return (Rcode)_body[KEY_RCODE].asInt();
        }

        //Success
        virtual void setRCode(Rcode rcode){
            _body[KEY_RCODE]=(int)rcode;
        }
    };


    //Rpc设置请求Message的参数,而请求系统的方法
    class RpcRequest:public JsonRequest{
        public:
        using ptr=std::shared_ptr<RpcRequest>;

        //Success
        RpcRequest(){setMType(MType::REQ_RPC);}

        //Success
        virtual bool check()override{
            if(_body[KEY_METHOD].isNull()==true||
            _body[KEY_METHOD].isString()==false)
            {
                ELOG("RPC请求中没有方法名称或方法名称类型错误");
                return false;
            }
            if(_body[KEY_PARAMS].isNull()==true||
            _body[KEY_PARAMS].isObject()==false)
            {
                ELOG("RPC请求中没用参数或参数类型错误");
                return false;
            }
            return true;
        }

        //Success
        std::string method()
        {
            return _body[KEY_METHOD].asString();
        }

        //Success
        void setMethod(const std::string &method_name)
        {
            _body[KEY_METHOD]=method_name;
        }


        //Success
        Json::Value params()
        {
            return _body[KEY_PARAMS];
        }

        //Success
        void setParams(const Json::Value& params)
        {
            _body[KEY_PARAMS]=params;
        }
    };


    class TopicRequest:public JsonRequest{
        public:
        using ptr=std::shared_ptr<TopicRequest>;

        //Success
        TopicRequest(){setMType(MType::REQ_TOPIC);}

        //Success
        virtual bool check()override{
            if(_body[KEY_TOPIC_KEY].isNull()==true||
                _body[KEY_TOPIC_KEY].isString()==false)
            {
                ELOG("主题请求中没用主题名称或主题名称类型错误!");
                return false;
            }
            if(_body[KEY_OPTYPE].isNull()==true||
                _body[KEY_OPTYPE].isIntegral()==false)
            {
                ELOG("主题请求中没有操作类型或操作类型名称类型错误");
                return false;
            }
            if(_body[KEY_OPTYPE].asInt()==(int)TopicOptype::TOPIC_PUBLISH && 
            (_body[KEY_TOPIC_MSG].isNull()==true ||
             _body[KEY_TOPIC_MSG].isString()==false))
            {
                ELOG("主题消息发布请求中没有消息内容字段或消息内容类型错误");
                return false;
            }
            return true;
        }

        //Success
        std::string topicKey()
        {
            return _body[KEY_TOPIC_KEY].asString();
        }


        //Success
        void setTopicKey(const std::string& key)
        {
            _body[KEY_TOPIC_KEY]=key;
        }

        //Success
        TopicOptype optype()
        {
            return (TopicOptype)_body[KEY_OPTYPE].asInt();
        }

        //Success
        void setOptype(TopicOptype optype)
        {
            _body[KEY_OPTYPE]=(int)optype;
        }

        //Success
        std::string topicMsg()
        {
            return _body[KEY_TOPIC_MSG].asString();
        }

        //Success
        void setTopicMsg(const std::string& msg)
        {
            _body[KEY_TOPIC_MSG]=msg;
        }
    };


    class ServiceRequest:public JsonRequest{
        public:

        //Success
        ServiceRequest(){setMType(MType::REQ_SERVICE);}
        using ptr=std::shared_ptr<ServiceRequest>;


        //Success
        virtual bool check() override{
            if(_body[KEY_METHOD].isNull()==true||
            _body[KEY_METHOD].isString()==false)
            {
                ELOG("服务请求中没有方法名称或方法名称类型错误!");
                return false;
            }
            if(_body[KEY_OPTYPE].isNull()==true||
            _body[KEY_OPTYPE].isIntegral()==false)
            {
                ELOG("服务请求中没有操作类型或操作类型的类型错误!");
                return false;
            }
            if(_body[KEY_OPTYPE].asInt()!=(int)(ServiceOptype::SERVICE_DISCOVERY)&&
            (_body[KEY_HOST].isNull()==true||
            _body[KEY_HOST].isObject()==false||
            _body[KEY_HOST][KEY_HOST_IP].isNull()==true||
            _body[KEY_HOST][KEY_HOST_IP].isString()==false||
            _body[KEY_HOST][KEY_HOST_PORT].isNull()==true||
            _body[KEY_HOST][KEY_HOST_PORT].isIntegral()==false))
            {
                ELOG("服务请求中主题地址错误!");
                return false;
            }
            return true;
        }

        //Success
        std::string method()
        {
            return _body[KEY_METHOD].asString();
        }

        //Success
        void setMethod(const std::string& name)
        {
            _body[KEY_METHOD]=name;
        }

        //Success
        ServiceOptype optype()
        {
            return (ServiceOptype)_body[KEY_OPTYPE].asInt();
        }

        //Success
        void setOptype(ServiceOptype optype)
        {
            _body[KEY_OPTYPE]=(int)(optype);
        }

        //Success
        Address host()
        {
            Address addr;
            addr.first=_body[KEY_HOST][KEY_HOST_IP].asString();
            addr.second=_body[KEY_HOST][KEY_HOST_PORT].asInt();
            return addr;
        }

        //Success
        void setHost(const Address& addr)
        {
            _body[KEY_HOST][KEY_HOST_IP]=addr.first;
            _body[KEY_HOST][KEY_HOST_PORT]=addr.second;

        }
    };

    class RpcResponse:public JsonResponse
    {
        public:

        //Success
        RpcResponse(){setMType(MType::RSP_RPC);}
        using ptr=std::shared_ptr<RpcResponse>;

        //Success
        virtual bool check()override{
            if(_body[KEY_RCODE].isNull()==true||
                _body[KEY_RCODE].isInt()==false)
            {
                ELOG("响应回复状态码为空,或状态码类型错误");
                return false;
            }
            if(_body[KEY_RESULT].isNull()==true)
            {
                ELOG("响应结果为空,或响应结果类型错误");
                return false;
            }
            return true;
        }

        //Success
        Json::Value result()
        {
            return _body[KEY_RESULT];
        }

        //Success
        void setResult(const Json::Value& result)
        {
            _body[KEY_RESULT]=result;
        }
    };


    class TopicResponse:public JsonResponse
    {
        public:
        using ptr=std::shared_ptr<TopicResponse>;
    };

    class ServiceResponse:public JsonResponse
    {
        public:

        //Success
        ServiceResponse(){setMType(MType::RSP_SERVICE);}
        using ptr=std::shared_ptr<ServiceResponse>;

        //Success
        virtual bool check()override{
            if(_body[KEY_RCODE].isNull()==true||
            _body[KEY_RCODE].isIntegral()==false)
            {
                ELOG("响应中没有状态码或者状态码类型错误");
                return false;
            }
            if(_body[KEY_OPTYPE].isNull()==true||
            _body[KEY_OPTYPE].isIntegral()==false)
            {
                ELOG("响应中没有操作类型或者操作类型的类型错误");
                return false;
            }

            if((_body[KEY_OPTYPE]==(int)ServiceOptype::SERVICE_DISCOVERY)&&
            (_body[KEY_METHOD].isNull()==true||
            _body[KEY_METHOD].isString()==false||
            _body[KEY_HOST].isNull()==true||
            _body[KEY_HOST].isArray()==false))
            {
                ELOG("服务发现响应中信息字段错误");
                return false;
            }
            return true;
        }

        //Success
        ServiceOptype optype()
        {
            return (ServiceOptype)_body[KEY_OPTYPE].asInt();
        }

        //Success
        void setoptype(const ServiceOptype& optype)
        {
            _body[KEY_OPTYPE]=(int)optype;
        }

        //Success
        std::string method()
        {
            return _body[KEY_METHOD].asString();
        }

        //Success
        void setMethod(const std::string& method)
        {
            _body[KEY_METHOD]=method;
        }

        //Success
        void setHost(std::vector<Address>addrs)
        {
            for(auto& addr:addrs)
            {
                Json::Value newValue;
                newValue[KEY_HOST_IP]=addr.first;
                newValue[KEY_HOST_PORT]=addr.second;
                _body[KEY_HOST].append(newValue);
            }
        }

        //Success
        std::vector<Address> hosts()
        {
            // std::vector<Address> address(0);
            int sz=_body[KEY_HOST].size();
            std::vector<Address> address(sz);
            for(int i=0;i<sz;i++)
            {
                address[i].first=_body[KEY_HOST][i][KEY_HOST_IP].asString();
                address[i].second=_body[KEY_HOST][i][KEY_HOST_PORT].asInt();
            }
            return address;
        }
    };

    class MessageFactory{
        public:

        //Success返回基类
        static BaseMessage::ptr create(MType mtype)
        {
            switch (mtype)
            {
                case MType::REQ_RPC:
                return std::make_shared<RpcRequest>();
                case MType::RSP_RPC:
                return std::make_shared<RpcResponse>();
                case MType::REQ_TOPIC:
                return std::make_shared<TopicRequest>();
                case MType::RSP_TOPIC:
                return std::make_shared<TopicResponse>();
                case MType::REQ_SERVICE:
                return std::make_shared<ServiceRequest>();
                case MType::RSP_SERVICE:
                return std::make_shared<ServiceResponse>();
            }
            return BaseMessage::ptr();
        }

        //Success返回派生类
        template<typename T,typename ...Args>
        static std::shared_ptr<T> create(Args&& ...args)
        {
            return std::make_shared<T>(std::forward<Args>(args)...);
        }
    };
}

下面是各个消息的继承关系,这里的回复要比请求多一个返回码KEY_CODE,每次服务器处理完后返回客户端返回结果
请添加图片描述

这张图能直观表示各种消息_body结构体的区别,最重要的还是这个工厂类的讲解

        template<typename T,typename ...Args>
        static std::shared_ptr<T> create(Args&& ...args)
        {
            return std::make_shared<T>(std::forward<Args>(args)...);
        }

这个模板类的作用是:不管调用者传了几个参数,也不管传的是左值还是右值,我这个中间商(Factory)都要一模一样地传给 T 的构造函数,绝不产生多余的拷贝

其中的两个核心技术点:可变参数模板 和 完美转发

  • typename ...ArgsArgs&& ...args

Args 是一个参数包,它表示任意数量、任意类型的参数

有的类构造函数不需要参数:new T(),有的需要一个 int:new T(10),有的需要 stringbool:new T("hello", true),工厂方法 create 为了能通吃所有情况,必须使用 ...Args 来打包所有参数

Args&&:这里的 && 不完全等于右值引用,当 && 用在推导类型(模板参数 T&&Args&&)时,它叫万能引用,如果你传进来的是左值(比如 int a = 1; create(a)),Args&& 会退化为 int&,如果你传进来的是右值(比如 create(10)),Args&& 会变成 int&&,它像一个“变色龙”,能准确捕捉到你传进来的参数到底是临时的(右值)还是持久的(左值)

  • std::forward<Args>(args)...

这部分解决了**“性质保留”**的问题,是这段代码的灵魂,参数有了名字就变成左值了

请看这个悖论:

template<typename T>
void wrapper(T&& arg) {
    // 假设调用 wrapper(10),arg 是右值引用。
    // 但是!在 wrapper 函数内部,arg 这个变量本身是有名字的。
    // 在 C++ 中,只要有名字的变量,它就是左值!
    
    new Object(arg); // 糟糕!这里把 arg 当作左值传过去了!
}

如果你传了一个临时对象(右值)给工厂,工厂转手传给构造函数时,却变成左值了。构造函数就会调用拷贝构造,而不是高效的移动构造。性能损失巨大

std::forward就像一个时光倒流机或者类型还原器,如果 Args 推导出来当初是个右值,那我就把 args 强制转回右值;如果当初是左值,我就不动它,

... (包展开)
std::forward<Args>(args)... 最后的那个 ... 是包展开

它的意思是:对 Args 包里的每一个参数,都执行一遍 std::forward

例如,如果你调用 create(a, 10):编译器展开为std::make_shared<T>(std::forward<TypeA>(a), std::forward<TypeB>(10))

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值