跨平台开发集群聊天项目零——大纲

项目由三个:服务器、客户端、负载均衡器

一、服务器

服务器分为四层:socket、service、model、mysql,除此之外还有main.cpp、json.cpp、public.cpp等文件
注:虽然网络层是调用muduo层,数据库层是调用mysql数据库,但我们仍需要封装库函数,并构成独立党的类,而不是在其他模块中随意去调用

  1. 网络层:
    成员变量:muduo::net::TcpServer _server; 作用:选定网络协议
    成员函数:
    (1)构造:绑定IP地址和端口号,绑定链接断开的回调函数onConnection,绑定服务层的回调函数onMessage。后两者onConnection和onMessage还需封装成私有成员方法。
    (2)start:启动,由于构造已经绑定了回调函数,开始后就可以处理业务了。
class Socket
{
public:
	Socket(muduo::net::EventLoop *loop, const muduo::net::InetAddress &addr)
		:_server(loop, addr, "ChatServer")
	{
		_server.setConnectionCallback(bind(&Socket::onConnection, this, _1));
		_server.setMessageCallback(bind(&Socket::onMessage, this, _1, _2, _3));
	}
	void start() {_server.start();}
	
private:
	muduo::net::TcpServer _server;
	
	void onConnection(const muduo::net::TcpConnectionPtr &con);
	void onMessage(const muduo::net::TcpConnectionPtr &con, muduo::net::Buffer *buf, muduo::Timestamp time);
};
  1. service层
    服务层就要具体处理业务,这里总的框架是基类派生类:基类(BaseService)、单服务器(SingService)、多服务器(ClusterService)

2.1 BaseService
按照以前的思维,服务层业务处理函数,网络层通过switch语句调用,但是这次,我们模仿MVC模型实现这个功能。
service层(MVC模型)成员函数:service层基类函数成员为unordered_map<消息类型,处理函数>,通过键值对的方法由消息类型找相应的处理方法。
订阅-》构造函数:将消息类型和相应的处理函数插入哈希表中,处理函数订阅相应的消息类型
发布-》发布肯定不可能在服务层,它实现在网络层,由网络层发布消息,具体就在上面的两个回调函数中,由键访问值。

class BaseService
{
	using Handler = std::function<void(const muduo::net::TcpConnectionPtr&, json&, muduo::Timestamp)>;
public:
	BaseService()
	{
		_handlerMap[MSG_LOGIN] = bind(&BaseService::Login, this, _1, _2, _3);//这里绑定this,不然类型就出错了
		_handlerMap[MSG_REG] = bind(&BaseService::Register, this, _1, _2, _3);
		_handlerMap[MSG_ADD_FRIEND] = bind(&BaseService::addFriend, this, _1, _2, _3);
		_handlerMap[MSG_ONE_CHAT] = bind(&BaseService::oneChat, this, _1, _2, _3);
	}

	virtual void Login(const muduo::net::TcpConnectionPtr& con, json& js, muduo::Timestamp time) = 0;
	virtual void Register(const muduo::net::TcpConnectionPtr& con, json& js, muduo::Timestamp time) = 0;
	virtual void addFriend(const muduo::net::TcpConnectionPtr& con, json& js, muduo::Timestamp time) = 0;
	virtual void oneChat(const muduo::net::TcpConnectionPtr& con, json& js, muduo::Timestamp time) = 0;

	std::unordered_map<int, Handler> handler()const//获取map{return _handlerMap;}
private:	
	std::unordered_map<int, Handler> _handlerMap;
};

2.2 SingService:
(1)业务处理函数是虚函数,派生类会覆盖。(2)由于网络层中开辟多个线程来处理业务,但是根本不需要构造多个服务层对象,所以SingService实现为单例模式(懒汉模式)。

class SingService : public BaseService
{
public:
	static SingService* getInstance()
	{
		static SingService instance;
		return &instance;
	}
	SingService(const SingService&) = delete;
	SingService operator=(const SingService&) = delete;

	virtual void Login(const muduo::net::TcpConnectionPtr& con, json& js, muduo::Timestamp time){}
	virtual void Register(const muduo::net::TcpConnectionPtr& con, json& js, muduo::Timestamp time){}
	virtual void addFriend(const muduo::net::TcpConnectionPtr& con, json& js, muduo::Timestamp time){}
	virtual void oneChat(const muduo::net::TcpConnectionPtr& con, json& js, muduo::Timestamp time){}

private:
	std::unique_ptr<userModel> userModelPtr;
	std::unique_ptr<friendModel> friendModelPtr;

	SingService() :userModelPtr(new userModel()), friendModelPtr(new friendModel()){}
};

2.3 ClusterService
多服务层是将单服务器的代码粘贴过来再进行扩展。试想如果聊天的双方不在同一台服务器上,那么就必须要一个消息中间站radis。
radis同service比较类似,service是转发客户端过来的消息,radis也是转发消息,不过它转发的是另一台服务器的消息。
radis的网络模块和MVC模型,源码都已经帮我们实现好了,同理,不去直接调用,而是封装成一个类myRadis。
radis看作是服务器,myRadis是客户端。
成员函数:
redisContext* _context; 订阅通道
redisContext* _pubcontext; 发布通道
channelHandler _channelHandler; 回调函数
(1)由于radis要向myRadis转发,所以myRadis中需要提供成员方法connect,作用是将myRadis的地址端口号发送过去。
(2)subscribe、unsubscribe、publish分别是订阅、取消订阅、发布。注意,这里不要因为服务器即使发布方又是订阅方就共用一个通道。试想一下,MVC模式大部分情况都是,发布方是发布方,订阅放是订阅方,在设计radis时应该把他们发到一起吗。
(3)notifyMsg检测通道消息,注意,它必须放到单独的线程里,不然会阻塞线程,试想一下,线程一直在这里检测消息,那么网络层的消息过来怎么办。

class RedisServer
{
public:
	RedisServer(){}
	~RedisServer(){}
	bool connect()
	{
		this->_context = redisConnect(redisHost.c_str(), port);
		this->_pubcontext = redisConnect(redisHost.c_str(), port);
	}
	
	void subscribe(int channel)
	{
		redisAppendCommand(this->_context, "SUBSCRIBE %d", channel))
	}
	void publish(int channel, std::string msg)
	{
		redisReply* reply = (redisReply*)redisCommand(this->_pubcontext, "PUBLISH %d %s", channel, msg.c_str());
    }
	void unsubscribe(int channel)
	{
		redisReply* reply = (redisReply*)redisCommand(this->_context, "UNSUBSCRIBE %d", channel);
	}
	
	void notifyMsg() {}
	using channelHandler = std::function<void(std::string)>;
	void setChannelMsgHandler(channelHandler handler)
	{
		_channelHandler = handler;
	}
private:
	redisContext* _context;
	redisContext* _pubcontext;
	channelHandler _channelHandler;
};

二、客户端

三、负载均衡器

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值