⽹络通信协议设计
其中⽣产者和消费者都是客⼾端,它们都需要通过⽹络和BrokerServer进⾏通信。具体通信的过程我们使⽤Muduo库来实现,使⽤TCP作为通信的底层协议,同时在这个基础上⾃定义应⽤层协议,完成客⼾端对服务器功能的远端调⽤。我们要实现的远端调⽤接⼝包括:
使⽤⼆进制的⽅式设计应⽤层协议。因为MQMessage的消息体是使⽤Protobuf进⾏序列化的,本⾝是按照⼆进制存储的,所以不太适合⽤json等⽂本格式来定义协议。
下⾯我们设计⼀下应⽤层协议:请求/响应报⽂设计
- len:4个字节,表⽰整个报⽂的⻓度
- nameLen:4个字节,表⽰typeName数组的⻓度
- typeName:是个字节数组,占nameLen个字节,表⽰请求/响应报⽂的类型名,作⽤是分发不同消息到对应的远端接⼝调⽤中
- protobufData:是个字节数组,占len-nameLen-8个字节,表⽰请求/响应参数数据通过protobuf序列化之后的⼆进制
- checkSum:4个字节,表⽰整个消息的校验和,作⽤是为了校验请求/响应报⽂的完整性
⼀个创建交换机的请求,如下图⽰:
信道管理模块
在AMQP模型中,除了通信连接Connection概念外,还有⼀个Channel的概念,Channel是针对Connection连接的⼀个更细粒度的通信信道,多个Channel可以使⽤同⼀个通信连接Connection进⾏通信,但是同⼀个Connection的Channel之间相互独⽴。
⽽信道模块就是再次将上述模块进⾏整合提供服务的模块
proto.proto
syntax = "proto3";
package nzq;
import "msg.proto";
//信道的打开与关闭
message openChannelRequest{
string rid = 1;
string cid = 2;
};
message closeChannelRequest{
string rid = 1;
string cid = 2;
};
//交换机的声明与删除
message declareExchangeRequest{
string rid = 1;
string cid = 2;
string exchange_name = 3;
ExchangeType exchange_type = 4;
bool durable = 5;
bool auto_delete = 6;
map<string, string> args = 7;
};
message deleteExchangeRequest{
string rid = 1;
string cid = 2;
string exchange_name = 3;
};
//队列的声明与删除
message declareQueueRequest{
string rid = 1;
string cid = 2;
string queue_name = 3;
bool exclusive = 4;
bool durable = 5;
bool auto_delete = 6;
map<string, string> args = 7;
};
message deleteQueueRequest{
string rid = 1;
string cid = 2;
string queue_name = 3;
};
//队列的绑定与解除绑定
message queueBindRequest{
string rid = 1;
string cid = 2;
string exchange_name = 3;
string queue_name = 4;
string binding_key = 5;
};
message queueUnBindRequest{
string rid = 1;
string cid = 2;
string exchange_name = 3;
string queue_name = 4;
};
//消息的发布
message basicPublishRequest {
string rid = 1;
string cid = 2;
string exchange_name = 3;
string body = 4;
BasicProperties properties = 5;
};
//消息的确认
message basicAckRequest {
string rid = 1;
string cid = 2;
string queue_name = 3;
string message_id = 4;
};
//队列的订阅
message basicConsumeRequest {
string rid = 1;
string cid = 2;
string consumer_tag =3;
string queue_name = 4;
bool auto_ack = 5;
};
//订阅的取消
message basicCancelRequest {
string rid = 1;
string cid = 2;
string consumer_tag = 3;
string queue_name = 4;
};
//消息的推送
message basicConsumeResponse {
string cid = 1;
string consumer_tag = 2;
string body = 3;
BasicProperties properties = 4;
};
//通用响应
message basicCommonResponse {
string rid = 1;
string cid = 2;
bool ok = 3;
}
- 管理信息:
- a. 信道ID:信道的唯⼀标识
- b. 信道关联的消费者:⽤于消费者信道在关闭的时候取消订阅,删除订阅者信息
- c. 信道关联的连接:⽤于向客⼾端发送数据(响应,推送的消息)
- d. protobuf协议处理句柄:⽹络通信前的协议处理
- e. 消费者管理句柄:信道关闭/取消订阅的时候,通过句柄删除订阅者信息
- f. 虚拟机句柄:交换机/队列/绑定/消息数据管理
- g. ⼯作线程池句柄(⼀条消息被发布到队列后,需要将消息推送给订阅了对应队列的消费者,过
程由线程池完成)
- 管理操作:
- a. 提供声明&删除交换机操作(删除交换机的同时删除交换机关联的绑定信息)
- b. 提供声明&删除队列操作(删除队列的同时,删除队列关联的绑定信息,消息,消费者信息)
- c. 提供绑定&解绑队列操作
- d. 提供订阅&取消订阅队列消息操作
- e. 提供发布&确认消息操作
using ProtobufCodecPtr = std::shared_ptr<ProtobufCodec>;
using openChannelRequestPtr = std::shared_ptr<openChannelRequest>;
using closeChannelRequestPtr = std::shared_ptr<closeChannelRequest>;
using declareExchangeRequestPtr = std::shared_ptr<declareExchangeRequest>;
using deleteExchangeRequestPtr = std::shared_ptr<deleteExchangeRequest>;
using declareQueueRequestPtr = std::shared_ptr<declareQueueRequest>;
using deleteQueueRequestPtr = std::shared_ptr<deleteQueueRequest>;
using queueBindRequestPtr = std::shared_ptr<queueBindRequest>;
using queueUnBindRequestPtr = std::shared_ptr<queueUnBindRequest>;
using basicPublishRequestPtr = std::shared_ptr<basicPublishRequest>;
using basicAckRequestPtr = std::shared_ptr<basicAckRequest>;
using basicConsumeRequestPtr = std::shared_ptr<basicConsumeRequest>;
using basicCancelRequestPtr = std::shared_ptr<basicCancelRequest>;
class Channel {
public:
using ptr = std::shared_ptr<Channel>;
Channel(const std::string &id,
const VirtualHost::ptr &host,
const ConsumerManager::ptr &cmp,
const ProtobufCodecPtr &codec,
const muduo::net::TcpConnectionPtr &conn,
const threadpool::ptr &pool):
_cid(id),
_conn(conn),
_codec(codec),
_cmp(cmp),
_host(host),
_pool(pool){
DLOG("new Channel: %p", this);
}
~Channel() {
if (_consumer.get() != nullptr) {
_cmp->remove(_consumer->tag, _consumer->qname);
}
DLOG("del Channel: %p", this);
}
//交换机的声明与删除
void declareExchange(const declareExchangeRequestPtr &req) {
bool ret = _host->declareExchange(req->exchange_name(),
req->exchange_type(), req->durable(),
req->auto_delete(), req->args());
return basicResponse(ret, req->rid(), req->cid());
}
void deleteExchange(const deleteExchangeRequestPtr &req) {
_host->deleteExchange(req->exchange_name());
return basicResponse(true, req->rid(), req->cid());
}
//队列的声明与删除
void declareQueue(const declareQueueRequestPtr &req) {
bool ret = _host->declareQueue(req->queue_name(),
req->durable(), req->exclusive(),
req->auto_delete(), req->args());
if (ret == false) {
return basicResponse(false, req->rid(), req->cid());
}
_cmp->initQueueConsumer(req->queue_name());//初始化队列的消费者管理句柄
return basicResponse(true, req->rid(), req->cid());
}
void deleteQueue(const deleteQueueRequestPtr &req) {
_cmp->destroyQueueConsumer(req->queue_name());
_host->deleteQueue(req->queue_name());
return basicResponse(true, req->rid(), req->cid());
}
//队列的绑定与解除绑定
void queueBind(const queueBindRequestPtr &req) {
bool ret = _host->bind(req->exchange_name(),
req->queue_name(