那些分布式系统设计常用的开源模块

云计算 分布式
分布式系统是个由多个互相连接的处理资源组成的计算机系统,它们在整个系统的控制下协同执行同一个任务,最少依赖于集中的程序、数据或硬件。本文对大家常用的分布式系统设计开源模块进行了总结,包括HBase、Nginx、Zookeeper。

HBase开发:

HBase

HBase介绍

HBase - Hadoop Base 是一个高可靠、高性能的、面向列、可伸缩的分布式系统、利用HBase技术可在廉价PC Server上搭建大规模结构化存储集群。HBase是Google Bigtable的 java开源实现,类似Google Bigtable利用GFS作为其文件存储系统,HBase利用Hadoop HDFS作为其文件存储系统,Google运行MapReduce来处理Bigtable中的海量数据,HBase同样利用Hadoop MapReduce来处理HBase中的海量数据,Google Bigtable利用Chubby作为协同服务,HBase利用Zookeeper作为协同服务。

HBase使用

适用场景

HBase的这些特点适用于数据海量、持续增长的情况,例如开源监控平台OpenTSDB;网易的logtracer数据存储、监控平台的数据存储;淘宝的交易历史记录查询系统,淘宝指数等。

Facebook公司整理HBase集群使用的场景如下:

  1. 需要很高的写吞吐量
  2. 在大规模数据集中进行随机访问
  3. 方便的水平扩展
  4. 结构化和半结构化数据
  5. 不需要全部的关系数据库特性,例如交叉列、交叉表,事务,连接等等

HBase表的设计

HBase的表包含如下属性:

(1)最大版本数:通常是3,如果对于更新比较频繁的应用完全可以设置为1,能够快速的淘汰无用数据,对于节省存储空间和提高查询速度有效果。

(2)压缩算法:可以尝试一下最新出炉的snappy算法,相对lzo来说,压缩率接近,压缩效率稍高,解压效率高很多。

(3)inmemory:表在内存中存放,一直会被忽略的属性。如果完全将数据存放在内存中,那么hbase和现在流行的内存数据库memorycached和redis性能差距有多少,尚待实测。

(4)bloomfilter:根据应用来定,看需要精确到rowkey还是column。不过这里需要理解一下原理,bloomfilter的作用是对一个region下查找记录所在的hfile有用。即如果一个region下的hfile数量很多,bloomfilter的作用越明显。适合那种 compaction赶不上flush速度的应用。

默认情况下,在创建HBase表的时候会自动创建一个region分区,当导入数据的时候,所有的HBase客户端都向这一个region写数据, 直到这个region足够大了才进行切分。一种可以加快批量写入速度的方法是通过预先创建一些空的regions,这样当数据写入HBase时,会按照 region分区情况,在集群内做数据的负载均衡。

有关预分区,详情参见:Table Creation: Pre-Creating Regions

HBase的RowKey设计

rowkey是hbase的key-value存储中的key,通常使用用户要查询的字段作为rowkey,查询结果作为value。可以通过设计满足几种不同的查询需求。

数字rowkey的从大到小排序:原生hbase只支持从小到大的排序,这样就对于排行榜一类的查询需求很尴尬。那么采用rowkey = Integer.MAX_VALUE-rowkey的方式将rowkey进行转换,最大的变最小,最小的变最大。在应用层再转回来即可完成排序需求。

rowkey的散列原则:如果rowkey是类似时间戳的方式递增的生成,建议不要使用正序直接写入rowkey,而是采用reverse的方式反转rowkey,使得rowkey大致均衡分布,这样设计有个好处是能将regionserver的负载均衡,否则容易产生所有新数据都在一个 regionserver上堆积的现象,这一点还可以结合table的预切分一起设计。

columnfamily columnfamily尽量少,原因是过多的columnfamily之间会互相影响。

Column 对于column需要扩展的应用,column可以按普通的方式设计,但是对于列相对固定的应用,最好采用将一行记录封装到一个column中的方式,这样能够节省存储空间。封装的方式推荐protocolbuffer。

Hbase的row key设计较为灵活,根据业务需求可灵活运用,在此不展开说明。Row key 高级设计

HBase Memstore

hbase.hregion.memstore.flush.size参数注意

hbase.hregion.memstore.flush.size 是出发系统对Memstore进行flush的阈值,但是该参数并非单个Memstore flush的阈值而是一个Region内所有Memstore的大小之后阈值

深入理解HBase Memstore

MemStore是HBase非常重要的组成部分,深入理解MemStore的运行机制、工作原理、相关配置,对HBase集群管理以及性能调优有非常重要的帮助。

首先通过简单介绍HBase的读写过程来理解一下MemStore到底是什么,在何处发挥作用,如何使用到以及为什么要用MemStore。

当RegionServer(RS)收到写请求的时候(write request),RS会将请求转至相应的Region。每一个Region都存储着一些列(a set of rows)。根据其列族的不同,将这些列数据存储在相应的列族中(Column Family,简写CF)。不同的CFs中的数据存储在各自的HStore中,HStore由一个Memstore及一系列HFile组成。 Memstore位于RS的主内存中,而HFiles被写入到HDFS中。当RS处理写请求的时候,数据首先写入到Memstore,然后当到达一定的阀值的时候,Memstore中的数据会被刷到HFile中。

用到Memstore最主要的原因是:存储在HDFS上的数据需要按照row key 排序。而HDFS本身被设计为顺序读写(sequential reads/writes),不允许修改。这样的话,HBase就不能够高效的写数据,因为要写入到HBase的数据不会被排序,这也就意味着没有为将来的检索优化。为了解决这个问题,HBase将最近接收到的数据缓存在内存中(in Memstore),在持久化到HDFS之前完成排序,然后再快速的顺序写入HDFS。需要注意的一点是实际的HFile中,不仅仅只是简单地排序的列数据的列表,详见Apache HBase I/O – HFile

除了解决“无序”问题外,Memstore还有一些其他的好处,例如:

作为一个内存级缓存,缓存最近增加数据。一种显而易见的场合是,新插入数据总是比老数据频繁使用。

在持久化写入之前,在内存中对Rows/Cells可以做某些优化。比如,当数据的version被设为1的时候,对于某些CF的一些数据,Memstore缓存了数个对该Cell的更新,在写入HFile的时候,仅需要保存一个最新的版本就好了,其他的都可以直接抛弃。

有一点需要特别注意:每一次Memstore的flush,会为每一个CF创建一个新的HFile。在读方面相对来说就会简单一些:HBase首先检查请求的数据是否在Memstore,不在的话就到HFile中查找,最终返回merged的一个结果给用户。

HBase Memstore关注要点

迫于以下几个原因,HBase用户或者管理员需要关注Memstore并且要熟悉它是如何被使用的:

  1. Memstore有许多配置可以调整以取得好的性能和避免一些问题。HBase不会根据用户自己的使用模式来调整这些配置,你需要自己来调整。
  2. 频繁的Memstore flush会严重影响HBase集群读性能,并有可能带来一些额外的负载。
  3. Memstore flush的方式有可能影响你的HBase schema设计

Memstore配置要点

第一组是关于触发“普通”flush,这类flush发生时,并不影响并行的写请求。该类型flush的配置项有:

hbase.hregion.memstore.flush.size

base.regionserver.global.memstore.lowerLimit

需要注意的是第一个设置是每个Memstore的大小,当你设置该配置项时,你需要考虑一下每台RS承载的region总量。可能一开始你设置的该值比较小,后来随着region增多,那么就有可能因为第二个设置原因Memstore的flush触发会变早许多。

第二组设置主要是出于安全考虑:有时候集群的“写负载”非常高,写入量一直超过flush的量,这时,我们就希望memstore不要超过一定的安全设置。在这种情况下,写操作就要被阻止(blocked)一直到memstore恢复到一个“可管理”(manageable)的大小。该类型 flush配置项有:

hbase.regionserver.global.memstore.upperLimit

hbase.hregion.memstore.block.multiplier

某个节点“写阻塞”对该节点来说影响很大,但是对于整个集群的影响更大。HBase设计为:每个Region仅属于一个RS但是“写负载”是均匀分布于整个集群(所有Region上)。有一个如此“慢”的节点,将会使得整个集群都会变慢(最明显的是反映在速度上)。

提示:严重关切Memstore的大小和Memstore Flush Queue的大小。理想情况下,Memstore的大小不应该达到 hbase.regionserver.global.memstore.upperLimit的设置,Memstore Flush Queue 的size不能持续增长。

频繁的Memstore Flushes带来的影响

要避免“写阻塞”,貌似让Flush操作尽量的早于达到触发“写操作”的阈值为宜。但是,这将导致频繁的Flush操作,而由此带来的后果便是读性能下降以及额外的负载。

每次的Memstore Flush都会为每个CF创建一个HFile。频繁的Flush就会创建大量的HFile。这样HBase在检索的时候,就不得不读取大量的HFile,读性能会受很大影响。

为预防打开过多HFile及避免读性能恶化,HBase有专门的HFile合并处理(HFile Compaction Process)。HBase会周期性的合并数个小HFile为一个大的HFile。明显的,有Memstore Flush产生的HFile越多,集群系统就要做更多的合并操作(额外负载)。更糟糕的是:Compaction处理是跟集群上的其他请求并行进行的。当 HBase不能够跟上Compaction的时候(同样有阈值设置项),会在RS上出现“写阻塞”。像上面说到的,这是最最不希望的。

提示:严重关切RS上Compaction Queue 的size。要在其引起问题前,阻止其持续增大。

想了解更多HFile 创建和合并,可参看 Visualizing HBase Flushes And Compactions。

理想情况下,在不超过hbase.regionserver.global.memstore.upperLimit的情况下,Memstore应该尽可能多的使用内存(配置给Memstore部分的,而不是真个Heap的)。

HLog (WAL) Size & Memstore Flush

当数据被写入时会默认先写入Write-ahead Log(WAL)。WAL中包含了所有已经写入Memstore但还未Flush到HFile的更改(edits)。在Memstore中数据还没有持久化,当RegionSever宕掉的时候,可以使用WAL恢复数据。

当WAL(在HBase中成为HLog)变得很大的时候,在恢复的时候就需要很长的时间。因此,对WAL的大小也有一些限制,当达到这些限制的时候,就会触发Memstore的flush。Memstore flush会使WAL 减少,因为数据持久化之后(写入到HFile),就没有必要在WAL中再保存这些修改。有两个属性可以配置:

l hbase.regionserver.hlog.blocksize

l hbase.regionserver.maxlogs

WAL的最大值由hbase.regionserver.maxlogs * hbase.regionserver.hlog.blocksize (2GB by default)决定。一旦达到这个值,Memstore flush就会被触发。所以,当你增加Memstore的大小以及调整其他的Memstore的设置项时,你也需要去调整HLog的配置项。否则,WAL 的大小限制可能会首先被触发,因而,你将利用不到其他专门为Memstore而设计的优化。抛开这些不说,通过WAL限制来触发Memstore的 flush并非最佳方式,这样做可能会会一次flush很多Region,尽管“写数据”是很好的分布于整个集群,进而很有可能会引发flush“大风暴”

提示:最好将hbase.regionserver.hlog.blocksize * hbase.regionserver.maxlogs 设置为稍微大于hbase.regionserver.global.memstore.lowerLimit * HBASE_HEAPSIZE.

HBase Compaction

Compaction是buffer->flush->merge的Log-Structured Merge-Tree模型的关键操作,主要起到如下几个作用:

  1. 合并文件
  2. 清除删除、过期、多余版本的数据
  3. 提高读写数据的效率

HBase 有两种方式的Compaction,Minor和Major

  1. Minor Compaction操作只用来做部分文件的合并操作以及包括minVersion=0并且设置ttl的过期版本清理,不做任何删除数据、多版本数据的清理工作。
  2. Major Compaction操作是对Region下的HStore下的所有StoreFile执行合并操作,最终的结果是整理合并出一个文件。

#p#

Compaction如何发生

在什么情况下会发生Compaction呢?

CompactionChecker是RS上的工作线程,设置执行周期是通过threadWakeFrequency指定,大小通过 hbase.server.thread.wakefrequency配置(默认10000),然后乘以默认倍数multiple(1000),毫秒时间转换为秒。因此,在不做参数修改的情况下,CompactionChecker大概是2hrs, 46mins, 40sec执行一次。

首先,对于HRegion里的每个HStore进行一次判断,needsCompaction()判断是否足够多的文件触发了Compaction 的条件。条件为:HStore中StoreFIles的个数 – 正在执行Compacting的文件个数 > minFilesToCompact

操作:以最低优先级提交Compaction申请。

在大多数情况下,Major是发生在storefiles和filesToCompact文件个数相同,并且满足各种条件的前提下执行。这里进行几个参数配置的简介:

  1. hbase.hregion.majorcompaction: 设置系统进行一次MajorCompaction的启动周期,如果设置为0,则系统不会主动触发MC过程。
  2. hbase.hstore.compaction.max:设置执行Compaction(包括Major &Minor)的待合并文件的最大个数。默认值为10,如果超过该设置值,会对部分文件执行一次MinorCompaction,选择算法如 Figure1。
  3. hbase.hstore.compactionThreshold: 设置执行Compaction(Major && Minor)操作的阈值,默认是3,如果想降低过频繁的合并操作,可以稍微调大一点,对于HBase负载较重的系统,可以设置成5。

Compaction对读写操作的影响

Compaction与Flush不同之处在于:Flush是针对一个Region整体执行操作,而Compaction操作是针对Region上的一个Store而言,因此,从逻辑上看,Flush操作粒度较大。这属于一个LSM存储模型最核心的设计:

Flush操作如果只选择某个Region的Store内的MemStore写入磁盘,而不是统一写入磁盘,那么HLog上key的一致性在Reigon不同ColumnFamily(Store)下的MemStore内就会有不一致的key区间。

如下图所示,我们假定该RegionServer上仅有一个Region,由于不同的Row是在列簇上有所区别,就会出现有些不同Store内占用的内存不一致的情况,这里会根据整体内存使用的情况,或者RS使用内存的情况来决定是否执行Flush操作。如果仅仅刷入使用内存较大的 memstore,那么在使用的过程中,一是Scan操作在执行时就不够统一,二是在HLog Replayer还原Region内Memstore故障前的状态,只需根据Hlog的Flush_marker的标记位来执行Replay即可。

Compaction执行结束之后会生成临时文件,临时文件所在的hdfs位置如下:/hbase-weibo/bi_weibo_cluster /ffd87a50c3df3080183d4910d183d0ee/.tmp ffd87a50c3df3080183d4910d183d0ee 是bi_weibo_cluster表格的Region名。临时文件的意义在于,在Compaction执行期间,对于原数据访问没有影响。 Compaction执行合并操作生成的文件生效过程,需要对Store的写操作加锁,阻塞Store内的更新操作,直到更新Store的 storeFiles完成为止。(注意,这个操作过程执行会影响到更新服务,但是影响不会太大)

对于读服务的影响,类似于Flush操作,也是通过ChangedReaderObserver为StoreScanner注册监听类来实现的。具体内容可以参考之前的”HBase Flush操作流程以及对读写服务的影响”。

HBase Region Server Compact过程占用大量网络出口带宽的问题

该问题较为普遍,当Regionserver带宽被大量占用的时候,客户端的写入基本全都超时,有网友分析并提供了解决方案。

HBase Region

Region的分配问题

当Region数量变多的时候,就需要把它们分配给不同的Region Server来保证每个Region Server都有事情做,但是当region数量远远大于Region Server个数的时候,就需要保证一个load balance问题,不能让一个Region Server上有大量的regions,而其他Region Server上只有几个regions,这样很可能导致负载不均衡。

Region split问题

hbase通过split region实现水平的sharding,但在split的过程中旧的region会下线,新region还会做compaction,中间有一段时间大量的数据不能被读写,这对于我们这种online系统是不能忍受的。我们同样禁掉自动的split,而在晚上系统空闲时执行我们的splittool手动的split。

禁用方法把hbase.hregion.max.filesize参数值设成较大的值例如536870912000

(500g)

在对Region进行手工split的过程中,出现以下异常

./hbase org.apache.hadoop.hbase.util.RegionSplitter -r lsmp_lottery_presplit HexStringSplit

这个HBase的一个bug,HBase0.94 出现以上错误,需要打补丁即可,https://issues.apache.org/jira/browse/HBASE-7048

HBase写客户端优化

多HTable并发写

创建多个HTable客户端用于写操作,提高写数据的吞吐量。另外还可以使用HTablePool来实现多线程写入。

HTable参数设置

  1. Auto Flush

通过调用HTable.setAutoFlush(false)方法可以将HTable写客户端的自动flush关闭,这样可以批量写入数据到 HBase,而不是有一条put就执行一次更新,只有当put填满客户端写缓存时,才实际向HBase服务端发起写请求。默认情况下auto flush是开启的。

  1. Write Buffer

通过调用HTable.setWriteBufferSize(writeBufferSize)方法可以设置 HTable客户端的写buffer大小,如果新设置的buffer小于当前写buffer中的数据时,buffer将会被flush到服务端。其 中,writeBufferSize的单位是byte字节数,可以根据实际写入数据量的多少来设置该值。

  1. WAL Flag

在HBae中,客户端向集群中的RegionServer提交数据时(Put/Delete操作),首先会先写WAL(Write Ahead Log)日志(即HLog,一个RegionServer上的所有Region共享一个HLog),只有当WAL日志写成功后,再接着写 MemStore,然后客户端被通知提交数据成功;如果写WAL日志失败,客户端则被通知提交失败。这样做的好处是可以做到RegionServer宕机 后的数据恢复。

因此,对于相对不太重要的数据,可以在Put/Delete操作时,通过调用Put.setWriteToWAL(false)或Delete.setWriteToWAL(false)函数,放弃写WAL日志,从而提高数据写入的性能。

需要注意的是:谨慎选择关闭WAL日志,因为这样的话,一旦RegionServer宕机,Put/Delete的数据将会无法根据WAL日志进行恢复。

批量写入

通过调用HTable.put(Put)方法可以将一个指定的row key记录写入HBase,同样HBase提供了另一个方法:通过调用HTable.put(List)方法可以将指定的row key列表,批量写入多行记录,这样做的好处是批量执行,只需要一次网络I/O开销,这对于对数据实时性要求高,网络传输RTT高的情景下可能带来明显的 性能提升。

多线程并发写

在客户端开启多个HTable写线程,每个写线程负责一个HTable对象的flush操作,这样结合定时flush和写 buffer(writeBufferSize),可以既保证在数据量小的时候,数据可以在较短时间内被flush(如1秒内),同时又保证在数据量大的 时候,写buffer一满就及时进行flush。

其他

enable和disable表时出现表未disable/enable异常处理

今天在为表增加LZO压缩,在enable表时,发现耗时很长都未结束,就ctrl+c退出hbase shell,再进入继续enable表,但此时出现如下错误:

错误说表未禁用,但进行disable表操作时,又出现如下错误:

那肯定是刚才强行退出导致出问题了,根据大家的经验,指出这是因为zookeeper保持了要enable的这个表信息,只需要登录zk删除该记录就行。

登录到zookeeper也有两种方式,一是到zk目录运行脚本连接,而是直接在hbase安装目录bin下运行:

hbase zkcli

都是小写字母。之后就能连接上zookeeper了,跟着删除/hbase/table下对应的表项就行:

delete /hbase/table/user_video_recommend2

之后就可以成功的enable表了。

如果此时仍然失败,则可以运行一下命了修复META:

hbase hbck -fixMeta -fixAssignments

HBase元数据损坏修复

hbase hbck -repair -sidelineDir hdfs://mycluster/hbase/rbu

但是出问题的时候有可能出现RIT中大量任务无法结束,只要没有长期PENDING_OPEN的任务,就可以先将有问题的表先disable一下。这里要提到的是hbase的hbck很弱,会形成一个死循环,一方面region由于元数据破坏无法开启需要repair,造成大量的RIT长期hang 在上面,但是repair的操作却要等待这些RIT结束才能继续,而且repair操作也不是离线操作,必须要先保证hbase能正常起来。因此打破死循环的关键就是先处理掉RIT中hang的操作,这需要经验和仔细排查。

如果出现长期的pending_open,则要观察pending_open的原因,要尝试解决,这次我们出现的问题是元数据出错,某个 Region的对row的加锁范围超出region本身的key的取值范围,引起不断地PENING_OPEN,通过离线的merge操作将该 region随便找了个region merge下解决了该问题,./hbase org.apache.hadoop.hbase.util.Merge

最后可通过hbck恢复元数据,但是hbck恢复过程中可能会丢失数据,因此要慎用,最好通过SNAPSHOT做备份来解决这类问题

HBaseClient异常:java.io.IOException: Connection reset by peer

hbase客户端每次和regionserver交互的时候,都会在服务器端生成一个租约(Lease),租约的有效期由参数hbase.regionserver.lease.period确定。

客户端去regionserver取 数据的时候,hbase中存得数据量很大并且很多region的时候的,客户端请求的region不在内存中,或是没有被cache住,需要从磁盘中加 载,如果这时候加载需要的时间超过hbase.regionserver.lease.period所配置的时间,并且客户端没有和 regionserver报告其还活着,那么regionserver就会认为本次租约已经过期,并从LeaseQueue从删除掉本次租约,当 regionserver加载完成后,拿已经被删除的租约再去取数据的时候,就会出现如上的错误现象。

解决的办法:

1)、适当的增大 hbase.regionserver.lease.period参数的值,默认是1分钟

2)、增大regionserver的cache大小

#p#

Nginx:

概要介绍

Nginx是一款高性能的HTTP和反向代理服务器,也是一个IMAP/POP3/SMTP代理服务器。第一个公开版本发布于2004年10月4日,其将源代码以类BSD许可证的形式发布,因它的稳定性、丰富的功能集和低系统资源的消耗而闻名。Nginx的最新稳定版本是1.4.4。与Apache、Lighttpd不同,Nginx充分使用异步逻辑,削减了上下文调度开销,所以并发服务能力更强。Nginx整体采用模块化设计,有丰富的模块库和第三方模块库,配置灵活。 在Linux操作系统下,Nginx使用epoll事件模型,得益于此,其在效率相当高。在国内,各大主流站点都在使用Nginx作为Web服务器或反向代理服务器。据统计,截止今年2013年7月,在全球Top 1000网站中,有34.9%的网站在使用Nginx,这使得Nginx超越Apache,成为了TOP站点最信任的Web服务器。下图可见Nginx在 TOP站点的受欢迎程度(见橙色条目)。

 

主要功能

  • 轻松搞定C10K问题,且占内存极少,据称C10K idle连接仅需内存2.5MB;
  • 处理静态文件的性能非常优秀,支持sendfile、AIO等高级特性,支持静态文件Auto Index;
  • 支持反向代理,对后端服务进行健康检查;
  • 支持反向代理缓存,避免与后端的频繁交互;
  • 支持邮件服务代理:IMAP、POP3、SMTP;
  • 支持负载均衡策略,包括:round-robin、weight、hash、consistent hash、fair等;
  • 支持HTTPS安全访问;
  • 支持虚拟主机的配置;
  • 支持基本Web认证机制;
  • 支持基于IP的访问控制;
  • 支持URL规则重写;
  • 支持并发连接数限制和单条连接的限速;
  • 支持WebSocket和SPDY;
  • 支持Referer 过滤防盗链;
  • 支持灵活配置输出日志;

扩展功能

  • 能够在配置文件中嵌入ngx_perl和ngx_lua脚本,扩展功能超强;
  • 提供了与Memcached、Redis、MySQL、PostgreSQL、MangoDB、MogileFS、TaobaoFS等常见服务器交互的模块;
  • Image 模块实现了简单的图片功能,包括:缩略、裁剪、质量参数等;
  • 视频支持包括:FLV和MP4视频播放,RTMP模块具有在线ffmpeg转码以及M3U8的支持;
  • PageSpeed模块实现网页加速功能,具体包括:图片优化,CSS&JS 压缩合并,资源内联,HTML重写;
  • 与后端Tomcat的连接可以使用AJP 模块进行协议优化;
  • Push 模块实现长连接轮询服务,实现Web推送服务(Comet);
  • UploadRate 模块实现上传的流量限制功能;

常用配置

虚拟主机

  1. http { 
  2.   index index.html; 
  3.   
  4.   # 虚拟主机 www.domain1.com; 
  5.   server { 
  6.     server_name www.domain1.com; 
  7.     access_log logs/domain1.access.log main; 
  8.     root /var/www/domain1.com/htdocs; 
  9.   } 
  10.   # 虚拟主机 www.domain2.com 
  11.   server { 
  12.     server_name www.domain2.com; 
  13.     access_log  logs/domain2.access.log main; 
  14.     root /var/www/domain2.com/htdocs; 
  15.   } 

负载均衡

  1. # 默认的负载均衡,Round-Robin 
  2. upstream backend1  { 
  3.   server backend1.example.com; 
  4.   server backend2.example.com; 
  5.   server backend3.example.com; 
  6. # 基于权重的负载均衡 
  7. upstream backend2  { 
  8.   server backend1.example.com weight=5; 
  9.   server backend2.example.com weight=2; 
  10.   server backend3.example.com weight=3; 
  11. # 基于request_uri hash的负载均衡 
  12. upstream backend3  { 
  13.   server backend1.example.com; 
  14.   server backend2.example.com; 
  15.   server backend3.example.com; 
  16.   hash   $request_uri; 
  17. # 基于request_uri consistent hash的负载均衡 
  18. upstream backend4  { 
  19.   server backend1.example.com; 
  20.   server backend2.example.com; 
  21.   server backend3.example.com; 
  22.   consistent_hash   $request_uri; 
  23. # 基于后端响应时间的负载均衡 
  24. upstream backend5  { 
  25.   server backend1.example.com; 
  26.   server backend2.example.com; 
  27.   server backend3.example.com; 
  28.   fair; 
  29. server { 
  30.   location / { 
  31.     proxy_pass  http://backend1; 
  32.     # proxy_pass  http://backend2; 
  33.     # proxy_pass  http://backend3; 
  34.     # proxy_pass  http://backend4; 
  35.     # proxy_pass  http://backend5; 
  36.   } 

以上可见,Nginx支持的负载均衡策略非常全面,总结有五种策略,即:

  • Round-Robin:轮询;
  • Weight:基于权重;
  • Hash:基于Nginx内置参数的哈希,内置参数可以为ClientIP、Request_URI等;
  • Consistent Hash:基于Nginx内置参数的一致性哈希;
  • Fair:公平策略,根据响应时间来判定应该将请求发往那个后端服务器;

除此之外,Nginx负载均衡还支持以下关键参数:

  • down:表示当前的server暂时不参与负载;
  • max_fails:允许重试的最大次数,当达到该次数,Nginx将暂停向该server发送请求;
  • fail_timeout:max_fails次失败后,暂停的时间;
  • backup: 其它所有的非backup机器down或者忙的时候,请求backup机器,这台机器压力会最轻;

流量限制

  1. server { 
  2.   location / { 
  3.     root html;            # 静态文件root目录 
  4.     limit_rate_after 1m;  # 文件下载1M后 
  5.     limit_rate 100k;      # 限制其下载流量为100k 
  6.   } 

注:limit_rate只能限制单条连接的流量

连接限制

  1. http { 
  2.   # 并发连接统计粒度为 $binary_remote_addr 
  3.   # 粒度还可以为 $request_uri 等任意的Nginx内置变量 
  4.   limit_conn_zone  $binary_remote_addr  zone=one:2m; 
  5.   server { 
  6.     location /download { 
  7.       limit_conn  one  100; # 同一个remote_addr只允许100个并发连接 
  8.     } 
  9.   } 

代理缓存

  1. server { 
  2.   # 设置Web缓存区名称为cache_one,内存缓存空间大小为200MB,1天没有被访问的内容自动清除,硬盘缓存空间大小为30GB。 
  3.   proxy_cache_path /data0/proxy_cache_dir levels=1:2 keys_zone=cache_one:200m inactive=1d max_size=30g; 
  4.   location / { 
  5.     # 如果后端的服务器返回502、504、执行超时等错误,自动将请求转发到upstream负载均衡池中的另一台服务器,实现故障转移。 
  6.     proxy_next_upstream http_502 http_504 error timeout invalid_header; 
  7.     proxy_cache cache_one; 
  8.     # 对不同的HTTP状态码设置不同的缓存时间 
  9.     proxy_cache_valid  200 304 12h; 
  10.     # 以域名、URI、参数组合成Web缓存的Key值,Nginx根据Key值哈希,存储缓存内容到二级缓存目录内 
  11.     proxy_cache_key $host$uri$is_args$args; 
  12.     proxy_set_header Host  $host; 
  13.     proxy_set_header X-Forwarded-For  $remote_addr; 
  14.     proxy_pass http://backend_server; 
  15.     expires 1d; 
  16.   } 

注1:Nginx反向代理缓存应用于云阅读缩略图服务的缓存;

注2:Nginx仍旧以小文件的方式存储缓存数据,导致磁盘IOUtil较高,当前线上使用SSD;

视频播放

  1. server { 
  2.   # mp4后缀的文件走mp4播放模块 
  3.   location ~ \.mp4$ { 
  4.     mp4; 
  5.     mp4_buffer_size       1m; 
  6.     mp4_max_buffer_size   5m; 
  7.     mp4_limit_rate        on; 
  8.     mp4_limit_rate_after  30s; 
  9.   } 
  10.   # flv后缀的文件走flv播放模块 
  11.   location ~ \.flv$ { 
  12.     flv; 
  13.   } 

注1:云课堂、公开课就是使用这种方式进行视频播放;

注2:线上环境更加复杂,还编译了第三方的mp4播放模块以支持android4.4的播放;

常见问题

  • CLOUD-6824:与后端服务务必保持长连接,否则将出现大量TIME_WAIT,并且会影响性能;
  • CLOUD-6832:HTTP1.0的请求在过Nginx反向代理时,默认将不会保持Keep-Alive,需要专门进行配置;
  • CLOUD-6833:错误日志error.log是监控Nginx状态的重要途径,可以写脚本监控其输出并报警;
  • CLOUD-6332:Nginx默认会缓存上传和下载的临时文件,大量的上传下载可能导致临时文件目录磁盘IO成为瓶颈,需要适当调整;
  • CLOUD-6519:Nginx与后端并发量不是很大的时候,可以开启流式下载,以避免临时文件导致的额外磁盘IO;
  • CLOUD-7493:Nginx读静态文件时默认不会开启AIO,可能导致Worker进程D状态;
  • CLOUD-7065:Nginx默认情况下,worker_rlimit_nofile为1024,导致大并发请求失败,因此大并发需要设置合理的worker_rlimit_nofile值;
  • CLOUD-6334:对于服务海量请求的Nginx,需要做专门的优化,具体这里有专门的总结;
  • CLOUD-5582:Nginx与后端服务器Keep-Alive连接的数量为 worker_count * keep_alive 配置;
  • CLOUD-7224:可以配置Nginx coredump时产生的core路径,以便事后分析原因;
  • CLOUD-7461:临时文件缓存大小和后端连接的超时需要综合考虑,否则可能出问题;
  • SDFS-497:如果后端服务返回的HTTP请求没有Content-Length头,Nginx默认将使用Transfer-Encoding:chunked对Body进行编码;

#p#

Zookeeper:

ZooKeeper

一、简介

ZooKeeper是一个开源的分布式应用协调服务,以Fast Paxos算法为理论基础,可保证分布式环境中数据的强一致性,可用于解决分布式应用中经常遇到的数据管理问题。

二、ZooKeeper编程模型

ZooKeeper的数据模型风格很像文件系统的目录结构,可看做一个树形结构的数据库,与文件系统不同的是文件系统中的节点有目录和文件的区分, 目录是中间节点,其本身并不存数据,而文件是叶节点,只有内容没有子项;而zookeeper的树形结构中所有的节点上都能存储数据内容,也能包含子项。 其节点被成为zNode,zNode有临时(EPHEMERAL)和递增(SEQUENTIAL)等可选属性,zNode可以通过配置Watcher来监 控状态变化,并引导客户端触发相应操作。关于Zookeeper的介绍和数据模型等可参考以下文章:

http://shiyanjun.cn/archives/474.html

http://stblog.baidu-tech.com/?p=1164

三、应用场景

常见的应用场景如下:

场景1 心跳状态检测

利用zookeeper的EPHEMERAL节点的特征,记录server的活动状态。server宕机时,zookeeper连接断开,EPHEMERAL自动删除,可通过watch EPHEMERAL节点的存在状况判断server的存活状态。

场景2 全局自增ID的生成

利用Zookeeper的SEQUENTIAL的特征,单个节点下的SEQUENTIAL节点名称都带有自增的序号,获取ID是只需创建一个SEQUENTIAL节点即可。

场景3 统一命名服务(Naming Service)

利用zk的树形结构特征,每个节点有全局唯一的路径,可用来提供全局命名。

景4 配置管理(即发布订阅)

利用zookeeper可以配置Watcher的特征,可以将分布式系统的配置写入到不同的树形节点中,客户端对关心的配置项设置watcher,当配置发生变更时自动获取最新配置,从而实现分布式环境下不同节点上配置的实时更新。

场景5 集群管理

利用zookeeper基于fast paxos算法的选举功能,实现集群master节点的选举。可以在集群环境中master宕机时动态选择master,解决传统上很容易出现的单点故障问题。

场景6 共享锁Locks

利用EPHEMERAL_SEQUENTIAL节点的特征,实现共享锁,需要获得锁的 Server 创建一个 EPHEMERAL_SEQUENTIAL 目录节点,然后调用 getChildren方法获取当前的目录节点列表中最小的目录节点是不是就是自己创建的目录节点,如果正是自己创建的,那么它就获得了这个锁,如果不是 那么它就调用 exists(String path, boolean watch) 方法并监控 Zookeeper 上目录节点列表的变化,一直到自己创建的节点是列表中最小编号的目录节点,从而获得锁,释放锁时直接删除自己创建的节点。

场景7 队列管理

ZooKeeper 可以处理两种类型的队列:

1. 当一个队列的成员都聚齐时,这个队列才可用,否则一直等待所有成员到达,这种是同步队列。

2. 队列按照 FIFO 方式进行入队和出队操作,例如实现生产者和消费者模型。关于应用场景的描述和实现具体可参考以下博文:

http://django-china.cn/topic/135/ (原文http://rdc.taobao.com/team/jm/archives/1232 )

四、应用陷阱

并不是任何分布式应用都适合使用ZooKeeper来构建协调服务,以下是几种可能出现的问题:

1. 丢失ZNode上的变更通知

客户端连接到ZooKeeper Server以后,会维护一个TCP连接,在CONNECTED状态下,客户端设置了某个ZNode的Watch监听器,可以收到来自该节点变更的通知并 触发相应的事件。但是如果由于网络异常客户端断开了与ZooKeeper Server的连接,在断开的过程中,是无法收到ZooKeeper在ZNode上发送的节点数据变更通知的。如果使用ZooKeeper的Watch, 必须要寻找保持CONNECTED的Watch,才能保证不会丢失该Watch监控的ZNode上的数据变更通知。

2. 无效ZooKeeper集群节点列表

与ZooKeeper集群交互时,一般情况下客户端会持有一个ZooKeeper集群节点的列表,或者列表的子集,那么会存在如下两种情况: 一种情况是,如果客户端持有ZooKeeper集群节点列表或列表子集,如果列表中的某些节点因为故障退出了集群客户端再次连接这一类失效的节点,就无法 获取服务。所以在应用中使用ZooKeeper集群时,一定要明确这一点,或者跳过无效的节点,或者重新寻找有效的节点继续业务处理,或者检查 ZooKeeper集群,使整个集群恢复正常。

3. 配置导致的性能问题

如果设置Java堆内存(Heap)不合理,会导致ZooKeeper内存不足,会在内存与文件系统之间进行数据交换,导致ZooKeeper的性 能极大地下降,从而可能会影响应用程序。为了避免Swapping问题的出现,主要考虑设置足够的Java堆内存,同时减少被操作系统和Cache使用的 内存,尽量避免在内存与文件系统之间发生数据交换,或者可以将交换限制在一定的范围之内。

4. 事务日志存储设备性能

ZooKeeper会同步事务到存储设备,如果存储设备不是专用的,而是和其他I/O密集型应用共享同一磁盘,会导致ZooKeeper的效率降 低。因为客户端请求ZNode数据变更而发生的事务,ZooKeeper会在响应之前将事务日志写入存储设备,为zookeeper配置专用的存储设备可 以使整个服务以及外部应用都会获得极大地性能提升。

5. ZNode存储大量数据导致性能问题

ZooKeeper的设计初衷是,每个ZNode只存放少量的同步数据(一般一个节点上的数据小于1M),如果存储了大量数据,导致 ZooKeeper每次节点发生变更时需要将事务写入存储设备,同时还要在集群内部复制传播,这将导致不可避免的延迟和性能问题。所以,如果需要与大量的 数据相关,可以将大量数据存储在其他设备中,而只是在ZooKeeper中存储一个简单的映射,如指针、引用等等。

五、其他

  • zookeeper节点的size限制,和超限后的解决方法

Zookeeper单个Znode能存放的数据大小在1M左右,如果数据量过大客户端会出现异常,异常信息类似如下:Packet len5187750 is out of range!

这种情况可以通过修改java环境变量来解决,但是还是需要在应用中避免在zookeeper上存储大量数据的情况,这是一种不好的使用方式。-Djute.maxbuffer=51877500

如:$JAVA -cp “$CLASSPATH” -Djute.maxbuffer=51877500 com.netease.datastream.tool.zkUtil.opers.ZkUtilMain $* 2>&1 &

  • 配置完ZooKeeper集群服务之后要配置Snapshot和Transaction log的定时清理任务

由于所有zookeeper上的数据操作都会触发事务日志,事务日志在达到一定大小之后会归档为镜像文件snapshot,如果不配置定时清理任务,这些文件会很快耗尽磁盘空间。可以在linux的crontab任务列表中配置如下项:# m h dom mon dow command

0 7 * * * /home/ds/zookeeper/bin/zkCleanup.sh -n 5 >/dev/null以上任务表示每日7:00执行清理任务,仅保留最新的5个镜像文件和事务日志。

  • 查看指定某个节点状态

可通过以下脚本echo stat|nc

  • 相关工具

Eclipse插件 update-site http://www.massedynamic.org/eclipse/updates/

Zookeeper浏览器客户端 https://issues.apache.org/jira/secure/attachment/12436620/ZooInspector.zip

附、参考文档

官方文档: http://zookeeper.apache.org/doc/r3.4.5/

安装配置: http://shiyanjun.cn/archives/469.html

原文链接:http://thinkinginjavablog.sinaapp.com/?p=599&utm_source=tuicool&utm_medium=referral

责任编辑:Ophira 来源: 机会永远留给有准备的人
相关推荐

2023-10-08 10:49:16

搜索系统分布式系统

2013-06-18 14:13:43

HDFS分布式文件系统

2021-07-09 05:49:53

分布式代码算法

2022-04-07 17:13:09

缓存算法服务端

2019-09-05 09:02:45

消息系统缓存高可用

2023-05-12 08:23:03

分布式系统网络

2013-06-13 11:29:14

分布式分布式缓存

2022-04-14 10:24:27

分布式系统性能

2015-05-26 11:18:06

分布式系统可扩展性

2023-02-11 00:04:17

分布式系统安全

2017-12-12 14:51:15

分布式缓存设计

2013-01-07 10:29:31

大数据

2023-05-29 14:07:00

Zuul网关系统

2020-10-30 07:47:42

分布式

2015-05-12 13:03:54

开源分布式存储HDFS

2023-11-07 12:00:05

分布式系统数据访问

2017-05-22 09:58:01

虚拟机虚拟化分布式

2017-10-27 08:40:44

分布式存储剪枝系统

2023-10-26 18:10:43

分布式并行技术系统

2015-06-01 10:31:43

微软开源rDSN
点赞
收藏

51CTO技术栈公众号