Archive for March, 2013

作者:AngryFox 分类: Uncategorized March 31st, 2013 暂无评论
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>

int main(void)
{
int i;
for(i=0; i<2; i++){
fork();
printf("-");
 }

 return 0;
}

如果你对fork()的机制比较熟悉的话,这个题并不难,输出应该是6个“-”,但是,实际上这个程序会很tricky地输出8个“-”。

要讲清这个题,我们首先需要知道fork()系统调用的特性,

  • fork()系统调用是Unix下以自身进程创建子进程的系统调用,一次调用,两次返回,如果返回是0,则是子进程,如果返回值>0,则是父进程(返回值是子进程的pid),这是众为周知的。
  • 还有一个很重要的东西是,在fork()的调用处,整个父进程空间会原模原样地复制到子进程中,包括指令,变量值,程序调用栈,环境变量,缓冲区,等等。

所以,上面的那个程序为什么会输入8个“-”,这是因为printf(“-”);语句有buffer,所以,对于上述程序,printf(“-”);把“-”放到了缓存中,并没有真正的输出(参看《C语言的迷题》中的第一题),在fork的时候,缓存被复制到了子进程空间,所以,就多了两个,就成了8个,而不是6个。

另外,多说一下,我们知道,Unix下的设备有“块设备”和“字符设备”的概念,所谓块设备,就是以一块一块的数据存取的设备,字符设备是一次存取一个字符的设备。磁盘、内存都是块设备,字符设备如键盘和串口。块设备一般都有缓存,而字符设备一般都没有缓存

对于上面的问题,我们如果修改一下上面的printf的那条语句为:

1
printf("-\n");

或是

1
2
printf("-");
fflush(stdout);

就没有问题了(就是6个“-”了),因为程序遇到“\n”,或是EOF,或是缓中区满,或是文件描述符关闭,或是主动flush,或是程序退出,就 会把数据刷出缓冲区。需要注意的是,标准输出是行缓冲,所以遇到“\n”的时候会刷出缓冲区,但对于磁盘这个块设备来说,“\n”并不会引起缓冲区刷出的 动作,那是全缓冲,你可以使用setvbuf来设置缓冲区大小,或是用fflush刷缓存。

我估计有些朋友可能对于fork()还不是很了解,那么我们把上面的程序改成下面这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main(void)
{
int i;
for(i=0; i<2; i++){
fork();
//注意:下面的printf有“\n”
printf("ppid=%d, pid=%d, i=%d \n", getppid(), getpid(), i);
}
sleep(10); //让进程停留十秒,这样我们可以用pstree查看一下进程树
return 0;
}

于是,上面这段程序会输出下面的结果,(注:编译出的可执行的程序名为fork)

1
2
3
4
5
6
7
8
9
10
ppid=8858, pid=8518, i=0
ppid=8858, pid=8518, i=1
ppid=8518, pid=8519, i=0
ppid=8518, pid=8519, i=1
ppid=8518, pid=8520, i=1
ppid=8519, pid=8521, i=1
$ pstree -p | grep fork
|-bash(8858)-+-fork(8518)-+-fork(8519)---fork(8521)
|            |            `-fork(8520)
作者:AngryFox 分类: Uncategorized March 31st, 2013 暂无评论

CAP即,一致性(Consistency), 可用性(Availability), 分区容忍性(Partition tolerance);

有数据模型的进化:

  • Key-Value 键值对存储是非常简单而强大的。下面的很多技术基本上都是基于这个技术开始发展的。但 是,Key-Value有一个非常致命的问题,那就是如果我们需要查找一段范围内的key。(陈皓注:学过hash-table数据结构的人都应该知 道,hash-table是非序列容器,其并不像数组,链接,队列这些有序容器,我们可以控制数据存储的顺序)。于是,有序键值 (Ordered Key-Value) 数据模型被设计出来解决这一限制,来从根本上提高数据集的问题。
  • Ordered Key-Value 有序键值模型也非常强大,但是,其也没有对Value提供某种数据模 型。通常来说,Value的模型可以由应用负责解析和存取。这种很不方便,于是出现了 BigTable类型的数据库,这个数据模型其实就是map里有map,map里再套map,一层一层套下去,也就是层层嵌套的key- value(value里又是一个key-value),这种数据库的Value主要通过“列族”(column families),列,和时间戳来控制版本。(陈皓注:关于时间戳来对数据的版本控制主要是解决数据存储并发问题,也就是所谓的乐观锁,详见《多版本并发控制(MVCC)在分布式系统中的应用》)
  • Document databases 文档数据库 改进了 BigTable 模型,并提供了两个有意义的改善。第一个是允许Value中有主观的模式(scheme),而不是map套map。第二个是索引。 Full Text Search Engines 全文搜索引擎可以被看作是文档数据库的一个变种,他们可以提供灵活的可变的数据模式(scheme)以及自动索引。他们之间的不同点主要是,文档数据库用字段名做索引,而全文搜索引擎用字段值做索引。
  • Graph data models 图式数据库 可以被认为是这个进化过程中从 Ordered Key-Value 数据库发展过来的一个分支。图式数据库允许构建议图结构的数据模型。它和文档数据库有关系的原因是,它的很多实现允许value可以是一个map或是一个document。
作者:AngryFox 分类: Uncategorized March 30th, 2013 暂无评论

在终端下使用vim进行编辑时,默认情况下,编辑的界面上是没有显示行号、语法高亮度显示、智能缩进等功能的。为了更好的在vim下进行工作,需要手动设置一个配置文件:.vimrc。
在启动vim时,当前用户根目录下的.vimrc文件会被自动读取,该文件可以包含一些设置甚至脚本,所以,一般情况下把.vimrc文件创建在当前用户的根目录下比较方便,即创建的命令为:
$vi  /root/.vimrc

显示光标
set nohls

设置完后
$:x 或者 $wq
进行保存退出即可。
下面给出一个例子,其中列出了经常用到的设置,详细的设置信息请参照参考资料:
“双引号开始的行为注释行,下同
“去掉讨厌的有关vi一致性模式,避免以前版本的一些bug和局限
set nocompatible
“显示行号
set nummber
“检测文件的类型
filetype on
“记录历史的行数
set history=1000
“背景使用黑色
set background=dark
“语法高亮度显示
syntax on
“下面两行在进行编写代码时,在格式对起上很有用;
“第一行,vim使用自动对起,也就是把当前行的对起格式应用到下一行;
“第二行,依据上面的对起格式,智能的选择对起方式,对于类似C语言编
“写上很有用
set autoindent
set smartindent
“第一行设置tab键为4个空格,第二行设置当行之间交错时使用4个空格
set tabstop=4
set shiftwidth=4
“设置匹配模式,类似当输入一个左括号时会匹配相应的那个右括号
set showmatch
“去除vim的GUI版本中的toolbar
set guioptions-=T
“当vim进行编辑时,如果命令错误,会发出一个响声,该设置去掉响声
set vb t_vb=
“在编辑过程中,在右下角显示光标位置的状态行
set ruler
“默认情况下,寻找匹配是高亮度显示的,该设置关闭高亮显示
set nohls
“查询时非常方便,如要查找book单词,当输入到/b时,会自动找到第一
“个b开头的单词,当输入到/bo时,会自动找到第一个bo开头的单词,依
“次类推,进行查找时,使用此设置会快速找到答案,当你找要匹配的单词
“时,别忘记回车
set incsearch
“修改一个文件后,自动进行备份,备份的文件名为原文件名加“~“后缀
if has(“vms”)
set nobackup
else
set backup
endif
如果去除注释后,一个完整的.vimrc配置信息如下所示:
set nocompatible
set nummber
filetype on
set history=1000
set background=dark
syntax on
set autoindent
set smartindent
set tabstop=4
set shiftwidth=4
set showmatch
set guioptions-=T
set vb t_vb=
set ruler
set nohls
set incsearch
if has(“vms”)
set nobackup
else
set backup
endif

如果设置完后,发现功能没有起作用,检查一下系统下是否安装了vim-enhanced包,查询命令为:
$rpm –q vim-enhanced
参考资料:
1.vim的完全翻译版在下面连接处可以找到
http://vimcdoc.sourceforge.net/
可以下栽其中的一个PDF版本,里面介绍的很详细,强烈推荐:)
2.更详细的vim信息可以访问:
http://www.vim.org/
3.一个带有英文注释的.vimrc例子
http://www.vi-improved.org/vimrc.php

作者:AngryFox 分类: Uncategorized March 28th, 2013 暂无评论

我更改了redis的conf,运行,出现了警告:

Warning: no config file specified, using the default config.
In order to specify a config file use ‘redis-server /path/to/redis.conf’

警告:没有明确的config文件,使用默认配置。为了明确配置文件请使用’redis-server /path/to/redis.conf’

需要用显示命令方式指定redis的conf运行:

#./redis-server /usr/local/src/redis-2.0.4/redis.conf

这样运行后,上个警告消失。

但是还有一个警告:

WARNING overcommit_memory is set to 0! Background save may fail under low memory condition.
To fix this issue add ‘vm.overcommit_memory = 1′ to /etc/sysctl.conf and then reboot or run the command ‘sysctl vm.overcommit_memory=1′ for this to take effect.

警告:过量使用内存设置为0!在低内存环境下,后台保存可能失败。为了修正这个问题,请在/etc/sysctl.conf 添加一项 ‘vm.overcommit_memory = 1′ ,然后重启(或者运行命令’sysctl vm.overcommit_memory=1′ )使其生效。

这个警告的处理就很简单了,按照他说的,改一下,重启。发现依旧报警告。然后只好再运行一下命令令’sysctl vm.overcommit_memory=1′ 。

所有警告排除。这下终于正常运行了!

 

最近一个后台常驻job通过redis的brpop阻塞读取消息时,设置的超时时间较长

list($key,$row)=$redis->brPop($queue_name,3600); //超时时间为1小时

但是在实际的使用中发现很短时间后就会退出,通过查看error log,发现:’RedisException’ with message ‘read error on connection‘ 提示

经过一番折腾,原来发现是php.ini文件中的一个配置项导致:

default_socket_timeout = 60
由于redis扩展也是基于php 的socket方式实现,因此该参数值同样会起作用。

找到了问题就比较好解决了:

1、直接修改php.ini,将其设置为我们想要的值(这个不推荐)

2、在我们的脚本中通过以下方式设置,这样就比较灵活,不对其他脚本产生影响

作者:AngryFox 分类: Uncategorized March 28th, 2013 暂无评论
在Linux中执行.sh脚本,异常/bin/sh^M: bad interpreter: No such file or directory。

分析:这是不同系统编码格式引起的:在windows系统中编辑的.sh文件可能有不可见字符,所以在Linux系统下执行会报以上异常信息。
解决:1)在windows下转换:
利用一些编辑器如UltraEdit或EditPlus等工具先将脚本编码转换,再放到Linux中执行。转换方式如下(UltraEdit):File–>Conversions–>DOS->UNIX即可。
2)也可在Linux中转换:
首先要确保文件有可执行权限
#sh>chmod a+x filename

然后修改文件格式
#sh>vi filename

利用如下命令查看文件格式
:set ff 或 :set fileformat

可以看到如下信息
fileformat=dos 或 fileformat=unix

利用如下命令修改文件格式
:set ff=unix 或 :set fileformat=unix

:wq (存盘退出)

最后再执行文件
#sh>./filename

////////////////////////////////////////////////////

解决了:configure是在window下写的,所以在每行后面会加个ctrl+m就是^M,所以后面的,sh就变成sh^M当然是没有这个命令的,所以脚本就不能运行了,把^M去掉就应该没问题了
cat ./configure | tr -d ‘\r’ > temp
mv temp configure
sudo chmod +x configure
./configure
make

作者:AngryFox 分类: Uncategorized March 25th, 2013 暂无评论

导读:和许多新兴的网站一样,著名的轻博客服务Tumblr在急速发展中面临了系统架构的瓶颈。每天5亿次浏览量,峰 值每秒4万次请求,每天3TB新的数据存储,超过1000台服务器,这样的情况下如何保证老系统平稳运行,平稳过渡到新的系统,Tumblr正面临巨大的 挑战。近日,HighScalability网站的Todd Hoff采访了该公司的分布式系统工程师Blake Matheny,撰文系统介绍了网站的架构,内容很有价值。

Tumblr每月页面浏览量超过150亿次,已经成为火爆的博客社区。用户也许喜欢它的简约、美丽,对用户体验的强烈关注,或是友好而忙碌的沟通方式,总之,它深得人们的喜爱。

每月超过30%的增长当然不可能没有挑战,其中可靠性问题尤为艰巨。每天5亿次浏览量,峰值每秒4万次请求,每天3TB新的数据存储,并运行于超过1000台服务器上,所有这些帮助Tumblr实现巨大的经营规模。

创业公司迈向成功,都要迈过危险的迅速发展期这道门槛。寻找人才,不断改造基础架构,维护旧的架构,同时要面对逐月大增的流量,而且曾经只有4位工程师。 这意味着必须艰难地选择应该做什么,不该做什么。这就是Tumblr的状况。好在现在已经有20位工程师了,可以有精力解决问题,并开发一些有意思的解决 方案。

Tumblr最开始是非常典型的LAMP应用。目前正在向分布式服务模型演进,该模型基于ScalaHBaseRedis(著名开源K-V存储方案)、Kafka(Apache项目,出自LinkedIn的分布式发布-订阅消息系统)、Finagle(由Twitter开源的容错、协议中立的RPC系统),此外还有一个有趣的基于Cell的架构,用来支持Dashboard(CSDN注:Tumblr富有特色的用户界面,类似于微博的时间轴)。

Tumblr目前的最大问题是如何改造为一个大规模网站。系统架构正在从LAMP演进为最先进的技术组合,同时团队也要从小的创业型发展为全副武装、随时待命的正规开发团队,不断创造出新的功能和基础设施。下面就是Blake Matheny对Tumblr系统架构情况的介绍。

网站地址

http://www.tumblr.com/

主要数据

▲每天5亿次PV(页面访问量)

▲每月超过150亿PV

▲约20名工程师

▲ 峰值请求每秒近4万次

▲每天超过1TB数据进入Hadoop集群

▲ MySQL/HBase/Redis/memcache每天生成若干TB数据

▲每月增长30%

▲近1000硬件节点用于生产环境

▲平均每位工程师每月负责数以亿计的页面访问

▲每天上传大约50GB的文章,每天跟帖更新数据大约2.7TB(CSDN注:这两个数据的比例看上去不太合理,据Tumblr数据科学家Adam Laiacano在Twitter上解释,前一个数据应该指的是文章的文本内容和元数据,不包括存储在S3上的多媒体内容)

软件环境

▲开发使用OS X,生产环境使用Linux(CentOS/Scientific)

▲Apache

▲PHP, Scala, Ruby

▲Redis, HBase, MySQL

Varnish, HAProxy, nginx

▲memcache, Gearman(支持多语言的任务分发应用框架), Kafka, Kestrel(Twitter开源的分布式消息队列系统), Finagle

▲ Thrift, HTTP

Func——一个安全、支持脚本的远程控制框架和API

▲ Git, Capistrano(多服务器脚本部署工具), Puppet, Jenkins

硬件环境

▲500台Web服务器

▲200台数据库服务器(47 pool,20 shard)

▲30台memcache服务器

▲22台Redis服务器

▲15台Varnish服务器

▲25台HAproxy节点

▲8台nginx服务器

▲14台工作队列服务器(Kestrel + Gearman)

架构

1. 相对其他社交网站而言,Tumblr有其独特的使用模式:

▲每天有超过5千万篇文章更新,平均每篇文章的跟帖又数以百计。用户一般只有数百个粉丝。这与其他社会化网站里少数用户有几百万粉丝非常不同,使得Tumblr的扩展性极具挑战性。

▲按用户使用时间衡量,Tumblr已经是排名第二的社会化网站。内容的吸引力很强,有很多图片和视频,文章往往不短,一般也不会太长,但允许写得很长。文章内容往往比较深入,用户会花费更长的时间来阅读。

▲用户与其他用户建立联系后,可能会在Dashboard上往回翻几百页逐篇阅读,这与其他网站基本上只是部分信息流不同。

▲用户的数量庞大,用户的平均到达范围更广,用户较频繁的发帖,这些都意味着有巨量的更新需要处理。

2. Tumblr目前运行在一个托管数据中心中,已在考虑地域上的分布性。

3. Tumblr作为一个平台,由两个组件构成:公共Tumblelogs和Dashboard

▲公共Tumblelogs与博客类似(此句请Tumblr用户校正),并非动态,易于缓存

▲Dashboard是类似于Twitter的时间轴,用户由此可以看到自己关注的所有用户的实时更新。与博客的扩展性不同,缓存作用不大,因为每次请求 都不同,尤其是活跃的关注者。而且需要实时而且一致,文章每天仅更新50GB,跟帖每天更新2.7TB,所有的多媒体数据都存储在S3上面。

▲大多数用户以Tumblr作为内容浏览工具,每天浏览超过5亿个页面,70%的浏览来自Dashboard。

▲Dashboard的可用性已经不错,但Tumblelog一直不够好,因为基础设施是老的,而且很难迁移。由于人手不足,一时半会儿还顾不上。

老的架构

Tumblr最开始是托管在Rackspace上的,每个自定义域名的博客都有一个A记录。当2007年Rackspace无法满足其发展速度不得不迁移 时,大量的用户都需要同时迁移。所以他们不得不将自定义域名保留在Rackspace,然后再使用HAProxy和Varnish路由到新的数据中心。类 似这样的遗留问题很多。

开始的架构演进是典型的LAMP路线:

▲最初用PHP开发,几乎所有程序员都用PHP

▲最初是三台服务器:一台Web,一台数据库,一台PHP

▲为了扩展,开始使用memcache,然后引入前端cache,然后在cache前再加HAProxy,然后是MySQL sharding(非常奏效)

▲采用“在单台服务器上榨出一切”的方式。过去一年已经用C开发了两个后端服务:ID生成程序Staircar(用Redis支持Dashboard通知)

Dashboard采用了“扩散-收集”方式。当用户访问Dashboard时将显示事件,来自所关注的用户的事件是通过拉然后显示的。这样支撑了6个月。由于数据是按时间排序的,因此sharding模式不太管用。

新的架构

▲由于招人和开发速度等原因,改为以JVM为中心。目标是将一切从PHP应用改为服务,使应用变成请求鉴别、呈现等诸多服务之上的薄层。

▲这其中,非常重要的是选用了Scala和Finagle

▲在团队内部有很多人具备Ruby和PHP经验,所以Scala很有吸引力。

▲Finagle是选择Scala的重要因素之一。这个来自Twitter的库可以解决大多数分布式问题,比如分布式跟踪、服务发现、服务注册等。

▲转到JVM上之后,Finagle提供了团队所需的所有基本功能(Thrift, ZooKeeper等),无需再开发许多网络代码,另外,团队成员认识该项目的一些开发者。

▲Foursquare和Twitter都在用Finagle,Meetup也在用Scala。

▲应用接口与Thrift类似,性能极佳。

▲团队本来很喜欢Netty(Java异步网络应用框架,2月4日刚刚发布3.3.1最终版),但不想用Java,Scala是不错的选择。

▲选择Finagle是因为它很酷,还认识几个开发者。

之所以没有选择Node.js,是因为以JVM为基础更容易扩展。Node的发展为时尚短,缺乏标准、最佳实践以及大 量久经测试的代码。而用Scala的话,可以使用所有Java代码。虽然其中并没有多少可扩展的东西,也无法解决5毫秒响应时间、49秒HA、4万每秒请 求甚至有时每秒40万次请求的问题。但是,Java的生态链要大得多,有很多资源可以利用。

内部服务从C/libevent为基础正在转向Scala/Finagle为基础。

开始采用新的NoSQL存储方案如HBase和Redis。但大量数据仍然存储在大量分区的MySQL架构中,并没有 用HBase代替MySQL。HBase主要支持短地址生产程序(数以十亿计)还有历史数据和分析,非常结实。此外,HBase也用于高写入需求场景,比 如Dashboard刷新时一秒上百万的写入。之所以还没有替换HBase,是因为不能冒业务上风险,目前还是依靠人来负责更保险,先在一些小的、不那么 关键的项目中应用,以获得经验。MySQL和时间序列数据sharding(分片)的问题在于,总有一个分片太热。另外,由于要在slave上插入并发, 也会遇到读复制延迟问题。

此外,还开发了一个公用服务框架

▲花了很多时间解决分布式系统管理这个运维问题。

▲为服务开发了一种Rails scaffolding,内部用模板来启动服务。

▲所有服务从运维的角度来看都是一样的,所有服务检查统计数据、监控、启动和停止的方式都一样。

▲工具方面,构建过程围绕SBT(一个Scala构建工具),使用插件和辅助程序管理常见操作,包括在Git里打标签,发布到代码库等等。大多数程序员都不用再操心构建系统的细节了。

200台数据库服务器中,很多是为了提高可用性而设,使用的是常规硬件,但MTBF(平均故障间隔时间)极低。故障时,备用充足。

为了支持PHP应用有6个后端服务,并有一个小组专门开发后端服务。新服务的发布需要两到三周,包括 Dashboard通知、Dashboard二级索引、短地址生成、处理透明分片的memcache代理。其中在MySQL分片上耗时很多。虽然在纽约本 地非常热,但并没有使用MongoDB,他们认为MySQL的可扩展性足够了。

Gearman用于会长期运行无需人工干预的工作。

可用性是以达到范围(reach)衡量的。用户能够访问自定义域或者Dashboard吗?也会用错误率。

历史上总是解决那些最高优先级的问题,而现在会对故障模式系统地分析和解决,目的是从用户和应用的角度来定成功指标。(后一句原文似乎不全)

最开始Finagle是用于Actor模型的,但是后来放弃了。对于运行后无需人工干预的工作,使用任务队列。而且Twitter的util工具库中有Future实现,服务都是用Future(Scala中的无参数函数,在与函数关联的并行操作没有完成时,会阻塞调用方)实现的。当需要线程池的时候,就将Future传入Future池。一切都提交到Future池进行异步执行。

Scala提倡无共享状态。由于已经在Twitter生产环境中经过测试,Finagle这方面应该是没有问题的。使用Scala和Finagle中的结 构需要避免可变状态,不使用长期运行的状态机。状态从数据库中拉出、使用再写回数据库。这样做的好处是,开发人员不需要操心线程和锁。

22台Redis服务器,每台的都有8-32个实例,因此线上同时使用了100多个Redis实例。

▲Redis主要用于Dashboard通知的后端存储。

▲所谓通知就是指某个用户like了某篇文章这样的事件。通知会在用户的Dashboard中显示,告诉他其他用户对其内容做了哪些操作。

▲高写入率使MySQL无法应对。

▲通知转瞬即逝,所以即使遗漏也不会有严重问题,因此Redis是这一场景的合适选择。

▲这也给了开发团队了解Redis的机会。

▲使用中完全没有发现Redis有任何问题,社区也非常棒。

▲开发了一个基于Scala Futures的Redis接口,该功能现在已经并入了Cell架构。

▲短地址生成程序使用Redis作为一级Cache,HBase作为永久存储。

▲Dashboard的二级索引是以Redis为基础开发的。

▲Redis还用作Gearman的持久存储层,使用Finagle开发的memcache代理。

▲正在缓慢地从memcache转向Redis。希望最终只用一个cache服务。性能上Redis与memcache相当。

内部的firehose(通信管道)

▲内部的应用需要活跃的信息流通道。这些信息包括用户创建/删除的信息,liking/unliking的提示,等等。挑战在于这些数据要实时的分布式处理。我们希望能够检测内部运行状况,应用的生态系统能够可靠的生长,同时还需要建设分布式系统的控制中心。

▲以前,这些信息是基于Scribe(Facebook开源的分布式日志系统。)/Hadoop的分布式系统。服务会先记录在Scribe中,并持续的长 尾形式写入,然后将数据输送给应用。这种模式可以立即停止伸缩,尤其在峰值时每秒要创建数以千计的信息。不要指望人们会细水长流式的发布文件和grep。

▲内部的firehose就像装载着信息的大巴,各种服务和应用通过Thrift与消防管线沟通。(一个可伸缩的跨语言的服务开发框架。)

▲LinkedIn的Kafka用于存储信息。内部人员通过HTTP链接firehose。经常面对巨大的数据冲击,采用MySQL显然不是一个好主意,分区实施越来越普遍。

▲firehose的模型是非常灵活的,而不像Twitter的firehose那样数据被假定是丢失的。

▲firehose的信息流可以及时的回放。他保留一周内的数据,可以调出这期间任何时间点的数据。

▲支持多个客户端连接,而且不会看到重复的数据。每个客户端有一个ID。Kafka支持客户群,每个群中的客户都用同一个ID,他们不会读取重复的数据。 可以创建多个客户端使用同一个ID,而且不会看到重复的数据。这将保证数据的独立性和并行处理。Kafka使用ZooKeeper(Apache推出的开 源分布式应用程序协调服务。)定期检查用户阅读了多少。

为Dashboard收件箱设计的Cell架构

▲现在支持Dashboard的功能的分散-集中架构非常受限,这种状况不会持续很久。

▲解决方法是采用基于Cell架构的收件箱模型,与Facebook Messages非常相似。

▲收件箱与分散-集中架构是对立的。每一位用户的dashboard都是由其追随者的发言和行动组成的,并按照时间顺序存储。

▲就因为是收件箱就解决了分散-集中的问题。你可以会问到底在收件箱中放了些什么,让其如此廉价。这种方式将运行很长时间。

▲重写Dashboard非常困难。数据已经分布,但是用户局部升级产生的数据交换的质量还没有完全搞定。

▲数据量是非常惊人的。平均每条消息转发给上百个不同的用户,这比Facebook面对的困难还要大。大数据+高分布率+多个数据中心。

▲每秒钟上百万次写入,5万次读取。没有重复和压缩的数据增长为2.7TB,每秒百万次写入操作来自24字节行键。

▲已经流行的应用按此方法运行。

▲cell

▲每个cell是独立的,并保存着一定数量用户的全部数据。在用户的Dashboard中显示的所有数据也在这个cell中。

▲用户映射到cell。一个数据中心有很多cell。

▲每个cell都有一个HBase的集群,服务集群,Redis的缓存集群。

▲用户归属到cell,所有cell的共同为用户发言提供支持。

▲每个cell都基于Finagle(Twitter推出的异步的远程过程调用库),建设在HBase上,Thrift用于开发与firehose和各种请求与数据库的链接。(请纠错)

▲一个用户进入Dashboard,其追随者归属到特定的cell,这个服务节点通过HBase读取他们的dashboard并返回数据。

▲后台将追随者的dashboard归入当前用户的table,并处理请求。

▲Redis的缓存层用于cell内部处理用户发言。

▲请求流:用户发布消息,消息将被写入firehose,所有的cell处理这条消息并把发言文本写入数据库,cell查找是否所有发布消息追随者都在本cell内,如果是的话,所有追随者的收件箱将更新用户的ID。(请纠错

▲cell设计的优点:

▲大规模的请求被并行处理,组件相互隔离不会产生干扰。 cell是一个并行的单位,因此可以任意调整规格以适应用户群的增长。

▲cell的故障是独立的。一个Cell的故障不会影响其他cell。

▲cell的表现非常好,能够进行各种升级测试,实施滚动升级,并测试不同版本的软件。

▲关键的思想是容易遗漏的:所有的发言都是可以复制到所有的cell。

▲每个cell中存储的所有发言的单一副本。 每个cell可以完全满足Dashboard呈现请求。应用不用请求所有发言者的ID,只需要请求那些用户的ID。(“那些用户”所指不清,请指正。)他可以在dashboard返回内容。每一个cell都可以满足Dashboard的所有需求,而不需要与其他cell进行通信。

▲用到两个HBase table :一个table用于存储每个发言的副本,这个table相对较小。在cell内,这些数据将与存储每一个发言者ID。第二个table告诉我们用户的 dashboard不需要显示所有的追随者。当用户通过不同的终端访问一个发言,并不代表阅读了两次。收件箱模型可以保证你阅读到。

▲发言并不会直接进入到收件箱,因为那实在太大了。所以,发言者的ID将被发送到收件箱,同时发言内容将进入cell。这个模式有效的减少了存储需求,只 需要返回用户在收件箱中浏览发言的时间。而缺点是每一个cell保存所有的发言副本。令人惊奇的是,所有发言比收件箱中的镜像要小。(请纠错)每天每个cell的发言增长50GB,收件箱每天增长2.7TB。用户消耗的资源远远超过他们制造的。

▲用户的dashboard不包含发言的内容,只显示发言者的ID,主要的增长来自ID。(请Tumblr用户纠错)

▲当追随者改变时,这种设计方案也是安全的。因为所有的发言都保存在cell中了。如果只有追随者的发言保存在cell中,那么当追随者改变了,将需要一些回填工作。

▲另外一种设计方案是采用独立的发言存储集群。这种设计的缺点是,如果群集出现故障,它会影响整个网站。因此,使用cell的设计以及后复制到所有cell的方式,创建了一个非常强大的架构。

▲一个用户拥有上百万的追随者,这带来非常大的困难,有选择的处理用户的追随者以及他们的存取模式(见Feeding Frenzy

▲不同的用户采用不同并且恰当的存取模式和分布模型,两个不同的分布模式包括:一个适合受欢迎的用户,一个使用大众。

▲依据用户的类型采用不同的数据处理方式,活跃用户的发言并不会被真正发布,发言将被有选择的体现。(果真如此?请Tumblr用户纠错)

▲追随了上百万用户的用户,将像拥有上百万追随者的用户那样对待。

▲cell的大小非常难于决定。cell的大小直接影响网站的成败。每个cell归于的用户数量是影响力之一。需要权衡接受怎样的用户体验,以及为之付出多少投资。

▲从firehose中读取数据将是对网络最大的考验。在cell内部网络流量是可管理的。

▲当更多cell被增添到网络中来,他们可以进入到cell组中,并从firehose中读取数据。一个分层的数据复制计划。这可以帮助迁移到多个数据中心。

在纽约启动运作

纽约具有独特的环境,资金和广告充足。招聘极具挑战性,因为缺乏创业经验。

在过去的几年里,纽约一直致力于推动创业。纽约大学和哥伦比亚大学有一些项目,鼓励学生到初创企业实习,而不仅仅去华尔街。市长建立了一所学院,侧重于技术。

团队架构

▲团队:基础架构,平台,SRE,产品,web ops,服务;

▲基础架构:5层以下,IP地址和DNS,硬件配置;

▲平台:核心应用开发,SQL分片,服务,Web运营;

▲SRE:在平台和产品之间,侧重于解决可靠性和扩展性的燃眉之急;

▲服务团队:相对而言更具战略性,

▲Web ops:负责问题检测、响应和优化。

软件部署

▲开发了一套rsync脚本,可以随处部署PHP应用程序。一旦机器的数量超过200台,系统便开始出现问题,部署花费了很长时间才完成,机器处于部署进程中的各种状态。

▲接下来,使用Capistrano(一个开源工具,可以在多台服务器上运行脚本)在服务堆栈中构建部署进程(开发、分期、生产)。在几十台机器上部署可以正常工作,但当通过SSH部署到数百台服务器时,再次失败。

▲现在,所有的机器上运行一个协调软件。基于Redhat Func(一个安全的、脚本化的远程控制框架和接口)功能,一个轻量级的API用于向主机发送命令,以构建扩展性。

▲建立部署是在Func的基础上向主机发送命令,避免了使用SSH。比如,想在组A上部署软件,控制主机就可以找出隶属于组A的节点,并运行部署命令。

▲部署命令通过Capistrano实施。

▲Func API可用于返回状态报告,报告哪些机器上有这些软件版本。

▲安全重启任何服务,因为它们会关闭连接,然后重启。

▲在激活前的黑暗模式下运行所有功能。

展望

▲从哲学上将,任何人都可以使用自己想要的任意工具。但随着团队的发展壮大,这些工具出现了问题。新员工想要更好地融入团队,快速地解决问题,必须以他们为中心,建立操作的标准化。

▲过程类似于Scrum(一种敏捷管理框架),非常敏捷。

▲每个开发人员都有一台预配置的开发机器,并按照控制更新。

▲开发机会出现变化,测试,分期,乃至用于生产。

▲开发者使用VIM和TextMate。

▲测试是对PHP程序进行代码审核。

▲在服务方面,他们已经实现了一个与提交相挂钩的测试基础架构,接下来将继承并内建通知机制。

招聘流程

▲面试通常避免数学、猜谜、脑筋急转弯等问题,而着重关注应聘者在工作中实际要做什么。

▲着重编程技能。

▲面试不是比较,只是要找对的人。

▲挑战在于找到具有可用性、扩展性经验的人才,以应对Tumblr面临的网络拥塞。

▲在Tumblr工程博客(Tumblr Engineering Blog),他们对已过世的Dennis Ritchie和John McCarthy予以纪念。

经验及教训

▲自动化无处不在

▲MySQL(增加分片)规模,应用程序暂时还不行

▲Redis总能带给人惊喜

▲基于Scala语言的应用执行效率是出色的

▲废弃项目——当你不确定将如何工作时

▲不顾用在他们发展经历中没经历过技术挑战的人,聘用有技术实力的人是因为他们能适合你的团队以 及工作。

▲选择正确的软件集合将会帮助你找到你需要的人

▲建立团队的技能

▲阅读文档和博客文章。

▲多与同行交流,可以接触一些领域中经验丰富的人,例如与在Facebook、Twitter、LinkedIn的工程师 多交流,从他们身上可以学到很多

▲对技术要循序渐进,在正式投入使用之前他们煞费苦心的学习HBase和Redis。同时在试点项目中使用 或将其控制在有限损害范围之内。

翻译:包研,张志平,刘江;审校:刘江

转自 http://blog.jobbole.com/13311/ 英文原文出自High Scalability

作者:AngryFox 分类: Uncategorized March 24th, 2013 暂无评论

infobright,一个基于mysql的数据仓库系统实现,它已经是很多开源或商用BI系统的底层存储引擎。

infobright 是基于mysql的,但不装mysql亦可,因为它本身就自带了一个。mysql可以粗分为逻辑层和物理存储引擎,infobright主要实现的就是一 个存储引擎,但因为它自身存储逻辑跟关系型数据库根本不同,所以,它不能像InnoDB那样直接作为插件挂接到mysql,它的逻辑层是mysql的逻辑 层加上它自身的优化器。

几大优点:

1、高压缩比率,平均压缩比可达10:1,甚至可以达到40:1,我用infobright把3.1G的数据存成不足300M。

2、列存储,即使数据量十分巨大,查询速度也很快。用于数据仓库,处理海量数据没一套可不行。

3、不需要建索引,就避免了维护索引及索引随着数据膨胀的问题。把每列数据分块压缩存放,每块有知识网格节点记录块内的统计信息,代替索引,加速搜 索。

4、单一台服务器可以高效地读写30T数据。具有可扩展性,这里是指对于同样的查询,当数据量是10T时,它耗费的时间不应该比1T数据量时慢太 多,基本是一个数量级内。

与mysql对比:

1、infobright适用于数据仓库场合,即非事务、非实时、非多并发;分析为主;存放既定的事实(基本不会再变),例如日志,或汇总的大量的 数据。所以它并不适合于应对来自网站用户的请求。实际上它取一条记录比mysql要慢很多,但它取100W条记录会比mysql快。

2、mysql的总数据文件占用空间通常会比实际数据多,因为它还有索引。infobright的压缩能力很强大,按列按不同类型的数据来压缩。

3、服务形式与接口跟mysql一致,可以用类似mysql的方式启用infobright服务,然后原来连接mysql的应用程序都可以以类似的 方式连接与查询infobright。这对熟练mysql者来说是个福音,学习成本基本为0。

infobright有两个发布版:开源的ICE及闭源商用的IEE。ICE提供了足够用的功能,但不能 INSERT,DELETE,UPDATE,只能LOAD DATA INFILE。IEE除提供更充分的功能外,据说查询速度也要更快。

参考:

1、infobright商业网站:http://www.infobright.com/

2、infobright社区交流网站:http://www.infobright.org/

3、mysql对infobright的介绍:http://dev.mysql.com/tech-resources/articles /datawarehousing_mysql_infobright.html

4、关于infobright的介绍视频:http://www.infobright.com/Resource-Library /Webcasts-Podcasts/?infobright_product_demo

作者:AngryFox 分类: Uncategorized March 18th, 2013 暂无评论

shell> strace -eopen iostat
open(“/proc/diskstats”, O_RDONLY)

注:关于diskstats的说明,参见官方文档(field1 ~ field11)。

我最常用的iostat命令格式是:『iostat -dx 1』,意思是每隔一秒显示一次IO扩展信息。

shell> iostat -dx 1
Device:         rrqm/s   wrqm/s   r/s   w/s   rsec/s   wsec/s
sda               0.18    37.71  0.65  2.63    50.18   322.08
avgrq-sz avgqu-sz   await  svctm  %util
113.46     0.35  107.49   1.67   0.55

Device:         rrqm/s   wrqm/s   r/s   w/s   rsec/s   wsec/s
sda               0.00  4208.00  0.00 165.00     0.00 163872.00
avgrq-sz avgqu-sz   await  svctm  %util
993.16   119.54 1144.36   6.07 100.10

注:开头显示的是自系统启动开始的平均值,后面显示的是每段时间间隔里的平均值。

介绍一下相关参数的含义:
# rrqm/s:队列中每秒钟合并的读请求数量
# wrqm/s:队列中每秒钟合并的写请求数量
# r/s:每秒钟完成的读请求数量
# w/s:每秒钟完成的写请求数量
# rsec/s:每秒钟读取的扇区数量
# wsec/s:每秒钟写入的扇区数量
# avgrq-sz:平均请求数据的大小
# avgqu-sz:平均请求队列的长度
# await:平均每次请求的等待时间
# svctm:平均每次请求的服务时间
# util:设备的利用率

注:建议对照源代码来记忆这些参数都是如何计算出来的。

关于这些参数,相对重要的是后面几个,具体来说是:util,svctm,await,avgqu-sz:

util是设备的利用率。如果它接近100%,通常说明设备能力趋于饱和(并不绝对,比如设备有写缓存)。有时候可能会出现大于100%的情况,这多半是计算时四舍五入引起的。

svctm是平均每次请求的服务时间。这里有一个公式:(r/s+w/s)*(svctm/1000)=util。举例子:如果util达到100%,那 么此时svctm=1000/(r/s+w/s),假设IOPS是1000,则svctm大概在1毫秒左右,如果长时间大于这个数值,说明系统出了问题。

await是平均每次请求的等待时间。这个时间包括了队列时间和服务时间,也就是说,一般情况下,await大于svctm,它们的差值越小,队列时间越短,反之差值越大,队列时间越长,说明系统出了问题。

avgqu-sz是平均请求队列的长度。毫无疑问,队列长度越短越好,这就不用多做解释了。

提醒:如果是RAID等多盘系统,iostat结果的参考价值可能有变化,建议查阅相关资料。

作者:AngryFox 分类: Uncategorized March 17th, 2013 暂无评论

https://code.google.com/p/xppass/downloads/list

http://www.oschina.net/p/hybridauth

c blog http://code.google.com/p/php-tokyocabinet/downloads/list

c实现的sso服务器端:

http://code.google.com/p/loongsso/downloads/list

原理:

http://bbs.chinaunix.net/thread-3633340-1-1.html

 

常用:

近日研究了 Sina CAS 的登陆过程,发现其实 sina 的 sso 实现了 yale-CAS 并且添加一丁点新的东西,基本认证过程交互流程仍然未变。其独创的一点是实现了 Ajax 单点登陆,算是比较牛。实现原理是 iframe+ javaScript 回调函数。

一,初级 SSO

初级的 SSO ,就是在同一个顶级域名下,通过种入顶级域名的 Cookie ,来实现统一登陆。例如:

单点登陆地址: sso.xxx.com/login.jsp

应用 1 : web1.xxx.com/login.jsp

应用 2 : web2.xxx.com/login.jsp

应用 3 : web3.xxx.com/login.jsp

登陆流程:

情况一:(用户从未登陆)

1,  用户访问 web1.xxx.com/login.jsp , web1 重定向到 sso.xxx.com/login.jsp

2,  用户输入验证,成功。 sso.xxx.com 种入 .xxx.com 域 Cookie 的 tokenid ,重定向到 web1.xxx.com/login.jsp , web1.xxx.com 访问 .xxx.com 域 Cookie 的 tokenid 判断出已经登陆,系统登陆完成。

情况二:(用户已经登陆)直接登陆。

二, Sina SSO

Sina 实现了跨域名的统一登陆,本质也是基于 Cookie 的。如果用户禁用 Cookie ,那么无论如何也是登陆不了的。例如: Sina SSO 服务器是 login.sina.com.cn/sso/login.php

,微博登陆地址为 weibo.com/login.php 。通过回调函数和 iframe 实现了跨一级域名的登陆。

认证过程具体流程:这里只介绍用户从未登陆过。

1,  用户进入 weibo.com/login.php

2,  用户输入用户名称。输入完毕后,当用户名输入框焦点失去的时候,页码通过 ajax 向服务器 login.sina.com.cn/sso/prelogin.php 发送请求,参数为 user (刚刚输入的用户名)。服务返回 server time 和 nonce 认证,通过回调函数写入到 javascript 变量中。

3,  用户输入密码,点击登陆,页面 POST 请求(注意是 ajax 请求,并不是 login.php 发送的 ),

login.sina.com.cn/sso/login.php?client=ssologin.js(v1.3.12) ,请求的发起的页面是 weibo.com/login.php 中的一个不可见 iframe 页面,参数为第二步得到的 server time 和 nonce ,已经用户名称和加密的密码。返回种入 Cookie  tgt 在 login.sina.com.cn 下。同时修改 iframe 地址为 weibo.com/ajaxlogin.php?ticket=XXXXXX, 注意 ticket 非常重要,这是用户登陆和服务的凭证。

4,  iframe 访问 weibo.com/ajaxlogin.php?ticket=XXXXXX ,用户登陆,返回种入 cookie 在 .weibo.com 下,记录用户登陆信息。

5,  通过 js 再次访问 weibo.com/login.php ,因为 cookie 已经写入,登陆成功,服务器发送 302 ,重定向到用户主页面。 Weibo.com/userid 。

6,  至此,登陆过程完成。

作者:AngryFox 分类: Uncategorized March 16th, 2013 暂无评论

数据库大概存储几十万条IP记录,记录集如下:
+———-+———-+————+———+———+——–+——–+
| ip_begin | ip_end | country_id | prov_id | city_id | isp_id | netbar |
+———-+———-+————+———+———+——–+——–+
| 0 | 16777215 | 2 | 0 | 0 | 0 | 0 |
| 16777216 | 33554431 | 2 | 0 | 0 | 0 | 0 |
| 33554432 | 50331647 | 2 | 0 | 0 | 0 | 0 |
| 50331648 | 67108863 | 3 | 0 | 0 | 0 | 0 |
| 67108864 | 67829759 | 3 | 0 | 0 | 0 | 0 |
+———-+———-+————+———+———+——–+——–+
这样做查询需要用到如下SQL:
<?php
$sql = \’SELECT * FROM i_m_ip WHERE ip_begin <= $client_ip AND ip_end >= $client_ip\’;
?>
这样的检索显然用不到索引,即使用到,MySQL查询效率也不大可能达到每秒500次以上,我做了很多

并发优化,最终平均查询效率也只有每秒200次左右,实在是头痛。一开始我也有想到借鉴纯真IP库的检

索方法,但是我一直对算法有抵触,也以为二分法很难,所以就没有尝试使用,直到最后没有办法了,才

最终实现了二分法的IP地址检索。
从上表可以看到IP库是从0到4294967295的一个连续数值,这个数值要是拆开存储,会有几百G的数据

,所以没办法使用索引也没办法哈希。最终我使用PHP将这些东东转为二进制存储,抛弃了数据库的检索

。可以看到IP起止长度为一个4字节的长整型,后面的国家ID、省份ID等,可以使用2个字节的短整型来存

储,总共一行数据就有18个字节,总共31万条数据,算起来也就5M的样子。具体IP库生成代码如下:

<?php
/*
IP文件格式:
3741319168 3758096383 182 0 0 0 0
3758096384 3774873599 3 0 0 0 0
3774873600 4026531839 182 0 0 0 0
4026531840 4278190079 182 0 0 0 0
4294967040 4294967295 312 0 0 0 0
*/
set_time_limit(0);
$handle = fopen(\'./ip.txt\', \'rb\');
$fp = fopen("./ip.dat", \'ab\');
if ($handle) {
while (!feof($handle)) {
$buffer = fgets($handle);
$buffer = trim($buffer);
$buffer = explode("t", $buffer);
foreach ($buffer as $key => $value) {
$buffer[$key] = (float) trim($value);
}
$str = pack(\'L\', $buffer[0]);
$str .= pack(\'L\', $buffer[1]);
$str .= pack(\'S\', $buffer[2]);
$str .= pack(\'S\', $buffer[3]);
$str .= pack(\'S\', $buffer[4]);
$str .= pack(\'S\', $buffer[5]);
$str .= pack(\'S\', $buffer[6]);
fwrite($fp, $str);
}
}
?>

这样IP就按照顺序每18字节一个单位排列了,所以很容易就使用二分法来检索出IP信息:
function getip($ip, $fp) {
fseek($fp, 0);
$begin = 0;
$end = filesize(\'./ip.dat\');
$begin_ip = implode(\'\', unpack(\'L\', fread($fp, 4)));
fseek($fp, $end - 14);
$end_ip = implode(\'\', unpack(\'L\', fread($fp, 4)));
$begin_ip = sprintf(\'%u\', $begin_ip);
$end_ip = sprintf(\'%u\', $end_ip);

do {
if ($end - $begin <= 18) {
fseek($fp, $begin + 8);
$info = array();
$info[0] = implode(\'\', unpack(\'S\', fread($fp, 2)));
$info[1] = implode(\'\', unpack(\'S\', fread($fp, 2)));
$info[2] = implode(\'\', unpack(\'S\', fread($fp, 2)));
$info[3] = implode(\'\', unpack(\'S\', fread($fp, 2)));
$info[4] = implode(\'\', unpack(\'S\', fread($fp, 2)));
return $info;
}

$middle_seek = ceil((($end - $begin) / 18) / 2) * 18 + $begin;

fseek($fp, $middle_seek);
$middle_ip = implode(\'\', unpack(\'L\', fread($fp, 4)));
$middle_ip = sprintf(\'%u\', $middle_ip);

if ($ip >= $middle_ip) {
$begin = $middle_seek;
} else {
$end = $middle_seek;
}
} while (true);
}

以上$fp为打开ip.dat的文件句柄,由于是循环检索,所以写在函数外面,免得每次检索都要打开一次文件,30W行数据二分法最多也只需要循环7次(2^7)左右即可找到准确的IP信息。之后本来还想将ip.dat放在内存中加快检索速度,后来发现,字符串定位函数的效率,根本和文件指针的偏移定位不是在一个数量级的,所以还是放弃使用内存来存放IP库。 这个实现,使IP检索效率提高了近百倍