在分布式系统中,通常采用RPC作为服务器之间的交互,优点自然是简单,不需要异步通信那样复杂的状态跟踪。
服务器RPC设计的几个基本流程:
1、制定协议
2、协议的序列化和反序列化
3、网络通信传输
通常通过简单的网络交互,我们可以在服务器之间交换数据。
下面说下我们的设计方法以及问题
由于不同的协议在序列化和反序列化方面会有很多公共的方法和处理,例如协议包头的处理,有可能还需要对包体进行加密和验证等,这些公共的操作我们可以抽象成一个基类,称为message类。其他的不同协议可以继承于message类,实现自己独有的方法,例如包体的序列化和反序列化,这样每一个消息实现为一个类。(当然也有其他的做法,例如不继承基类,以函数的形式提供包体的序列化和反序列)。
class Message{
public:
Message();
virtual ~Message();
bool BuildHeader(std::string& header);
bool ParseHeader(const std::string& header);
bool BuildMessage();
bool ParseMessage();
virtual uint16_t message_type() = 0;
virtual string message_name() = 0;
protected:
virtual bool BuildBody(std::string& message_body) = 0;
virtual bool ParseBody(const std::string& message_body) = 0;
};
class CreateFileReq : public Message
{
public:
CreateFileReq();
virtual ~CreateFileReq();
virtual uint16_t message_type()
{
return MSG_TYPE_CREATE_FILE_REQ;
}
virtual string message_name()
{
return "CreateFileReq";
}
inline string& parent_path()
{
return parent_path_;
}
inline uint64_t& file_size()
{
return file_size_;
}
protected:
virtual bool BuildBody(std::string& message_body);
virtual bool ParseBody(const std::string& message_body);
private: string parent_path_; uint64_t file_size_;};
继承于Message的派生类主要实现BuildBody和ParseBody函数即可,这样我们可以使用基类里的BuildMessage和ParseMessage即可对消息进行序列化和反序列,而不需要知道消息的类型
消息实现后,我们需要提供一个发送函数,我们称为接口1,比较简单的类似于:
uint16_t SendMessage(const string& ip, const uint16_t port, Message* req, Message* rsp);
这样可以做到比较通用,适合各种消息的处理。
另外如果我们需要更友好的接口,可以针对每个消息具体提供,我们称为接口2,这个接口可以基于接口1实现:
uint16_t SendCreateFile(const string& ip,const uint16_t port,const string& parent_path,const uint64_t file_size)