<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/">
<channel>
<title><![CDATA[IT青藤屋]]></title>
<link><![CDATA[http://www.itivy.com/ivy]]></link>
<description><![CDATA[关注架构、关注前端、关注生活，愿天下的程序员像常青藤一样绿意盎然、自强不息]]></description>
<language><![CDATA[zh-cn]]></language>
<copyright><![CDATA[]]></copyright>
<webMaster><![CDATA[]]></webMaster>
<generator><![CDATA[]]></generator>
<Image><![CDATA[]]></Image>
<item>
<link><![CDATA[http://www.itivy.com/ivy/archive/2012/5/2/hadoop-develop.html]]></link>
<title><![CDATA[分布式计算平台Hadoop发展现状乱而稳定的解读]]></title>
<author><![CDATA[ivy]]></author>
<category><![CDATA[]]></category>
<pubDate>Wed, 02 May 2012 14:07:25 GMT</pubDate>
<guid><![CDATA[]]></guid>
<description><![CDATA[<p><strong>【</strong><strong>导读</strong><strong>】</strong><strong>：</strong></p>
<p>雅虎开发者Doug 
Cutting六年前创建了一个用于管理，存储和分析大量数据的分布式计算平台hadoop，现在大家也称云计算平台，用他儿子的玩具大象命名，并把它交
给阿帕奇软件基金会。鉴于围绕Hadoop建立的整个行业的迅速，这会使某些人觉得非常惊讶，那就是阿帕奇软件基金会最近才推出了Apache 
Hadoop 1.0——被认为是足够稳定而成为“企业就绪”的第一个版本。</p>
<p><strong>Hadoop乱象</strong></p>
<p>雅虎开发者Doug 
Cutting六年前创建了一个用于管理，存储和分析大量数据的平台，用他儿子的玩具大象命名，并把它交给阿帕奇软件基金会。鉴于围绕Hadoop建立的
 整个行业的迅速，这会使某些人觉得非常惊讶，那就是阿帕奇软件基金会最近才推出了Apache Hadoop 
1.0——被认为是足够稳定而成为“企业就绪”的第一个版本。</p>
<p>
但这并没有延缓创业和建立供应商加入Hadoop的队伍。随着大数据这一热点，解决方案提供商需要跟上行业中的关键角色。以下就是围绕Hadoop的11个创新业务。</p>
<p><img src="/Upload/EditorImage/image/ivy/201205/20120502140448_1841.jpg" alt="" border="0" /></p>
<p><strong>1. Cloudera</strong></p>
<p>Cloudera，成立于2008年，也许是最成立的年轻的致力于 Hadoop的公司。 帕洛阿尔托，加州公司提供了一个Apache 
Hadoop软件的商业发行版叫做Cloudera Enterprise，它包括支持，咨询服务，培训和一个称为Cloudera 
Management Suite的软件。</p>
<p>一个例子说明了Cloudera的技术如何寻求广泛使用，Oracle在一月表示它已经将Cloudera的Hadoop发行版和Cloudera Manager整合到Oracle Big Data Appliance中。</p>
<p>Cloudera在2009年聘请Doug Cutting担任“架构师”时有了炫耀的资本。Cutting是原阿帕奇软件基金会Hadoop项目的创始人并且是现任董事。</p>
<p>&nbsp;</p>
<p><strong>2. Datameer</strong></p>
<p>业务产生和存储的数据量每三年翻一番。加之数据是结构化和非结构化信息的混合体这一事实，往往分散在不同的IT系统，对于有商业智能项目的任何公司都是一个严重的挑战。</p>
<p>Datameer分析解决方案始于Datameer(2009年于加州圣马刁成立)，用一个电子表格界面与Apache Hadoop结合，帮助企业用户对非常大的数据集进行分析——多种来源的结构化和非结构化数据——无需编程。</p>
<p>虽然分析大型数据一直是一个大的公司难题，但Datameer作出令人信服的观点：中小企业现在面临着类似的挑战，成本低的商品存储使收集大量的数据在经济上可行的。</p>
<p>&nbsp;</p>
<p><strong>3. Hadapt</strong></p>
<p>Hadapt称其Hadapt自适应分析平台结合了Hadoop和关系数据库管理软件的优点成为一个单独的数据平台。其成果就是一个高性能分析系统，对结构化和非结构化数据都能很好处理。</p>
<p>公 司成立于2010年七月，在十月的第一轮融资公司募集950万美元并在十一月推出了Hadapt 
1.0，为潜在客户试用。依照公司称，该软件对Hadoop和其蜂巢数据仓储技术做了“巨大的性能改进”。软件有云和企业版，不久还有一个免费的社区版。
 它们可运行在所有主流的Hadoop发行版上，包括Amazon EMR, Apache, Cloudera, EMC, Hortonworks,
 IBM和MapR。</p>
<p>&nbsp;</p>
<p><strong>4. Hortonworks</strong></p>
<p>2011年七月推出的Hortonworks是雅虎Hadoop工程团队的一个分拆，提供其自有的 
Hadoop版本称为Hortonworks数据平台。相对年轻的公司，阿帕奇项目的贡献者，这都被广泛视为Cloudera的主要竞争对手。一月公司推
 
出Hortonworks数据平台第二版，通过下一代MapReduce架构提供更好的性能和可用性，用Hadoop分布式文件系统(HDFS)提高可扩
 展性，并且由HDFS名字节点的高可用性提高了数据完整性。</p>
<p>对了，加利福尼亚州，桑尼维尔，公司的名字来自于苏斯博士的书《霍顿与无名氏》，为了符合Hadoop大象主题。</p>
<p>&nbsp;</p>
<p><strong>5. HStreaming</strong></p>
<p>虽然Hadoop事实上也许是处理大量数据的引擎，但它主要用于批处理。实时分析数据把Hadoop的价值提升到一个全新的水平。这就是HStreaming的由来。</p>
<p>成立于2010年，位于芝加哥的HStreaming是一个建立在Hadoop上的可扩展的，可持续的数据分析系统。它可以分析，可视化并处理大量连续数据——比如一个金融交易系统——实时。</p>
<p>&nbsp;</p>
<p style="text-align:left;" align="center"><strong>6. Hyve Solutions</strong></p>
<p>虽然大多数Hadoop相关的公司都是独立创业，但Hyve Solutions是联强IT经销商的一个部门。成立于去年，Hyve 
Solutions提供了成套的配置，称为Big D Series 8，公司表示它能让开发基于Hadoop的大数据分析系统在数天完成而不是数月。</p>
<p>Hyve Solutions平台包含了Zettaset的基于Hadoop的容错系统，Arista Networks的云网络设备，Solarflare Communications的网络接口硬件和软件，还有Fusion-io的闪存数据存储技术。</p>
<p>&nbsp;</p>
<p><strong>7. Karmasphere</strong></p>
<p>位于加州库比提诺的Karmasphere称自己为“大数据智能”引领者，其软件工具可从Hadoop提取和分析数据。</p>
<p>Karmasphere 
Analyst为信息分析员提供对Hadoop中结构化和非结构化数据的访问，使他们能够进行点对点查询，对结果可视化并可操作。Karmasphere
 Studio提供了用于开发运行于Hadoop自定义算法的工具。Karmasphere Analytics 
Engine(分析引擎)是公司软件的基础。</p>
<p>Karmasphere，2010年三月推出，已经于几乎Hadoop的所有供应商和组织合作 
过，包括阿帕奇软件基金会，IBM，Cloudera，亚马逊云计算服务(AWS)和Hortonworks。公司在二月推出Karmasphere 
Analyst 1.8，带有新的并行查询功能。</p>
<p>&nbsp;</p>
<p><strong>8. MapR Technologies</strong></p>
<p>MapR Technologies提供了一个Apache 
Hadoop的发行版，将之与Cloudera和HortonWorks还有其他公司竞争。公司成立于2009年年六月，有一些关键优势，包括与EMC有
 战略联盟，并且在八月的第二轮融资募集2000万美元资金。</p>
<p>MapR位于加州圣若泽，十二月的MapR Hadoop发行版1.2有新的虚拟机功能，高性能的本地访问库，Mac和Windows客户端，和利用MapReduce 2.0技术的能力。</p>
<p>&nbsp;</p>
<p><strong>9. Mortar Data</strong></p>
<p>Mortar Data宣称自己是“Hadoop，没有复杂性”。总部设在纽约，该公司为那些“没有充分利用数据”的客户提供基于云的Hadoop服务，并表示它可以请客户来且运行不到一小时。</p>
<p>Mortar 
Data，成立于2010年，为客户的大数据项目建立私有的，按需求的Hadoop集群，并使用Pig和Python建立“为执行优化过的工作”。亚马逊
 的S3云存储用于数据读写。客户只在运行他们任务时支付，没有与之相关的基础设施和雇佣和培训工程师的费用。</p>
<p>&nbsp;</p>
<p><strong>10. Tidemark Systems</strong></p>
<p>Tidemark Systems，位于加州红木城，开发其称为第一个企业级性能管理平台和为云计算建立的应用。因为Tidemark EPM应用系统是建立在Cloudera的Hadoop发行版基础上，大数据随之而来，它可以从海量复杂数据中进行提取。</p>
<p>公司成立于2010年，Tidemark主要面向制造业，消费电子产品，零售业和高科技公司上的应用。一月公司从风险投资家和仁科创始人戴夫杜菲尔德获得240万美元的第三轮融资。</p>
<p>&nbsp;</p>
<p><strong>11. Zettaset</strong></p>
<p>最早与2009年以GOTO Metrics的名字推出，Zettaset已经开发出建立在Hadoop和其他用于聚集和大量数据分析的开源技术上的一个容错系统。依照公司所称，该技术有助于掌控整个企业Hadoop系统的健康度，安全性和管理。</p>
<p>Zettaset位于加州山景城，在十二月推出其第四版，带有新的服务管理特性和一个独特的可视化用户界面。公司在七月(获得三百万美元融资后)根据zettabyte更名——等于一百万petabyte或一亿terabyte的数据。</p>
<p>文章来源：http://cloud.doit.com.cn/article/2012/0416/9007432.shtml</p>]]></description>
</item>

<item>
<link><![CDATA[http://www.itivy.com/ivy/archive/2012/4/21/my-mac-1.html]]></link>
<title><![CDATA[我的Mac之路（一）]]></title>
<author><![CDATA[ivy]]></author>
<category><![CDATA[]]></category>
<pubDate>Sat, 21 Apr 2012 20:42:46 GMT</pubDate>
<guid><![CDATA[]]></guid>
<description><![CDATA[<div><span style="color:#e53333;">走没走过的路，路上留点脚印，一来证明走过，二来方便回头重新再走。</span></div>
<div>通过这几天的了解，我最终从iPhone、Android、Windows 
Phone这3个平台中选择了iPhone作为移动开发的起点，这点可能认识我的朋友会觉得比较奇怪，因为之前我对C/C++都是避而远之的，呵呵，所以我说是走没走过的路。由于iPhone开发只能在苹果自己的操作系统（Mac 
OS）下进行，所以我之前也YY过要不要整台Mac机玩玩，但是按照我现在的GDP水平，表示压力很大。再所以呢，我就想那咱就在普通电脑上装一个Mac操作系统吧，这似乎是比较完美的方案，于是就google了一把，发现网民们纷纷表示这个比较难搞，过程很复杂，操作不慎很可能会丢失全部的数据，而且装不装得上还得看人品（各种迹象表明最近我的人品不行），就算装上了，也很容易崩溃掉，然后就是重装。好吧，我现在的笔记本上有太多重要的数据了，折腾不起，行，那咱就去配个普通台式机来吧，装个Mac系统，摆在家里，算是我的二奶吧。</div>
<div>于是在一个春光明媚的早晨，叫上朋友去了数码港，准备实现我的伪Mac梦。是我先到数码港的，于是便随便逛了一圈，看到了苹果店，想起了有个朋友向我提起过的Mac 
mini，相对其一般Mac机来说还是便宜点，低端配置4688 
RMB，长得大概是下面的样子，19.7厘米×19.7厘米×3.6厘米，就一个主机，没有显示器。<br />
<img src="/Upload/EditorImage/image/ivy/201204/20120421204025_7153.jpg" alt="" border="0" /><br />
<p>我当时想这个价格如果有个显示器可能就搞一个了，哈哈。别跑题了，今天我们的目标是来配一台普通台式机然后装上Mac系统的，对于Mac 
mini，如果确实在普通台式机上装Mac来开发不合适的话，可以考虑。</p>
<p>过了会儿，朋友也到了，于是我们就附近找了家吃的，坐下来边吃东西边确定硬件的配置（Mac系统比较特殊，所以硬件方面要考虑的比较多，主要是CPU和显卡）。最后我们确定的主要配置如下：</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;主板：华硕P8H61</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;CPU：酷睿 i3&nbsp; 2120&nbsp; 
3.3GHZ</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;内存：4GB&nbsp; DDR3</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;显卡：128位 2GB显存</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;硬盘：500GB 希捷</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;另外就是光驱、机箱电源这些小东西，就不说了。</p>
<p>好了，似乎事情是那么的完美，就等着去挑选这些配件了，这时候我似乎已经看到了我的伪Mac机的雏形，另外上面运行着XCode，和一行行优美的objective-c代码。然而事情并没有那么简单，一切的悲剧才刚刚开始。</p>
<p>说实在，从数码港店员招揽客人的热情度来看，这年代做生意真的不容易。刚走进店里，各种美女帅哥就向你抛媚眼，纷纷亮出了自家的营销绝招。后来我还是被一个帅哥吸引了过去（千万别误会我的性取向，我只是觉得他面善而已，而且他理直气壮的告诉我装Mac系统完全没问题）。于是，我们就坐了下来谈机器的配置、谈价钱、谈人生、谈理想，无论是对于他还是对于我来说，想要谈成一宗买卖甚是不容易，不过他能很爽快的说给装Mac系统让我很放心，因为我之前问过几家都说很麻烦不好装，也不太愿意装。于是这么三下五除二算是谈成了，接下来就是验货、装机了，哈哈，离我的目标越来越近了，心里还是挺激动的。</p>
<p>可是我幼小的心灵也是从那一刻开始受伤的，被那里的老板娘深深的伤害了（别想多了，不是你想的那样）。她居然告诉我，她在数码港做了6年了从来没给客户在台式机上装过Mac系统，肯定不能装的。我回礼道：刚才那位销售帅哥不是很自信的说能装的么？她回答道：他一个销售小P孩懂什么，总之这个装不来的，要么给你装XP。我擦，XP，XP的话我就用自己笔记本了，哪里还用来配台式机，总之装不上Mac那笔买卖就做不成。我告诉她装肯定是能装的，有成功的案例，就是要看具体的配置，所以需要花时间试试，但是她也不愿意了，她的时间很宝贵吧。后来她送我了一句我觉得不是那么正确的话：小伙子，当10个人中有9个人说这个装不了那就是装不了了，就算勉强装上去了也会出问题，装电脑是这样，以后做其他事情也是这样，可以尝试，但不能太执着，谨记！</p>
<p>好吧，最后我们的交易还是泡汤了，本来我打算机子买回去自己去装Mac系统，但是考虑到装的时候可能会因为硬件不合适而更换硬件，所以最终还是空手而回，My 
God，苦了我朋友陪了我一天，抱歉，中午那顿大娘水饺已经弥补不了你今天的时间损失，再次抱歉！</p>
<p>最后总结一下：</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="font-weight:bold;color:#e53333;">1、面对客户一定要坦诚，能做就做，不能做就不能做，说了能做后来说不能做，那就算以后真的能做客户也不会找你做了。</span></p>
<span style="color:#e53333;"> </span><p style="font-weight:bold;"><span style="color:#e53333;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2、10个中只要有1个人说能做的，那就一定能做的，只有自己尝试过不能做那才是真的不能做了，适当的放弃也是一种承担。</span></p>
<p>虽然这次Mac之旅失败了，但是我还是会继续探索尝试的，实在不行，那台Mac 
mini不错哦，考虑考虑，哈哈哈哈</p>
<p>
</p>
<div id="blogDetailDiv">
<div>
<div>
<p>也许下一篇《我的Mac之路（二）》出来的时候，我的第一个hello 
world程序已经在Mac上运行了，下次见，祝福我吧！</p>
</div>
</div>
</div>
</div>]]></description>
</item>

<item>
<link><![CDATA[http://www.itivy.com/ivy/archive/2012/4/8/lucene-and-hbase.html]]></link>
<title><![CDATA[集成Lucene和HBase]]></title>
<author><![CDATA[ivy]]></author>
<category><![CDATA[]]></category>
<pubDate>Sun, 08 Apr 2012 13:53:30 GMT</pubDate>
<guid><![CDATA[]]></guid>
<description><![CDATA[<p>在所有先进的应用程序中，不管是购物站点还是社交网络乃至风景名胜站点，搜索都扮演着关键的角色。Lucene搜索程序库事实上已经成为实现搜索引
擎的标准。苹果、IBM、Attlassian（Jira）、Wolfram以及很多大家喜欢的公司【1】都使用了这种技术。因此，大家对任何能够提升
Lucene的可伸缩性和性能的实现都很感兴趣。</p>
		











       	<h2>Lucene简介</h2>
<p>Lucene中可搜索的实体都表现为文档（document），它由字段（field）和值（value）组成。每个字段值都由一个或多个可搜索的
元素——即词汇（term）——组成。Lucene搜索基于反向索引，其中包含了关于可搜索文档的信息。在使用正常索引时，你可以搜索文档，以了解它包含
哪些字段，但使用反向索引与之不同，你会搜索字段的词汇，以了解所有包括该词汇的文档。</p>
<p>图1显示的是高层次的Lucene架构【2】。它的主要组件包括IndexSearcher、IndexReader、IndexWriter 
和Directory。IndexSearcher实现了搜索逻辑。IndexWriter为每个插入的文档写入反向索引。IndexReader会在
IndexSearcher的支持下读取索引的内容。IndexReader和IndexWriter都依赖于Directory，它会提供操作索引数据
集的API，而该API会直接模拟文件系统API。</p>
<p><img src="/Upload/EditorImage/image/ivy/201204/20120408134623_0312.jpg" alt="" border="0" /></p>
<p><strong>图1： 高层次的Lucene架构</strong></p>
<p>标准的Lucene分发包中有多个目录实现，包括基于文件系统和基于内存的<a name="0.1__ftnref1_8028" href="http://www.infoq.com/cn/articles/LuceneHbase#0.1__ftn1_8028">[1]</a>。</p>
<p>标准基于文件系统的后端的缺点在于，随着索引增加性能会下降。人们使用了各种不同的技术来解决这个问题，包括负载均衡和索引分片（index 
sharding）——在多个Lucene实例之间切分索引。尽管分片功能很强大，但它让总体的实现架构变得更复杂，并且需要大量对期望文档的预测知识，
才能对Lucene索引进行合适地分片。</p>
<p>另一种不同的方法是，让索引后端自身对数据进行正确地分片，并基于这样的后端构建出实现。这种后端可以是NoSQL数据库。在本文中我们会描述基于HBase的实现【4】。</p>
<h2>实现方法</h2>
<p>正如在【3】中所说明的，在高层次上，Lucene会操作两个单独的数据集：</p>
<ul><li>索引数据集中保存了所有字段/词汇对（还有其他信息，像术语频率、位置等），以及在恰当的字段包含这些词汇的文档。</li>
<li>文档数据集中存储所有文档，包括存储的字段等。</li>
</ul>
<p>正如我们已经在上面提到的，想要把Lucene移植到新的后端中，直接实现directory接口并不总会是最简单（最方便）的方法。所以，很多对
Lucene的移植，包括从Lucene的contrib.module支持的优先内存索引、Lucandra【5】和HBasene【6】分别采用了不
同的方法【2】，不仅重写了directory，还重写了高级的Lucene类——IndexReader和IndexWriter，从而绕开了
Directory的API（如图2）。</p>
<p><img src="/Upload/EditorImage/image/ivy/201204/20120408134701_4687.jpg" alt="" border="0" /></p>
<p><strong>图2： 将Lucene和没有文件系统的后端整合</strong></p>
<p>尽管这种方法通常需要更多工作【2】，但是它能够带来更强大的实现，让我们可以完全利用后端的本地功能。</p>
<p>文中所展现的实现<a name="0.1__ftnref2_8028" href="http://www.infoq.com/cn/articles/LuceneHbase#0.1__ftn2_8028">[2]</a>也遵循了这种方法。</p>
<h2>总体架构</h2>
<p>总体上的实现（如图3）是在基于内存的后端之上构建的，并将其用作内存缓存、同步缓存和HBase后端的机制。</p>
<p><img src="/Upload/EditorImage/image/ivy/201204/20120408134737_9843.jpg" alt="" border="0" /></p>
<p><strong>图3： 基于HBase的Lucene实现的总体架构</strong></p>
<p>该实现试图平衡两种相互冲突的需求，性能： 在内存中，缓存能够最小化HBase用于搜索和文件返回的数据读取量，从而极大提升性能；可伸缩性： 
按照需要运行为多个Lucene示例以支持日益增长的搜索客户端的能力。后者需要最小化缓存的生命周期，从而和HBase实例（上面提到实例的副本）中的
内容同步。通过为活动参数实现可配置的缓存时间，限制每个Lucene实例中展现的缓存，我们可以达成一种折中方案。</p>
<h2>内存缓存中的底层数据模型</h2>
<p>正如之前所提到的，内部Lucene数据模型基于两种主要的数据集——索引和文档，它们会被实现为两种模型——IndexMemoryModel和
DocumentMemoryModel。在我们的实现中，读写操作（IndexReader/IndexWriter）都是通过内存缓存完成的，但是它
们的实现有很大区别。对于读取操作，缓存首先会检查所需要的数据是否在内存中，并且没有过期<a name="0.1__ftnref3_8028" href="http://www.infoq.com/cn/articles/LuceneHbase#0.1__ftn3_8028">[3]</a>，
如果那样的话就会直接使用。否则缓存就会从HBase读取或者刷新数据，然后把它返回给IndexReader。相反，对于写操作，数据会直接写入到
HBase，而不会在内存中存储。尽管这在实际的数据可用性方面会有延迟，但是它会让实现过程非常简单——我们不需要考虑把新建或者更新的数据发送给哪个
缓存。这里的延迟可以通过设置合适的缓存过期时间来控制，从而符合业务需求。</p>
<h3>IndexMemoryModel</h3>
<p>索引内存模型的类图如图4所示。</p>
<p><img src="/Upload/EditorImage/image/ivy/201204/20120408134805_5781.jpg" alt="" border="0" /></p>
<p><strong>图4： IndexMemoryModel类图</strong></p>
<p>在这个实现中：</p>
<ul><li>LuceneIndexMemoryModel类包含了当前存在于内存中所有字段的FieldTermDocuments类。它还提供了所有对于实现IndexReader和IndexWriter必要的内部API。</li>
<li>FieldTermDocuments类会为每个字段值管理TermDocuments。通常，对于可扫描的数据库，字段的列表和字段值
的列表可以组合在可导航的字段/词汇值列表中。对于基于内存的缓存实现，我们已经把它们切分为两个独立的部分，从而让搜索的时间更可预测。</li>
<li>TermDocuments类为每个文档ID包含了一系列TermDocument类。</li>
<li>TermDocument类包含了针对给定文档在索引中存储的信息——文档使用频度和位置的数组。</li>
</ul>
<h3>DocumentMemoryModel</h3>
<p>文档内存模型的类图如图5所示。</p>
<p><img src="/Upload/EditorImage/image/ivy/201204/20120408134836_5156.jpg" alt="" border="0" /></p>
<p><strong>图5： DocumentMemoryModel类图</strong></p>
<p>在这个实现中：</p>
<ul><li>LuceneDocumentMemoryModel类包含DocumentStructure类与每个被索引文档的映射关系。</li>
<li>DocumentStructure类中包含了单个文档的信息。针对每个文档，它都包含了为每个索引后字段保存的字段和信息。</li>
<li>FieldData类包含为存储字段（stored field）所保存的信息，包括字段名称、值和二进制或者字符串型的标识。</li>
<li>DocumentTermFrequency类包含了关于每个被索引字段的信息，包括对相应索引结构（索引、词汇）的向后引用、文档中词汇使用频率、词汇在文档中的位置以及从文档开始的偏移量。</li>
</ul>
<h3>LuceneDocumentNormMemoryModel</h3>
<p>正如在【9】中说明的，规范（norm）是用于表现文档或字段的加权因子，从而提供更好的搜索结果排序，这需要耗费大量内存。类的实现基于对映射的映射（map of maps），内部映射会存储规范和文档的映射关系，而外部映射会存储规范和字段的映射关系。</p>
<p>尽管规范信息的键值是字段名称，从而可以添加到LuceneIndexMemoryModel类中，但是我们还是决定把对规范的管理实现为单独的类——LuceneDocumentNormMemoryModel。这么做的原因在于，在Lucene使用规范是可选的操作。</p>
<h3>IndexWriter</h3>
<p>有了之前所描述的底层内存模型，实现索引写入程序就很简单了。因为Lucene不会定义IndexWriter接口，所以想要实现
IndexWriter，我们需要实现所有标准Lucene实现中的方法。这个类的主要内容在于addDocument方法。这个方法会遍历所有文档的字
段。对于每个字段，方法都会检查它是否可以令牌化（tokenized），并使用特定的分析器来做到这一点。这个方法还会更新所有三种内存结构——索引、
文档和（可选的）规范，它们会为新增的文档存储信息。</p>
<h3>IndexReader</h3>
<p>IndexReader会实现Lucene核心所提供的IndexReader接口。因为Hbase中所获得的列表和单独的读操作相比要快很多，所以我们使用一些方法来扩展这个类，从而可以读取多个文档。类本身没有把更多的处理转交给几个类，它会对其进行管理：</p>
<ul><li>尽管文档ID通常是字符串，但Lucene内部还是对整型数操作。DocIDManager这个类会负责管理从字符串到数字的转换。IndexReader会以ThreadLocalStorage的形式使用这个类，从而可以在线程结束之后自动清理。</li>
<li>MemoryTermEnum类扩展了Lucene提供的TermEnum类，负责扫描字段/词汇的值。</li>
<li>MemoryTermFrequencyVector类会实现Lucene提供的TermDocs和TermPositions接口，负责为给定的字段/词汇对（field/tem pair）处理与文档相关的信息。</li>
<li>MemoryTermFrequencyVector类实现了Lucene提供的TermFreqVector和TermPositionVector接口，负责针对给定的文档ID返回文档字段频率和位置信息。</li>
</ul>
<h2>HBase表</h2>
<p>以上提出的解决方案基于两个主要的HBase表——Index表（图6）和document表（图7）。</p>
<p><img src="/Upload/EditorImage/image/ivy/201204/20120408134908_7656.jpg" alt="" border="0" /></p>
<p><strong>图6： HBase的Index表</strong></p>
<p><img style="width:712px;height:139px;" src="/Upload/EditorImage/image/ivy/201204/20120408134958_2500.jpg" alt="" border="0" /></p>
<p><strong>图7： HBase的document表</strong></p>
<p>如果需要支持规范的话，可选择实现第三个表（图8）。</p>
<p><img src="/Upload/EditorImage/image/ivy/201204/20120408135027_7343.jpg" alt="" border="0" /></p>
<p><strong>图8： HBase的norm表</strong></p>
<p>HBase的Index表（图6）会完成实现的主要工作。这个表对每个Lucene实例所知道的字段/词汇组合都设置了入口，其中包含一个列族
（column 
family）——documents族。这个列族为包含这个字段或词汇的所有文档都包含了一列（名称是文档的ID）。每个列的内容都是
TermDocument类的值。</p>
<p>HBase的document表（图7）存储了文档本身、对索引或规范的向后引用，它会为文档处理引用这些文档以及一些Lucene使用的附加信
息。它对所有Lucene实例知道的文档都设置了入口（row）。每个文档都通过文档ID（键值）唯一标识，并包含两个列族——字段族和索引族。字段列族
针对所有存储在Lucene中的文档字段包含一列（名称为字段的名称）。列的值是值的类型（字符串或者字符数组）和值本身的组合。索引列族为每个引用这个
文档的索引包含了一列（名称是字段或者术语）。列的值包括给定字段/词汇在文档的使用频率、位置和偏移量。</p>
<p>HBase的norm表（图8）为每个字段存储了文档的规范。它对所有Lucene实例知道的每个字段（键值）都设置了入口（行）。每行都只包含一个列族——规范族。这个族对每个需要存储给定字段规范的文档都有一列（名称是文档ID）。</p>
<h2>数据格式</h2>
<p>最终的设计方案确定了在HBase中存储数据的数据格式。对于这个实现，我们基于性能、结果数据的最小规模以及和Hadoop的紧密整合程度选择了Avro【10】。</p>
<p>实现主要使用的数据结构是TermDocument（代码1）、文档的FieldData（代码2）和DocumentTermFrequency（代码3）。</p>
<pre class="brush:js;">{
  "type" : "record",
  "name" : "TermDocument",
  "namespace" : "com.navteq.lucene.hbase.document",
  "fields" : [ {
    "name" : "docFrequency",
    "type" : "int"
  }, {
    "name" : "docPositions",
    "type" : ["null", {
      "type" : "array",
      "items" : "int"
   }]
  } ]
}</pre><strong>代码1 词汇文档AVRO定义</strong><br />
<pre class="brush:js;">{
  "type" : "record",
  "name" : "FieldsData",
  "namespace" : "com.navteq.lucene.hbase.document",
  "fields" : [ {
    "name" : "fieldsArray",
    "type" : {
      "type" : "array",
      "items" : {
        "type" : "record",
        "name" : "singleField",
        "fields" : [ {
          "name" : "binary",
          "type" : "boolean"
        }, {
          "name" : "data",
          "type" : [ "string", "bytes" ]
        } ]
      }
    }
  } ]
}</pre><strong>代码2 字段数据AVRO定义</strong><br />
<pre class="brush:js;">{
  "type" : "record",
  "name" : "TermDocumentFrequency",
  "namespace" : "com.navteq.lucene.hbase.document",
  "fields" : [ {
    "name" : "docFrequency",
    "type" : "int"
  }, {
    "name" : "docPositions",
    "type" : ["null",{
      "type" : "array",
      "items" : "int"
    }]
  }, {
    "name" : "docOffsets",
    "type" : ["null",{
      "type" : "array",
      "items" : {
        "type" : "record",
        "name" : "TermsOffset",
        "fields" : [ {
          "name" : "startOffset",
          "type" : "int"
        }, {
          "name" : "endOffset",
          "type" : "int"
        } ]
      }
    }]
  } ]
}</pre><p></p>
<p><strong>代码3 TermDocumentFrequency的AVRO定义</strong></p>
<h2>结论</h2>
<p>本文中描述的简单实现完全支持所有Lucene功能，针对Lucene核心和普通模块的单元测试都验证了这一点。我们可以将它作为基础，构建可扩展
性很强的搜索实现，支持HBase固有的可扩展性以及完全对称的设计，让我们可以添加任意数量服务于HBase数据的进程。它还可以避免需要关闭打开状态
的Lucene索引读取程序，就可以包含新的索引数据，那会经过一定延迟之后为用户所用，而延迟是通过活动参数的缓存时间所控制的。在下一篇文章中我们会
展示如何扩展这个实现，以包含地理搜索支持。</p>
<h2>关于作者</h2>
<p><b>Boris Lublinsky</b>是NAVTEQ的首席架构师，他的工作是为大型数据管理和处理、SOA以及实现各种NAVTEQ项目
定义架构的愿景。他还是InfoQ的SOA编辑，并且参与了OASIS的SOA 
RA工作组。Boris是一位作者，并经常发表演讲，他最新的著作是《Applied SOA》。</p>
<p><b>Michael Segel</b>拥有二十多年和客户协作的经验，和他们一起确定并解决业务问题。Michael曾经在多个领域以多种角色工作过。他是一位独立顾问，期望解决任何有挑战性的问题。Michael拥有俄亥俄州立大学的软件工程学位。</p>
<h3>参考文献</h3>
<ol><li><a target="_blank" href="http://wiki.apache.org/lucene-java/PoweredBy">Lucene-java Wiki</a><a name="0.1__Ref312216881"></a></li>
<li>Animesh Kumar. <a target="_blank" href="http://anismiles.wordpress.com/2010/05/19/apache-lucene-and-cassandra/">Apache Lucene and Cassandra </a></li>
<li>3. Animesh Kumar. <a target="_blank" href="http://anismiles.wordpress.com/2010/05/27/lucandra-an-inside-story/">Lucandra - an inside story! </a></li>
<li><a name="0.1__Ref280263202" target="_blank" href="http://hbase.apache.org/">HBase </a></li>
<li><a name="0.1__Ref309658101" target="_blank" href="https://github.com/tjake/Lucandra">Lucandra </a></li>
<li><a name="0.1__Ref280259776" target="_blank" href="https://github.com/akkumar/hbasene">HBasene </a></li>
<li><a name="0.1__Ref280263175" target="_blank" href="http://en.wikipedia.org/wiki/BigTable">Bigtable </a></li>
<li><a name="0.1__Ref280263215" target="_blank" href="http://cassandra.apache.org/">Cassandra </a></li>
<li>Michael McCandless, Erik Hatcher, Otis Gospodnetic. <a target="_blank" href="http://www.amazon.com/Lucene-Action-Second-Covers-Apache/dp/1933988177/ref=sr_1_1?ie=UTF8&amp;qid=1292717735&amp;sr=8-1">Lucene in Action, Second Edition. </a></li>
<li>Boris Lublinsky. <a target="_blank" href="http://www.infoq.com/articles/ApacheAvro">Using Apache Avro</a>.</li>
</ol>
<hr align="left" size="1" width="33%" />
<p><a name="0.1__ftn1_8028" href="http://www.infoq.com/cn/articles/LuceneHbase#0.1__ftnref1_8028">[1]</a>附加的Lucene程序包含了为Berkley DB构建的DB目录。</p>
<p><a name="0.1__ftn2_8028" href="http://www.infoq.com/cn/articles/LuceneHbase#0.1__ftnref2_8028">[2]</a>这个实现受到了Lusandra源代码【3】的启发。</p>
<p><a name="0.1__ftn3_8028" href="http://www.infoq.com/cn/articles/LuceneHbase#0.1__ftnref3_8028">[3]</a>没有在内存中存在太长时间。</p>
<p><b>查看英文原文：</b><a target="_blank" href="http://www.infoq.com/articles/LuceneHbase">Integrating Lucene with HBase</a></p>
<p>文章来源：<a target="_blank" href="http://www.infoq.com/cn/articles/LuceneHbase">infoq中文</a></p>]]></description>
</item>

<item>
<link><![CDATA[http://www.itivy.com/ivy/archive/2012/4/4/qing-ming-yi-gu-ren.html]]></link>
<title><![CDATA[清明，忆故人]]></title>
<author><![CDATA[ivy]]></author>
<category><![CDATA[]]></category>
<pubDate>Wed, 04 Apr 2012 08:57:14 GMT</pubDate>
<guid><![CDATA[]]></guid>
<description><![CDATA[<p><span style="font-size:16px;">清风醉，明月辉，满园春色，百蝶贺新岁；</span></p>
<span style="font-size:16px;"> </span><p><span style="font-size:16px;">故人归，齐相随，一朝离别，万年相思泪。</span></p>
<span style="font-size:16px;"> </span><p><span style="font-size:16px;">文/国峰&nbsp;&nbsp;&nbsp; 致/故人</span></p>]]></description>
</item>

<item>
<link><![CDATA[http://www.itivy.com/ivy/archive/2012/3/29/jquery-rotate3di-3d-rotate.html]]></link>
<title><![CDATA[jQuery结合CSS3实现3D翻转]]></title>
<author><![CDATA[ivy]]></author>
<category><![CDATA[]]></category>
<pubDate>Thu, 29 Mar 2012 22:43:31 GMT</pubDate>
<guid><![CDATA[]]></guid>
<description><![CDATA[<p>这个月最后一篇技术博文，就介绍一个jQuery 3D翻转的插件Rotate3Di吧，该插件结合CSS3的一些特性来实现页面元素的3D翻转效果，个人觉得还不错。</p>
<p>版权声明：非商业自由转载，保留原文完整性，并注明以下信息：</p>
<p>英文原文：<a href="http://www.zachstronaut.com/projects/rotate3di/" target="_blank">zachstronaut</a><br />
译文作者：<a href="http://www.itivy.com/ivy" target="_blank">王国峰</a><br />
译文链接：<a target="_blank" href="http://www.itivy.com/ivy/archive/2012/3/29/jquery-rotate3di-3d-rotate.html">http://www.itivy.com/ivy/archive/2012/3/29/jquery-rotate3di-3d-rotate.html</a></p>
<p>Rotate3Di是一个可以让任何HTML元素实现等距3D翻转和3D旋转的jQuery插件，它甚至允许你实现自定义3D旋转动画。当然这中间用到了CSS3的transform变换属性，所以这个3D翻转效果也只能在以下浏览器上有效果：Safari, Chrome, Webkit,&nbsp;<a href="http://www.firefox.com/" target="_firefox">Firefox 3.5+</a>, IE9+, and Opera 11+。该插件的功能包括：给HTML元素设置任意的等距旋转角度，翻转对象，或者切换对象翻转的状态。</p>
<p style="font-weight:bold;">使用插件</p>
<p>为了使用Rotate3Di插件，我们需要再页面上引用jquery 1.2.6或者更高版本的脚本库，jQuery CSS Transform补丁插件，和Rotate3Di自身的插件，代码如下：</p>
<pre class="brush:xhtml;">&lt;script type="text/javascript" src="jquery.js"&gt;&lt;/script&gt;
&lt;script type="text/javascript" src="rotate3Di/jquery-css-transform/jquery-css-transform.js"&gt;&lt;/script&gt;
&lt;script type="text/javascript" src="rotate3Di/rotate3Di.js"&gt;&lt;/script&gt;</pre>接下来我们就可以用下面的方法来使用Rotate3Di插件了：<br />
<pre class="brush:js;">$('#rot-ex').rotate3Di(180, 3000);</pre><span style="font-weight:bold;">API说明</span><p></p>
<p>方法：<span style="background-color:#ffe500;">rotate3Di(degrees, [duration], [options])</span></p>
<p><span style="background-color:#b8d100;">参数1：degrees</span></p>
<p>类型：Number,String</p>
<p>描述：相对于Y轴的旋转角度，可以是整数，负数和相对值，就像<a href="http://docs.jquery.com/Effects/animate" target="_jquery">jQuery animate effect</a>中的那样（如：<code>180</code>,&nbsp;<code>-360</code>,&nbsp;<code>'+=270'）。或者直接用字符串</code><code>'flip'</code>,&nbsp;<code>'unflip'</code>和&nbsp;<code>'toggle'。</code></p>
<p><code><span style="background-color:#99bb00;">参数2：</span></code><span style="background-color:#99bb00;">duration（可选）</span></p>
<p>类型：Number,String</p>
<p>描述：旋转的时间间隔，可以用三个字符串中的一个来表示翻转速度，<code>'slow'</code>,&nbsp;<code>'normal'</code>, 和&nbsp;<code>'fast'，也可以用一个毫秒数来表示比如（1000）。</code></p>
<p><code><span style="background-color:#99bb00;">参数3：options（可选）</span></code></p>
<p><code>类型：Object</code></p>
<p><code>描述：动画的一些其他配置，所有</code>&nbsp;<a href="http://docs.jquery.com/Effects/animate#toptions" target="_jquery">jQuery animate effect options</a>&nbsp;中的options都支持，除此之外，下面还有一些Rotate3Di自己的options，下面会介绍</p>
<p style="font-weight:bold;">选项（options）说明</p>
<p><span style="background-color:#99bb00;">选项1：</span><span style="background-color:#99bb00;">direction</span></p>
<p>类型：String</p>
<p>描述：当degrees参数的值为<code>'flip'</code>, <code>'unflip'</code>, 或<code>'toggle'时，direction选项可以使用</code><code>'clockwise'</code> 或 <code>'anticlockwise'</code> / <code>'counterclockwise'。当degrees参数为数值时，direction值就由degrees的值控制。</code></p>
<p><code><span style="background-color:#99bb00;">选项2：sideChange</span></code></p>
<p><code>类型：</code><code>function ([front])</code></p>
<p><code>描述：在任何时候对象的面被翻转时都会执行该回调函数，该回调函数可以带一个参数，如果对象被翻转至正面则该参数值为true。</code></p>
<p><code><span style="background-color:#99bb00;">选项3：complete</span></code></p>
<p><code>类型：function()</code></p>
<p><code>描述：翻转完成后执行的回调函数。</code></p>
<p><code><span style="background-color:#99bb00;">选项4：easing</span></code></p>
<p><code>类型：String</code></p>
<p><code>描述：这个和</code><a href="http://docs.jquery.com/Effects/animate#toptions" target="_jquery">jQuery animate</a>中的easing是一样的，主要是一些翻转的动画特效，可以选择 <code>'linear'</code> 和<code>'swing'，默认值是'swing'。</code></p>
<p><code>最后是这个插件的在线demo</code></p>
<p><a target="_blank" href="http://www.itivy.com/html/3d-rotate/index.html"><code><span style="font-size:16px;font-weight:bold;">点击查看3d翻转在线示例</span></code></a></p>]]></description>
</item>

<item>
<link><![CDATA[http://www.itivy.com/ivy/archive/2012/3/21/talk-about-cookie-security.html]]></link>
<title><![CDATA[Cookie安全漫谈]]></title>
<author><![CDATA[ivy]]></author>
<category><![CDATA[]]></category>
<pubDate>Wed, 21 Mar 2012 21:52:23 GMT</pubDate>
<guid><![CDATA[]]></guid>
<description><![CDATA[<p>在Web应用中，Cookie很容易成为安全问题的一部分。从以往的经验来看，对Cookie在开发过程中的使用，很多开发团队并没有形成共识或者一定的
规范，这也使得很多应用中的Cookie成为潜在的易受攻击点。在给Web应用做安全架构评审（Security architecture 
review）的时候，我通常会问设计人员以下几个问题：</p>
<ol><li>你的应用中，有使用JavaScript来操作客户端Cookie吗？如果有，那么是否必须使用JavaScript才能完成此应用场景？如果没有，你的Cookie允许JavaScript来访问吗？</li>
<li>你的网站（可能包含多个Web应用）中，对于Cookie的域（Domain）和路径（Path）设置是如何制定策略的？为何这样划分？</li>
<li>在有SSL的应用中，你的Cookie是否可以在HTTP请求和HTTPS请求中通用？</li>
</ol>
<p>在实际的应用场景中，Cookie被用来做得最多的一件事是保持身份认证的服务端状态。这种保持可能是基于会话（Session）的，也有可能是持
久性的。不管哪一种，身份认证Cookie中包含的服务端票据（Ticket）一旦泄露，那么服务端将很难区分带有此票据的用户请求是来自于真实的用户，
或者是来自恶意的攻击者。在实际案例中，造成Cookie泄露最多的途径，是通过跨站脚本（XSS, Cross Site 
Script）漏洞。攻击者可以通过一小段JavaScript代码，偷窃到代表用户身份的重要的Cookie标示。由于跨站脚本漏洞是如此的普遍（不要
以为简单的HTML 
Encode就可以避免被跨站，跨站是一门很深的学问，以至于在业界衍生出一个专用的名词：跨站师），几乎每一个网站都无法避免，所以这种方式是实际攻防
中被普遍使用的一种手段。</p>
<p>避免出现这种问题的首要秘诀就是尽所有的可能，给你的Cookie加上HttpOnly的标签。HttpOnly的具体使用不在本文的讨论范围内，
否则作者略有骗InfoQ稿酬的嫌疑。一个大家所不太熟悉的事实是，HttpOnly是由微软在2000年IE6 
Sp1中率先发明并予以支持的。截止现在，HttpOnly仍然只是一个厂商标准，但是在过去的十余年中，它得到了众多浏览器的广泛支持。</p>
<p>下表是OWASP整理的关于主流浏览器对HttpOnly支持情况的一个总结。从表中可以看出，目前主流的浏览器，除了Android之外，几乎都无一例外对这一属性予以了支持。</p>
<p>当然对于中国开发者来说，需要考虑的问题更加复杂一些：在这个神奇的地方，有大量的用户使用的浏览器并不在以下的列表中，他们使用的是以下面浏
览器中的一种或者数种为内核的“精装版”浏览器。这些浏览器厂商对于同源策略、HttpOnly 
Cookie、证书管理等安全规范的支持情况，有待于进一步调查。</p>
<table border="1" cellpadding="0" cellspacing="0" width="617">
    <tbody>
        <tr>
            <td width="205">
            <p align="center"><b>Browser</b></p>
            </td>
            <td width="143">
            <p align="center"><b>Version</b></p>
            </td>
            <td width="113">
            <p align="center"><b>Prevents Reads</b></p>
            </td>
            <td width="154">
            <p align="center"><b>Prevents Writes</b></p>
            </td>
        </tr>
        <tr>
            <td width="205">
            <p align="center">Microsoft Internet Explorer</p>
            </td>
            <td width="143">
            <p align="center">10</p>
            </td>
            <td width="113">
            <p align="center">Yes</p>
            </td>
            <td width="154">
            <p align="center">Yes</p>
            </td>
        </tr>
        <tr>
            <td width="205">
            <p align="center">Microsoft Internet Explorer</p>
            </td>
            <td width="143">
            <p align="center">9</p>
            </td>
            <td width="113">
            <p align="center">Yes</p>
            </td>
            <td width="154">
            <p align="center">Yes</p>
            </td>
        </tr>
        <tr>
            <td width="205">
            <p align="center">Microsoft Internet Explorer</p>
            </td>
            <td width="143">
            <p align="center">8</p>
            </td>
            <td width="113">
            <p align="center">Yes</p>
            </td>
            <td width="154">
            <p align="center">Yes</p>
            </td>
        </tr>
        <tr>
            <td width="205">
            <p align="center">Microsoft Internet Explorer</p>
            </td>
            <td width="143">
            <p align="center">7</p>
            </td>
            <td width="113">
            <p align="center">Yes</p>
            </td>
            <td width="154">
            <p align="center">Yes</p>
            </td>
        </tr>
        <tr>
            <td width="205">
            <p align="center">Microsoft Internet Explorer</p>
            </td>
            <td width="143">
            <p align="center">6 (SP1)</p>
            </td>
            <td width="113">
            <p align="center">Yes</p>
            </td>
            <td width="154">
            <p align="center">No</p>
            </td>
        </tr>
        <tr>
            <td width="205">
            <p align="center">Microsoft Internet Explorer</p>
            </td>
            <td width="143">
            <p align="center">6 (fully patched)</p>
            </td>
            <td width="113">
            <p align="center">Yes</p>
            </td>
            <td width="154">
            <p align="center">Unknown</p>
            </td>
        </tr>
        <tr>
            <td width="205">
            <p align="center">Mozilla Firefox</p>
            </td>
            <td width="143">
            <p align="center">3.0.0.6+</p>
            </td>
            <td width="113">
            <p align="center">Yes</p>
            </td>
            <td width="154">
            <p align="center">Yes</p>
            </td>
        </tr>
        <tr>
            <td width="205">
            <p align="center">Netscape Navigator</p>
            </td>
            <td width="143">
            <p align="center">9.0b3</p>
            </td>
            <td width="113">
            <p align="center">Yes</p>
            </td>
            <td width="154">
            <p align="center">Yes</p>
            </td>
        </tr>
        <tr>
            <td width="205">
            <p align="center">Opera</p>
            </td>
            <td width="143">
            <p align="center">9.23</p>
            </td>
            <td width="113">
            <p align="center">No</p>
            </td>
            <td width="154">
            <p align="center">No</p>
            </td>
        </tr>
        <tr>
            <td width="205">
            <p align="center">Opera</p>
            </td>
            <td width="143">
            <p align="center">9.5</p>
            </td>
            <td width="113">
            <p align="center">Yes</p>
            </td>
            <td width="154">
            <p align="center">No</p>
            </td>
        </tr>
        <tr>
            <td width="205">
            <p align="center">Opera</p>
            </td>
            <td width="143">
            <p align="center">11</p>
            </td>
            <td width="113">
            <p align="center">Yes</p>
            </td>
            <td width="154">
            <p align="center">Unknown</p>
            </td>
        </tr>
        <tr>
            <td width="205">
            <p align="center">Safari</p>
            </td>
            <td width="143">
            <p align="center">3</p>
            </td>
            <td width="113">
            <p align="center">No</p>
            </td>
            <td width="154">
            <p align="center">No</p>
            </td>
        </tr>
        <tr>
            <td width="205">
            <p align="center">Safari</p>
            </td>
            <td width="143">
            <p align="center">5</p>
            </td>
            <td width="113">
            <p align="center">Yes</p>
            </td>
            <td width="154">
            <p align="center">Yes</p>
            </td>
        </tr>
        <tr>
            <td width="205">
            <p align="center">iPhone (Safari)</p>
            </td>
            <td width="143">
            <p align="center">iOS 4</p>
            </td>
            <td width="113">
            <p align="center">Yes</p>
            </td>
            <td width="154">
            <p align="center">Yes</p>
            </td>
        </tr>
        <tr>
            <td width="205">
            <p align="center">Google's Chrome</p>
            </td>
            <td width="143">
            <p align="center">Beta (initial public release)</p>
            </td>
            <td width="113">
            <p align="center">Yes</p>
            </td>
            <td width="154">
            <p align="center">No</p>
            </td>
        </tr>
        <tr>
            <td width="205">
            <p align="center">Google's Chrome</p>
            </td>
            <td width="143">
            <p align="center">12</p>
            </td>
            <td width="113">
            <p align="center">Yes</p>
            </td>
            <td width="154">
            <p align="center">Yes</p>
            </td>
        </tr>
        <tr>
            <td width="205">
            <p align="center">Android</p>
            </td>
            <td width="143">
            <p align="center">Android 2.3</p>
            </td>
            <td width="113">
            <p align="center">Unknown</p>
            </td>
            <td width="154">
            <p align="center">Unknown</p>
            </td>
        </tr>
    </tbody>
</table>
<p>在我看来，一个Web应用的每一个Cookie都应该带上HttpOnly的标签。我没有看到这个决定可能带来的负面作用，如果一定要说有的话，那
么就是你的应用将不能再通过JavaScript来读写Cookie了。可是这真有必要吗？个人认为，JavaScript操作Cookie是一种不正常
的做法；可以用JavaScript操作Cookie完成的功能，一样可以用服务端响应Http头设置Cookie来完成。相反，设置所有的Cookie
为HttpOnly带来的巨大好处则非常明显：尽管你需要继续修复XSS漏洞，但是这些漏洞至少暂时不会让你的用户遭受大规模的账户损失。</p>
<p>关于Cookie的第二个话题是域设置。</p>
<p>浏览器在选择发送哪些本地Cookie到本次请求的服务端时，有一系列的比较和甄别。这些甄别中最重要的部分是Domain和Path的吻合。
Domain形如.abc.com的Cookie，会被发送给所有abc.com在80端口上的子域请求。但是反之则不行，这就是Cookie的域匹配
（domain match）原则。</p>
<p>在一个大型Web站点中，往往有多个应用，生存在不同的子域名或路径下。这些应用之间由于共享同一个域名，所以往往可能会彼此有操作对方应用
Cookie的能力。这种情况下，设计好Cookie的Domain和Path尤为重要。在实际设计工作中，最重要的一个安全原则就是：最小化授权。这意
味着，你需要将自己的Cookie可被访问到的范围降至最低。应用之间传递数据和共享信息的解决方案非常多，而通过Cookie这种用户输入（User 
input）来共享数据，是最不安全的解决方案之一。</p>
<p>Cookie另外一个不太常被使用的属性是Secure. 
这个属性启用时，浏览器仅仅会在HTTPS请求中向服务端发送Cookie内容。如果你的应用中有一处非常敏感的业务，比如登录或者付款，需要使用
HTTPS来保证内容的传输安全；而在用户成功获得授权之后，获得的客户端身份Cookie如果没有设置为Secure，那么很有可能会被非HTTPS页
面中拿到，从而造成重要的身份泄露。所以，在你的Web站点中，如果使用了SSL，那么你需要仔细检查在SSL的请求中返回的Cookie值，是否指定了
Secure属性。</p>
<p>除此之外还值得特别指出的是，一些Web应用除了自己的程序代码中生成的Cookie，往往还会从其他途径生成一些Cookie。例如由Web 
Server或者应用容器自动生成的会话Cookie，由第三方库或者框架生成的Cookie等等。这些都需要进行有针对性的加固处理。</p>
<p>几乎每个站点都难以离开Cookie，但Cookie的使用因其貌似简单，而很容易被人轻视。重新审视应用中的Cookie代码，几乎只需要很小的代价就可以获得巨大的安全收益。</p>
<p>作者：殷钧钧&nbsp;&nbsp;&nbsp; 来源：<a target="_blank" href="http://www.infoq.com/cn/articles/cookie-security">infoq中文</a></p>]]></description>
</item>

<item>
<link><![CDATA[http://www.itivy.com/ivy/archive/2012/3/18/gogo6-ipv6.html]]></link>
<title><![CDATA[我是如何用IPV6快速“翻墙”的]]></title>
<author><![CDATA[ivy]]></author>
<category><![CDATA[]]></category>
<pubDate>Sun, 18 Mar 2012 20:37:38 GMT</pubDate>
<guid><![CDATA[]]></guid>
<description><![CDATA[<p>版权声明：非商业自由转载，并保留原文完整性，署名作者<a target="_blank" href="http://www.itivy.com/ivy">王国峰</a>和<a target="_blank" href="http://www.itivy.com/ivy/archive/2012/3/18/gogo6-ipv6.html">原文链接</a></p>
<p>这个社会很和谐，真的很“和谐”，在google中搜索我的大名“王国峰”居然被连接重置，这个尚可以原谅，毕竟我未来会是一个非凡的人物，甚至非凡到足以威胁到社会秩序和国家安全。但如果你在google中搜索“胡萝卜”一词也被ZF重置的话，那就让人匪夷所思了，难道今后会演绎一场“一个胡萝卜引发的血案”？我仅仅是想查一下胡萝卜是否对壮阳有功效啊，仅此而已啊，何必这样对我，何必呢！！</p>
<p>上面的这些搜索词或许你不会经常使用，但现在国内的互联网确实存在一个令人咬牙切齿的问题：很多国外优秀的网站均被GFW封杀，就算我们用得最多的Google也时常因为一些敏感词（比如“胡萝卜”，可笑）而被连接重置。所以喜欢自由的你或许会用各种方法去打破这种禁锢，专业一点地说就是“翻墙”。比如用在线代理、http代理、VPN代理、SSH代理等，但你会发现这些代理要么免费但速度慢不稳定，要么就要钱，总之用起来不是那么爽。</p>
<p>那么为何不试试ipv6来翻墙呢，因为当初GFW在设计的时候只支持ipv4，对于现在刚出来的ipv6还不支持，所以对于ipv6来说，根本没有“墙”这一说。今天就向大家介绍一下我是如何使用ipv6和gogo6来翻墙的。</p>
<p>其实很多地方有讲如何给你的xp、win7启用ipv6的方法，但我觉得这个还是麻烦了点，如果能有一个软件直接运行一下就能实现那就好了，于是gogo6就出现了。</p>
<p>gogo6是一个帮你自动获取ipv6地址的软件，实现ipv4 to ipv6，这样你才可以用这个ipv6地址去访问各大网站的ipv6地址，具体实现方法如下：</p>
<p>1、下载和安装gogo6客户端</p>
<p>可以去gogo6的官网注册下载，如果你英文不咋的，又不想注册，可以从我这里下载，当然我这里只提供windows 32位的版本和64位版本（大部分朋友是32位的），linux或者其他的就去官网下吧。</p>
<p>官网地址：http://gogonet.gogo6.com/profile/gogoCLIENT</p>
<p>或者<a target="_blank" href="http://www.itivy.com/DownloadFile.ashx?id=634676981461239103">点击下载windows 32位版本的gogo6软件</a>。</p>
<p>或者<a target="_blank" href="http://api.ning.com/files/eTQek6oePnyf09jlLkAAW4a-kmGIPcck3HM13gtCQib3eoGx9KaoLLlm5DuufWnrx5b1irL5pXkT0feqbBiA2aPjgUwoSrUS/gogoc1.2RELEASEwin64.exe">点击下载windows 64位版本的gogo6软件</a>。</p>
<p>下载完后点击安装，安装完成后启动gogo6软件，不出意外的话应该是这个界面</p>
<p><img src="/Upload/EditorImage/image/ivy/201203/20120318191739_4839.jpg" alt="" border="0" /></p>
<p>这里使用默认设置好了，然后点击connect按钮，连接过程中我们可爱的360可能会弹出一些警告，我劝你“允许”它，或者直接先退出360。</p>
<p>连接成功后系统任务栏会显示如下提示：</p>
<p><img src="/Upload/EditorImage/image/ivy/201203/20120318192438_2401.jpg" alt="" border="0" /></p>
<p>这样你就顺利连接上了gogo6服务器，并获取了ipv6地址。</p>
<p>现在我们可以访问ipv6的google了，地址<a href="http://ipv6.google.com/ncr" target="_blank"><span style="color:#800080;">http://ipv6.google.com/ncr</span></a>。在这里试试搜索我的名字“王国峰”试试呢，或者搜一下该死的“胡萝卜”，看看是否能搜到结果了，哈哈，成功。</p>
<p>到这里，我们解决了google搜索的问题，但是我还想去facebook和老外交流，想去twitter上发微博调戏美女、想去youtube上看看好玩的视频，如果你和我有着同样的爱好，那么请继续看下文。</p>
<p>2、修改hosts文件，加入各大网站的ipv6地址</p>
<p>hosts文件一直被人们尊称为一个神奇的文件，之前我们翻墙也用过它，但这次我们要在hosts文件中加入的是各大网站的ipv6地址。</p>
<p>你可以复制下面的代码至你的hosts文件中（双击代码部分即可全选代码）：</p>
<p>需要加入hosts文件的代码可以从下面这个文件中找到：<br />
<a target="_blank" href="http://www.itivy.com/html/gogo6-hosts.txt">http://www.itivy.com/html/gogo6-hosts.txt</a><br />
打开上面的页面，将里面的代码复制粘贴到你的hosts文件中即可，你的hosts文件应该在C:\Windows\System32\drivers\etc下</p>
<p>就这样，你就可以访问facebook、twitter和youtube了，突然间，这个世界清静了许多。</p>
<p><span style="color:#e53333;font-weight:bold;">注意：如果gogo6软件connect失败，那检查是否安装了正确版本的gogo6，主要是区分32位和64位。然后就是connect过程中360弹出的要允许它，或者直接关掉360。</span></p>
<p>我刚在twitter开通了账户，翻墙成功的朋友可以follow我，我的twitter主页：<a target="_blank" href="http://twitter.com/sxwgf">http://twitter.com/sxwgf</a></p>
<p><a target="_blank" href="http://twitter.com/sxwgf"></a></p>
<p>另外，我是一个良民，翻墙是不做坏事的，我希望你们也不要做坏事，否则被妈妈知道不好，嘘....</p>
<p>为了您和您家人的健康，转载请保留本文链接：<br />
<a target="_blank" href="http://www.itivy.com/ivy/archive/2012/3/18/gogo6-ipv6.html">http://www.itivy.com/ivy/archive/2012/3/18/gogo6-ipv6.html</a></p>]]></description>
</item>

<item>
<link><![CDATA[http://www.itivy.com/ivy/archive/2012/3/16/jquery-delegate-and-event.html]]></link>
<title><![CDATA[jQuery代码优化：事件委托篇]]></title>
<author><![CDATA[ivy]]></author>
<category><![CDATA[]]></category>
<pubDate>Fri, 16 Mar 2012 21:46:18 GMT</pubDate>
<guid><![CDATA[]]></guid>
<description><![CDATA[<p>看到一篇讲jQuery委托和事件比较靠谱的文章，现转给有兴趣的朋友的看看：</p>
<p>随着DOM结构的复杂化和Ajax等动态脚本技术的运用，事件委托自然浮出了水面。jQuery为绑定和委托事件提供了.bind()、.live()和.delegate()方法。本文在讨论这几个方法内部实现的基础上，展示它们的优劣势及适用场合。</p>
<h2>事件委托</h2>
<p>事件委托的事例在现实当中比比皆是。比如，有三个同事预计会在周一收到快递。为签收快递，有两种办法：一是三个人在公司门口等快递；二是委托给前台
MM代为签收。现实当中，我们大都采用委托的方案（公司也不会容忍那么多员工站在门口就为了等快递）。前台MM收到快递后，她会判断收件人是谁，然后按照
收件人的要求签收，甚至代为付款。这种方案还有一个优势，那就是即使公司里来了新员工（不管多少），前台MM也会在收到寄给新员工的快递后核实并代为签
收。</p>
<p>我们知道，DOM在为页面中的每个元素分派事件时，相应的元素一般都在事件冒泡阶段处理事件。在类似 body &gt; div &gt; a 
这样的结构中，如果单击a元素，click事件会从a一直冒泡到div和body（即document对象）。因此，发生在a上面的单击事件，div和
body元素同样可以处理。而利用事件传播（这里是冒泡）这个机制，就可以实现事件委托。具体来说，事件委托就是事件目标自身不处理事件，而是把处理任务
委托给其父元素或者祖先元素，甚至根元素（document）。</p>
<h2>.bind()</h2>
<p>假设有一个多行多列的表格，我们想让用户单击每个单元格都能看到与其中内容相关的更多信息（比如，通过提示条）。为此，可以为每个单元格都绑定click事件：</p>
<pre class="brush:js;">$("info_table td").bind("click", function(){/*显示更多信息*/});</pre><p></p>
<p>问题是，如果表格中要绑定单击事件的有10列500行，那么查找和遍历5000个单元格会导致脚本执行速度明显变慢，而保存5000个td元素和相应的事件处理程序也会占用大量内存（类似于让每个人亲自站在门口等快递）。</p>
<p>在前面这个例子的基础上，如果我们想实现一个简单的相册应用，每页只显示50张照片的缩略图（50个单元格），用户点击“第x页”（或“下一页”）
链接可以通过Ajax从服务器动态加载另外50张照片。在这种情况下，似乎使用.bind()方法为50个单元格绑定事件又可以接受了。</p>
<p>事实却不然。使用.bind()方法只会给第一页中的50个单元格绑定单击事件，动态加载的后续页面中的单元格都不会有这个单击事件。换句话说，.bind()只能给调用它的时候已经存在的元素绑定事件，不能给未来新增的元素绑定事件（类似于新来的员工收不到快递）。</p>
<p>事件委托可以解决上述两个问题。具体到代码上，只要用jQuery 1.3新增的.live()方法代替.bind()方法即可：</p>
<pre class="brush:js;">$("#info_table td").live("click",function(){/*显示更多信息*/});</pre><p></p>
<p>这里的.live()方法会把click事件绑定到$(document)对象（但这一点从代码中体现不出来，这也是.live()方法饱受诟病的
一个重要原因，稍后再详细讨论），而且只需要给$(document)绑定一次（不是50次，更不是5000次），然后就能够处理后续动态加载的照片单元
格的单击事件。在接收到任何事件时，$(document)对象都会检查事件类型和事件目标，如果是click事件且事件目标是td，那么就执行委托给它
的处理程序。</p>
<h2>.live()</h2>
<p>到目前为止，一切似乎很完美。可惜，事实并非如此。因为.live()方法并不完美，它有如下几个主要缺点：</p>
<ul><li>$()函数会找到当前页面中的所有td元素并创建jQuery对象，但在确认事件目标时却不用这个td元素集合，而是使用选择符表达式与event.target或其祖先元素进行比较，因而生成这个jQuery对象会造成不必要的开销；</li>
<li>默认把事件绑定到$(document)元素，如果DOM嵌套结构很深，事件冒泡通过大量祖先元素会导致性能损失；</li>
<li>只能放在直接选择的元素后面，不能在连缀的DOM遍历方法后面使用，即$(“#info<em>table td”).live…可以，但$(“#info</em>table”).find(“td”).live…不行；</li>
<li>收集td元素并创建jQuery对象，但实际操作的却是$(document)对象，令人费解。</li>
</ul>
<h3>解决之道</h3>
<p>为了避免生成不必要的jQuery对象，可以使用一种叫做“早委托”的hack，即在$(document).ready()方法外部调用.live()：</p>
<pre class="brush:js;">(function($){
    $("#info_table td").live("click",function(){/*显示更多信息*/});
})(jQuery);</pre><p></p>
<p>在此，(function($){…})(jQuery)是一个<strong>“立即执行的匿名函数”</strong>，构成了一个闭包，可以
防止命名冲突。在匿名函数内部，$参数引用jQuery对象。这个匿名函数不会等到DOM就绪就会执行。注意，使用这个hack时，脚本必须是在页面的
head元素中链接和(或)执行的。之所以选择这个时机，因为这时候刚好document元素可用，而整个DOM还远未生成；如果把脚本放在结束的
body标签前面，就没有意义了，因为那时候DOM已经完全可用了。</p>
<p>为了避免事件冒泡造成的性能损失，jQuery从1.4开始支持在使用.live()方法时配合使用一个上下文参数：</p>
<pre class="brush:js;">$("td",$("#info_table")[0]).live("click",function(){/*显示更多信息*/});</pre><p></p>
<p>这样，“受托方”就从默认的$(document)变成了$(“#info<em>table”)[0]，节省了冒泡的旅程。不过，与.live()共同使用的上下文参数必须是一个单独的DOM元素，所以这里指定上下文对象时使用的是$(“#info</em>table”)[0]，即使用数组的索引操作符来取得的一个DOM元素。</p>
<h2>.delegate()</h2>
<p>如前所述，为了突破单一.bind()方法的局限性，实现事件委托，jQuery 
1.3引入了.live()方法。后来，为解决“事件传播链”过长的问题，jQuery 
1.4又支持为.live()方法指定上下文对象。而为了解决无谓生成元素集合的问题，jQuery 
1.4.2干脆直接引入了一个新方法.delegate()。</p>
<p>使用.delegate()，前面的例子可以这样写：</p>
<pre class="brush:js;">$("#info_table").delegate("td","click",function(){/*显示更多信息*/});</pre><p></p>
<p>使用.delegate()有如下优点（或者说解决了.live()方法的如下问题）：</p>
<ul><li>直接将目标元素选择符（”td”）、事件（”click”）及处理程序与“受拖方”$(“#info_table”)绑定，不额外收集元素、事件传播路径缩短、语义明确；</li>
<li>支持在连缀的DOM遍历方法后面调用，即支持$(“table”).find(“#info”).delegate…，支持精确控制；</li>
</ul>
<p>可见，.delegate()方法是一个相对完美的解决方案。但在DOM结构简单的情况下，也可以使用.live()。</p>
<blockquote><p>提示：使用事件委托时，如果注册到目标元素上的其他事件处理程序使用.stopPropagation()阻止了事件传播，那么事件委托就会失效。</p>
</blockquote>
<h2>结论</h2>
<p>在下列情况下，应该使用.live()或.delegate()，而不能使用.bind()：</p>
<ul><li>为DOM中的很多元素绑定相同事件；</li>
<li>为DOM中尚不存在的元素绑定事件；</li>
</ul>
<blockquote><p>PS：根据<a href="http://blog.jquery.com/2011/09/28/jquery-1-7-beta-1-released/" target="_blank">jQuery 1.7 Beta 1</a>的发版说明，jQuery 1.7为了解决.bind()、.live()和.delegate()并存造成的不一致性问题，将会增加一对新的事件方法：.on()和.off()：<br />
$(elems).on(events, selector, data, fn);<br />
$(elems).off(events, selector, fn);<br />
如果指定selector，则为事件委托；否则，就是常规绑定。新旧API对应如下：<br />
<img src="/Upload/EditorImage/image/ivy/201203/20120316214813_5240.jpg" alt="" border="0" /></p>
</blockquote>
<p>（注：本文基于《jQuery基础教程（第3版）》相关章节内容编撰而成，同时参考了</p>
<ul><li><a href="http://www.alfajango.com/blog/the-difference-between-jquerys-bind-live-and-delegate/" target="_blank">The Difference Between jQuery’s .bind(), .live(), and .delegate()</a></li>
<li><a href="http://jquerybyexample.blogspot.com/2010/08/bind-vs-live-vs-delegate-function.html" target="_blank">bind() vs live() vs delegate() function</a></li>
<li><a href="http://api.jquery.com/" target="_blank">jQuery API</a></li>
</ul>
<p>文章来源：<a target="_blank" href="http://www.ituring.com.cn/article/467">http://www.ituring.com.cn/article/467</a></p>]]></description>
</item>

<item>
<link><![CDATA[http://www.itivy.com/ivy/archive/2012/3/12/where-is-our-copyright-in-china.html]]></link>
<title><![CDATA[在中国，我们的知识产权真的陨落了吗？]]></title>
<author><![CDATA[ivy]]></author>
<category><![CDATA[]]></category>
<pubDate>Mon, 12 Mar 2012 23:17:38 GMT</pubDate>
<guid><![CDATA[]]></guid>
<description><![CDATA[<p>今天去逛我一个朋友的博客，突然看到一篇文章非常眼熟，标题是《这样的设计师，你们伤不起啊》，是的，没错，这是我2011年7月9日写的文章，原文：<a target="_blank" href="http://www.itivy.com/ivy/archive/2011/7/9/this-design-can-not-be-hurt.html">这样的设计师，你们伤不起啊</a>。当时我心想，你这小子怎么会来转我的文章，结果点击去一看，没有标明原文地址，我想算了算了，给了他一条评论，算作是朋友间的沟通和交流。后来我无聊手贱了，拿着这个标题去百度了一下，结果让我大吃一惊，居然有那么多网站转载这篇文章，而且都是一些大站，截图如下：</p>
<p><img src="/Upload/EditorImage/image/ivy/201203/20120312221659_0283.jpg" alt="" border="0" /></p>
<p>其中有cnbeta、iteye、网易、博客园等等，当时我暗自高兴了一把，心想肯定是这篇文章大家都比较喜欢。于是我又手贱了，点进了cnbeta看这篇文章，一看到文章开头，我郁闷了</p>
<p><img src="/Upload/EditorImage/image/ivy/201203/20120312222337_2600.jpg" alt="" border="0" /></p>
<p>怎么来源成了伯乐在线了？我20几年来坐不改名，从来没有叫过“伯乐在线”，我老爸老妈也从没叫我“伯乐在线”，真是让我百思不得其解。于是我给cnbeta提交了一条咨询，请他们更正这滑稽可笑的行为。</p>
<p>看完cnbeta后，我还是手贱，又点进去了网易，发现网易上写的来源是cnbeta：</p>
<p><img src="/Upload/EditorImage/image/ivy/201203/20120312223222_5832.jpg" alt="" border="0" /></p>
<p>我更纳闷了，网易怎么也这样的，他不是在我们这个和谐社会中一直扮演着非常酷的角色么，今天怎么变那么猥琐了？让我非常不解，真的非常不解。没办法，只能再手贱一把，给网易编辑写邮件解释这一切，让他们更正新闻来源。即便面对网易这个大巨头，申辩希望非常渺茫，但我觉得这是我应该要做的，因为我还活着。</p>
<p>后来还是不停地手贱，在百度上翻了几页，发现了博客园的新闻频道，我想博客园是我最喜欢的地方之一了，点击去一看，纳尼？这回我真的射了。</p>
<p><img src="/Upload/EditorImage/image/ivy/201203/20120312224053_4611.jpg" alt="" border="0" /></p>
<p>连我最喜欢的博客园都无情地强奸我，这时的我真的非常失落，非常。我本来不想再写邮件了的，但是后来还是写了，还自己一个清白，也还博客园一个清白。</p>
<p>至此，我已经无法再手贱去其他网站上检查了，因为我实在是不太习惯发邮件。</p>
<p>虽然被侵犯的那篇文章没有太多的技术含量，里面的漫画也是出自台湾设计师马克之手，但是这中间有我自己的点评和自己的感想，我想这是需要去维护的。</p>
<p>我写这篇文章的目的并没有炫耀自己的意思（也没啥好炫耀的），<span style="font-weight:bold;color:#e53333;">也并没有责备上面提到的3个网站</span>。我只是觉得在国内，知识产权正在一步步被有意无意的摧残，我们如何去维护它的尊严，维护自己的尊严，我想这是值得我们静下心来认真去思考的问题。大家觉得呢？我很想听听大家的意见。另外，如果大家在我的博客中发现有侵犯您和您朋友知识产权的地方，请立即邮件联系我：sxwgf.com@gmail.com 我将第一时间作出处理，谢谢！</p>
<p><span style="font-size:18px;color:#e53333;font-weight:bold;">截至2012.3.12&nbsp; 23:00:00&nbsp; <a target="_blank" href="http://www.cnbeta.com/articles/175853.htm">cnbeta</a>已经将来源更正，谢谢！</span></p>]]></description>
</item>

<item>
<link><![CDATA[http://www.itivy.com/ivy/archive/2012/3/10/squid-usage.html]]></link>
<title><![CDATA[浅谈Squid在图片存储架构中的应用]]></title>
<author><![CDATA[ivy]]></author>
<category><![CDATA[]]></category>
<pubDate>Sat, 10 Mar 2012 20:04:52 GMT</pubDate>
<guid><![CDATA[]]></guid>
<description><![CDATA[<p>将近快一个月没写文章了，太懒散了，今天振作了一下，写了篇关于Squid的文章，Squid作为分布式代理缓存服务器真的非常的棒，希望本文对你有帮助。</p>
<p>版权声明：非商业自由转载，保留原文内容完整性，并署名作者<a target="_blank" href="http://www.itivy.com/ivy">王国峰</a>和<a target="_blank" href="http://www.itivy.com/ivy/archive/2012/3/10/squid-usage.html">原文链接</a></p>
<p style="font-weight:bold;"><span style="font-size:14px;color:#006600;">前言</span></p>
<p>上一篇我表明了自己对缓存的立场：<span style="color:red;font-weight:bold;">缓存一切可以缓存的资源</span>。并讨论了如何利用IIS自身的缓存功能来轻松满足中小规模的图片缓存需求。关于IIS的客户端缓存和服务器端缓存的介绍大家可以回顾<a target="_blank" href="http://www.itivy.com/ivy/archive/2012/2/18/image-storage-cache-1.html">这篇文章</a>。自从上一篇文章发表后，有不少朋友向我反馈：用IIS自身的缓存功能负载有限，建议使用<span id="comment_body_2312014" class="blog_comment_body">Varnish或Squid。是的，我非常同意，所以我今天就和大家来探讨一下分布式代理缓存服务器Squid在图片存储架构中的应用，文中的一些观点，如有错误，敬请指出，谢谢。</span></p>
<p style="font-weight:bold;"><span style="color:#006600;font-size:14px;" id="comment_body_2312014" class="blog_comment_body">Squid简介</span></p>
<p><span id="comment_body_2312014" class="blog_comment_body">Squid是一款高性能分布式代理缓存服务器，它一般用来做前置Web Cache，加快用户访问Web的速度。为了说明其运行过程，我将仍旧沿用我前一篇的YD风格，再一次请出人见人爱、花见花开、车见爆胎的空空老师，请读者自觉送出掌声：</span></p>
<blockquote>
<p><span id="comment_body_2312014" class="blog_comment_body">Squid君是</span><span id="comment_body_2312014" class="blog_comment_body">时下最受欢迎的空空作品代理人，为了让所有空空迷能更快捷地欣赏到高质量的空空作品，Squid君天天加班加点，整理和保存空空的最新作品，并对其作进一步的优化处理，比如去码。</span></p>
<p><span id="comment_body_2312014" class="blog_comment_body">有一天，我无聊至极，便打电话给Squid君，向他要最新的空空写真图（<span style="color:#006600;">向Squid发出下载请求</span>）；</span></p>
<p><span id="comment_body_2312014" class="blog_comment_body">Squid君仔细辨别了一下我的声音，发现是老朋友，于是便答应了我的请求（<span style="color:#006600;">Squid中的ACL访问控制，只接受合法的请求</span>）；</span></p>
<p><span id="comment_body_2312014" class="blog_comment_body">Squid君对我说他手头刚好有一周前的空空写真，问我看过没有，我说发过来看看呗，于是他很快发给了我（<span style="color:#006600;">Squid缓存命中，下载极快，哦耶！</span>）；</span></p>
<p><span id="comment_body_2312014" class="blog_comment_body">我收到写真图，发现是两天前已经看过了的，于是告诉Squid君要最新的，Squid君看了看库存，发现手头没有最新的了，于是就说让我等一会儿，他去问问其他的代理兄弟有没有，我说好的，谢谢（<span style="color:#006600;">Squid支持分布式集群方式，各个父子节点、兄弟节点之间的缓存数据可以互相同步</span>）；</span></p>
<p><span id="comment_body_2312014" class="blog_comment_body">结果空欢喜一场，他回来和我说其他兄弟也没有，当我非常失落的时候，他突然说空空今天来杭州，他可以直接向空空要写真，我一开心就对他说，快去快去（<span style="color:#006600;">当Squid缓存不命中时，只能从源服务器获取数据</span>）；</span></p>
<p><span id="comment_body_2312014" class="blog_comment_body">过了好久，Squid君回来了，他说见空空的人实在太多了，排了好久的队才拿到她最新的写真图呢，我谢过之后他就把写真很快地发给了我。之后他说他一定要把这个写真保存起来，以便提供给其他空空迷和其他兄弟代理（<span style="color:#006600;">Squid会把从源服务器获取的数据保存到自己的缓存中，如果下一个用户提出同样的下载请求，Squid直接把缓存中的数据给用户，当然也可以把数据奉送给其他兄弟代理，独乐乐不如众乐乐</span>）。</span></p>
</blockquote>
<p><span id="comment_body_2312014" class="blog_comment_body">到这里，我相信你已经大概明白Squid的工作过程了吧，</span><span id="comment_body_2312014" class="blog_comment_body">下面我就对Squid特点做一下简要概括：</span></p>
<ol><li><span id="comment_body_2312014" class="blog_comment_body">开源，基于</span>GNU通用公共许可证，意味着你可以在同等开源协议下使用和修改Squid。</li>
<li>支持多种协议，目前支持http、ftp、gopher、wais、ssl等网络协议。</li>
<li>支持分布式集群，Squid使用TCP(HTTP)和UDP(ICP/HTCP)通讯来确定邻居cache的状态。</li>
<li>支持访问控制，通过设置ACL和ARL来限制某些访问。</li>
</ol>
<p>
Squid官方网站：<a target="_blank" href="http://www.squid-cache.org/">http://www.squid-cache.org/</a></p>
<p>你可以在官网上下载Squid以及阅读相关文档，当然接下来我也会用Chinese向大家介绍Squid的用法。</p>
<p style="font-weight:bold;"><span style="font-size:14px;color:#006600;">Squid配置详细说明</span></p>
<p>下面是一份详细的Squid的配置清单及其说明：</p>
<pre class="brush:bash;">#http_port指令告诉squid在哪个端口侦听HTTP请求。默认端口是3128,除下面的形式外,也可以是http_port 192.168.63.50:3128
http_port 3128
icp_port 3130

#缓存目录的设置,可以设置多个缓存目录,语法为:&lt;cache_dir&gt; &lt;aufs|ufs&gt; &lt;目录所在&gt; &lt;MBytes大小&gt; &lt;dir1&gt; &lt;dir2&gt;
cache_dir ufs /var/spool/squid 1000 64 1024

#下面是关于日志文件的放置目录与文件名！
cache_access_log /var/log/squid/access.log
cache_log /var/log/squid/cache.log
cache_store_log /var/log/squid/store.log
pid_filename /var/run/squid.pid

#关闭认证机制，有些版本的　squid 会自动的加入代理认证机制，而普通情况下是不需要的,故找到包括auth_param的行，给它们加上注释
#auth_param basic children 5
#auth_param basic realm Squid proxy-caching web server
#auth_param basic credentialsttl 2 hours

#设置squid用户及用户组、管理员账号
cache_effective_user squid
cache_effective_group squid
cache_mgr youraccount@your.e.mail

# 与内存有关的配置：因为我的系统内存很小，所以只给 8 MB！如果您的物理内存很大的情况下，例如 512 MB，可以考虑加大到 64 或 128 MB。
cache_mem 128 MB

# 与磁盘容量有关的配置(注：下列的 90 与 95 是百分比 )，如果您的 cache_dir 所在磁盘很大时，可以考虑将 4096 改成 32768 KB
cache_swap_low 90
cache_swap_high 95
maximum_object_size 4096 KB

# 与内存保存资料有关的配置
maximum_object_size_in_memory 8 KB

#定义acl(访问控制列表), 语法为:acl&lt;acl&gt; &lt;acl名称&gt; &lt;acl类型&gt; &lt;配置的内容&gt;
#黑体为用户自定义部分

acl All src 0/0
acl Manager proto cache_object
acl Localhost src 127.0.0.1/32
acl Safe_ports port 80 21 443 563 70 210 280 488 591 777 1025-65535
acl SSL_ports 443 563
acl CONNECT method CONNECT
acl MyNetwork src 192.168.0.0/16

#利用前面定义的acl,定义访问控制规则
http_access allow Manager Localhost
http_access deny Manager
http_access deny !Safe_ports
http_access deny CONNECT !SSL_ports
http_access allow MyNetwork
http_access deny All

#定义与其它代理服务器的关系,语法: &lt;cache_peer&gt; &lt;主机名称&gt; &lt;类别&gt; &lt;http_port&gt; &lt;icp_port&gt; &lt;其它参数&gt;
cache_peer 192.168.60.6 parent 4480 7 no-query default

#设置与其它代理服务器的关系:
# &lt;cache_peer_access&gt; &lt;上层 Proxy &gt; &lt;allow|deny&gt; &lt;acl名称&gt;
#cache_peer_access 192.168.60.6 allow aclxxx
#cache_peer_access 192.168.60.6 deny !aclxxx

coredump_dir /var/spool/squid</pre><span style="font-weight:bold;color:#e53333;">注意</span>：以上的配置说明来自<a target="_blank" href="http://www.linux.gov.cn/netweb/squid.htm">这里</a>。<p></p>
<p>配置好以后，我们就可以启动squid了，启动的步骤如下：</p>
<p><span style="font-weight:bold;">1)</span>. 我们可以运行下面的命令来检查配置文件的正确性：</p>
<p># squid -k parse</p>
<p>只有这一步正确你才可以进行下一步的操作</p>
<p><span style="font-weight:bold;">2)</span>. 初始化cache目录.即建立缓存目录的存储格式</p>
<p>只需在第一次启动squid服务之前执行(在初次运行squid之前，或者无论何时你增加了新的cache_dir，你必须初始化cache目录。)</p>
<p># squid -z </p>
<p>cache目录初始化可能花费一些时间，依赖于cache目录的大小和数量，以及磁盘驱动器的速度。假如你想观察这个过程，请使用-X选项：</p>
<p># squid -zX</p>
<p><span style="font-weight:bold;">3)</span>. 启动squid服务</p>
<p># service squid start</p>
<p>假定squid安装在/usr/local/squid目录下,也可以</p>
<p>  # /usr/local/squid/sbin/squid -sD</p>
<p>最后，我推荐你去看看<a target="_blank" href="http://blog.s135.com/book/squid/">Squid权威指南</a>，并希望你可以利用Squid成功搭建一个分布式图片缓存系统，Squid真的很棒！</p>
<p>为了你和你家人的健康，转载请注明原文出处：<a target="_blank" href="http://www.itivy.com/ivy/archive/2012/3/10/squid-usage.html">http://www.itivy.com/ivy/archive/2012/3/10/squid-usage.html</a></p>
另外，我觉得你可能还会对下面架构相关的文章感兴趣：<br />
<a target="_blank" href="http://www.itivy.com/ivy/archive/2011/8/13/the-architecture-of-youku.html">优酷网架构学习笔记</a><br />
<a target="_blank" href="http://www.itivy.com/ivy/archive/2012/1/30/prepare-to-do-million-website.html">百万级访问量网站的技术准备工作</a><br />
<a target="_blank" href="http://www.itivy.com/ivy/archive/2012/1/26/facebook-image-storage.html">Facebook图片存储架构的学习</a><br />
<a target="_blank" href="http://www.itivy.com/ivy/archive/2011/5/17/image-storage-of-facebook.html">facebook图片存储架构技术全解析</a><br />
<p>
全文完，谢谢阅读！</p>]]></description>
</item>

<item>
<link><![CDATA[http://www.itivy.com/ivy/archive/2012/2/23/ipv6-ddos.html]]></link>
<title><![CDATA[【转播新闻】IPv6分布式拒绝服务攻击首度出现]]></title>
<author><![CDATA[ivy]]></author>
<category><![CDATA[]]></category>
<pubDate>Thu, 23 Feb 2012 22:53:09 GMT</pubDate>
<guid><![CDATA[]]></guid>
<description><![CDATA[<p>朋友们，IPv6这个新东西要谨慎使用啊，下面转播一则关于IPv6 DDos攻击的新闻，原文如下：</p>
<p>IPv6的时代已然来临，相信每个人在迎接它的同时也已经做好了面对新型安全问题的准备。终于，IPv6分布式拒绝服务攻击首度出现。</p>
<p>IPv4继续服务于互联网的时间已经即将进入尾声，但即使如此下一代互联网流量协议IPv6的普及速度始终相当缓慢，不过IPv6至少已经为大家所
广泛接受。根据ArborNetworks日前发布的报告，“IPv6发展道路上的新里程碑：IPv6分布式拒绝服务攻击（简称DDoS）首度出现。”</p>
<p>出现这种情况的原因在于，基于IPv6的网络终端已经发展得相当庞大，这种规模已经足以为攻击者提供大量注入点以进行针对IPv6协议的攻击。同样，这也说明攻击者们认为在使用IPv6的网络中劳心费力并组织攻击活动是完全值得的。</p>
<p style="text-align:center;"><img src="/Upload/EditorImage/image/ivy/201202/20120223225134_7102.jpg" alt="" border="0" /></p>
<p style="text-align:center;">网络工程师们开始意识到IPv6的安全性问题（图片由ArborNetworks提供）</p>
<p>其实除了人为因素以外，上述情况都在意料之中。ArborNetworks在其发布的全球基础设施安全报告中已经对IPv6DDoS攻击的出现做出
了明确预期。“这是一大关键性里程碑，标志着攻击者与维护者之间的军备竞赛将就此展开，”报告中提到。“我们认为IPv6DDoS攻击在广泛性及出现比例
方面将随时间推移而逐步上升，因为IPv6本身的普及度正在稳步提高。”而时至今日，这种预期已经开始变成现实。</p>
<p>根据ArborNetworks高级软件质量保障工程师BillCerveny的说法，“在过去一段时间中，IPv6网络中出现的故障与问题往往被
人们忽视甚至未能发现，因为当时没人注意到（或是关心）这一点……这种关注度上的缺失又反过来促使攻击者们将IPv6当作实施攻击的大好起点。尽管目前来
看IPv6所遭受的攻击频率相对较低、损失也较小，但随着普及度的逐渐提高，相信攻击者们也将迅速跟上，带来更多令人头痛的安全难题。”</p>
<p>这些攻击现象背后还隐藏着另一个问题，即人们普遍相信IPv6比IPv4更为安全。的确，Cerveny援引的例子的确很说明问题：“美国政府机构
内部的某个网络安全团队日前宣布解散，因为他们认为IPv6比旧有网络协议更加安全。鉴于下一代互联网协议自有的安全能力非常强大，该团队认为各类安全问
题都将自行瓦解，团队本身也就没有必要继续存在下去。”</p>
<p>他们代表了大多数用户的想法，但需要强调的是，这种观点并不正确。诚然，IPv6自身整合了互联网协议安全性（简称IPsec）的诸多内容，不过光
是一套协议根本无法保证万全。IPv6的标头设计同样有助于安全保护，因为它能够被用来为加密元数据与被加密的有效负载之间提供一套更加干净利落的隔离机
制。此外，IPv6带来的海量地址空间在经过部署之后，会使扫描攻击在子网内很难通过随机地址分配方式为非作歹。然而，这一切效果的实现都取决于我们能否
正确部署IPv6。就自身而言，IPv6与我们小时候裹在身上的厚毯子颇为相似，如果不用心处理，那是半点保护作用也谈不上的。</p>
<p>正如来自云及IPv6集成业务企业Nephos6的JohnSpence所说：“大多数认为IPv6比IPv4更加安全的想法，都源于RFC（即网
络请求注解）强制要求IPv6堆栈中包含IPsec支持；但无疑是一种太过简单的看法（而且在最近发布的RFC6434中，这一强制要求已经被取消）。”
也就是说，我们根本无法确定IPsec保护机制是否将作用于某些IPv6软件及硬件上。</p>
<p>Spence还观察到，“由于IPv6固有的自动转换机制努力在为双堆栈节点提供IPv6服务（例如WindowsVista或
Windows7），因此在表面上只部署了IPv4的设备往往正是IPv6安全漏洞的高发环境。我将这种情况称为‘IPv6意外部署’，因为此类状态虽然
未经管理、症状隐晦，但却很可能被攻击者为利用。总而言之，在大多数设备上，IPv6都处于默认设置状态，所以即使是自己的机构根本就没有部署过
IPv6，我仍然呼吁大家尽快制订一套相关安全规划。”</p>
<p>以上各类问题都是真实存在的。幸运的是，解决方案也将很快出台。互联网工程任务组（简称IETF）已经拿出一份草案——《IPv6路由器通告（简称RA）防护实施意见》，但项目尚处于进展当中。</p>
<p>正如华盛顿城际网络公司TachyonDynamics所指出的那样，目前“像WindowsXP、2003、Vista、7以及2008等操作系
统都没有限制所监听路由器的数量。而Linux及Mac设备则将监听对象的数量设定为15个，这才是正确的做法。一旦有15个以上的不同RA发至设
备，Linux与Mac都能及时停止为其配置地址及路由。而在Windows系统中则不是这样，广受好评的漏洞工具flood_router6正是由这一
点出发，在许多个人计算机上横行无忌，并成为黑客首选IPv6攻击工具包（简称THC-IPv6）中的一员。Flood_router6基本上是在短时间
内向子网发送数以百万计的、各自拥有不同IPv6前缀的RA源地址。只在数秒之内，一台功能齐备的Windows设备就会瞬间变砖。</p>
<p>由于互联网上针对IPv6系统的威胁如此之多，我们显然应该马上行动起来脱离原本坐以待毙的状况，努力保证IPv6在自己的网络中安全运作。此外，Windows用户必须明确一点：在未能彻底防范已知的各种IPv6攻击之前，不要轻易使用这套新兴的网络协议。</p>
<p>原文链接：</p>
<p><a href="http://www.zdnet.com/blog/networking/first-ipv6-distributed-denial-of-service-internet-attacks-seen/2039">http://www.zdnet.com/blog/networking/first-ipv6-distributed-denial-of-service-internet-attacks-seen/2039</a></p>]]></description>
</item>

<item>
<link><![CDATA[http://www.itivy.com/ivy/archive/2012/2/18/image-storage-cache-1.html]]></link>
<title><![CDATA[图片存储架构学习：缓存，架构师的美丽小三（一）]]></title>
<author><![CDATA[ivy]]></author>
<category><![CDATA[]]></category>
<pubDate>Sat, 18 Feb 2012 18:47:04 GMT</pubDate>
<guid><![CDATA[]]></guid>
<description><![CDATA[<p>版权声明：非商业自由转载，保留原文内容完整性，并署名作者<a target="_blank" href="http://www.itivy.com/ivy">王国峰</a>和<a target="_blank" href="http://www.itivy.com/ivy/archive/2012/2/18/image-storage-cache-1.html">原文链接</a></p>
<p style="font-weight:bold;"><span style="font-size:14px;color:#009900;">前言</span></p>
<p>在上一篇中，我<a target="_blank" href="http://www.itivy.com/ivy/archive/2012/2/16/image-storage-1.html">强行拆散了WEB服务器和图片服务器这对恋人</a>，这样做或许有点不太厚道，但对于他们未来各自的发展绝对有好处，不久的将来，他们会感谢我的。</p>
<p>好了，回到今天的话题，今天这篇文章主要来谈谈缓存技术在图片存储架构中的重要地位，并一起来探索一下实现图片缓存的方案。我个人的观点是：一个性能优良的、扩展性强的大型系统，势必要缓存一切可以缓存的资源，因为没有什么比在内存中操作更快的了（CPU不算，那里可存的空间太小了）。</p>
<p>PS：本文部分内容将有空空老师友情出演。</p>
<p style="font-weight:bold;"><span style="color:#009900;font-size:14px;">为什么要缓存图片</span></p>
<p>简单的说缓存可以通过直接访问内存来提高图片读取速度，缓解因站点高访问量而带来的图片I/O瓶颈。很多时候，图片是一类不经常更新的静态资源，是典型的读远大于写的情况，因此完全满足缓存的原则：一次写入，无数次读取。当然也不是所有满足这点的都会用到缓存，比如上次写的那篇<a target="_blank" href="http://www.itivy.com/ivy/archive/2011/8/13/the-architecture-of-youku.html">优酷网架构学习笔记</a>中就提到，优酷网在存储视频时并没有大量采用视频缓存技术，原因有二：</p>
<p>1、Squid 的 write() 用户进程空间有消耗，Lighttpd 1.5 的 AIO(异步I/O) 读取文件到用户内存导致效率也比较低下，这是内存锁造成的。<br />
2、如接到老大哥通知要把某个视频撤下来，如果在缓存里是比较麻烦的（这个是亮点，O(∩_∩)O）</p>
<p>但总的来说，对于像图片、视频之类的静态文件，还是非常适合做缓存的。缓存永远是架构师的美丽小三，哈哈。</p>
<p style="font-weight:bold;"><span style="color:#009900;font-size:14px;">图片缓存方案探究</span></p>
<p>接下来就重点来探究一下实现图片缓存的具体方案，文章尽量按人的正常思维来步步深入，从简单到复杂，一起来看看吧，希望看完后能略微提升你的设计品味。时尚的设计是一个不断推敲和磨合的过程，这个相信各位程序猿和攻城狮都深有体会吧。关于这一块，我将分2篇文章来解说，这一篇先介绍如何在IIS等web容器中设置Http Headers来实现图片的缓存。</p>
<p><span style="font-weight:bold;color:#e53333;">注意</span>：这里的web容器是指IIS、Tomcat或者其他的WEB服务器软件，下面我以IIS来举例。</p>
<p>IIS中的缓存分为<span style="font-weight:bold;">服务器缓存</span>和<span style="font-weight:bold;">客户端缓存</span>，对于静态资源（html、css、js、图片等），服务器缓存是默认开启的，也就是我们在向服务器请求静态资源时，服务器是先从其内存中取文件的，取不到再去硬盘中找（真累啊！），有时候开启服务器缓存很让人讨厌，后面我们会提到。客户端缓存顾名思义是把待请求的资源缓存在客户端的，用户请求资源时先从本地找，找不到再去麻烦服务器。下面先具体谈谈客户端缓存。</p>
<p><span style="font-weight:bold;">1、客户端缓存</span></p>
<p><span style="font-weight:bold;color:#e53333;">如何设置呢？</span>请看下图</p>
<p><img src="/Upload/EditorImage/image/ivy/201202/20120218165626_3397.png" alt="" border="0" /><br />
（<a target="_blank" href="http://ssbird.blog.51cto.com/277690/59297">图片来源</a>）</p>
<p>上图表明开启IIS的缓存模块，并设置过期时间为1天，也就是1天之内，用户访问这张图片时均可以从其本地的缓存副本中读取，而不必来服务器下载，当然1天之后，缓存失效，图片下载后又重新会被载入浏览器缓存中。当然这种方式需要你有管理IIS的权限，我想作为架构师的你这点权限算个皮毛啊。</p>
<p><span style="font-weight:bold;color:#e53333;">这个什么原理呀？</span>别急，请听我娓娓道来，小伙子要耐心</p>
<p>这个主要是靠Http Headers来控制的，Http Headers是HTTP请求（Request）和响应（Response）的核心，它承载了关于客户端浏览器，请求页面，服务器等相关的信息。简单地说，它是浏览器和服务器之间交互的信息牌，浏览器通过它告知服务器客户端的相关信息和请求信息（比如我是什么类型的浏览器、我是否可以接收你服务器gzip过的内容、请求的长度是多少、是否允许缓存等等内容）；服务器通过它告知浏览器服务器响应这次请求的相关信息（比如服务器变量、返回的长度、cookie等信息），具体Http Headers的内容可以参看<a target="_blank" href="http://wenku.baidu.com/view/80df15e7524de518964b7d0f.html">这篇文章</a>。</p>
<p>了解了Http Headers，那这个缓存问题就好解释了，我的解释如下：当我屁颠屁颠地带着这个Http Headers去向服务器请求空空老师的写真图片时，服务器提取出Http Headers发现其中的<code>Cache-Control是Public的，也就是可以缓存的，然后服务器再看看自己设置的缓存过期时间，发现还没过期，于是告诉我，小子，先在你本地去找空空老师的图片吧，找不到再来向我要。于是我回去找了，发现本地真的有空空老师的写真图，他没有骗我，好开心啊<img src="http://www.itivy.com/Scripts/kind-editor/plugins/emoticons/2.gif" alt="" border="0" /></code></p>
<p>不知道我这样解释空空迷们懂了么？不管你懂不懂，我反正是懂了！当然有不正确的地方请指出，谢谢。</p>
<p style="font-weight:bold;">2、服务器缓存</p>
<p>上面我已经说过，IIS中的服务器缓存是默认开启的，IIS默认会把静态资源缓存起来，以便快速读取，当静态文件有改动时，缓存也能够自动更新。但是有一个很讨厌的问题，假如我这些图片都是大量的（几百万几千万）且都是实时更新的（比如股票行情图），这样问题来了，我这么多图片一更新，IIS缓存还没来得及更新（量实在是太TM大了），于是我会在很长一段时间内访问到的图片都是旧版本的，这令炒股的我非常懊恼。那么既然它违背缓存的原则，我们怎么禁用它呢？方法绝对没你想得那么简单，我建议大家看看<a target="_blank" href="http://www.cnblogs.com/yukaizhao/archive/2011/06/16/disable-iis-cache.html">这篇文章</a>，是直接修改MetaBase.xml文件，如果对服务器不熟的家伙是不敢随便动这个文件的，不过架构师的你嘛，这点小儿科了，哈哈哈。</p>
<p>通过上面的阐述，我想聪明的你应该会使用这个最简单的方法设置缓存了，如果你真的是很笨还木有理解，那么请先用你的左手打右手两下，还不懂，那再用你的右手打左手两下，我就这么打过来的，直到懂了为止，哈哈哈。</p>
<p>好了，以上是关于IIS中设置缓存的方法，这里好几次引用了空空老师，让您受累了，谢谢。</p>
<p>下一篇将继续谈谈图片的缓存技术，不过是关于分布式缓存的，比这篇稍微进了一个阶梯，敬请大家关注。</p>
<p>转载请自觉注明原文链接：<a target="_blank" href="http://www.itivy.com/ivy/archive/2012/2/18/image-storage-cache-1.html">http://www.itivy.com/ivy/archive/2012/2/18/image-storage-cache-1.html</a></p>]]></description>
</item>

<item>
<link><![CDATA[http://www.itivy.com/ivy/archive/2012/2/16/image-storage-1.html]]></link>
<title><![CDATA[图片存储架构学习：独立的图片服务器，给爱一个独立的空间]]></title>
<author><![CDATA[ivy]]></author>
<category><![CDATA[]]></category>
<pubDate>Thu, 16 Feb 2012 21:07:21 GMT</pubDate>
<guid><![CDATA[]]></guid>
<description><![CDATA[<p>版权声明：非商业自由转载，署名<a target="_blank" href="http://www.itivy.com/ivy">王国峰</a>，<a target="_blank" href="http://www.itivy.com/ivy/archive/2012/2/16/image-storage-1.html">原文链接</a></p>
<h2>前言</h2>
<p>去年我凭着对网站架构的浓厚兴趣陆陆续续给大家分享了不少大型网站架构的经典案例，但是大部分都只是介绍了大概，并没有深入地研究，有兴趣的朋友可以去我博客的<a href="http://www.itivy.com/ivy/category/634334857626039072" target="_blank">网站架构</a>分类下学习讨论。今年我打算继续学习网站架构方面的知识，并对此作更加深入地分析与实践，当然学习成果会及时和大家分享和交流，希望今年自己的能力可以更上一层楼吧。</p>
<p>这几天我一直在关注大型网站中图片存储方面的相关问题，通过了解和实践，体会颇深，我想我可以针对图片存储这个话题写一个系列文章，以便对这次学习的总结。</p>
<ol></ol>
<p>第一篇，让我们从独立图片服务器开始说起，真爱，不是须要让自己更加独立的么？come on！</p>
<h2>正文</h2>
<p><b><span style="font-size:14px;color:#006600;">一、部署独立图片服务器的必要性</span></b></p>
<p>我们知道，无论对于Apache还是IIS，图片始终是最消耗系统资源的，如果将图片服务和应用服务放在同一个服务器的话，应用服务器很容易会因为图片的高I/O负载而崩溃，因此对于有些大型网站项目，我们有必要将图片服务器和应用服务器分离。部署独立的图片服务器（甚至是服务器集群）是大型网站图片存储解决方案中最基础的，因为有了独立的图片服务器后，我们才能对图片服务器做更有针对性的性能优化，比如从硬件角度说，图片服务器可以配置高端的硬盘，7200转的换成15000转的，而CPU却只要一般就可以了；从软件角度说，可以为图片服务器配置特殊的文件系统来满足对图片的I/O请求，如淘宝的TFS，就很好地解决了大规模小图片文件带来的I/O噩梦，同时，我们也可以采用nginx、squid来代理图片请求等等。</p>
<p><span style="color:#006600;"><b><span style="font-size:14px;">二、采用独立域名</span></b></span></p>
<p>注意，这里是指独立域名，不是子域哦，比如yahoo.com图片服务器用了yimg.com的域名，而不是用二级域名img.yahoo.com，这是为什么呢？个人觉得原因主要有以下几点：</p>
<p>1、同一域名下浏览器的并发连接数有限制，一般在2 - 6之间，下图列举了各个浏览器的并发连接数（来自网络，未经我亲自考证，供参考）</p>
<p>&nbsp;&nbsp;&nbsp; <img src="/Upload/EditorImage/image/ivy/201202/20120214133523_2642.jpg" alt="" border="0" /></p>
<p>这样，我们如果给图片服务器配置独立的域名，那么在一个页面中加载图片时，就可以突破浏览器连接数的限制，理论上，增加一个独立域名，并发连接数加倍。</p>
<p>2、由于cookie的原因，对缓存不利</p>
<p>比如有一张图片http://www.test.com/img/xx.gif，那么当我们向它发起请求的时候，会带上www.test.com域名下的cookie，由于大部分web cache都只缓存不带cookie的请求，这样就导致每次的图片请求都不能命中cache，而仍旧要去原始服务器获取图片，导致图片缓存意义不大。所以，还是给单独搞一个图片独立域名吧，当然，不只是图片，css和js文件也可以参照这个思路来搞。</p>
<p>3、方便CDN同步</p>
<p>这个我不太清楚是怎么回事，我个人猜测和第二点cookie有点关系，还望资深人士留言分享，谢谢。</p>
<p><b><span style="color:#006600;font-size:14px;">三、图片服务器分离后，如何进行图片上传和图片同步</span></b></p>
<p>当然任何事物都具有两面性，图片服务器分离固然提升了图片访问的效率，大大缓解了服务器因图片造成的I/O瓶颈，但是分离以后图片的上传和同步就成了一个大问题了。下面就我个人的想法谈谈几种解决方案。</p>
<p>1、NFS共享方式</p>
<p>如果你不想在每台图片服务器同步所有图片，那NFS共享是最简单也最实用的方式。NFS是个分布式的客户机/服务器文件系统，NFS的实质在于用户间计算机的共享，用户可以联结到共享计算机并象访问本地硬盘一样访问共享计算机上的文件。</p>
<p>具体实现思路是：web服务器通过nfs挂载多台图片服务器export出来的目录，用户先将图片上传到web服务器，然后将上传的图片通过程序拷贝到这个mount目录中去，这样那几台图片服务器就也能访问到刚上传的图片了（注意，只是共享了，并没有真正拷贝到图片服务器）。再给那几台图片服务器绑定独立域名，于是浏览器端就可以用单独的域名来访问图片了。这种方式基本不会有因同步造成的延时，但需要依赖nfs，nfs挂掉会影响web服务器。为了更直观的表达，我还是上一幅图吧，画得比较粗糙，大家将就着看看。</p>
<img src="/Upload/EditorImage/image/ivy/201202/20120214163243_1923.gif" alt="" style="border:1px solid #eeeeee;padding:3px;" /><p>至于如何配置nfs，大家google一下，或者看一下这篇文章，是在Linux下配置NFS的<a href="http://blog.csdn.net/lixinso/article/details/6639643">http://blog.csdn.net/lixinso/article/details/6639643</a></p>
<p>2、利用FTP同步</p>
<p>和上面nfs不一样的是，用户上传完图片后是利用ftp同步到各个图片服务器的，php、java、asp.net基本上都能操作ftp。这样的话每个图片服务器就都保存一份图片的副本，也起到了备份的作用。但是缺点是将图片ftp到服务器比较耗时，如果异步去同步的话又会有延时，不过一般的小图片文件也还好了。</p>
<p>当然除了上面两种方法，还有诸如安装同步软件、webservice等方法，但我个人觉得上面2种比较靠谱一点，所以其他的就暂时不介绍了，如果各位朋友有更好地建议，请留言分享。</p>
<p>好了，对于独立图片服务器的介绍就到这里了，欢迎大家补充，咱们下回见。</p>]]></description>
</item>

<item>
<link><![CDATA[http://www.itivy.com/ivy/archive/2012/2/13/hbase-database.html]]></link>
<title><![CDATA[HBase数据容灾技术方案]]></title>
<author><![CDATA[ivy]]></author>
<category><![CDATA[]]></category>
<pubDate>Mon, 13 Feb 2012 22:36:16 GMT</pubDate>
<guid><![CDATA[]]></guid>
<description><![CDATA[<p><a href="http://hbase.apache.org/">HBase</a>是一个分布式的、非关系型开源数据库。
HBase有如下几个特点：首先HBase是No-SQL的一个典型实现，提升了系统的可扩展性；其次HBase支持线性水平扩展，极大提升了系统的可伸
缩性和运算能力；最后HBase和Google的BigTable有异曲同工之妙，底层也是建立在HDFS(Hadoop分布式文件系统)之上，可以搭建
在廉价的PC机集群上。No-SQL、云计算、海量数据分析的普及，使我们越来越关注系统的可靠性（High 
Availability），数据容灾/数据恢复是高可用系统的一个很重要的技术组成，本文由简入深，一步步搭建一个HBase数据集群，并详细说明生产
环境如何使用HBase数据容灾方案。</p>
<h2>HBase架构简介</h2>
<p>HBase在完全分布式环境下，由Master进程负责管理RegionServers集群的负载均衡以及资源分配，ZooKeeper负责集群元
数据的维护并且监控集群的状态以防止单点故障，每个RegionServer会负责具体数据块的读写，HBase所有的数据存储在HDSF系统上。</p>
<p><img style="width:729px;height:480px;" src="/Upload/EditorImage/image/ivy/201202/20120213222058_3666.jpg" alt="" border="0" /></p>
<p><strong>图一 HBase逻辑架构</strong><sup><a href="http://www.infoq.com/cn/articles/lp-hbase-data-disaster-recovery#_Ref1"><strong>[1]</strong></a></sup></p>
<h2>HBase集群部署</h2>
<h3><strong>HBase集群物理架构</strong></h3>
<p>物理机</p>
<pre class="brush:bash;">192.168.0.105 Master Ubuntu Desktop 11.10 Desktop
192.168.0.102 Slave1 Ubuntu Desktop 11.10 Desktop
192.168.0.103 Slave2 Ubuntu Desktop 11.10 Desktop
192.168.0.104 Slave3 Ubuntu Desktop 11.10 Desktop
192.168.0.101 Recover Ubuntu Desktop 11.10 Desktop</pre><img src="/Upload/EditorImage/image/ivy/201202/20120213222236_6766.jpg" alt="" border="0" /><p></p>
<p><strong>图二 集群物理架构 </strong></p>
<h3><strong> 先决条件</strong></h3>
SSH协议<sup><a href="http://www.infoq.com/cn/articles/lp-hbase-data-disaster-recovery#_Ref2">[2]</a></sup><p>Hadoop集群之间的通讯采用的是SSH协议，所以要保证Master、Slave之间可以自由的通讯，一般推荐使用无验证通讯</p>
    <ul><li>安装SSH
        <br />
<pre class="brush:bash;">apt-get install openssh-server
apt-get install openssh-client </pre></li>
<li>创建相同用户名的SSH公钥
        <p>在master主机和slave机上创建相同的用户hadoop</p>
<pre class="brush:bash;">sudo adduser hadoop </pre><p></p>
</li>
<li>在主机上生成公私钥key pair<br />
<pre class="brush:bash;">ssh-keygen -t rsa -P ""
cat $HOME/.ssh/id_rsa.pub &gt;&gt; $HOME/.ssh/authorized_keys</pre></li>
<li>将key值复制到slave1和slave2上<br />
<pre class="brush:bash;">scp $HOME/.ssh/id_rsa.pub hadoop@slave1:/home/hadoop/.ssh/authorized_keys
scp $HOME/.ssh/id_rsa.pub hadoop@slave2:/home/hadoop/.ssh/authorized_keys
scp $HOME/.ssh/id_rsa.pub hadoop@slave3:/home/hadoop/.ssh/authorized_keys </pre></li>
</ul>
<p>这样master就可以自由的访问slave节点了</p>
<p>
    Java安装</p>
<pre class="brush:bash;">sudo apt-get install sun-java6-jdk</pre><p></p>
<h3><strong>Hadoop部署</strong></h3>
Hadoop配置<sup><a href="http://www.infoq.com/cn/articles/lp-hbase-data-disaster-recovery#_Ref3">[3]</a></sup>     <p>下载Hadoop 0.20.2版本<sup><a href="http://www.infoq.com/cn/articles/lp-hbase-data-disaster-recovery#_Ref4">[4]</a></sup></p>
    <ul><li>hadoop-env.sh
        <br />
<pre class="brush:bash;">export JAVA_HOME=/usr/lib/jvm/java-6-sun
export HADOOP_HOME=/home/hadoop/hadoop-0.20.2 </pre></li>
<li>master,slaves<br />
<pre class="brush:bash;">Master, Slave1, Slave2, Slave3</pre></li>
<li>core-site.xml
        <br />
<pre class="brush:xml;">&lt;property&gt;
    &lt;name&gt;fs.default.name&lt;/name&gt;
    &lt;value&gt;hdfs://master:9000&lt;/value&gt;
&lt;/property&gt;
&lt;property&gt;
    &lt;name&gt;hadoop.tmp.dir&lt;/name&gt; //临时文件目录
    &lt;value&gt;/data/tmp/hadoop&lt;/value&gt; //注意不要放到/tmp目录下
&lt;/property&gt; </pre></li>
<li>hdfs-site.xml<br />
<pre class="brush:xml;">&lt;property&gt;
    &lt;name&gt;dfs.replication&lt;/name&gt; //备份文件
    &lt;value&gt;1&lt;/value&gt;
&lt;/property&gt;</pre></li>
<li>mapred.xml <br />
<pre class="brush:xml;">&lt;property&gt;
    &lt;name&gt;mapred.job.tracker&lt;/name&gt;
    &lt;value&gt;master:9001&lt;/value&gt;
&lt;/property&gt; </pre></li>
</ul>
启动Hadoop
    <p>hadoop namenode format //首先需要格式化namenode</p>
<pre class="brush:bash;">bin/start-all.sh</pre><p></p>
<p>验证服务：MapReduce管理界面http://master:50030/jobtracker.jsp</p>
<h3><strong>HBase部署</strong></h3>
HBase配置
    <p>下载HBase 0.90.50版本<sup><a href="http://www.infoq.com/cn/articles/lp-hbase-data-disaster-recovery#_Ref5">[5]</a></sup></p>
    <ul><li>HBase-env.sh
        <br />
<pre class="brush:bash;">export JAVA_HOME=/usr/lib/jvm/java-6-sun
export HBase_MANAGES_ZK=true //zookeeper随HBase启动 </pre></li>
<li>HBase-site.xml <br />
<pre class="brush:xml;">&lt;property&gt;
    &lt;name&gt;HBase.rootdir&lt;/name&gt;
    &lt;value&gt;hdfs://master:9000/HBase&lt;/value&gt; //端口号和名称和Hadoop配置一致
&lt;/property&gt;
&lt;property&gt;
    &lt;name&gt;HBase.cluster.distributed&lt;/name&gt;
    &lt;value&gt;true&lt;/value&gt;
&lt;/property&gt;
&lt;property&gt;
    &lt;name&gt;dfs.replication&lt;/name&gt;
    &lt;value&gt;1&lt;/value&gt;
&lt;/property&gt;
&lt;property&gt;
    &lt;name&gt;HBase.master&lt;/name&gt;
    &lt;value&gt;master&lt;/value&gt;
&lt;/property&gt;
&lt;property&gt;
    &lt;name&gt;HBase.zookeeper.quorum&lt;/name&gt;
    &lt;value&gt;slave1,slave2,slave3&lt;/value&gt;
&lt;/property&gt; </pre></li>
</ul>
启动HBase集群
    <p>Master主机上执行 $HBase_HOEM/bin/start-HBase.sh</p>
<p>验证：使用jps命令查看HBase的集群进程</p>
<p><img src="/Upload/EditorImage/image/ivy/201202/20120213223146_7244.jpg" alt="" border="0" /></p>
<h2>HBase数据容灾</h2>
<p>前面我们已经介绍过，如果HBase单个节点出现故障，Zookeeper会通知master主进程，master会将HLog日志进行拆分，分发
到其他RegionServer上进行数据恢复。HBase对于单点故障的容错能力还是不错的，但是如果发生多点故障，现有的基本容错功能是远远不够的
(会造成数据丢失)。</p>
<h3><strong>HBase Replication机制<sup><a href="http://www.infoq.com/cn/articles/lp-hbase-data-disaster-recovery#_Ref6">[6]</a></sup></strong></h3>
<p>HBase 0.90以后开始支持Replication机制，该机制设计的主导思想是基于操作日志(put/get/delete)做数据同步，这点很像MySQL基于Binary Log做statement-based replication<sup><a href="http://www.infoq.com/cn/articles/lp-hbase-data-disaster-recovery#_Ref7">[7]</a></sup>。</p>
<p>如下图所示，客户端的put/delete操作会被RegionServer写入本地的HLog中去，与此同时每个RegionServer会将
Hlog放入对应znode上的Replication队列，HBase集群会有一个独立的线程，根据固定大小的buffer值，将HLog内容推送到
Slave Cluster集群中的某个RegionServer上(当前版本只支持单个Slave 
Cluster复制)，并在将当前复制的偏移量保存在ZooKeeper上，整个过程是异步完成的。</p>
<p><img style="width:768px;height:584px;" src="/Upload/EditorImage/image/ivy/201202/20120213223220_0013.jpg" alt="" border="0" /></p>
<p><strong>图三 HBase数据同步</strong><sup><a href="http://www.infoq.com/cn/articles/lp-hbase-data-disaster-recovery#_Ref8"><strong>[8]</strong></a></sup></p>
<h3><strong>HBase Replication启动</strong></h3>
<ol><li>HBase-env.sh <br />
<pre class="brush:bash;">export JAVA_HOME=/usr/lib/jvm/java-6-sun
export HBase_MANAGES_ZK=false //ZooKeeper独立启动 </pre></li>
<li>HBase-site.xml
    <p>master集群和slave集群的配置需要同时修改</p>
<pre class="brush:xml;">&lt;property&gt;
    &lt;name&gt;HBase.replication&lt;/name&gt;
    &lt;value&gt;true&lt;/value&gt;
&lt;/property&gt;</pre><p></p>
</li>
<li>Shell启动复制功能<br />
<pre class="brush:bash;">add_peer disable 'my_table_name'  //表名字
alter ' my_table_name ', {NAME =&gt; 'family_name', REPLICATION_SCOPE =&gt; '1'}  //修改表schema
enable ' my_table_name' </pre></li>
</ol>
验证：查看RegionServer的日志<br />
<pre class="brush:bash;">Considering 1 rs, with ratio 0.1
Getting 1 rs from peer cluster # 1
Choosing peer 192.168.0.101:62020</pre><h3><strong>数据校验</strong></h3>
<p>为了保证数据一致性，生产环境上做异地容灾需要增加数据校验/数据监控。HBase的Replication机制，根据官方的文档提供了数据比对的工具类VerifyReplication<sup><a href="http://www.infoq.com/cn/articles/lp-hbase-data-disaster-recovery#_Ref9">[9]</a></sup>。我们可以将其功能包装起来，做自动化校验。下面是代码片段：</p>
<pre class="brush:java;">final String[] argumentsArray = new String[] {
    "--starttime=xxxxxxxxxxx", //开始时间戳根据具体的业务需要
    "--stoptime=" + new Date().getTime(), //选取当前时间戳作为结束的时间戳
    "1", //peer node id
    "my_table_name" //表名
};
final Timer timer = new Timer();
timer.schedule(new TimerTask() {@Override
    public void run() {
        try {
            Configuration conf = HBaseConfiguration.create();
            Job job = VerifyReplication.createSubmittableJob(conf, argumentsArray);
            job.waitForCompletion(true);
            long value = job.getCounters().findCounter(VerifyReplication.Verifier.Counters.BADROWS).getValue();
            if (value &gt; 0) {
                Logger.getLogger("Finding Unmatched Rows! " + value);
            }
        } catch (Exception e) {
            //异常处理策略
            final String msg = "Comparing Job Error!";
            Logger.getLogger(this.getClass()).error(msg, e);
            try {
                SMTPClientWrapper.send("xxx@xxx.com", "HBase replication error!", msg);
            } catch (Exception e1) {
                //考虑邮件服务器down机, failover
                Logger.getLogger(this.getClass()).error("send alarm email error!", e);
            }
        }
    }
}, 0, 600000); //十分钟校验一次</pre><p></p>
<h2>小结与展望</h2>
<p>HBase的Replication机制，为增强系统可靠性提供了有力支持，但目前单节点Slave Cluster复制会增加系统的负荷并间接形成Slave Cluster的数据热点，期待HBase后续的版本支持多节点Slave Clusters复制。</p>
<p><strong>引用</strong></p>
<p><a name="_Ref1"></a>[1] <a href="http://ofps.oreilly.com/titles/9781449396107/intro.html">http://ofps.oreilly.com/titles/9781449396107/intro.html</a></p>
<p><a name="_Ref2"></a>[2] <a href="http://en.wikipedia.org/wiki/Secure_Shell">http://en.wikipedia.org/wiki/Secure_Shell</a></p>
<p><a name="_Ref3"></a>[3] <a href="http://hadoop.apache.org/common/docs/current/cluster_setup.html">http://hadoop.apache.org/common/docs/current/cluster_setup.html</a></p>
<p><a name="_Ref4"></a>[4] <a href="http://hadoop.apache.org/common/releases.html#Download">http://hadoop.apache.org/common/releases.html#Download</a></p>
<p><a name="_Ref5"></a>[5] <a href="http://www.apache.org/dyn/closer.cgi/hbase/">http://www.apache.org/dyn/closer.cgi/HBase/</a></p>
<p><a name="_Ref6"></a>[6] <a href="http://hbase.apache.org/replication.html">http://HBase.apache.org/replication.html</a></p>
<p><a name="_Ref7"></a>[7] <a href="http://dev.mysql.com/doc/refman/5.1/en/replication-formats.html">http://dev.mysql.com/doc/refman/5.1/en/replication-formats.html</a></p>
<p><a name="_Ref8"></a>[8] <a href="http://hbase.apache.org/replication.html">http://HBase.apache.org/replication.html</a></p>
<p><a name="_Ref9"></a>[9] <a href="http://ofps.oreilly.com/titles/9781449396107/intro.html">http://HBase.apache.org/xref/org/apache/hadoop/HBase/mapreduce/replication/VerifyReplication.html</a></p>
<p>作者：李湃&nbsp;&nbsp;&nbsp; 来源：<a target="_blank" href="http://www.infoq.com/cn/articles/lp-hbase-data-disaster-recovery">Infoq中文</a></p>]]></description>
</item>

<item>
<link><![CDATA[http://www.itivy.com/ivy/archive/2012/2/11/use-your-rp-to-improve-pr.html]]></link>
<title><![CDATA[用人品构建高效的外部链接 —— 致为SEO而到处发布垃圾评论的苦逼]]></title>
<author><![CDATA[ivy]]></author>
<category><![CDATA[]]></category>
<pubDate>Sat, 11 Feb 2012 22:18:55 GMT</pubDate>
<guid><![CDATA[]]></guid>
<description><![CDATA[<p>因为最近博客屡遭各种恶意垃圾评论的攻击，让我不得不暂停网站的评论功能（待出来解决方案后继续开放评论功能），因此让我头痛脑胀，内分泌失调，遂含泪写下此文，以此告诫意图不轨之人，也算是自己的一点技术和经验的分享。</p>
<p>--by 王国峰<br />
--@ 末日之年&nbsp; 02月11日</p>
<p>亲，如果你看到这个标题，觉得菊花一紧，心里有中说不出的内疚感，那么我就暂且原谅你刚刚在我博客中乱发广告链接的行为，并欢迎一起交流SEO经验。</p>
<p>这里我首先可以明确地告诉你一点，这种垃圾评论的行为不会对你的网站带来一点点的好处，相反，你的网站会因此遭到baidu和google的严重惩罚。要知道，一旦你的帖子被我删除，就相当于告诉baidu和google这条链接是毫无相关的，是垃圾的，那么久而久之，搜索引擎就会对这个网站进行降权处理，严重的还会封站，你的页面将消失在整个搜索引擎中，直到你改邪归正为止。</p>
<p>那么，看在你如此看得起我的博客，愿意辛苦地在我的博客上留下许多评论（尽管都是垃圾评论）的份上，我就简单来谈谈个人站长如何获取高效的外部链接，得到搜索引擎的权重。如果你有幸看到此文，希望你能改邪归正，愿你的网站蒸蒸日上。</p>
<p style="font-weight:bold;"><span style="color:#e53333;">外链，难道仅仅是给搜索引擎看的吗？</span></p>
<p>绝对不是，如果你的外链并没有任何导入流量（导入流量即从这个外部链接进入你的网站的流量），那么其实作用并不是很大，一般来说，搜索引擎对那些没有任何导入流量的外部链接是不会重视的，也不会给予太高的权重值。所以从这点上讲，外链还是给读者，给用户看的，用户看了链接有兴趣，点击进去看了，那么，好，你的外链就多了一次导入流量，搜索引擎认识到这个链接对用户有一定的帮助，就会给予一定的权重，随着导入流量的增加，这个外链所指向页面的权重也就逐渐升高了。而对于那些垃圾链接，只要你还是地球人，你会去点吗？不管你会不会，我反正是绝对不会的。那么这样的垃圾链接是得不到任何导入流量的，又怎能让搜索引擎亲睐呢？至于搜索引擎是如何判断这次流量是否是由这个外部链接导入的，这个问题对于搞搜索出身的baidu和google来说还不好解决么？要知道，你用的流量统计工具中，有多少是用google流量统计或者baidu流量统计的呢，很多对么，那么baidu和google就完全可以从这些工具中获取你的流量来源，所以他们可以轻而易举地获取由外链带来的具体流量信息。这时候你或许要说了，我用其他统计工具呢，我不用baidu和google就可以了。是的，但从互联网的整体来说，使用google统计占有大部分，国内的话baidu统计也用的比较多，所以从概率论的角度来说，发生上述现象的数据对baidu和google还是有很大参考价值的。当然通过统计工具只是其中一种手段罢了，要知道，他们这些巨头可是具有监视整个互联网的能力哦（当然这种“监视”并不是病毒木马的监视），这只是一种帮助搜索引擎分析数据的手段。举一个例子，以前Alexa（全球网站流量排名系统）是靠用户在浏览器安装的Alexa工具条来统计一个网站的大致流量，从而计算排名的，但是随着其算法的更新，它已不仅仅是靠安装的工具条来统计，因为那玩意儿实在太让人讨厌了，而且各个地区的安装率大不相同。所以现在Alexa已经参考了部分第三方的统计数据，更重要的一点是，他早就在各个区域加入了一些服务器节点来监视整个网络，从而让统计更加精确，让这个Alexa排名值更具有参考价值。所以搜索引擎也可以通过监视整个网络来为自己的搜索分析作出参考。</p>
<p>说了这么多，总结一下是：外链只有得到用户的认可，那才能得到搜索引擎的认可，发外链的目的还是在于让更多的用户看到这个外链所指向的网页，并因此对页面产生兴趣，从而替搜索引擎决定这个网页的优劣，从这个角度来说，搜索引擎是被动的，至少从分析能力上来说是被动的。</p>
<p style="font-weight:bold;"><span style="color:#e53333;">那么，如何发布高效的外链呢？</span></p>
<p>这里我就不赘述很多资料中提到的常规方法了，什么评论啊，网摘啊，软文啊等等。。。</p>
<p>这里我只想阐述一点，那就是用人品（RP），有句话说得好，要靠RP来争得PR（PR是google对一个页面的评级，PageRank的简称）。</p>
<p>为什么这么说呢？难道在现今这个社会，人品真的还是如此重要么？也许这个社会不，但在站长这个圈子中我认为是。</p>
<p>做网站的目的是什么，有很多，分享、交流、纯兴趣、交朋友、赚钱、出名、追凤姐、成为第二个凤姐等等等等，但有一点是共同的，那就是你为达到上述各种目的，必须要为那些对如上话题感兴趣的读者或者通俗点讲是用户负责。这一句负责可是重于泰山啊，估计像你和一个女孩发生一夜情后对她说“我会对你负责”差不多重，因为这象征着一种责任，这种责任中缠绕着许多微妙的联系，这些微妙的联系又会让你懂得如何去对待这种责任。</p>
<p>说到责任，我就开始切入重点了。总的来说，发布外链要找对点，要针对那些真正对你产品感兴趣的人群，并且再三斟酌这个链接是否真的对访客有所帮助。很多资料中提到的外链发布方法，有一条规则是说，不要只发一个链接。是的，这个有部分道理，但是我想说的是，如果只是发了一个链接，但这个链接对用户有帮助，那也是可取的。我曾经也在CSDN上发过外链，有不少是在技术版块中逛，然后看到合适的问题，就从自己网站是找到一篇文章，然后把链接发上去，帮助提问者解决问题。是的，这种链接不仅不是广告，反而能得到大家的称赞，当然导入流量也就多了，权重自然也就上去了。因为我在决定发这个链接的时候，我对文章中提到的问题解决方法做过再三的测试，生怕发的文章误导提问者，这就是一种责任，你可以和任何女孩发生一夜情，但只要你敢于负责任，那也就。。。不说下去了。</p>
<p>好了好了，不说了，再说下去就要分不清这是一篇技术文章还是情感随笔了。但无论如此，我还是要感谢那位在我博客中乱发垃圾评论的读者，原因其一，让我认识到网站仍旧需要不断完善从而解决一些意想不到的问题；其二，让我又进一步认识了责任一词，重温自己坚持的原则是一件多么幸福的事情。亲，感谢你，当然也希望你能明白我上面讲的一些或许让你不能明白的道理。</p>]]></description>
</item>

<item>
<link><![CDATA[http://www.itivy.com/ivy/archive/2012/2/10/css3-doraemon-cat.html]]></link>
<title><![CDATA[分享一个纯CSS3的机器猫，喜欢的请分享]]></title>
<author><![CDATA[ivy]]></author>
<category><![CDATA[]]></category>
<pubDate>Fri, 10 Feb 2012 12:13:25 GMT</pubDate>
<guid><![CDATA[]]></guid>
<description><![CDATA[<p>还记得之前给大家分享的纯CSS3人物步行动画么？如果没看过，那就请移驾至<a target="_blank" href="http://www.itivy.com/ivy/archive/2011/10/27/css3-walking-example.html">一个用纯CSS3制作的人物步行动画，很帅</a>。</p>
<p>今天再来一个更犀利的（以前听说过的，也看过，今天收藏一下），CSS3画的机器猫，哆啦A梦，友爱的，关键是整幅图没有用一张图片，是纯CSS3画的，相信这只用CSS3画的机器猫更加能帮你实现自己的梦想，一起来临幸它吧，喜欢的请自觉分享：P&nbsp; 注意：只有支持CSS3的浏览器才行</p>
<link rel="stylesheet" type="text/css" href="http://www.itivy.com/html/doraemon.css"> <div id="doraemon"> 

	<div id="dface"> 

    	<div id="head_light"></div>
 

    	<div id="eyes"> 

    	<div class="eye eye_left"></div>
 

        	<div class="black_eye black_left"></div>
 

        

      	<div class="eye eye_right"></div>
 

        	<div class="black_eye black_right"></div>
 

        </div>
 

        <div id="dbase"> 

			<div id="base_white"></div>
 

				<div id="nose"> 

					<div id="nose_light"></div>
 

				</div>
 

                <div id="nose_line"></div>
 

                <div id="mouth"></div>
 

                <div id="mouth_rewrite"></div>
 

                <div id="firefox_mouth"></div>
 

 

				<div class="whiskers whi_right_top rotate160"></div>
 

				<div class="whiskers whi_right"></div>
 

				<div class="whiskers whi_right_bottom rotate20"></div>
 

                    

				<div class="whiskers whi_left_top rotate20"></div>
 

				<div class="whiskers whi_left"></div>
 

				<div class="whiskers whi_left_bottom rotate160"></div>
 

        </div>
 

	</div>
 

    <div id="choker"> 

    	<div id="belt"></div>
 

    	<div id="bell"> 

       	  <div id="bell_line"></div>
 

            <div id="bell_circle"></div>
 

            <div id="bell_under"></div>
 

		<div id="bell_light"></div>
 

		</div>
 

    </div>
 

	<div id="dbody"> 

    <div id="doutai"></div>
 

		<div class="base_white2 doutai_center"></div>
 

        		<div id="pocket"> 

                	<div id="circle"></div>
 

                    <div id="circle_rewrite"></div>
 

                </div>
 

	</div>
 

	<div id="hand_right"> 

    	<div id="arm_right"></div>
 

		<div class="hand_circle hand_right"></div>
 

		<div class="arm_rewrite_right"></div>
 

    </div>
 

	<div id="hand_left"> 

    	<div id="arm_left"></div>
 

		<div class="hand_circle hand_left"></div>
 

	  <div class="arm_rewrite_left"></div>
 

    </div>
 

	<div id="dfoot"> 

    	<div id="foot_left"></div>
 

        <div id="foot_right"></div>
 

        <div id="foot_rewrite"></div>
 

    </div>
 

	<div id="shadow_doutai_arm"></div>
 

	<div id="shadow_doutai_left"></div>
 

	<div id="shadow_doutai_right"></div>
 

    <div id="shadow_belt"></div>
 </div>
 <p>哈哈，给力吧，哆啦A梦，哥哥爱死你了。。</p>]]></description>
</item>

<item>
<link><![CDATA[http://www.itivy.com/ivy/archive/2012/2/7/cloud-application-architecture.html]]></link>
<title><![CDATA["伤得起"的云计算应用——对云端应用之架构的思考]]></title>
<author><![CDATA[ivy]]></author>
<category><![CDATA[]]></category>
<pubDate>Tue, 07 Feb 2012 17:07:24 GMT</pubDate>
<guid><![CDATA[]]></guid>
<description><![CDATA[<h2>天有不测风云</h2>
<p>云计算是一种新型的计算模式。其显著优势之一是云计算用户可以随时随地的使用来自互联网的服务，并且按使用量付费。在需要增加（或减少）计算能力
时，可立即获得（或释放）资源，而在传统数据中心（非私有云环境）中，用户却需要采购硬件，安装配置操作系统和中间件软件，再部署应用。两种方式在运维工
作上的差异一目了然，这正是云计算能够受到业界热捧和追逐的主要原因之一。那么，云计算是完美无缺的么？不尽然如此。在云计算的世界里，运维工作不再由云
计算用户承担，转而交给虚拟化和自动化技术以及云计算提供商来承担。同时，云计算用户在享受弹性资源扩张或收缩的同时，也在一定程度上失去了对其使用资源
的控制权，而如果云服务供应商的服务出现故障，或者云服务供应商由于商业运作的失败或其他原因而关门倒闭时，云计算用户就可能会面临巨大的风险。</p>
<p>2011年4月21日至22日是值得云计算从业者纪念的日子。Amazon的IaaS服务出现故障，导致许多商业网站的服务中断，影响非常严重。据Amazon官方网站称，受影响最严重的网站中有<a href="http://reddit.com/"><u>Reddit</u></a>, <a href="http://foursquare.com/"><u>Foursquare</u></a>和<a href="http://quora.com/"><u>Quora</u></a>等。此事件发生后，人们一时陷入慌乱，对云计算可用性的担心骤然升温。同时，也有人乘机夸大云计算的弊端，并宣称“云计算已死”（这让笔者想起当年关于“<a href="http://www.infoq.com/cn/news/2009/01/is-soa-dead">SOA已死</a>”的论战）。炒作行为是可以理解的，我们自然不用去理会。但是，作为云计算用户，我们需要思考的是，如何保证即便在云服务不可用的情况，我们的应用架构仍然能够屹立不倒？本文正是站在云计算用户的角度试图探讨这一问题。</p>
<h2>“4- 21”事故分析</h2>
<p>Amazon将其基础设施划分为“区域（Region）”，这些区域好比一个个数据中心。例如，US-East-1是Amazon位于北弗吉尼亚的
数据中心，而US-West-1则是位于硅谷的数据中心。每个数据中心又被划分成多个可用分区（后简称为AZ），AZ就好比资源池，它由一组物理和逻辑资
源组成。</p>
<p>Amazon的提供的虚拟机是EC2（Elastic Compute Cloud）。而其存储服务是EBS（Elastic Block 
Storage）。EBS是基于网络高性能且高可用的块（block）级别的持久化存储服务。EBS可以挂接到某一个EC2实例上作为其文件系统使用。</p>
<p>当用户获得一个EC2分区实例时，同时会得到一块存储区。不过，该存取区是透明的，而且其生存周期与EC2实例是同步的。当EC2实例销毁时，该存储区也一同销毁，基于此，需要持久化的用户数据是不应该放在该存储上。</p>
<p>一般而言，运行网站和产品的云计算用户至少需要申请一个EC2实例和一个EBS存储。在EC2上运行应用和数据库，而数据则存储在EBS中。但是，
若要将EBS存储挂接到EC2实例上，二者必须在同一AZ中（如图1.a所示）。用户也可以直接使用Amazon提供的RDS（Relational 
Database 
Service），它是一个运行着MySQL的EC2实例，此时应用和数据就可分别位于不同的AZ中（如图1.b所示）。当然，将应用和存储分开还有其他
方法，本文不再赘述。</p>
<p><img src="/Upload/EditorImage/image/ivy/201202/20120207170536_0647.jpg" alt="" border="0" /></p>
<p>“4-21”事故中，位于US-East-1区域的EBS存储和Amazon 
RDS服务都出现了问题。表现出来的现象是：不在US-East-1区域的用户未受影响，未使用EBS和RDS的用户不受影响。一般情况下，使用EBS或
RDS是非常普遍的现象，因为大多数软件或网站都依赖于数据存储，所以，即便运行应用的EC2未受影响，也会由于应用需要访问已出现问题的ESB或RDS
上的数据，仍然会导致服务不可用或服务变慢的问题。</p>
<p>Amazon事故的的发生加速了人们对云可用性的思考，也进一步凸显了好的应用架构的优势，多家企业纷纷通过博客分享了他们使用Amaozon AWS的经验，对云计算用户端架构的建议。接下来，我们看看他们是怎么做的。</p>
<h2>来自一线的经验</h2>
<p>早在2010年12月，在线影片租赁提供商NetFlix的技术博客上就发表了文章<a href="http://techblog.netflix.com/2010/12/5-lessons-weve-learned-using-aws.html">《使用AWS的5大经验》</a>。在这篇文章中，他们给出了以下5条建议：</p>
<ol><li>云环境和传统数据中心不同，需改变思考问题的方法</li>
<p>许多传统数据中心的应用部署和运维经验在云中不再适用，所以不能用传统的解决方法去解决云中出现的问题。比如，在自己的数据中心里，使用基
于内存的session管理就是很好的方法，因为每个硬件实例出现故障的可能性微乎其微。然而，AWS实例的出错率却高很多，所以需要不同高可用方案。</p>
<li>共租是难以实现的</li>
<p>在云环境中设计面向用户的软件时，主要工作是降低整体上响应的延时。由于AWS是基于资源（硬件、网络、存储等）共享模式构建的，共租会引起各个层次上吞吐量的波动。或者你放弃任何特殊的操作，或者在AWS里管理好资源，这等于放弃了共租。</p>
<li>避免失败的最好方法是不停地出错</li>
<p>Netflix的技术人员喜欢将自己的软件架构称之为兰博架构，不论在何种情况下，每个系统必须靠自己存活。他们在设计分布式系统时考虑了其所依赖的其他系统的故障并且能够容忍故障。</p>
<p>他们在AWS中创建了一个被称为“捣乱猴”的系统。该“捣乱猴”的任务就是随意杀死软件架构中的任意实例和服务。他们的原则是，即便局部出错了，他们的系统和架构仍然要整体上保持正确，只有这样，才能躲过预料之外的故障。</p>
<li>测试就要做真实情况的模拟，不能儿戏！</li>
<p>在完全依靠AWS之前，他们就使用真实数据对AWS上的系统做测试。他们模拟所有发往数据中心的请求，然后发送到AWS做实测。</p>
<p>测试能够发现架构的瓶颈，有些架构决定和设计决定，在图纸上看似完美，但是一旦应用到大规模的实际情形，就不那么奏效了。</p>
<li>坚韧与信念，永不言弃</li>
<p>你会碰到许多问题！毕竟云计算的发展也没有几年，许多方面仍在不断完善之中。用户需要改变过去的观念，放弃过去的固定模式。关键是要保持坚忍不拔地战胜一切困难的意志力，坚定对胜利的信念。</p>
</ol>
<p><a href="http://www.codinghorror.com/blog/">Coding Horror</a>博主Jeff Atwood非常赞同“捣乱猴”的思路。他在<a href="http://www.codinghorror.com/blog/2011/04/working-with-the-chaos-monkey.html">博文</a>中
分享了他的团队花好几个月解决一个诡异问题的经历。为了跟踪该问题，他们分析和尝试了可能导致该问题的所有原因，但是仍然未找到其根本原因。每隔几天，就
会有服务器（不知道是那台）从网络中脱离，他们称此现象为“捣乱猴”又发怒了。他们被此问题困扰数月之久，团队曾陷入崩溃，但是即便在最困难的时期，他们
仍然采取了积极的行动：</p>
<ul><li>但凡某关键功能现在由一台服务器负责，就切换成两台。</li>
<li>但凡发现哪里缺乏可靠性，就建立其可靠性。</li>
<li>消除所有的依赖，一步一步，直到依赖减到不能再少。</li>
<li>设计替代方案，使我们的服务始终保持运行，即便我们先前认为关键的服务突然失效。</li>
</ul>
<p>SmugMug也<a href="http://don.blogs.smugmug.com/2011/04/24/how-smugmug-survived-the-amazonpocalypse/">分享了他们的经验</a>，他们网站未受影响的原因在于以下四点：</p>
<ol><li>部署在AWS中的服务分布在多个AZ中。所以，一个AZ出问题，就可以切换到另一个AZ。</li>
<li>其架构从设计之初就考虑了故障。他们的每个实例，或一个AZ中的任意一组实例，可以瞬间倒下，系统会很快恢复。</li>
<li>在这次事故中，他们没有使用EBS。因为他们一直不放心EBS的性能和持久性，所以一直没有使用它。基于同样的原因，他们也没有使用RDS。</li>
<li>他们尚未达到100%的云计算。</li>
</ol>
<p>此外，对于100%纯度的云应用，作者给出以下建议：</p>
<ul><li>分散到多个AZ。</li>
<li>分散到多个区域（Region）。</li>
<li>分散到多个云计算供应商。</li>
<li>意识到分散带来的附加工作和复杂度，始终保持清醒认识。</li>
<li>从架构上考虑故障。</li>
<li>熟知应用的哪些地方可能会出现故障。</li>
<li>系统组件化。</li>
<li>对组件进行测试。</li>
<li>保持放松的心态——故障是常有的事情，泰然处之。</li>
<li>Twilio也针对AWS给出了自己的<a href="http://www.infoq.com/cn/news/2011/04/twilio-cloud-architecture">云架构原则</a>，如下：</li>
<li>将故障单元限定在单台主机上。</li>
<li>设定较短超时时间，快速重试。</li>
<li>保持服务接口的幂等性。</li>
<li>服务细粒度，无状态。</li>
<li>宽松的一致性要求。</li>
</ul>
<h2>云端应用架构设计的建议</h2>
<p>结合上文对Amazon事件的分析以及众多来自一线工作人员的经验和分享。我们不难看出，对于云计算用户来说，单纯地祈祷云计算供应商提供100%可靠的云服务不是解决问题的根本所在。相反，云计算用户应该从其应用和架构本身寻找解决问题的根本方法。简单总结如下：</p>
<ol><li>充分理解云计算供应商的服务可用性，在条件允许的情况下准备相应的对策。</li>
<li>以AWS服务为例，若用户对Amazon的EC2，EBS，RDS等服务的可用性及其依赖关系有充分的了解，就可以根据自己的实际需要为
自己的服务架构不同级别的可用性。在多个区域（Region），AZ（可用分区），和EC2上分别实施的高可用方案的可用性级别是不同的，在不同区域上建
立的高可用方案的可用性级别最高，基于AZ的次之，EC2上的最低。但是，高可用性越高，其复杂度和成本越高，反之亦然。用户可根据不同的业务需求为系统
中的不同功能提供不同级别的高可用性方案。</li>
<li>在架构设计中考虑故障的可能性。</li>
<li>不论在传统的数据中心还是在云环境中，设计软件架构师都需要考虑到故障。不同的是，传统方式下，从软件到硬件，架构师都是可控的，而在云
计算环境中，使用的云中服务确实是不可控的。这就需要在做架构设计，或采用云服务时，充分了解云服务的可用性，并将此知识作为架构设计的输入之一。</li>
<li>应用系统的组件化和服务化。</li>
<li>使用SOA架构方法构建应用系统，将应用功能划分成细粒度、无状态的组件，并将组件封装成服务，将同一服务的不同实例分散到多个实例中运行，从而提高服务整体的可用性。</li>
<li>在条件允许的情况下，使用多个云服务提供商。</li>
<li>在云计算标准尚未成熟的大环境下，实现这一理想是困难的。但是，随着云计算标准逐渐成熟，在设计云端应用软件的架构时，就可考虑在多个云
服务供应商之间实现高可用性，但是就目前而言，由于各个云计算供应商的服务的功能和接口缺乏一致性和标准型，完成可用性的设计和实现是需要付出代价的。路
漫漫其修远兮，但希望总在前方。</li>
</ol>
<h2>小结</h2>
<p>云计算是当前的Bizzword，但是我们需要清楚的认识，和当年的SOA一样，云计算也不是银弹，不能解决所有问题。它的好处多，坏处也不少（除
了本文提到的可用性不可控之外，还有许多不好的地方，如数据的安全性、云端应用整合的复杂性等），只有清楚地地认识它，还能更好地避开其劣势，发扬其优
势。本文结合Amazon“4-21”事故，谈到了如何从架构的角度思考云端应用，解决因所使用云服务的可用性问题导致云端应用不可用的问题，以期即享受
云计算带来弹性扩展、自动供应等优势，又避免因云服务不可用而导致的用户体验的下降。希望能给读者带来一些参考，也欢迎读者给出批评建议。</p>
<p>作者：马国耀&nbsp;&nbsp;&nbsp; 来源：<a target="_blank" href="http://www.infoq.com/cn/articles/mgy-cloud-application-architecture">Infoq中文</a></p>]]></description>
</item>

<item>
<link><![CDATA[http://www.itivy.com/ivy/archive/2012/2/3/facebook-flash-arch-3.html]]></link>
<title><![CDATA[基于Facebook和Flash平台的应用架构解析（三）]]></title>
<author><![CDATA[ivy]]></author>
<category><![CDATA[]]></category>
<pubDate>Fri, 03 Feb 2012 14:08:04 GMT</pubDate>
<guid><![CDATA[]]></guid>
<description><![CDATA[<ol><li><a target="_blank" href="http://www.itivy.com/ivy/archive/2012/2/3/facebook-flash-arch-1.html">基于Facebook和Flash平台的应用架构解析（一）</a></li>
<li><a target="_blank" href="http://www.itivy.com/ivy/archive/2012/2/3/facebook-flash-arch-2.html">基于Facebook和Flash平台的应用架构解析（二）</a></li>
<li>基于Facebook和Flash平台的应用架构解析（三）</li>
</ol>
<h2>独立Flash Facebook站点应用</h2>
<p>图5描绘了独立的Flash Facebook站点应用的架构。其主要区别是Facebook服务器不再代为处理全部浏览器请求。另外，现在你还必须在客户端代码中用<a target="_blank" href="http://wiki.developers.facebook.com/index.php/API">Facebook API</a> 或 <a target="_blank" href="http://wiki.developers.facebook.com/index.php/Facebook_Connect">Facebook Connect</a>处理用户的登录。如果使用Facebook API处理登录，用户需在新的浏览器窗口中登录Facebook并返回到你的应用中。为了避免在Facebook站点登录，为用户提供更无缝的登录体验，你可以使用<a target="_blank" href="http://wiki.developers.facebook.com/index.php/Facebook_Connect">Facebook Connect</a>。</p>
<p><img src="/Upload/EditorImage/image/ivy/201202/20120203135740_6454.jpg" alt="" border="0" /></p>
<p><b>图5</b> 独立的Flash Facebook站点应用</p>
<ol><li>当用户在你的站点上访问应用时，浏览器向你的服务器发送HTTP请求——请求一个HTML或任何服务端页面。</li>
<li>服务器返回包含了对你的SWF文件的引用的HTML/JS页面。如果使用Facebook Connect，该HTML页面会包括部分用于初始化Facebook Connect的JavaScript代码（<a target="_blank" href="http://wiki.developers.facebook.com/index.php/Facebook_Connect">说明</a>）。</li>
<li>用户浏览器向你服务器请求内嵌在HTML页面中的SWF文件。</li>
<li>你的服务器返回SWF文件。</li>
<li>SWF 文件中的ActionScript 代码直接异步请求Facebook服务器——方法是使用官方提供的ActionScript
 3.0 Library for Facebook 
Platform。你每次可以提交单独一个调用，也可以提交成批调用。在这种情况下，最初对Facebook服务器的调用必须获得授权；一旦用户成功登录
 （最好使用Facebook Connect），得到了Session Key，那么后续所有Facebook 
API调用所需的签名就会由ActionScript 3.0 Library for Facebook 
Platform的类生成。当然，Facebook必须通过跨域策略文件开放了访问权限，且API调用中传送了所需参数。有关此问题的更多信息，请参看前
 面在Flash iFrame应用部分的讨论。</li>
<li>Facebook服务器向你的Flash应用返回XML或JSON格式的数据，并由你的应用处理这些数据。</li>
<li>若 需实现任何服务端处理功能（如在你的服务器上保存某些数据），可在ActionScript代码中通过远程过程调用方法实现（可以是
 HTTP、Web Service和Flash Remoting）。其中最便捷的方法当属Flash 
Remoting——它通过开源的二进制Action Message Format（AMF）实现服务器和Flash Player间的数据交换。</li>
<li>若有必要，服务器可与Facebook服务器进行其他通讯。</li>
<li>你的服务器处理Facebook服务器返回的结果数据。</li>
<li>你的服务器将数据返回给用户浏览器中的Flash应用。图5中，我们利用Flash Remoting和AMF交换数据，当然你也可用Web Service、SOAP、HTTP实现文本或XML格式的数据交换。</li>
</ol>
<h2>Flash Facebook桌面应用</h2>
<p>最后，让我们来讨论Flash Facebook桌面应用的架构。<b>基于Flash平台的桌面应用，就是AIR应用</b>（这个地方请再斟酌一下）。有关构建AIR应用的更多信息，请参阅<a target="_blank" href="http://help.adobe.com/en_US/AIR/1.5/devappsflex/">AIR文档</a>和<a href="http://www.adobe.com/devnet/air/">AIR开发者中心</a>。Flash Facebook桌面应用（如图6）的架构，和前面讨论过的独立Flash Facebook站点应用非常类似，唯一的不同是此时不需要浏览器，SWF文件也存在于安装了AIR应用的用户本地计算机上。</p>
<p><img src="/Upload/EditorImage/image/ivy/201202/20120203135949_4013.jpg" alt="" border="0" /></p>
<p><b>图6</b> Flash Facebook桌面应用</p>
<ol><li>用户安装并运行AIR桌面程序。</li>
<li>SWF文件中的ActionScript 代码直接异步请求Facebook服务器——方法是使用<b>宿主</b>在<a href="http://www.adobe.com/go/facebooklibrary" target="_blank" title="Google代码">Google代码</a>上
 官方提供的ActionScript 3.0 Library for Facebook 
Platform。你每次可以提交单独一个调用，也可以提交成批调用。在这种情况下，最初对Facebook服务器的调用必须获得授权；一旦用户成功登录
 （最好使用Facebook Connect），得到了Session Key，那么后续所有Facebook 
API调用所需的签名就会由ActionScript 3.0 Library for Facebook 
Platform的类生成。当然，Facebook必须通过跨域策略文件开放了访问权限，且API调用中传送了所需参数。有关此问题的更多信息，请参看前
 面在Flash iFrame应用部分的讨论。</li>
<li>Facebook服务器向你的Flash应用返回XML或JSON格式的数据，并由你的应用处理这些数据。</li>
<li>若 需实现任何形式的服务端处理功能（如在你的服务器上保存某些数据），可在ActionScript代码中通过远程过程调用方法实现 
（可以是HTTP、Web Service和Flash Remoting）。其中最便捷的方法当属Flash 
Remoting——它通过开源的二进制Action Message Format (AMF)实现服务器和Flash Player间的数据交换。</li>
<li>若有必要，服务器可与Facebook服务器进行其他通讯。</li>
<li>你的服务器处理Facebook服务器返回的结果数据。</li>
<li>你的服务器将结果数据返回给Flash桌面程序。图6中利用Flash Remoting和AMF交换数据，当然你也可用Web Service、SOAP、HTTP实现文本或XML格式的数据交换。</li>
</ol>
<h2>总结与引申</h2>
<p>本系列文章介绍了三类基于Flash和Facebook平台的应用：基于Facebook的嵌入式应用、Web站点式的独立应用和桌面应用。对于任何Facebook应用，你都可将Flash程序包纳在iFrame或FBML应用中。具体来说，<b>从架构和处理流程角度可分为六种子类型？(本文的架构图和流程处理适用于）</b>：基于Facebook非Flash的iFrame/FBML应用，基于Facebook的Flash iFrame/FBML应用，以及Flash站点应用、Flash桌面应用。 有关iFrame和FBML应用区别的更多信息，请参考<a href="http://www.adobe.com/devnet/facebook/articles/iframe_fbml_flash_platform_comparison.html">iFrame、FBML Flash Facebook应用比较</a>。有关构架基于Facebook的Flash应用的详细步骤，请观看<a href="http://www.adobe.com/devnet/facebook/articles/video_facebook_quick_start.html">快速构建Facebook应用视频</a>，或阅读<a href="http://www.adobe.com/devnet/facebook/articles/build_your_first_facebook_app.html">利用Flexible构建Facebook应用快速入门。</a></p>
<p></p>
<ol><li><a target="_blank" href="http://www.itivy.com/ivy/archive/2012/2/3/facebook-flash-arch-1.html">基于Facebook和Flash平台的应用架构解析（一）</a></li>
<li><a target="_blank" href="http://www.itivy.com/ivy/archive/2012/2/3/facebook-flash-arch-2.html">基于Facebook和Flash平台的应用架构解析（二）</a></li>
<li>基于Facebook和Flash平台的应用架构解析（三）</li>
</ol>
<p></p>
<p><strong>阅读英文原文</strong>：<a href="http://www.adobe.com/devnet/facebook/articles/facebook_architecture_overview.html">Understanding the architecture of applications built on the Facebook and Flash Platforms</a>。</p>
<p>
作者：罗小平&nbsp;&nbsp;&nbsp; 来源：<a target="_blank" href="http://www.infoq.com/cn/articles/facebook-arch-overview-final">Infoq中文<br />
</a></p>]]></description>
</item>

<item>
<link><![CDATA[http://www.itivy.com/ivy/archive/2012/2/3/facebook-flash-arch-2.html]]></link>
<title><![CDATA[基于Facebook和Flash平台的应用架构解析（二）]]></title>
<author><![CDATA[ivy]]></author>
<category><![CDATA[]]></category>
<pubDate>Fri, 03 Feb 2012 14:06:22 GMT</pubDate>
<guid><![CDATA[]]></guid>
<description><![CDATA[<ol><li><a target="_blank" href="http://www.itivy.com/ivy/archive/2012/2/3/facebook-flash-arch-1.html">基于Facebook和Flash平台的应用架构解析（一）</a></li>
<li>基于Facebook和Flash平台的应用架构解析（二）</li>
<li><a target="_blank" href="http://www.itivy.com/ivy/archive/2012/2/3/facebook-flash-arch-3.html">基于Facebook和Flash平台的应用架构解析（三）</a></li>
</ol>
<h2>Flash iFrame Facebook应用</h2>
<p>至此，我们已经介绍了iFrame和FBML的情况，接下来开始讨论在应用中如何集成Flash内容的问题。虽然你完全可以构建一个包含了 
HTML/JavaScript/ActionScript的混合应用，但为了说明的方便，我们还是将注意力集中在基本的Flash应用层面——其全部视
 觉效果和功能都封装在SWF文件（Flash Player可识别并渲染的、编译后的字节码格式）中。 
在上述混合应用中，可以使用到多种API调用，比如服务端API、前面已讨论过的客户端JavaScript 
API，以及这里将讨论的客户端ActionScript API调用。 
你可将SWF文件集成到iFrame或FBML应用中。图3说明了在iFrame应用中的简单实现方法。</p>
<p><img src="/Upload/EditorImage/image/ivy/201202/20120203135103_4930.jpg" alt="" border="0" /></p>
<p><b>图3</b> Flash iFrame Facebook应用</p>
<ol><li>用户在Facebook站点上访问你的应用时，浏览器向Facebook服务器发出一个HTTP请求。</li>
<li>Facebook服务器返回一个HTML/JS页面，其中包含Facebook站点容器和一个iFrame HTML标签。</li>
<li>用户浏览器向你的服务器请求包含在iFrame中的页面。和前面讨论过的情况不同，这个页面不再是一个服务端页面，而是一个简单的 
HTML页面（内含SWF文件）。此时，Session信息仍通过GET请求的URL参数传递；你的服务器解析这些参数，并转换为内嵌的SWF所需的 
flash参数，这样，在SWF中的ActionScript代码就可以根据这些参数，直接向Facebook服务器发出请求了。</li>
<li>你的服务器将HTML/JS页面返回给用户浏览器，并在iFrame中展示。</li>
<li>用户浏览器向你的服务器发出其他请求，即请求展示在iFrame中的HTML页面中内嵌的SWF文件。</li>
<li>你的服务器返回SWF文件。</li>
</ol>
<p>当用户和你的应用交互时，SWF可向Facebook服务器、或你的服务器发送异步请求。</p>
<ul><li>SWF文件中的ActionScript脚本直接访问Facebook服务器（步骤7-8）。你可以使用<a target="_blank" href="http://www.adobe.com/go/facebooklibrary">Google代码</a>中的<a target="_blank" href="http://wiki.developers.facebook.com/index.php/Client_Libraries">ActionScript 3.0 Library for Facebook Platform</a>。<br />
    出于Flash 
Player安全性的考虑，SWF文件只能从两类服务器获取数据：（1）提供SWF文件的服务器（这里即你的服务器）；（2）有跨域策略文件（在文件中列
 
出了SWF来源服务器）的服务器。也就是说，若要你的SWF能直接访问Facebook服务器，Facebook服务器必须在跨域策略文件中开放了访问权
 限。如果看过<a target="_blank" href="http://api.facebook.com/crossdomain.xml">Facebook的跨域策略文件</a>，你会发现它通过一个通配符，授予了来自任何服务器的SWF文件的访问权：<br />
<pre class="brush:xml;">&lt;cross-domain-policy&gt;
      &lt;site-control permitted-cross-domain-policies="master-only"/&gt;
      &lt;allow-access-from domain="*"/&gt;
 &lt;/cross-domain-policy&gt;</pre></li>
<li>你的ActionScript访问Facebook服务器时，必须像前面非Flash的iFrame和FBML应用部分讲到的那样，传送
应用API Key和用于说明访问来自何处的签名信息。利用ActionScript 3.0 Library for Facebook 
Platform中的类可自动生成签名；为此，你只需向ActionScript session类的构造函数传入应用的API Key和密钥。<br />
    <br />
    但是，你不应在SWF文件以硬编码方式写入上述数据，因为SWF文件可用多种软件反编译。相反，SWF应该在运行时向你的服务器发出请求（可 
HTTP或Flash Remoting方式），从而获得应用的密钥，接下来再传给ActionScript 
session类的构造函数，从而生成访问Facebook服务器时所需的签名。<br />
    <br />
    记住，此签名由下列信息组成：传递给Facebook服务器的URL参数、Session Key（用户访问你的应用时分配）的MD5哈希串、应用的密钥等。</li>
<li>若需实现任何服务端处理功能（如在你的服务器上保存某些数据），可在ActionScript代码中通过远程过程调用方法实现（见步骤 
9-10）。对于利用Flex构建的Flash平台应用而言，这些方法包括HTTP、Web Service和Flash 
Remoting请求技术。其中最便捷的方法当属Flash Remoting——它通过开源的二进制Action Message Format 
(AMF)实现服务器和Flash 
Player间的数据交换。当然，服务端代码在向客户端返回数据之前，可根据需要访问Facebook服务器（此点未包含在图3中）。</li>
</ul>
<h2>Flash FBML Facebook应用</h2>
<p>除了在iFrame中集成你的Flash应用，还可以通过FBML标签<code>&lt;fb:swf&gt;</code>将其植入到FBML应用中。在这种情况中，你的应用会由一或多个服务端页面组成；这些页面包含FBML（用于获取或展示Facebook形式的可视化内 容、对话框和小控件）、不能通过简单的FBML标签实现的服务端API调用，以及包纳了你的Flash内容的<code>&lt;fb:swf&gt;</code>标
签。FBML应用的好处是在访问Facebook提供的社会化特性时非常简单，向Facebook传送FBML标签，就可自动渲染为HTML内容。 
而其不足，则是此处在多个地方编写表现层和逻辑层的代码更为复杂：SWF文件中、服务端页面中，以及发向Facebook服务器的FBML代码中（见图 
4）。有关iFrame和FBML应用区别的更详细信息，请参看<a href="http://www.adobe.com/devnet/facebook/articles/iframe_fbml_flash_platform_comparison.html">iFrame、FBML Flash Facebook应用比较</a>。</p>
<p><img src="/Upload/EditorImage/image/ivy/201202/20120203135350_5883.jpg" alt="" border="0" /></p>
<p><b>图4</b> Flash FBML Facebook应用</p>
<ol><li>用户通过Facebook站点访问你的应用时，浏览器向Facebook服务器发送HTTP请求。</li>
<li>Facebook服务器向你的服务器发出请求——一般请求的是PHP、JSP或ColdFusion式的服务端页面。在这种情况 
下，Session信息通过POST请求（而非iFrame中的GET请求）中的URL参数传递，那么，你的应用服务器就知道该请求来自 
Facebook、是哪个用户发出的请求了。</li>
<li>服务端页面执行时，可根据需要访问数据库和其他服务器，当然包括利用REST API访问Facebook服务器。<br />
    <br />
    API调用必须包含认证信息——包括在Facebook上注册应用时获得的API Key、该调用的签名（通过传给Facebook方法的参数、用户请求你的应用时指定的Session的MD5哈希串生成）、应用的密钥和其他信息。<br />
    <br />
    对于非Flash的iFrame和FBML应用来说，服务端页面通常可利用<a target="_blank" href="http://wiki.developers.facebook.com/index.php/Client_Libraries">现成的代码库</a>实现对Facebook的访问，以及生成签名。因此对于所有FBML应用而言，用户浏览器的所有请求都是通过Facebook代理的，你的应用在请求Facebook服务器时，就没必要每次单独调用一个API；同时，诸如获得用户姓名、图片、生成对话框等等，都可以利用<a target="_blank" href="http://wiki.developers.facebook.com/index.php/FBML">FBML 标签</a>实现。<br />
    <br />
    Facebook服务器在将页面返回给用户浏览器前，会自动将FBML转换为HTML和JavaScript代码。当然，不是所有功能都有标签支持的，比如要取得朋友的生日信息，还是得从你的服务器向Facebook发送对应的API调用请求。</li>
<li>Facebook服务器将被请求数据返回给你的服务器，格式可为XML或JSON。</li>
<li>你的服务器向Facebook服务器返回包括HTML/JS和FBML内容的页面。</li>
<li>Facebook服务器向用户浏览器返回HTML/JS页面。该页面包括了（用标签<code>&lt;fb:swf&gt;指定</code>）对你的服务器上SWF文件的引用。</li>
<li>用户浏览器向你的服务器请求内嵌在HTML页面中的SWF文件。</li>
<li>你的服务器返回SWF文件。</li>
</ol>
<p>在用户和你的应用交互过程中，SWF可向Facebook服务器或你的服务器发出异步调用。</p>
<ul><li>SWF中ActionScript代码直接访问Facebook服务器（见步骤9-10），这可利用宿主在<a href="http://www.adobe.com/go/facebooklibrary" target="_blank" title="Google代码">Google代码</a>上
 官方支持的ActionScript 3.0 Library for Facebook 
Platform实现。当然，Facebook必须通过跨域策略文件开放了访问权限，且API调用中传送了所需参数。有关这部分的详细问题，前面的 
Flash iFrame应用中已有讨论。</li>
<li>若需实现任何服务端处理功能（如在你的服务器上保存某些数据），可在ActionScript代码中通过远程过程调用方法实现（见 
步骤11-12）。对于利用Flex构建的Flash平台应用而言，这些方法包括HTTP、Web Service和Flash 
Remoting请求技术。其中最便捷的方法当属Flash Remoting——它通过开源的二进制Action Message Format 
(AMF)实现服务器和Flash 
Player间的数据交换。当然，服务端代码在向客户端返回数据之前，可根据需要访问Facebook服务器（此点未包含在图4中）。</li>
</ul>
<ol><li><a target="_blank" href="http://www.itivy.com/ivy/archive/2012/2/3/facebook-flash-arch-1.html">基于Facebook和Flash平台的应用架构解析（一）</a></li>
<li>基于Facebook和Flash平台的应用架构解析（二）</li>
<li><a target="_blank" href="http://www.itivy.com/ivy/archive/2012/2/3/facebook-flash-arch-3.html">基于Facebook和Flash平台的应用架构解析（三）</a></li>
</ol>
<p><strong>阅读英文原文</strong>：<a href="http://www.adobe.com/devnet/facebook/articles/facebook_architecture_overview.html">Understanding the architecture of applications built on the Facebook and Flash Platforms</a>。<br />
作者：罗小平&nbsp;&nbsp;&nbsp; 来源：<a target="_blank" href="http://www.infoq.com/cn/articles/facebook-arch-overview-part2">Infoq中文<br />
</a></p>]]></description>
</item>

<item>
<link><![CDATA[http://www.itivy.com/ivy/archive/2012/2/3/facebook-flash-arch-1.html]]></link>
<title><![CDATA[基于Facebook和Flash平台的应用架构解析（一）]]></title>
<author><![CDATA[ivy]]></author>
<category><![CDATA[]]></category>
<pubDate>Fri, 03 Feb 2012 14:05:56 GMT</pubDate>
<guid><![CDATA[]]></guid>
<description><![CDATA[<p>Flash平台可帮助你构建富用户体验的应用，而Facebook平台可帮助你构建富社会化体验的应用。将二者合而为一，你就可以构建高交互性、富于表现力，并融入了社会化功能的杀手级应用了。</p>
<p>本系列文章（共分三部分）将为你介绍基于Facebook和Flash平台的应用程序架构，解析你能在此平台上构建的各种应用类型，并说明这些应用如何与你的服务器、Facebook服务器通讯。</p>
<h2>可构建的应用类型</h2>
<p>你可构建三种Flash与Facebook平台集成的应用：基于Facebook的嵌入式应用、对外服务的Web应用和桌面应用。</p>
<ul><li><b>基于Facebook的嵌入式应用</b>，部署在你自己的服务器上，但其用户通过Facebook站点访问。用户看到的是一个将
你的应用包容其中的Facebook容器。你访问Facebook上的某个应用（通常是因为收到朋友邀请，或搜索某个应用时得到一个链接）
时，Facebook服务器会将请求转发给你的应用服务器，得到一个页面（HTML和Javascript代码），最后在Facebook站点上展示出
来。这种方式对于访问Facebook站点的用户而言，提供的体验是无缝的。这类应用的例子很多，比如来自Playfish和Zyng<a href="http://apps.facebook.com/texas_holdem/%22%20%5Ct%20%22_blank">德克萨斯扑克</a>的<a href="http://apps.facebook.com/biggestbrain/%22%20%5Ct%20%22_blank">谁有最聪明的大脑</a>应用。</li>
<li><b>提供对外服务的独立Web应用</b>，也部署在你自己的服务器上，但用户通过你提供的URL而不是Facebook站点来访问该应用。在外部站点中，你可以通过<a href="http://wiki.developers.facebook.com/index.php/API">Facebook API</a>和<a href="http://wiki.developers.facebook.com/index.php/Facebook_Connect">Facebook Connect</a>来
增加Facebook的特性。比如利用Facebook 
API实现用户登录，应用在一个新的浏览器窗口中打开Facebook登录页面，用户必须在这里登录后，才能访问你本身的应用。为了避免在 
Facebook站点上登录，为用户提供无缝体验，你就需要使用Facebook 
Connect了。比如，某用户阅读一篇博客后，可能想写点评论。那么不必让用户在你的站点上再注册一个账号吧，用他/她在Facebook上的帐户就好
了。为提升用户体验，Facebook API和Facebook 
Connect还允许你访问用户的全部数据，比如在评论旁边显示评论者的姓名和个人图片。类似的如购物网站，通过让用户用他们的Facebook帐户登
录，你可以查看其朋友是否推荐或评论过什么商品。这类的应用如<a href="http://connect.redbullusa.com/">RedBull Connect</a>和<a href="http://www.citysearch.com/">City Search</a>，在这些网站上你可发表评论、阅读朋友的评论，或将自己的评论发表到Facebook Wall或朋友的新闻种源中。</li>
<li><b>桌面应用</b>，除了基于Flash平台的桌面AIR这个特点外，其他和对外服务的Web站点是大致相同的。AIR桌面应用同样是借助Facebook Connect为用户提供无缝的Facebook登录体验。这类应用的例子有Seesmic和Nomee。</li>
</ul>
<p>在开发Facebook应用之前，必须在<a href="http://www.facebook.com/developers">Facebook开发者应用</a>上注册，获得API授权和应用的密钥。至于具体步骤，请参考<a href="http://www.adobe.com/devnet/facebook/articles/build_your_first_facebook_app.html">构建你的第一个Facebook应用快速入门</a>。在注册过程中，需要你指定要开发应用的一些设定，例如应用是基于Web还是桌面的，是否使用FBML或iFrame。</p>
<ul><li>开发基于Facebook的嵌入式应用时，你需指定为Web应用类型，并选择使用iFrame或FBML。</li>
<li>开发对外服务的独立Web应用时，你需指定Facebook Connect信息。</li>
<li>开发桌面应用时，你需指定为桌面应用类型。</li>
</ul>
<p>现在，在我们深入分析基于Flash平台的嵌入式应用、对外Flash平台站点和Flash桌面应用的架构前，先让我们大致了解一下常规嵌入式的、非Flash的、基于iFrame和FBML的Facebook应用的架构。</p>
<h2>iFrame Facebook应用</h2>
<p>当用户访问Facebook的某个应用（比如<a href="http://apps.facebook.com/someapp">http://apps.facebook.com/someapp</a>）时，Facebook对此请求的处理方式，与应用是基于iFrame还是FBML有关。如果是iFrame应用，Facebook服务器返回的页面包含一个Facebook容器，容器容纳一个iFrame，你的应用就在其中加载（如图1）。 </p>
<p><img src="/Upload/EditorImage/image/ivy/201202/20120203134532_7886.jpg" alt="" border="0" /></p>
<p><b>图1</b> iFrame应用架构</p>
<ol><li>当用户在Facebook网站上访问你的应用时，浏览器会向Facebook服务器发送一个HTTP请求。</li>
<li>Facebook服务器返回一个HTML/JavaScript(JS)页面，其中包含了Facebook站点容器和一个iFrame HTML标记。</li>
<li>用户的浏览器向你的服务器请求将显示在iFrame中的页面（一般是一个PHP、ColdFusion或者JSP式的服务端页面）。 
Session信息会通过GET请求中的URL参数传递，这样你的应用服务器就知道此请求是来自Facebook，以及请求是哪个用户发出的。</li>
<li>服务端页面执行时，可能会根据需要访问数据库或其他服务器，其中也包括通过REST 
API向Facebook服务器发出请求。调用REST API时，必须包含认证信息，比如在Facebook上注册应用时获得的API 
Key、该调用的签名（通过传给Facebook方法的参数、用户请求你的应用时指定的Session的MD5哈希串生成）、应用的密钥和其他信息。通常
来说，服务端页面会利用标准的代码库生成对Facebook的请求，并且其签名也在服务端脚本中产生。尽管Facebook官方只提供了一个服务端用户库
（支持<a href="http://wiki.developers.facebook.com/index.php/PHP">PHP 5</a>），不过其他的服务端库已由<a href="http://wiki.developers.facebook.com/index.php/Client_Libraries">社区</a>开发了很多。另外，Facebook官方还提供了两个客户端库，分别支持<a href="http://wiki.developers.facebook.com/index.php/JavaScript_Client_Library">JavaScript</a>和<a href="http://wiki.developers.facebook.com/index.php/ActionScript">ActionScript 3.0</a>。</li>
<li>Facebook服务器将被请求的数据（XML或JSON格式）返回到你的服务器。</li>
<li>你的服务器向用户浏览器返回HTML/JS页面，并由客户端浏览器显示在iFrame中。在用户和你的应用交互时，交互行为包括：</li>
</ol>
<ul><li>如果你的应用包含了新的服务端页面请求，将重复步骤3-6。</li>
<li>如果你的应用包括对你的服务器的JavaScript异步调用，那么步骤7-10将被执行。和上面有所不同的是，此时向用户浏览器返回的通常是XML或JSON数据，由页面的JavaScript脚本负责处理。</li>
<li>还有一种情况，是页面中的JavaScript代码直接访问Facebook服务器，而不通过你的应用服务器中转（步骤11-12）。Facebook官方提供了对应的<a href="http://wiki.developers.facebook.com/index.php/Client_Libraries">JavaScript</a> API库。利用这个库，你可将多个API调用打包，通过一个HTTP请求向Facebook服务器发出。这项技术有利有弊。好处是减少了总的HTTP访问次数，缺点是导致页面大小和复杂度的上升。</li>
</ul>
<p><b>提示</b>：你也可以通过XFBML技术，在你的应用中放置一些简单的FBML标签（具体会在下一个部分中讨论）；当然为了使用这些标签，你必须用JavaScript代码扫描标签的DOM，然后也可以将一批API请求组织成向Facebook服务器的一次调用。</p>
<h2>FBML Facebook应用</h2>
<p>现在，你已经对iFrame应用的工作原理有了一个大致把握，接下来我们讨论FBML 
Facebook应用（见图2）。在前面，你的应用是存在iFrame中的一个独立实体，现在，它将变成Facebook服务器响应请求时返回的HTML
 
页面的一个部分。在这种情况下，Facebook服务器会代为处理所有对你的应用的调用。当然，除了返回HMTL和JavaScript，你的服务端页面
也可以返回FBML代码，Facebook服务器在将它返回给用户浏览器前，会自动将其转换为HTML和JavaScript代码。</p>
<p><img src="/Upload/EditorImage/image/ivy/201202/20120203134554_6188.jpg" alt="" border="0" /></p>
<p><b>图2</b> FBML Facebook应用架构</p>
<ol><li>当用户在Facebook网站上访问你的应用时，浏览器会向Facebook服务器发送一个HTTP请求。</li>
<li>Facebook服务器将请求转给你的服务器，一般来说都是请求一个服务端页面（如PHP、ColdFusion或JSP）。在这种情况
下，Session信息将通过POST请求（而iFrame通常是GET请求）中的URL参数传递，这样你的应用服务器就知道此请求来自 
Facebook，以及请求的发送用户是谁。</li>
<li>服务端页面执行时，可能会根据需要访问数据库或其他服务器，其中也包括通过REST 
API向Facebook服务器发出请求。调用REST API时，必须包含认证信息，比如在Facebook上注册应用时获得的API 
Key、该调用的签名（通过传给Facebook方法的参数、用户请求你的应用时指定的Session的MD5哈希串生成）、应用的密钥和其他信息。<br />
    <br />
    和iFrame应用一样，服务端页面通常都借助一个<a href="http://wiki.developers.facebook.com/index.php/Client_Libraries">代码库</a>生成对Facebook的请求和签名。因为所有返回都是通过Facebook代理的，你的应用请求Facebook服务器时，就没必要每次单独调用一个API。<br />
    <br />
    FBML提供了大量 <a href="http://wiki.developers.facebook.com/index.php/FBML">标签</a>，
可用于获得用户姓名、图片、创建对话框和小组件等。对于这类要求，你只需要直接返回FBML代码，后面的工作留给Facebook服务器就可以了，它在将
页面返回给用户浏览器前，会自动将FBML转换为HTML和JavaScript代码。当然，不是所有功能都有标签支持的，比如要取得朋友的生日信息，还
是得从你的服务器向Facebook发送对应的API调用请求。</li>
<li>Facebook服务器向你的服务器返回被请求的数据，格式是XML或JSON。</li>
<li>你的服务器向Facebook服务器返回HTML/JS/FBML页面。</li>
<li>Facebook服务器将HTML/JS页面返回给用户浏览器。在用户和你的应用交互过程中产生的交互行为包括：
    <ul><li>如果你的应用包括新的服务端页面请求，重复步骤1-6。</li>
<li>不同于请求新的页面，你应用程序中的JavaScript可通过使用官方提供的<a href="http://wiki.developers.facebook.com/index.php/JavaScript_Client_Library">JavaScript库</a>直接向Facebook服务器发出请求（同上面iFrame讨论中的7-10步骤）。</li>
</ul>
    </li>
</ol>
<p><b>提示</b>：虽然在FBML应用中，你也可以向你的应用服务器发出异步请求（同图1的步骤7-10），但这些调用必须位于通过&lt;fb:iFrame&gt; 标签在iFrame里加载的内容中。</p>
<ol><li>基于Facebook和Flash平台的应用架构解析（一）</li>
<li><a target="_blank" href="http://www.itivy.com/ivy/archive/2012/2/3/facebook-flash-arch-2.html">基于Facebook和Flash平台的应用架构解析（二）</a></li>
<li><a target="_blank" href="http://www.itivy.com/ivy/archive/2012/2/3/facebook-flash-arch-3.html">基于Facebook和Flash平台的应用架构解析（三）</a></li>
</ol>
<p><strong>阅读英文原文</strong>：<a href="http://www.adobe.com/devnet/facebook/articles/facebook_architecture_overview.html">Understanding the architecture of applications built on the Facebook and Flash Platforms</a>。<br />
作者：罗小平&nbsp;&nbsp;&nbsp; 来源：<a target="_blank" href="http://www.infoq.com/cn/articles/facebook-arch-overview-part1">Infoq中文<br />
</a></p>]]></description>
</item>


</channel>
</rss>

