项目第十弹:连接管理模块与服务器模块

一、连接管理模块的设计

我们之前说:RabbitMQ是将连接细分为信道,从而复用TCP连接
这个连接就是我们今天要实现的连接模块

muduo库当中有一个TcpConnection连接类,它是对底层TCP套接字的封装,不是我们要实现的业务上的连接模块

我们要实现的业务上的连接模块要怎么设计呢?

1.连接模块的设计

信道是连接的细分,因此连接的任务就是:

  1. 打开/关闭信道
  2. 获取信道

而且声明/删除信道都是接收Request,响应Response

成员:

  1. 信道管理模块句柄
  2. muduo网络通信连接TcpConnectionPtr
  3. ProtobufCodecPtr
  4. 信道模块构造所需资源:
  5. 虚拟机管理模块句柄
  6. 消费者管理模块句柄
  7. 异步工作线程池句柄

依旧是无需互斥锁

2. 连接管理模块的设计

增、删、查

1.连接ID? TcpConnectionPtr!!

我们肯定是需要将连接放到哈希表当中组织起来的
问题是应该让谁当key呢?

跟信道一样,搞一个“连接ID”??
是可以的,只不过要给所有的请求都在加上一个连接ID,而我们说信道是用户视角中的通信通道,现在如果要给所有的请求都加上一个连接ID,未免有点“违背”了这个信道的概念,而且很不优雅

那怎么办呢?
有没有什么优雅一些的方法?
在这里插入图片描述
接口:

  1. 创建连接
  2. 销毁连接
  3. 获取连接

成员:
4. 互斥锁
5. unordered_map<TcpConnectionPtr,Connection::ptr> _connection_map;

注意:

unordered_map 直接对 shared_ptr 的底层原始指针进行哈希,而不是对 shared_ptr 对象本身进行哈希

二、连接管理模块的实现

1.连接模块的实现

因为信道是连接的细分,所以对应的信道管理模块也由连接所创建,所独占,所管理

using ProtobufCodecPtr = std::shared_ptr<ProtobufCodec>;

using OpenChannelRequestPtr = std::shared_ptr<OpenChannelRequest>;
using CloseChannelRequestPtr = std::shared_ptr<CloseChannelRequest>;

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

    //因为信道是连接的细分,所以对应的信道管理模块也由连接所创建,所独占,所管理
    Connection(const muduo::net::TcpConnectionPtr &conn, const ProtobufCodecPtr &codec, const ConsumerManager::ptr &consumer_manager_ptr,
               const VirtualHostManager::ptr &vhost_manager_ptr, const threadpool::ptr &pool_ptr)
        : _conn(conn), _codec(codec), _consumer_manager_ptr(consumer_manager_ptr), _vhost_manager_ptr(vhost_manager_ptr), _pool_ptr(pool_ptr), _channel_manager_ptr(std::make_shared<ChannelManager>()) {}

    void openChannel(const OpenChannelRequestPtr &req)
    {
        // 1. 处理请求
        _channel_manager_ptr->OpenChannel(req->channel_id(), _conn, _codec, _consumer_manager_ptr, _vhost_manager_ptr, _pool_ptr);
        // 2. 返回响应
        basicResponse(req->channel_id(), req->req_id(), true);
    }

    void closeChannel(const CloseChannelRequestPtr &req)
    {
        // 1. 处理请求
        _channel_manager_ptr->CloseChannel(req->channel_id());
        // 2. 返回响应
        basicResponse(req->channel_id(), req->req_id(), true);
    }

    Channel::ptr getChannel(const std::string &channel_id)
    {
        return _channel_manager_ptr->getChannel(channel_id);
    }

private:
    void basicResponse(const std::string &channel_id, const std::string &req_id, bool ret)
    {
        BasicCommonResponse resp;
        resp.set_channel_id(channel_id);
        resp.set_req_id(req_id);
        resp.set_ok(ret);

        _codec->send(_conn, resp);
    }

    muduo::net::TcpConnectionPtr _conn;
    ProtobufCodecPtr _codec;

    ConsumerManager::ptr _consumer_manager_ptr;
    VirtualHostManager::ptr _vhost_manager_ptr;
    threadpool::ptr _pool_ptr;

    ChannelManager::ptr _channel_manager_ptr;
};

2.连接管理模块的实现

就是增、删、查而已

class ConnectionManager
{
public:
    using ptr = std::shared_ptr<ConnectionManager>;
    
    void createConnection(const muduo::net::TcpConnectionPtr &conn, const ProtobufCodecPtr &codec,
                          const ConsumerManager::ptr &consumer_manager_ptr, const VirtualHostManager::ptr &vhost_manager_ptr,
                          const threadpool::ptr &pool_ptr)
    {
        std::unique_lock<std::mutex> ulock(_mutex);
        if (_connection_map.count(conn))
            return;
        _connection_map.insert(std::make_pair(conn, std::make_shared<Connection>(conn, codec, consumer_manager_ptr, vhost_manager_ptr, pool_ptr)));
    }

    void destroyConnection(const muduo::net::TcpConnectionPtr &conn)
    {
        std::unique_lock<std::mutex> ulock(_mutex);
        _connection_map.erase(conn);
    }

    Connection::ptr getConnecion(const muduo::net::TcpConnectionPtr &conn)
    {
        std::unique_lock<std::mutex> ulock(_mutex);
        auto iter = _connection_map.find(conn);
        if (iter == _connection_map.end())
        {
        	default_warning("未找到该TcpConnectionPtr对象所关联的连接对象");
            return Connection::ptr();
        }
        return iter->second;
    }

private:
    std::mutex _mutex;
    std::unordered_map<muduo::net::TcpConnectionPtr, Connection::ptr> _connection_map;
};

三、服务器模块的设计

1.前言

在完成了信道管理模块和连接管理模块之后,即具体的TCP连接所对应的网络服务模块已经完成了之后

下面我们就可以直接搭建服务器了
我们之前使用过muduo库来搭建基于protobuf的网络服务器和客户端,知道muduo库是基于事件注册与响应机制的
都已经写过了,所以怎么设计,为何要这么写我就不赘述了,用法都是一样的

2.设计

注册的事件响应函数:

  1. 打开/关闭信道
  2. 声明/删除虚拟机
  3. 声明/删除交换机
  4. 声明/删除队列
  5. 绑定/解绑队列
  6. 订阅/取消订阅队列
  7. 发布/确认消息

注册的连接响应函数:
OnConnectionCallback

对外接口:
构造
start开启服务函数(死循环进行循环监听)

成员:

  1. muduo库网络通信模块:
    EventLoop
    TcpServer
  2. muduo库基于protobuf的协议处理模块
    ProtobufDispatcher
    ProtobufCodec
  3. 连接所需资源模块句柄
    VirtualHostManager::ptr
    ConsumerManager::ptr
    threadpool::ptr
  4. 连接管理模块句柄
    ConnectionManager::ptr

四、服务器模块的实现

其实那些注册的函数就是先获取连接,后获取信道
然后复用信道的接口即可

注意:虚拟机在创建的时候要恢复历史消息,恢复历史消息时顺便初始化对应队列的消费者管理结构

using namespace ns_helper;

namespace ns_google
{
    using MessagePtr = std::shared_ptr<google::protobuf::Message>;
}
const std::string vhost_dbfile = "main.db";
const int default_thread_num = 5;

class Server
{
public:
    Server(uint16_t port, const std::string &dbfile = vhost_dbfile, int thread_num = default_thread_num)
        : _server(&_baseloop, muduo::net::InetAddress("0.0.0.0", port), "Server", muduo::net::TcpServer::kReusePort), _dispatcher(std::bind(&Server::OnUnknownCallback, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)), _codec(std::make_shared<ProtobufCodec>(std::bind(&ProtobufDispatcher::onProtobufMessage, &_dispatcher, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3))), _consumer_manager_ptr(std::make_shared<ConsumerManager>()), _vhost_manager_ptr(std::make_shared<VirtualHostManager>(dbfile)), _pool_ptr(std::make_shared<threadpool>(thread_num)), _connection_manager_ptr(std::make_shared<ConnectionManager>())
    {
    	// 0.针对历史消息中的所有队列,初始化队列的消费者管理结构
        VirtualHostMap vhmap = _vhost_manager_ptr->getAllVirtualHost();
        for (auto &vhost_kv : vhmap)
        {
            MsgQueueMap mqmap = _vhost_manager_ptr->getAllMsgQueue(vhost_kv.first);
            for (auto &q_kv : mqmap)
            {
                _consumer_manager_ptr->initQueueConsumerManager(vhost_kv.first,q_kv.first);
            }
        }
        // 1. 注册_dispatcher的事件响应回调函数

        // 1. 打开/关闭信道
        _dispatcher.registerMessageCallback<OpenChannelRequest>(std::bind(&Server::OnOpenChannel, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
        _dispatcher.registerMessageCallback<CloseChannelRequest>(std::bind(&Server::OnCloseChannel, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));

        // 2. 声明/删除虚拟机
        _dispatcher.registerMessageCallback<DeclareVirtualHostRequest>(std::bind(&Server::OnDeclareVirtualHost, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
        _dispatcher.registerMessageCallback<EraseVirtualHostRequest>(std::bind(&Server::OnEraseVirtualHost, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));

        // 3. 声明/删除交换机
        _dispatcher.registerMessageCallback<DeclareExchangeRequest>(std::bind(&Server::OnDeclareExchange, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
        _dispatcher.registerMessageCallback<EraseExchangeRequest>(std::bind(&Server::OnEraseExchange, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));

        // 4. 声明/删除队列
        _dispatcher.registerMessageCallback<DeclareMsgQueueRequest>(std::bind(&Server::OnDeclareMsgQueue, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
        _dispatcher.registerMessageCallback<EraseMsgQueueRequest>(std::bind(&Server::OnEraseMsgQueue, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));

        // 5. 绑定/解绑队列
        _dispatcher.registerMessageCallback<BindRequest>(std::bind(&Server::OnBind, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
        _dispatcher.registerMessageCallback<UnbindRequest>(std::bind(&Server::OnUnbind, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));

        // 6. 订阅/取消订阅队列
        _dispatcher.registerMessageCallback<BasicConsumeRequest>(std::bind(&Server::OnBasicConsume, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
        _dispatcher.registerMessageCallback<BasicCancelRequest>(std::bind(&Server::OnBasicCancel, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));

        // 7. 发布/确认消息
        _dispatcher.registerMessageCallback<BasicPublishRequest>(std::bind(&Server::OnBasicPublish, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
        _dispatcher.registerMessageCallback<BasicAckRequest>(std::bind(&Server::OnBasicAck, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));

        // 2. 注册_server的事件回调函数和连接回调函数

        _server.setConnectionCallback(std::bind(&Server::OnConnection, this, std::placeholders::_1));
        _server.setMessageCallback(std::bind(&ProtobufCodec::onMessage, _codec.get(), std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
    }

    void start()
    {
        // 开始监听
        _server.start();
        // 开始事件死循环监控
        _baseloop.loop();
    }

    // 回调接口
private:
    void OnConnection(const muduo::net::TcpConnectionPtr &conn)
    {
        if (conn->connected())
        {
            // 创建连接
            _connection_manager_ptr->createConnection(conn, _codec, _consumer_manager_ptr, _vhost_manager_ptr, _pool_ptr);
            default_info("连接建立成功");
        }
        else
        {
            // 删除连接
            _connection_manager_ptr->destroyConnection(conn);
            default_info("连接断开成功");
        }
    }

    void OnUnknownCallback(const muduo::net::TcpConnectionPtr &conn, const ns_google::MessagePtr &message, muduo::Timestamp)
    {
        default_warning("非法请求,即将断开连接");
        if (conn->connected())
        {
            conn->shutdown();
        }
    }

    // 1. 打开/关闭信道
    void OnOpenChannel(const muduo::net::TcpConnectionPtr &conn, const OpenChannelRequestPtr &req, muduo::Timestamp)
    {
        // 1. 先看有无该连接
        Connection::ptr myconn = _connection_manager_ptr->getConnecion(conn);
        if (myconn.get() == nullptr)
        {
            default_info("打开信道时,没有找到连接对应的Connection对象");
            return;
        }
        myconn->openChannel(req);
    }

    void OnCloseChannel(const muduo::net::TcpConnectionPtr &conn, const CloseChannelRequestPtr &req, muduo::Timestamp)
    {
        // 1. 先看有无该连接
        Connection::ptr myconn = _connection_manager_ptr->getConnecion(conn);
        if (myconn.get() == nullptr)
        {
            default_info("关闭信道时,没有找到连接对应的Connection对象");
            return;
        }
        myconn->closeChannel(req);
    }

    // 2. 声明/删除虚拟机
    void OnDeclareVirtualHost(const muduo::net::TcpConnectionPtr &conn, const DeclareVirtualHostRequestPtr &req, muduo::Timestamp)
    {
        // 1. 先看有无该连接
        Connection::ptr myconn = _connection_manager_ptr->getConnecion(conn);
        if (myconn.get() == nullptr)
        {
            default_info("声明虚拟机时,没有找到连接对应的Connection对象");
            return;
        }
        // 2. 获取信道
        Channel::ptr mychannel = myconn->getChannel(req->channel_id());
        if (mychannel.get() == nullptr)
        {
            default_info("声明虚拟机失败,因为获取信道失败");
            return;
        }
        mychannel->declareVirtualHost(req);
    }

    void OnEraseVirtualHost(const muduo::net::TcpConnectionPtr &conn, const EraseVirtualHostRequestPtr &req, muduo::Timestamp)
    {
        // 1. 先看有无该连接
        Connection::ptr myconn = _connection_manager_ptr->getConnecion(conn);
        if (myconn.get() == nullptr)
        {
            default_info("删除虚拟机时,没有找到连接对应的Connection对象");
            return;
        }
        // 2. 获取信道
        Channel::ptr mychannel = myconn->getChannel(req->channel_id());
        if (mychannel.get() == nullptr)
        {
            default_info("删除虚拟机失败,因为获取信道失败");
            return;
        }
        mychannel->eraseVirtualHost(req);
    }

    // 3. 声明/删除交换机
    void OnDeclareExchange(const muduo::net::TcpConnectionPtr &conn, const DeclareExchangeRequestPtr &req, muduo::Timestamp)
    {
        // 1. 先看有无该连接
        Connection::ptr myconn = _connection_manager_ptr->getConnecion(conn);
        if (myconn.get() == nullptr)
        {
            default_info("声明交换机时,没有找到连接对应的Connection对象");
            return;
        }
        // 2. 获取信道
        Channel::ptr mychannel = myconn->getChannel(req->channel_id());
        if (mychannel.get() == nullptr)
        {
            default_info("声明交换机失败,因为获取信道失败");
            return;
        }
        mychannel->declareExchange(req);
    }

    void OnEraseExchange(const muduo::net::TcpConnectionPtr &conn, const EraseExchangeRequestPtr &req, muduo::Timestamp)
    {
        // 1. 先看有无该连接
        Connection::ptr myconn = _connection_manager_ptr->getConnecion(conn);
        if (myconn.get() == nullptr)
        {
            default_info("删除交换机时,没有找到连接对应的Connection对象");
            return;
        }
        // 2. 获取信道
        Channel::ptr mychannel = myconn->getChannel(req->channel_id());
        if (mychannel.get() == nullptr)
        {
            default_info("删除交换机失败,因为获取信道失败");
            return;
        }
        mychannel->eraseExchange(req);
    }

    // 4. 声明/删除队列
    void OnDeclareMsgQueue(const muduo::net::TcpConnectionPtr &conn, const DeclareMsgQueueRequestPtr &req, muduo::Timestamp)
    {
        // 1. 先看有无该连接
        Connection::ptr myconn = _connection_manager_ptr->getConnecion(conn);
        if (myconn.get() == nullptr)
        {
            default_info("声明队列时,没有找到连接对应的Connection对象");
            return;
        }
        // 2. 获取信道
        Channel::ptr mychannel = myconn->getChannel(req->channel_id());
        if (mychannel.get() == nullptr)
        {
        	default_info("声明队列失败,因为获取信道失败");
            return;
        }
        mychannel->declareMsgQueue(req);
    }

    void OnEraseMsgQueue(const muduo::net::TcpConnectionPtr &conn, const EraseMsgQueueRequestPtr &req, muduo::Timestamp)
    {
        // 1. 先看有无该连接
        Connection::ptr myconn = _connection_manager_ptr->getConnecion(conn);
        if (myconn.get() == nullptr)
        {
            default_info("删除队列时,没有找到连接对应的Connection对象");
            return;
        }
        // 2. 获取信道
        Channel::ptr mychannel = myconn->getChannel(req->channel_id());
        if (mychannel.get() == nullptr)
        {
        	default_info("删除队列失败,因为获取信道失败");
            return;
        }
        mychannel->eraseMsgQueue(req);
    }

    // 5. 绑定/解绑队列
    void OnBind(const muduo::net::TcpConnectionPtr &conn, const BindRequestPtr &req, muduo::Timestamp)
    {
        // 1. 先看有无该连接
        Connection::ptr myconn = _connection_manager_ptr->getConnecion(conn);
        if (myconn.get() == nullptr)
        {
            default_info("绑定队列时,没有找到连接对应的Connection对象");
            return;
        }
        // 2. 获取信道
        Channel::ptr mychannel = myconn->getChannel(req->channel_id());
        if (mychannel.get() == nullptr)
        {
            default_info("绑定队列失败,因为获取信道失败");
            return;
        }
        mychannel->bind(req);
    }

    void OnUnbind(const muduo::net::TcpConnectionPtr &conn, const UnbindRequestPtr &req, muduo::Timestamp)
    {
        // 1. 先看有无该连接
        Connection::ptr myconn = _connection_manager_ptr->getConnecion(conn);
        if (myconn.get() == nullptr)
        {
            default_info("解除绑定队列时,没有找到连接对应的Connection对象");
            return;
        }
        // 2. 获取信道
        Channel::ptr mychannel = myconn->getChannel(req->channel_id());
        if (mychannel.get() == nullptr)
        {
        	default_info("解除绑定队列失败,因为获取信道失败");
            return;
        }
        mychannel->unBind(req);
    }

    // 6. 订阅/取消订阅队列
    void OnBasicConsume(const muduo::net::TcpConnectionPtr &conn, const BasicConsumeRequestPtr &req, muduo::Timestamp)
    {
        // 1. 先看有无该连接
        Connection::ptr myconn = _connection_manager_ptr->getConnecion(conn);
        if (myconn.get() == nullptr)
        {
            default_info("订阅队列时,没有找到连接对应的Connection对象");
            return;
        }
        // 2. 获取信道
        Channel::ptr mychannel = myconn->getChannel(req->channel_id());
        if (mychannel.get() == nullptr)
        {
            default_info("订阅队列失败,因为获取信道失败");
            return;
        }
        mychannel->basicConsume(req);
    }

    void OnBasicCancel(const muduo::net::TcpConnectionPtr &conn, const BasicCancelRequestPtr &req, muduo::Timestamp)
    {
        // 1. 先看有无该连接
        Connection::ptr myconn = _connection_manager_ptr->getConnecion(conn);
        if (myconn.get() == nullptr)
        {
        	default_info("取消订阅队列时,没有找到连接对应的Connection对象");
            return;
        }
        // 2. 获取信道
        Channel::ptr mychannel = myconn->getChannel(req->channel_id());
        if (mychannel.get() == nullptr)
        {
        	default_info("取消订阅队列失败,因为获取信道失败");
            return;
        }
        mychannel->basicCancel(req);
    }

    // 7. 发布/确认消息
    void OnBasicPublish(const muduo::net::TcpConnectionPtr &conn, const BasicPublishRequestPtr &req, muduo::Timestamp)
    {
        // 1. 先看有无该连接
        Connection::ptr myconn = _connection_manager_ptr->getConnecion(conn);
        if (myconn.get() == nullptr)
        {
        	default_info("发布消息时,没有找到连接对应的Connection对象");
            return;
        }
        // 2. 获取信道
        Channel::ptr mychannel = myconn->getChannel(req->channel_id());
        if (mychannel.get() == nullptr)
        {
        	default_info("发布消息失败,因为获取信道失败");
            return;
        }
        mychannel->basicPublish(req);
    }

    void OnBasicAck(const muduo::net::TcpConnectionPtr &conn, const BasicAckRequestPtr &req, muduo::Timestamp)
    {
        // 1. 先看有无该连接
        Connection::ptr myconn = _connection_manager_ptr->getConnecion(conn);
        if (myconn.get() == nullptr)
        {
        	default_info("确认消息时,没有找到连接对应的Connection对象");
            return;
        }
        // 2. 获取信道
        Channel::ptr mychannel = myconn->getChannel(req->channel_id());
        if (mychannel.get() == nullptr)
        {
        	default_info("确认消息失败,因为获取信道失败");
            return;
        }
        mychannel->basicAck(req);
    }

    muduo::net::EventLoop _baseloop;
    muduo::net::TcpServer _server;

    ProtobufDispatcher _dispatcher;
    ProtobufCodecPtr _codec;

    ConsumerManager::ptr _consumer_manager_ptr;
    VirtualHostManager::ptr _vhost_manager_ptr;
    threadpool::ptr _pool_ptr;

    ConnectionManager::ptr _connection_manager_ptr;
};

五、服务器源文件编写,编译与运行

#include "broker.hpp"

int main()
{
    ns_mq::Server server(8888);
    server.start();
    return 0;
}

注意:因为muduo_base库要依赖muduo_net库,所以要先连接muduo_net,后连接muduo_base

server:server.cc ../mqthird/include/proto/codec.cc ../mqcommon/mq_msg.pb.cc ../mqcommon/mq_proto.pb.cc
	g++ -o $@ $^ -std=c++11 -L ../mqthird/lib -I ../mqthird/include -lprotobuf -pthread -lmuduo_net  -lmuduo_base -lsqlite3 -lz 
.PHONY:clean
clean:
	rm -f server

在这里插入图片描述
编译并运行成功
在这里插入图片描述
套接字的确处于监听状态
等到我们写完客户端之后在进行一次功能联合大测试,到时候统一排查BUG

在这里插入图片描述
至此,服务器大功告成,总共3347行代码

以上就是项目第十弹:连接管理模块与服务器模块的全部内容

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

program-learner

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值