本文共 2979 字,大约阅读时间需要 9 分钟。
集群是多个服务器同时去做一个事情,相比于单个的服务器,集群可同时处理更多的业务,但是集群的设计上也必须要处理好各个服务器之间的关系,首先要考虑的就是服务器的负载均衡、数据的备份、多个服务器之间的数据的同步的问题,要根据服务器实现的业务来确定服务器采用什么样的同步的方式才能使得同步的效率更高。
在本次的项目中,采用一个负载均衡器,负责根据服务器的当前的负载的情况向客户端分发一个负载相对较小的服务器,然后负载均衡器断开与客户端的连接,客户端拿到服务器的地址后,直接与服务器连接,中间的数据不经过负载均衡器。
这样做的原因,因为本系统是文件传输系统客户端与服务器的交互大多是上传下载,逻辑判断的部分不是主要消耗资源的部分,如果每条消息都经过负载均衡器,所有的客户端的连接都集中在负载均衡器上,这样就丧失了集群的优点。
负载均衡器除了要负责向客户端分发服务器以外,还要处理服务器的数据同步的问题,如果有一台新上线的服务器,新的服务器要先向负载均衡器发送信息同步的请求,这时候,负载均衡器分配一个服务器向新上线的服务器发送同步的数据。
这样每台服务器既需要于客户端进行交互,又需要与其他的服务器进行交互,如果只通过一个端口进行数据的收发,对服务器来说就需要进行的消息来源的判断。这样在服务器端的编程的难度会加大,逻辑也不容易理清,所以,这里我才用一台服务器监听两个端口的数据,一个端口负责与客户端的通信,另一个端口负责与其他的服务器的交互。着这样两个端口的信息的通信并不影响,采用并发的方式的需要也不容易出错。
对于数据同步问题的处理,这个问题我分了两种情况
在同步的方式下,当我们发送给一个请求的时候,希望得到对方的确认,如果此刻我们阻塞等待消息的到来,而一个线程同时处理着多个连接,此时其他的请求我们也就不能去执行了,这对于其他的客户端是不公平的,而如果我们此刻不再等待去处理其他的请求,当该确认消息到达的时候,我们怎样确定用什么样的方式的去处理该确认消息呢?
因为每个连接处理是串行的,例如:当一个客户端在上传文件的时候,不能进行其他的操作,只能等待,这样我们可以给每个连接一个状态,当接收到up 的命令后,我们知道接下来的数据就是文件的内容,这时候,服务器接收到一个up 请求之后,就将该链接的状态改为RECVINF_STATUS ,在接收的消息之后,判断是这个状态,就将接收到的消息写到文件中。
这样对于我们可以给每一个连接设定几个状态,以标志下一条消息的处理方式 服务器端的几个状态#define RECV_STATUS 1 //正在接收文件#define CMD_STATUS 2 //客户端发送过来的消息作为命令处理#define WAIT_AFFIRM 3 //等待客户端的确认#define SEND_STATUS 4 //正在发送文件
客户端的几个状态:
#define DOWN_AFFIRM_STATUS 9 //下载文件时等待对方的确认状态#define UP_AFFIRM_STATUS 5 //上传文件的时候等待对方的确认状态#define RECVING_STATUS 6 //正在接收文件的状态#define SENDING_STATUS 7 //正在发送文件的状态#define CMD_STATUS 8 //命令状态,此刻从键盘终端获取消息的状态
多并发的方式:采用muduo的reactor模式,对于一个客户端的处理只能是一个线程,不能出现多个线程同时处理一个客户端请求的情况。
新上线的服务器要向负载均衡服务器发送给自己的ip地址和端口号,负载均衡服务器接收到信号之后,要通知一个在线的服务器,向新上线的服务器发送同步的消息。
服务器端保存的是文件都是以文件的hash 值命名,文件名存储在数据库中,与相应的hash 对应,其结构为:
第一个表中存储文件名, hash , flag 表示该文件是否完整, flag的作用是,如果文件不完整,当客户端再次上传的时候,先判断文件名,再判断hash值,如果都相同的话,从断点的位置开始上传,不需要从头在重新上传 hash 的作用是,当上传的文件在本地已经存在的时候,不需要重复上传,这样就节省了上传的时间。 因为当接受到新的文件的时候,当前的服务器需要向其他的服务器发送同步数据的消息,如果上传的消息本地已经存在,就不用在向其他的服务器来发送同步的消息了,这样对其他的服务器也是一种节省开销的方式,对用户来说,提高了上传的速度,也是一种很好的用户体验, 第二个表,hash 值相同的文件的引用计数。允许上传文件名不同的相同文件,这里会在第一个数据库中添加一个新的文件名对应的项,在第二个表中该文件对应的hash 的计数会加一, 删除一个文件的时候,相应的引用计数减一,当引用计数为0的时候,删除数据库中的记录,并且删除本地文件。负载均衡服务器处在客户端与服务器的中间,起始就在一开始的时候在两者之间,之后当客户端与服务器连接的时候,负载均衡器与客户端的连接就断开了,所以负载均衡服务器的开销还是很小的,因为这种客户端与服务器的连接属于长连接,不会出现客户端频繁请求负载均衡服务器的情况。
客户端同样采用了非阻塞IO 的方式,尽管这里我觉得是没有必要的,因为在设计的最初,就确定了操作是串行的,当上传文件的时候,不能其他的操作。在具体的实现的过程中其实是可以的,客户端这里采用了异步的方式,当没有消息的时候,会阻塞在键盘的输入端,获取下一条命令。同样,在客户端并没有在指定的位置去等待消息的到来,这样,当消息到来的时候,怎让判断消息的类型,用谁来处理消息,成了问题。
这里采用了与服务器同样的方式,设置当前客户端的状态。以不同的状态处理不同的消息的类型,之所以能够这样做,就是因为tcp通信的特点,消息到来的顺序并不会改变,所以不管有多小跳消息到来,我们都能确定每条消息在缓冲区中的位置,从而取出指定的消息进行过操作。 总体的框架就是这样,具体实现可以根据不同的功能进行填充。对异步IO 消息的处理
对集群的整体的设计思想 以上的设计并不都是在最初的时候就设计出的,很对都是在实现的时候,根据出现的bug 修改出得出的。所以编程的实践很重要。转载地址:http://aanwi.baihongyu.com/