背景:随着计算机网络技术的发展和普及,出现了越来越多像”淘宝“,”京东“等大型电子商务网站。这类网站都保存有大量图片资源。用户在访问这些站 点网页时,网页中图片信息占到页面数据流量的大部分。由于受客户端浏览器限制,无法从一台服务器上同时下载页面中的所有图片信息,因此即使服务器又很高带 宽,用户的放弃问速度还是会受到很大影响,由于图片保存在物理硬盘上,访问图片需要频繁进行I/O操作,因此当并发用户数越来越多的时候,I/O操作就会 成为整个系统的性能瓶颈。
对于大型的网站系统来说,由于拥有雄厚的资金,可以使用NFS,CDN,Lighttpd等技术(好吧,除了CDN,在JS中稍微有点认识,其 他都没听过),提高用户的访问速度。但这些技术需要庞大的资金支持,对于处于创业初期中等规模的商务网站,由于缺少必要的资金支持,因此无法采用这些技术 提升网站的访问速度。今天我们讲一个适用于中等规模商务网站的海量图片数据分布式动态存储及负载均衡的解决方案。该方案只需增加很少的硬件成本,即可提升 网站的访问速度,并且可以根据需要动态调整图片服务器的数量及图片的存储目录,确保系统具有可扩展性和伸缩性。
系统架构设计需满足一下4点要求:
以上只是为了随笔的完整性而收录,好了,直接进入技术阶段。
本随笔就以图片为例,对分布式存储进行一个简单的介绍。流程是这样的:
存储阶段:用户上传图片-->web服务器接收-->存储到另一台图片服务器上
请求阶段:用户请求图片-->web服务器从数据库获取图片信息,请求对应图片服务器-->对应图片服务器进行I/O操作获取图片返回
以上过程,我都尽量简化了。不要在乎细节~
好了,先说说存储阶段,粗一看很容易理解,不过具体实施起来,就需考虑实现所需的技术了,其中的关键就是web服务器接收图片后如何存储到图片服务器上。这里(笔者玩.net)有这么几种技术(评论可以补充):
【1】、将数据进行数据流操作后,即变成bytu[],通过webClient,来进行两个站点间的数据传输。
byte[] fileByte = 将图片处理成byte[]; string imageUrl = "http://192.168.2.41:8899/UploadImage.ashx?Id=1&Ext=png"; WebClient wc = new WebClient(); wc.UploadData(imageUrl , fileByte);
//图片服务器接收(这里用的是一般处理程序) int imageId = Convert.ToInt32( context.Request["Id"]); string ext = context.Request["Ext"]; //这两个就组成了我们图片的名称,当然名称如何组成可以你自己定 using(FileStream fs = File.OpenWrite("D:/"+imageId+ext)) { //获取传输过来的Byte[] //context.Request.InputStream:web服务器wc.UploadData的第二个参数的数据流 context.Request.InputStream.CopyTo(fs); }
【2】、通过在图片服务器中设置一个小的web站点,然后建立一个webService,web服务器通过这个开放的服务进行数据传输,同理,用我们的wcf,webapi也可以实现。这个懂的自然懂,不懂的可以查阅下资料哈,也不是一两行代码能解释的。
【3】、我们的web服务器与图片服务器放置在一起的话,其实就可以是个局域网,这样一来,我们直接可以将图片服务器的某个文件夹进行”共享“,这样我们的web服务器直接就可以进行I/O操作。
目前为止,我们存储阶段的流程是走通了,但是问题来了:
Q1:如果我们有多台图片服务器呢?
A1:首先,我们应该抓住问题重点,多台服务器,多台怎么了,造成什么问题了?回头看上面【1】的代码,会有哪些变动?就四行代码,很容易看 出,多台服务器的区别就是我们的IP变多个了,我们应该用哪个呢?多台的存在肯定是一台不够用,所以我们需要让它们雨露均沾对吧。由此,引出了上文背景的 一个词儿:负载均衡。好了,不卖关子,其实很简单,不就是对IP这个字符串数组进行管理嘛,首先建个表,记录我们的IP以及服务器状态(可用不可用之 类),其他的字段可以任意扩展。然后我们将图片数据流传输前,就先用随机算法获取我们数据表中的IP,然后WebClient或其他技术,传输即可。
注意点:图片服务器对图片进行存储完后,需对web服务器说一声,我这边存完了,好让web服务器进行后续操作,如将图片信息保存入数据库,字 段应该有:图片Id,图片所在的图片服务器IP,存储路径,时间等。怎么”说一声“呢,很简单,如果传输用的 webClient的话,”说一声“也用这个不就行了。随便在web服务器定义个接口,可以也是一般处理程序,然后定义个接收字段,约束下即可。如果是用 wcf,则更简单,方法定义为回调函数即可(我好啰嗦啊,不是怕你们看不懂,是怕我自己日后看不懂~)
说完存储阶段,下面的请求阶段就很简单了。
用户请求图片-->web服务器从数据库获取图片信息,请求对应图片服务器-->对应图片服务器进行I/O操作获取图片返回
请求图片,肯定是带着Id来的,这样我们就从图片信息数据表中查询出对应信息,主要是存储该图片的对应图片服务器的IP,存储路径。然后WebClient等技术请求即可。额,好像完事儿了,确实很简单。
好了,一堆文字,看着烦。用图片总结下:
两张表的设计:
补充:上面提到的满足负载均衡的算法,这里给个小例子:
var list = db.ImageServerInfo.Where<ImageServerInfo>(c=>c.State==1).ToList(); int serverCount = list.Count(); Random r = new Random(); int i = r.Next(); int j = i%r; //获取状态为1(normal)的图片服务器总数,随机数去对其取余。得到的就是我们的随机索引,于是就可通过list[j]去获取ServerId,然后再去获取ServerIP