正在加载...
分页: 2/4 第一页 上页 1 2 3 4 下页 最后页 [ 显示模式: 摘要 | 列表 ]

架构师(6月刊)

[ 2010/06/23 10:21 | by selboo ]
点击在新窗口中浏览此图片

篇首语:寻找内在的力量
      
正在研究Rsync, 在Rsync的主页上看到了这篇对Rsync解释的不那么枯燥的文章. 尝试着翻译了一下. 原文虽然在大方向上清晰的解释了Rsync的工作方式, 但在具体的细节的时候还是觉的很晦涩. 在有些地方, 完全去我自己理解后的"意译"了. 对于这篇文章, 也许重写是个更好的选择.

这是我第一次翻译技术文档, 疏漏难免, 欢迎斧正.

原文URL,  http://samba.anu.edu.au/rsync/how-rsync-works.html , 不知作者是谁.

How Rsync Works
A Practical Overview
从实际应用的角度看Rsync是如何工作的
前言     对于理解Rsync的算法理论和机制, 最初的RsyncTechnicalReport和AndrewTridgell的博士论文都是非常优秀的文档. 不幸的是, 他们都太过于理论, 而缺乏Rsync实践应用.
在这份文档中我希望能够描述

-一份非数学的Rsync算法综述
-这个算法在Rsync程序中是如何实现的
-Rsync使用的协议
-Rsync各个进程的一个可定义的角色


这份文档对于需要更多信息的编程者可以作为一份指导手册, 但是最主要的作用是从读者能够理解的角度给他们一个以下几方面的基础知识,

-为什么Rsync是这样表现的
-Rsync的限制
-为什么有些功能请求对于目前的程序是不合适的.


这篇文档泛泛的描述了Rsync的构成和行为. 在有些情况下, 为了满足一个更广泛的目标, 有些可以使文档更准确的细节和特例被牺牲掉了.
进程和他们所扮演的角色    当我们谈论Rsync的时候, 我们使用了特定的名词来代表Rsync执行过程中不同的进程和他们所扮演的角色. 为了更好的沟通, 我们使用相同的语言是很重要的. 我们在使用某一特定的名词的时候指的是同一件事情. 在Rsync邮件列表上, 总有关于进程和角色的疑惑. 出于这个原因我将定义几个名词, 接下来我会用他们描述进程和相应的角色.

客户机     角色       初始化同步任务的一方.
服务器     角色       指客户机通过本地传输, 或者远端SHELL, 或者网络插口
                 , 连接到远端的Rsync进程或者系统. 这是一个通用的名
                 词, 不应该和守护程序混淆. 一旦客户机和服务器的连
                 接建立, 这两个名词会被发送端和接收端替代.
守护程序    角色和进程    守护程序指等待客户机连接的一个Rsync进程. 在某些平
                 台上, 它被称为一项服务.
远端SHELL   角色和一组进程  一个或者多个进程, 能够提供客户机和远端的服务器的
                 连接.
发送端     角色和进程    能够访问需要同步的源文件的Rsync进程.
接收端     角色和进程    作为一种功用, 接收端是一个目标系统. 作为一个进程
                 , 接收端接受需要更新的数据并把它们写到磁盘上.
生成器     进程       生成器进程确认改变了的文件, 并管理文件等级逻辑.

进程的启动    客户机启动后的第一件事是和服务器建立连接. 这个连接可以通过管道或者通过网络插口.

当Rsync 通过一个远端SHELL和一个没有启动守护程序的服务器通讯的时候, Rsync所使用的启动方法是在远端系统上派生一个远端SHELL,然后使用这个远端SHELL启动一个Rsync进程. Rsync的客户机和服务器通过远端SHELL的管道进行通讯. (As far as the rsync  processes concerned there is no network. --不知道怎么翻译)  在这种模式下, Rsync服务器选项被传送给命令行, 用于启动远端SHELL.  
当Rsync和一个守护程序通讯的时候, 它直接和网络插口通讯. 这是唯一一种可以被称为涉及网络的的Rsync通讯. 在这种模式下, Rsync的选项必须发送到网络插口上. 下面是具体的描述.
客户机和服务器最开始通讯的时候, 他们各自发送自己所支持的最高的协议版本号给对方. 两边会使用其中的小的版本作为用来传输的协议版本. 如果是一个守护模式连接, Rsync的参数会被从客户机发送给服务器. 然后, 排出列表会被传送. 然后, 客户机服务器的关系就只和错误和日志发送有关了.

本地的Rsync任务(原地址和目标地址都是本地挂载的文件系统)就像一个推送. 客户机, 作为发送端, 派生一个服务器进程去行使服务器的功能. 客户机/发送端和服务器/接收端通过管道相互通讯.


文件列表    文件列表不仅包括路径名,也包括所有者,模式, 读写权限, 大小和修改时间. 如果设置了--checksum选项, 文件列表还要包括文件的校验值.

Rsync启动完成后的第一件事, 发送端会建立文件列表. 在建立过程中, 每个条目都会通过一种优化的网络传送方式发送给接收方.

传输结束后, 两侧会以目录对基础目录的相关性来编排顺序. (具体的算法会和每次传输实用的协议版本有关). 一旦排序开始, 所以关于文件的指向都是使用他们在文件列表中的目录顺序.
如果必要, 发送者遵从文件列表中用户和组的id->name对应表 接收者会使用它来为文件列表中的每个文件作id->name->id翻译.

接收端收到完全的文件列表, 会派生出一个生成器, 和接收端一起建立一个完整的管道.

管道    Rsync严重依赖於管道. 这意味着一组进程间的的单向通讯. 一旦文件列表被共享, 管道就表现为如下的形式,  生成器->发送端->接收端
生成器的输出是发送端的输入, 发送端的输出是接收端的输入. 每个进程独立的运行, 只有在管道延迟,或者等待硬盘读写或CPU资源的时候才会有延迟.

生成器    生成器比较文件列表和本地目录树. 如果设置了--delete参数, 在开始它的主要工作前, 它首先会甄别在本地存在而在发送端上不存在的文件, 然后在接收端删除它们.

接下来生成器会开始遍历文件列表. 每个文件都被检查, 以确定是否需要同步. 大多数情况下如果修改时间和大小不同, 文件需要同步.  如果设置了--checksum, 文件校验会被计算并比较. 目录, 设备文件和链结不会被跳过. 缺失的目录会被创建.

如果一个文件需要同步, 在接收端的任何版本的该文件都会被作为一个传输的"基础文件". "基础文件"作为一个数据源, 两侧比较下来一致的数据就不需要被传输了. 为了更有效的在远端匹配数据, 基础文件的块校验被计算, 并和文件的目录号一起送给发送端. 如果设置了--whole-file, 空的块校验值用于新文件.
块大小, 以及在后期的版本中块校验的大小, 是基于每个文件的大小计算的.

发送端   发送端进程一次从生成器读一组文件号和相关联的块校验.

对每一个生成器发送的文件号, 发送端会存储块校验, 并建立一个哈希索引以快速检索.接着本地文件会被读取, 生成一个从文件的第一个字节开始的块作的校验. 这个校验会和生成器发过来的校验比较, 如果不相符, "不匹配"的字节会被加入到不匹配的数据中, 接着比较下一个字节的块. 这被称为"循环校验"

如果一个块的校验匹配就会被认为是一个匹配的块, 已经积累的不匹配块会被发送给接收端, 一起发送的还有块的偏移量和在接受端文件中的匹配块的长度. 块校验生成器会提前去检查匹配字节后面的一个字节.
即使块的顺序或者偏移量不同, 以这种方法匹配的块也能够被确认. 这个程序是Rsync最核心的算法.
通过这种方式, 发送者告诉接收端如何重组源文件成为一个目标文件. 这些指令包括所有的可以从基础文件拷贝的数据(如果存在的话), 和任何本地没有的新的数据, 的细节. 在处理末尾, 一个全文件的校验会被发送, 然后发送端去处理下一个文件.

生成循环校验以及在校验中找到匹配的数据, 对CPU的能力有很大的需求. 在所有的Rsync进程中,发送端是最消耗CPU资源的.


接收端    接收端会从发送端的数据中读取由文件索引号确认的文件. 然后打开本地文件(被称为基础文件), 建立一个临时文件.

接收端会读取非匹配数据和匹配数据, 并按顺序重组他们成为最终文件. 当非匹配数据被读取, 它会被写入到临时文件. 当收到一个块匹配记录, 接收端会寻找这个块在基础文件中的偏移量, 将这个块拷贝到临时文件. 通过这种方式, 临时文件被从头到尾建立起来.

建立临时文件的时候生成了文件的校验. 重建文件结束后, 这个校验和来自发送端的校验比较. 如果校验不符, 临时文件会被删除. 如果失败一次, 文件会再被处理一次. 如果失败第二次, 一个错误会被报告.
临时文件建立后, 所有者, 权限和修改时间会被设置. 然后它会被重命名已替代基础文件.

从基础文件拷贝数据到临时文件, 使接收端成为所有进程中对硬盘要求最高的一个. 小文件还有可能在缓存中, 可以减轻对硬盘的压力; 但是对于大文件, 在生成器去处理下一个文件的时候,或者还有由发送端造成的时延, 缓存中已经无法容纳更多的数据,只能清除掉旧的. 另外, 数据是随机的从一个文件中读取, 并被写入另外一个, 如果读写的数据超过了硬盘缓存空间, 一个所谓的"寻找风暴"有可能发生, 会进一步的损害性能.

守护程序    守护程序, 向所有的其他守护进程一样, 为每一个连接派生子进程. 启动的时候, 它解释rsyncd.conf, 以确认存在的模块, 并设置一些全局变量.

当接收到一个对已经定义的模块的连接时, 守护进程派生一个子进程去处理这个连接. 这个子进程然后去读取rsyncd.conf, 为被请求的模块设置变量, 这个工作有可能改变模块的root路径, 或者抛弃已设定的用户号和组号. 然后, 它就像其他的Rsync服务进程一样, 或者作为发送端, 或者作为接收端.

Rsync协议   一个良好定义的通讯协议有以下几个特性,

-所有的数据都在良好定义的包中发送, 包括包头, 可选的包体, 或者数据净荷.
-每个包头中, 明确的指定数据类型或者命令.
-每个包都有一个确定的长度.


除了这些特性以外, 协议还应支持不同等级的状态, 包与包间的独立性, 人类可读性, 和重建一个断掉连接的能力.

Rsyncs协议不具备任何以上一点优秀特性. 数据作为不间断的字节流被传输. 不匹配的文件数据是一个特例, 没有包长度, 没有计数器. 每个字节的意义都是根据协议等级决定的, 都是独立的.

例如, 发送端要发送一个文件列表, 它就是简单的发送文件列表中的每一条, 发送结束就是一个NULL字节. 在文件列表的每一条中, 有一个比特表示数据的结构, 这些变长的字符串只是被NULL字节简单的终结. 发送端发送文件索引号和块校验对的时候, 工作方式是一样的.

在可靠的连接上, 这种方法工作的很好, 它比正式的协议拥有较少的开销. 但是很不幸, 它造成协议很难被文档化, 调试, 或者扩展. 每个版本的协议在线路上表现的都不同, 除非知道确切的版本号才可以参与.

后记
文档还在继续被整理. 作者知道一定有一些明显的疏忽, 对于有些读者来说, 它更容易造成混乱, 而不是清晰. 希望它可以进化成一个有用的参考.欢迎提意见, 甚至重写的建议.
Tags:
点击在新窗口中浏览此图片

这个结构的优点:
1、可以使用nginx前端进行诸多复杂的配置,这些配置从前在squid是没法做或者做起来比较麻烦的,比如针对目录的防盗链。
2、nginx前端可以直接转发部分不需要缓存的请求。
3、因为nginx效率高于squid,所以某些情况下可以利用nginx的缓存来减轻squid压力。
4、可以实现url hash等分配策略。
5、可以在最前端开启gzip压缩,这样后面的squid缓存的纯粹是无压缩文档,可以避免很多无谓的穿透。
6、因为nginx稳定性比较高,所以lvs不需要经常调整,通过nginx调整就可以。
7、squid的文件打开数按默认的1024就绰绰有余,不过处理的请求可一个都不会少。
8、可以启用nginx的日志功能取代squid,这样做实时点击量统计时可以精确定位到url,不必要再用低效率的grep来过滤。
9、因为nginx的负载能力高于squid,所以在用lvs分流时可以不必分得特别均衡,出现单点故障的几率比较低。
Tags: ,

海量小文件系统架构方案

[ 2009/12/14 08:24 | by selboo ]
现在的网站越做越大了,存储的东西越来越多,如何解决这些文件存储也成了新的难题。如果把这些文件都完全采用大硬盘存储来解决,并不是一个好主意,因为数据量越大风险就越高,虽然文件能存得下,但是故障率相应会较高,另外重建耗费时间也比较长。所以最好的办法是尽可能考虑分布式存储,把文件想办法利用网络分散到多个机器上。
从我所了解的存储结构来看,分布式存储大致可以分为几种:

1、类googlefs的分布式文件系统
因为目前googlefs没有开源,所以网上出现的分布式文件系统都是利用google的方案自行实现的。这个方案的优点是可用性比较高,基本上基于硬盘的应用都可以处理,可用范围就比较广泛。我看了gfs、gfs2、ocfs2、FastDFS、MogileFS的一些相关介绍,大致有一些认识。
首先是文档比较少而出现的问题倒不少;然后是目前这些还没有一个能称得上是稳定版本,如果有的话,估计也就是其中一些收费的版本。因为磁盘存储乃是致关重要,所以目前建议还是不要轻易把这些东西部署到重要的地方。假如非常想使用的话,最好是做好充分测试,确保它的功能完全能够满足需要;然后还要想办法在传统的文件系统中做好完全的备份,以免造成损失。
另外可以提的一个东西是memcached,这个东西实现了内存的分布式共享,稳定度貌似比以上这些分布式文件系统要稳定。不过是完全基于内存的,如果数据量不是很大,可以一试。

2、手工使用文件路径分散存储
这个结构通常使用在web静态文件中,就以这种情形作为例子。
如果这些文件数量比较大,可以通过分散文件路径,把某个文件的访问指定到特定的一台或几台服务器上。例如:
1)采用域名的分散策略
例如使用a.xxx.com/b.xxx.com…来区分标记为a或b的一系列文件,这些文件存储的时候,依然按照标记,存到a或b的服务器上。这个策略将区分机器的任务交由dns服务器来执行,扩容时会相应轻松。这需要web项目初期就规划好这些东东,后期才转用域名策略的成本比较高甚至不可以实现。
2)采用目录的分散策略
假如域名初期并没有规划使用域名策略,那么可以采用代理服务器来进行目录级的划分。比如一般存储大量文件时,因为文件系统的限制以及效率问题,都会按照一定规则划分了很多级的目录,按这些目录拆分机器也并不是困难的事情。这种架构的问题在于代理服务器的性能和可靠性问题,需要在这点上稍下一点功夫。
以上这两个方案,都要自行制定策略实现分散同步传输,传输一般可以归纳为推送和抓取两种办法,同步的话可以采用日志同步(把要同步的数据记入日志,通过日志记录来传输相应文件)、比较同步(使用rsync等同步软件)或即时同步(有新的修改就立刻传输);另外要实现单点故障剔除的话,首先找一个策略把文件存储到多个节点上,例如,a.xxx.com或目录a的文件相应也存到b和c节点;然后在环境中使用故障剔除技术(lvs或nginx等),就可以解决问题,例如:采用域名的话,可以采用lvs,缺点是使用的机器就会成倍增加;亦可再用一级代理服务器,缺点是会牺牲性能。采用目录的话,因为本身就用到了代理服务器,所以只要存储得当,实现比较容易。

Tags:

nginx图片服务器的架构方案

[ 2009/12/14 08:23 | by selboo ]
图片服务通常数据容量较大,而且访问也频繁,鉴于此,图片服务就会有两种问题,一是存储问题,二是访问量问题。

存储问题就是硬盘容量问题,花钱买硬盘就可以了,看似简单,但着实也是最苦的问题。按目前探索来看,最好的方式是:在任何时刻遇到硬盘空间不够时,买颗硬盘插上,最多改改配置,就能立刻利用;另外,硬盘要能充分利用,不然图片存储量大再加上备份,很恐怖,最好是每颗硬盘都用上100%的空间。
访问量也是个大问题,如果服务不允许防盗链,那么访问量会引起带宽、服务器压力等问题,有钱的话直接扔CDN,没钱或者有更多的钱,就自己做吧。根据垣古不变的真理“越老的图,访问量也相对较少”这一点,分成两大部分,一边处理最新的图片,一边处理老旧的图片。最新的图片访问量大,但存储量较少;老图片访问量低,但存储量大。
大概分析完了,开始制定方案。

一、拟定一个存储目录规则:
在现有的/a/b/abcde.jpg这样的hash方式下多加一个日期的目录变成:/200810/16/a/b/abcde.jpg或者/2008/10/16/a/b/abcde.jpg。按日期制定这个目录规则后,就可以按年月来拆机器了。

二、分机器,分硬盘
按之前的计划,分成两个组,一组服务器用lvs做负载均衡负责新图片;另一组服务器做旧图片访问和备份。新图机器找几台好点的服务器,SCSI硬盘;旧图机器没太大要求,PC机就行,找够硬盘就可以,现在IDE的1T硬盘也不太贵,最好再搭个raid就省事了,最主要是这些机器要多。
照这个图,搭一搭

点击在新窗口中浏览此图片

说明一下:
1、图片服务通过lvs作为入口,处理能力上还是有保障的。
2、利用nginx直接对外服务,不必用squid。
3、图中的红线是指主nginx会将/2006和/2007年的图片分别代理到两台存档服务器,如果发现主nginx的cpu占用比较大,那么可以考虑使用nginx的proxy_store将图片存到主服务器上,定期清理。
4、图中有一台存储分配服务器,作为图片服务更新图片的统一入口,有新图片或者修改图片的话,由这台服务器负责将图片放到正确的服务器上去。
5、旧图片服务器当前用年份来划分,每年增加两台服务器,亦可是加两块硬盘,注意,不要相信raid,一定要有两台机器,地理上分在两个城市则更好。
6、因为旧数据2006和2007年的数据基本上是没有变化的,所以假如硬盘够大,那么可以把两年的数据合并在一起。
7、如果细心定制,那么旧图片服务器的硬盘100%塞满是可以的,旧数据的容量基本上不会大幅增长,小小预留1-2G空间就可以了。
使用这个架构的话,到了2009年,我会把2008年的数据想办法迁到旧图服务器上,硬盘不够的话,加硬盘就可以了。如果图片量实在太大,主服务器连一年的数据都装不下,那可以用启用月份来划分;如果一个月都装不下了,那也太夸张了,那就启用日期吧;如果一天的数据都装不下,那就◎#¥%……※。
Tags: , ,
分页: 2/4 第一页 上页 1 2 3 4 下页 最后页 [ 显示模式: 摘要 | 列表 ]