博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
高并发集群式的文件传输系统
阅读量:3941 次
发布时间:2019-05-24

本文共 2979 字,大约阅读时间需要 9 分钟。

文章目录

总体设计思路

集群是多个服务器同时去做一个事情,相比于单个的服务器,集群可同时处理更多的业务,但是集群的设计上也必须要处理好各个服务器之间的关系,首先要考虑的就是服务器的负载均衡、数据的备份、多个服务器之间的数据的同步的问题,要根据服务器实现的业务来确定服务器采用什么样的同步的方式才能使得同步的效率更高。

在本次的项目中,采用一个负载均衡器,负责根据服务器的当前的负载的情况向客户端分发一个负载相对较小的服务器,然后负载均衡器断开与客户端的连接,客户端拿到服务器的地址后,直接与服务器连接,中间的数据不经过负载均衡器。

这样做的原因,因为本系统是文件传输系统客户端与服务器的交互大多是上传下载,逻辑判断的部分不是主要消耗资源的部分,如果每条消息都经过负载均衡器,所有的客户端的连接都集中在负载均衡器上,这样就丧失了集群的优点。

负载均衡器除了要负责向客户端分发服务器以外,还要处理服务器的数据同步的问题,如果有一台新上线的服务器,新的服务器要先向负载均衡器发送信息同步的请求,这时候,负载均衡器分配一个服务器向新上线的服务器发送同步的数据。

这样每台服务器既需要于客户端进行交互,又需要与其他的服务器进行交互,如果只通过一个端口进行数据的收发,对服务器来说就需要进行的消息来源的判断。这样在服务器端的编程的难度会加大,逻辑也不容易理清,所以,这里我才用一台服务器监听两个端口的数据,一个端口负责与客户端的通信,另一个端口负责与其他的服务器的交互。着这样两个端口的信息的通信并不影响,采用并发的方式的需要也不容易出错。

对于数据同步问题的处理,这个问题我分了两种情况

  1. 不存在新上线的服务器的情况
    这是最简单的情况,采用的方法就是当本地服务器上的数据发生改变的时候,立即向其他的服务器发送数据同步的消息,其他服务器根据消息做出相应的改变。
  2. 当有新上线的服务器正在同步数据,这时候其他的服务器上有同步的消息发过来。
    处理的方式,当新的服务器上线的时候,向负载均衡器发送一个同步的请求,负载均衡器这时候生成一个时间戳,发送给两个要进行数据同步的服务器,新上线的服务器需要同步的是该时间戳之前的数据,在该时间戳之后的消息都被视为新的同步消息二保存起来,当两个服务器之间的数据同步全部完成的时候,两台服务器开始处理新的同步请求。
    服务器数据的备份,这里没有去刻意备份服务器上的数据,而是采用多台服务器之间互为备份的方法,数据同步就是备份的过程。即能保证多台服务器之间数据的一致性,也有备份的过程。

服务器端的设计

在同步的方式下,当我们发送给一个请求的时候,希望得到对方的确认,如果此刻我们阻塞等待消息的到来,而一个线程同时处理着多个连接,此时其他的请求我们也就不能去执行了,这对于其他的客户端是不公平的,而如果我们此刻不再等待去处理其他的请求,当该确认消息到达的时候,我们怎样确定用什么样的方式的去处理该确认消息呢?

因为每个连接处理是串行的,例如:当一个客户端在上传文件的时候,不能进行其他的操作,只能等待,这样我们可以给每个连接一个状态,当接收到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/

你可能感兴趣的文章
Python pass语句作用与用法
查看>>
Java double,float设置小数点位数
查看>>
PyCharm & Jupyter
查看>>
为什么要用Jupyter Notebook
查看>>
sklearn中的LogisticRegression模型
查看>>
pandas.get_dummies 的用法
查看>>
机器学习-训练模型的保存与恢复(sklearn)
查看>>
Spark(二): spark-submit命令详解
查看>>
细品 - 逻辑回归(LR)*
查看>>
hive: size与spilt连用
查看>>
Python:ModuleNotFoundError: No module named 模块名 错误及解决方案
查看>>
Python中os与sys两模块的区别
查看>>
nohup详解
查看>>
idea .gitignore对.idea不起作用解决
查看>>
深度学习中的注意力机制(2017版)-易理解
查看>>
Transformer解析-易理解
查看>>
多维数组[:,0]和[:0:1]获取的区别
查看>>
复原Ip地址
查看>>
重建二叉树
查看>>
二叉树根节点到叶子节点的路径数字之和
查看>>