放弃用你的InnerHTML来输出HTML吧,jQuery Tmpl不详细讲解

在Ajax横道的今天,我们在页面交互上有了更高的要求,动态生成HTML毫无疑问是其中的一种。动态生成HTML的方式多种多样,其核心不外乎在前段(JS)或者后端(C#/PHP…)将数据组装成我们想要的模版,最终通过一定的方法输出给用户(innerHTML、documentWrite等方式)。

缺点

1)拼接字符串的过程容易出错,常常忘了’/”>等匹配的符号。

2)修改前台模版的同时容易遗忘同步更改动态生成的模版。

3)拼接字符串不直观和美观,不利于查找错误,例如:数据中如果存在HTML内容,会导致种种麻烦。

4)不能满足较高的业务逻辑需求,处理判断较为麻烦,例如:当A情况生成X模版,B情况生成Y模版。

5)复用性低,较为相似的模版难以公用。

 

需求

1)简单,直观的模版

2)易于维护(方便查找错误,有代码着色等)

3)模版的可复用性

4)处理一定的逻辑判断

 

解决方案

基于以上的缺点,jQuery Tmpl这个插件能够很好的满足了我们的需求。使用这种新型的模版技术仅仅需要引入jQuery Tmpl这个插件而已。该插件十分小巧(5.97KB),对于性能的影响并不大。而且据闻这个插件是Microsoft开发的,对于ASP.NET MVC的友好度是大大的。点击这里进去项目地址

实现原理

image

通过图片我们能够很直观的看到Tmpl的工作原理,我们仅仅需要提供数据和模版。数据我们能够通过后台的Json方法直接传到前台,而模版则是接下来要讲的东西了。

1)模版代码写在哪里?
<script type="text/x-jquery-tmpl" id="testTemplate">
    /*模版代码*/
</script>

我们可以看到其实模版代码的容器就是我们的<script>标签,不过type类型是’text/x-jquery-tmpl’而不是我们平常用的’text/javascript’而已,type类型难记?没关系,在Visual Studio2012中已经有了这个类型的智能提示了(没有验证过2010)。

2)模版的语法

我把jQuery Tmpl的语法分为三大类:

1.显示类:

{{html }} / {{= }} / ${ },这三个标签都能够将数据输出到模版中,但是{{html}}不会对数据进行编码,用于输出数据中的HTML代码段的,而{{=}}和${}则会对数据进行编码,防止数据对于模版结构的破坏。

模版代码: image
数据及JS代码: image
页面效果: image

当我们把data里面的name的值换成一段HTML'<a href=”http://www.google.com”>点击有好玩的东西哟!</a>’就可以看到{{html }} / {{= }} / ${ }的区别了。

页面效果: image

由此我们可以见得使用{{html}}来输出模版里面的内容是带有一定的风险的(XSS攻击),所以在非确定数据的安全性下最好还是使用${}来输出内容既简单又简洁。当然直接输出内容远远不能满足我们的要求,如果能够调用函数来处理一下输出结果就更棒啦!

模版代码: image
函数代码: image
页面效果: image
2.条件判断及循环:

{{if}} / {{else}} / {{/if}} / {{each}} 请注意是没有for / while / switch的,相对于来说jQuery Tmpl只支持较为简单的逻辑判断,当然如果你感觉这些满足不了你的需求的话,可以自己写函数然后再调用。给一个简单的例子来说明一下:

模版代码: image
数据及JS代码: image
页面输出: image

在代码中我们可以注意到{{each}}是有两种写法的,如果不在each后面加(i, v)时在{{each}}代码块中使用$value特指当前项的值,而你需要项的序号则可以使用{{each(i, v) }}其中i代表当前项的次序、v代表当前项的值。如果你的条件判断比较复杂则可以使用函数来判断(完全和JS一样)

例如:imageimage效果是和上面的完全一样。

3.复用类

{{temp}}  当分支模版过长(写在一个模版中较为混乱)或者使用已经写好的通用模版,{{temp}}的作用就是调用指定ID模版来显示数据。

模版代码: image

这个模版的最终效果和之前的完全一样,不过分别区分独立开来。提高了代码的可阅读性和复用性。在这里只是一次性调用其他模版,如果想循环调用呢?例如在例子当中输出兴趣爱好那样。让我们来看看代码应该如何写。

模版代码: image

其中在eachTemplate模版中用$data来特指传进来的遍历项的值。怎么样感觉还是相当方便的吧^_^。

{{wrap}}使用于指定模版来包含当前模版,类似于指定母版页当前模版则属于子页面。

模版代码: image
页面效果: image

我们可以看出’wrapTemplate’的功效就是作为公共部分,在模版中用{{html $item.html}}来输出子页面的HTML内容。
其中$item.html还具备一定的筛选功能。

模版代码: image
页面效果: image

可以看到在这里只输出了第二个P。$item.html方法还有一个选项$item.html( filter, textOnly ),textOnly为bool值如果为true则只会输出元素的文字而忽略它原有的元素标签。

模版代码: image
页面效果: image

可以看到strong元素没有了加粗效果只有文字。

总结

其实在使用过程当中并不会经常运用到模版的一些高级功能,本文章也仅仅是介绍了一些关于jQuery Tmpl的基础性的东西。使用这个插件能够给我们带来的好处实在不少,之前我在动态输出HTML代码段的时候(在JS文件中拼接字符串)经常修改了前台页面而忘记修改JS中的拼接字符串,导致经常出些莫名其妙的错误,而且在多个分支判断下拼接字符串的劣处体现的更加明显,现在的话我会把Tmpl模版的代码就放在前台页面要生成代码的地方,这样就大大减少了错误的发生啦!

以上的内容并没有涉及到与ASP.NET的交互,以及更多的一些高级应用技巧,如果大家反映良好就会专门写一篇结合ASP.NET MVC的应用文章啦~^_^

如何选择Javascript模板引擎(javascript template engine)?

随着前端开发的密集度越来越高,Ajax和JSON的使用越来越频繁,大家肯定免不了在前台开发中大量的使用标签,常见到的例子如下:

你的到了一个JSON对象,如下:

1
2
3
4
var data={
  email: 'terry.li@gbin1.com,
  gender: 'male'  
}

然后你需要将json数据组织成页面内容,如下:

1
2
3
4
var email, gender;
email= '&lt;div&gt;' + data.email+ &lt;/div&gt;'; 
gender= '&lt;div&gt;' + data.gender + &lt;/div&gt;'; 
$('#contentwrapper‘).append(content).append(gender);

代码执行功能非常简单,将json数据生成web页面中的内容,如下:

<div>www.hua3.cc/blog</div>
<div>Male</div>

如果只是简单代码组合可能还好一些,但是如果页面大量使用类似的结构的话,除非一直是同一个程序员维护,否则后期的管理成本会相对非常的高。

为 了解决这个问题, 我们通常会引入”javascript模板引擎概念“,提到模板,相信大家都不会陌生,如果你开发过服务器端程序,例如J2EE相关技术的话,肯定对于 Freemarker,sitemesh等模板类库非常熟悉,它能够有效的帮助你组织和维护代码结构,提高代码编写效率和降低维护成本。

目前开源的javascript模板引擎很多,抛给我们的问题是 – 如何选择一个js模板引擎?

为了更好的帮助大家辅助选择模板引擎,这里推荐一个不错的工具性网站:Template-engine-chooser 

打开这个网站后,我们可以看到列出了比较常用的js模板引擎,例如,mustache.js,jQuery之父John 的micorotemplate.js,还有jQuery Tmpl的替代品 – jsrender等等。

在类库列表的左边是一系列的问题,如下:

  • 模板需要在客户端使用还是服务器端?
  • 逻辑复杂程度有多少?
  • 是不是有运行速度要求?
  • 是不是需要预定义的模板?
  • 需要Partials支持吗?
  • 需要DOM结构或者String字符串类型?
  • 除了模板标签,是不是在渲染前后开发语言一致?

如果你也在寻找js模板的话,你肯定也有一些问题,或者就是上面列出的问题里。你只需要针对不同的问题回答:是或者不是,这个工具会自动选择合适的模板。

当然,这里的模板并不是很全,但是包含了比较知名的模板,希望以后它能够添加更多模板选择项。

希望大家喜欢这个工具!如果你有任何问题,请给我们留言!

提升tomcat服务器性能的七条经验

在线上环境中我们是采用了tomcat作为Web服务器,它的处理性能直接关系到用户体验,在平时的工作和学习中,归纳出以下七种调优经验。

1. 服务器资源

服务器所能提供CPU、内存、硬盘的性能对处理能力有决定性影响。
(1) 对于高并发情况下会有大量的运算,那么CPU的速度会直接影响到处理速度。
(2) 内存在大量数据处理的情况下,将会有较大的内存容量需求,可以用-Xmx -Xms -XX:MaxPermSize等参数对内存不同功能块进行划分。我们之前就遇到过内存分配不足,导致虚拟机一直处于full GC,从而导致处理能力严重下降。
(3) 硬盘主要问题就是读写性能,当大量文件进行读写时,磁盘极容易成为性能瓶颈。最好的办法还是利用下面提到的缓存。

2. 利用缓存和压缩

对于静态页面最好是能够缓存起来,这样就不必每次从磁盘上读。这里我们采用了Nginx作为缓存服务器,将图片、css、js文件都进行了缓存,有效的减少了后端tomcat的访问。

另外,为了能加快网络传输速度,开启gzip压缩也是必不可少的。但考虑到tomcat已经需要处理很多东西了,所以把这个压缩的工作就交给前端的Nginx来完成。可以参考之前写的《利用nginx加速web访问》。

除了文本可以用gzip压缩,其实很多图片也可以用图像处理工具预先进行压缩,找到一个平衡点可以让画质损失很小而文件可以减小很多。曾经我就见过一个图片从300多kb压缩到几十kb,自己几乎看不出来区别。

3. 采用集群

单个服务器性能总是有限的,最好的办法自然是实现横向扩展,那么组建tomcat集群是有效提升性能的手段。我们还是采用了Nginx来作为请求分流的服务器,后端多个tomcat共享session来协同工作。可以参考之前写的《利用nginx+tomcat+memcached组建web服务器负载均衡》。

4. 优化tomcat参数

这里以tomcat7的参数配置为例,需要修改conf/server.xml文件,主要是优化连接配置,关闭客户端dns查询。

1
2
3
4
5
6
7
8
9
10
<Connector port="8080"
           protocol="org.apache.coyote.http11.Http11NioProtocol"
           connectionTimeout="20000"
           redirectPort="8443"
           maxThreads="500"
           minSpareThreads="20"
           acceptCount="100"
           disableUploadTimeout="true"
           enableLookups="false"
           URIEncoding="UTF-8" />

5. 改用APR库

tomcat默认采用的BIO模型,在几百并发下性能会有很严重的下降。tomcat自带还有NIO的模型,另外也可以调用APR的库来实现操作系统级别控制。

NIO模型是内置的,调用很方便,只需要将上面配置文件中protocol修改成org.apache.coyote.http11.Http11NioProtocol,重启即可生效。上面配置我已经改过了,默认的是HTTP/1.1。

APR则需要安装第三方库,在高并发下会让性能有明显提升。具体安装办法可以参考http://www.cnblogs.com/huangjingzhou/articles/2097241.html。安装完成后重启即可生效。如使用默认protocal就是apr,但最好把将protocol修改成org.apache.coyote.http11.Http11AprProtocol,会更加明确。

在官方找到一个表格详细说明了这三种方式的区别:

1
2
3
4
5
6
7
8
9
10
11
            Java Blocking Connector   Java Nio Blocking Connector   APR/native Connector
                             BIO                         NIO                       APR
Classname                AjpProtocol               AjpNioProtocol           AjpAprProtocol
Tomcat Version           3.x onwards                 7.x onwards              5.5.x onwards
Support Polling              NO                          YES                       YES
Polling Size                 N/A                   maxConnections             maxConnections
Read Request Headers      Blocking                  Sim Blocking                   Blocking
Read Request Body         Blocking                  Sim Blocking                   Blocking
Write Response            Blocking                  Sim Blocking                   Blocking
Wait for next Request     Blocking                  Non Blocking               Non Blocking
Max Connections        maxConnections              maxConnections             maxConnections

6. 优化网络

Joel也明确提出了优化网卡驱动可以有效提升性能,这个对于集群环境工作的时候尤为重要。由于我们采用了linux服务器,所以优化内核参数也是一个非常重要的工作。给一个参考的优化参数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
1. 修改/etc/sysctl.cnf文件,在最后追加如下内容:
 
net.core.netdev_max_backlog = 32768
net.core.somaxconn = 32768
net.core.wmem_default = 8388608
net.core.rmem_default = 8388608
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
net.ipv4.ip_local_port_range = 1024 65000
net.ipv4.route.gc_timeout = 100
net.ipv4.tcp_fin_timeout = 30
net.ipv4.tcp_keepalive_time = 1200
net.ipv4.tcp_timestamps = 0
net.ipv4.tcp_synack_retries = 2
net.ipv4.tcp_syn_retries = 2
net.ipv4.tcp_tw_recycle = 1
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_mem = 94500000 915000000 927000000
net.ipv4.tcp_max_orphans = 3276800
net.ipv4.tcp_max_syn_backlog = 65536
 
2. 保存退出,执行sysctl -p生效

7. 让测试说话

优化系统最忌讳的就是只调优不测试,有时不适当的优化反而会让性能更低。以上所有的优化方法都要在本地进行性能测试过后再不断调整参数,这样最终才能达到最佳的优化效果。

补充Bio、Nio、Apr模式的测试结果:

对于这几种模式,我用ab命令模拟1000并发测试10000词,测试结果比较意外,为了确认结果,我每种方式反复测试了10多次,并且在两个服务器上都测试了一遍。结果发现Bio和Nio性能差别非常微弱,难怪默认居然还是Bio。但是采用apr,连接建立的速度会有50%~100%的提升。直接调用操作系统层果然神速啊,这里强烈推荐apr方式!

看看传说5亿行代码的Healthcare.gov网站的架构

HealthCare.gov是美国联邦健康保险交换系统的核心,该网站自10月1日开通运营以来一直遭受各种问题的困扰,比如性能、数据问题等等。

《纽约时报》曾报道称“大约1%的代码——相当于500万行——需要重写”,如果这个数据正确,也就是说Healthcare.gov的总代码量达到了惊人的5亿行。5亿行意味着什么呢?这意味着Healthcare.gov的代码量是Facebook、Windows XP和OS X之和的2倍还多。看下图你就一目了然了。

网站架构

也有一些“无聊”的人,比如Eduardo Garcia这位老兄,在帮父母买保险时遇到了问题,就利用自己的开发技术和浏览器中的开发者工具分析了这个网站的架构,见下面的信息图。图中的CMS指的是Centers for Medicare and Medicaid Services(医疗保险和医疗补助服务中心)。

该网站主要由6个复杂的系统组成,分别由6个承包商负责开发,而且这些承包商下面还有承包商(多达55个),由于没有一个总的负责人,网站最终出现各种各样的问题也在所难免。

其中FFM(Federally Facilitated Marketplace,联邦政府促进市场)系统最为复杂。从图中可以看出,该网站前端使用了HTML5技术,包括Twitter Bootstrap、jQuery、Backbone.js等,使用Pingdom工具进行监控和优化,虚拟基础架构是由Terremark公司提供的。这个系统的合同金额高达2.93亿美元。

导致网站“瓶颈”的其中一个部分就是EIDM(Enterprise Identity Management,企业身份管理系统),目前还不清楚该系统使用什么技术,但很明显,它通过一个RESTful API提供身份验证、授权功能。

Healthcare.gov的核心是数据服务中心,该系统由Optum/QSSI开发,主要作为所有保险公司的数据库、CMS数据库和Equifax(美国三大信用局之一)收入验证服务的数据层和Web服务层,该系统由JBoss服务器、数据访问组件以及使用Java开发的RESTful Web服务所构成。该技术堆栈也被认为是导致应用比较慢的原因之一。

来看看一些细节,这个all.js文件中集合了Bootstrap.js、jQuery、Backbone.js、json2.js以及各种各样的插件和应用的代码。下面这个图中显示individualApplication.js文件中的第18415行代码出现问题,可见该工程的庞大。

问题根源

Ars网站列出了HealthCare.gov的七宗罪

 

  1. 承包商太多,导致最终结构过度复杂;
  2. 整个项目都依赖于Experian提供的数据,而无论是政府还是承包商都对数据质量没有发言权,数据出现问题会导致整个网站遭遇问题;
  3. 从未经过时间检验的全新结构;
  4. 需求不断变化,设计不断改变;
  5. 由于需求直到上线最后一分钟还在改,导致网站根本无法进行全面的测试;
  6. 不是逐步增加新特性,网站将全部功能一下子推出,由于没有办法在高负荷下测试网站的性能,政府无法根据测试结果扩建基础设施;
  7. 网站没有有效的方法在多层组件中管理bug跟踪,没有方法识别问题的根源。

 

奥巴马:新产品总是会有问题的

针对这些问题,美国政府如今也没有什么好的解决办法。美国总统奥巴马之前对此表示:

每种新法律、新产品发布时,总会在初期遇到很多问题,这些问题将很快被修复。
这就像几周之前,苹果发布了全新的移动操作系统,几天之后,苹果发现了问题,公司马上发布了修正补丁。我可不记得有人建议苹果应该停止销售iPhone、iPad或者威胁公司应该关门倒闭。

据悉,已经有三名分别来自甲骨文、谷歌和红帽的工程师正在帮助政府改善这个网站,并且他们已经参与此事近一个月了。

这是一个典型的政府式项目。话说,有人知道12306的代码量是多少么?

构建高可扩Web架构和分布式系统实战

本文作者Kate Matsudaira是一位美丽的女工程副总裁,曾在Sun Microsystems、微软、亚马逊这些一流的IT公司任职。她有着非常丰富的工作经验和团队管理经验,当过程序员、项目经理、产品经理以及人事经理。专注于构建和操作大型Web应用程序/网站,目前她的主要研究方向是SaaS(软件即服务)应用程序和云计算(如大家所说的大数据)。

本文是作者在AOSA一书介绍如何构建可扩展的分布式系统里的内容,在此翻译并分享给大家。

开源软件已经成为许多大型网站的基本组成部分,随着这些网站的逐步壮大,他们的网站架构和一些指导原则也开放在开发者们的面前,给予大家切实有用的指导和帮助。

这篇文章主要侧重于Web系统,并且也适用于其他分布式系统。

Web分布式系统设计的原则

构建并运营一个可伸缩的Web站点或应用程序到底是指什么?在最初,仅是通过互联网连接用户和访问远程资源。

和大多数事情一样,当构建一个Web服务时,需要提前抽出时间进行规划。了解大型网站创建背后的注意事项以及学会权衡,会给你带来更加明智的决策。下面是设计大型Web系统时,需要注意的一些核心原则:

  • 可用性
  • 性能
  • 可靠性
  • 可扩展
  • 易管理
  • 成本

上面的这些原则给设计分布式Web架构提供了一定的基础和理论指导。然而,它们也可能彼此相左,例如实现这个目标的代价是牺牲成本。一个简单的例子:选择地址容量,仅通过添加更多的服务器(可伸缩性),这个可能以易管理(你不得不操作额外的服务器)和成本作为代价(服务器价格)。

无论你想设计哪种类型的Web应用程序,这些原则都是非常重要的,甚至这些原则之间也会互相羁绊,做好它们之间的权衡也非常重要。

基础

当涉及到系统架构问题时,这几件事情是必须要考虑清楚的:什么样的模块比较合适?如何把它们组合在一起?如何进行恰当地权衡?在扩大投资之前,它通常需要的并不是一个精明的商业命题,然而,一些深谋远虑的设计可以帮你在未来节省大量的时间和资源。

本文讨论的重点几乎是构建所有大型Web应用程序的核心:服务、冗余、分区和故障处理能力。这里的每个因素都会涉及到选择和妥协,特别是前面所讨论的那些原则。解释这些核心的最佳办法就是举例子。

图片托管应用程序

有时,你会在线上传图片,而一些大型网站需要托管和传送大量的图片,这对于构建一个具有成本效益、高可用性并具有低延时(快速检索)的架构是一项挑战。

在一个图片系统中,用户可以上传图片到一个中央服务器里,通过网络连接或API对这些图片进行请求,就像Flickr或者Picasa。简单点,我们就假设这个应用程序只包含两个核心部分:上传(写)图片和检索图片。图片上传时最好能够做到高效,传输速度也是我们最关心的,当有人向图片发出请求时(例如是一个Web页面或其他应用程序)。这是非常相似的功能,提供Web服务或内容分发网络(一个CDN服务器可以在许多地方存储内容,所以无论是在地理上还是物理上都更加接近用户,从而导致更快的性能)边缘服务器。

该系统需要考虑的其他重要方面:

  • 图片存储的数量是没有限制的,所以存储应具备可伸缩,另外图片计算也需要考虑
  • 下载/请求需要做到低延迟
  • 用户上传一张图片,那么图片就应该始终在那里(图片数据的可靠性)
  • 系统应该易于维护(易管理)
  • 由于图片托管不会有太高的利润空间,所以系统需要具备成本效益

图1是个简化的功能图

图1 图片托管系统的简化结构图

在这个例子中,系统必须具备快速、数据存储必须做到可靠和高度可扩展。构建一个小型的应用程序就微不足道了,一台服务器即可实现托管。如果这样,这篇文章就毫无兴趣和吸引力了。假设我们要做的应用程序会逐渐成长成Flickr那么大。

服务

当我们考虑构建可伸缩的系统时,它应有助于解耦功能,系统的每个部分都可以作为自己的服务并且拥有清晰的接口定义。在实践中,这种系统设计被称作面向服务的体系结构(SOA)。对于此类系统,每个服务都有它自己的独特功能,通过一个抽象接口可以与外面的任何内容进行互动,通常是面向公众的另一个服务API。

把系统分解成一组互补性的服务,在互相解耦这些操作块。这种抽象有助于在服务、基本环境和消费者服务之间建立非常清晰的关系。这种分解可以有效地隔离问题,每个块也可以互相伸缩。这种面向服务的系统设计与面向对象设计非常相似。

在我们的例子中,所有上传和检索请求都在同一台服务器上处理。然而,因为系统需要具备可伸缩性,所以把这两个功能打破并集成到自己的服务中是有意义的。

快进并假设服务正在大量使用;在这种情况下,很容易看到写图片的时间对读图片时间会产生多大影响(他们两个功能在彼此竞争共享资源)。根据各自体系,这种影响会是巨大的。即使上传和下载速度相同(这是不可能的,对于大多数的IP网络来说,下载速度:上传速度至少是3:1),通常,文件可以从缓存中读取,而写入,最终是写到磁盘中(也许在最终一致的情况下,可以被多写几次)。即使是从缓存或者磁盘(类似SSD)中读取,数据写入都会比读慢(Pole Position,一个开源DB基准的开源工具和结果)。

这种设计的另一个潜在问题是像Apache或者Lighttpd这些Web服务器通常都会有一个并发连接数上限(默认是500,但也可以更多),这可能会花费高流量,写可能会迅速消掉所有。既然读可以异步或利用其他性能优化,比如gzip压缩或分块传输代码,Web服务可以快速切换读取和客户端来服务于更多的请求,超过每秒的最大连接数(Apache的最大连接数设置为500,这种情况并不常见,每秒可以服务几千个读取请求)。另一方面,写通常倾向于保持一个开放的链接进行持续上传,所以,使用家庭网络上传一个1 MB的文件花费的时间可能会超过1秒,所以,这样的服务器只能同时满足500个写请求。

图2:读取分离

规划这种瓶颈的一个非常好的做法是把读和写进行分离,如图2所示。这样我们就可以对它们单独进行扩展(一直以来读都比写多)但也有助于弄明白每个点的意思。这种分离更易于排除故障和解决规模方面问题,如慢读。

这种方法的优点就是我们能够彼此独立解决问题——在同种情况下,无需写入和检索操作。这两种服务仍然利用全球语料库的图像,但是他们可以自由地优化性能和服务方法(例如排队请求或者缓存流行图片——下面会介绍更多)。从维护和成本角度来看,每一个服务都可以根据需要独立进行扩展,但如果把它们进行合并或交织在一起,那么有可能无意中就会对另一个性能产生影响,如上面讨论的情景。

当然,如果你有两个不同的端点,上面的例子可能会运行的很好(事实上,这非常类似于几个云存储供应商之间的实现和内容分发网络)。虽然有很多种方法可以解决这些瓶颈,但每个人都会有不同的权衡,所以采用适合你的方法才是最重要的。

例如,Flickr解决这个读/写问题是通过分发用户跨越不同的碎片,每个碎片只能处理一组用户,但是随着用户数的增加,更多的碎片也会相应的添加到群集里(请参阅Flickr的扩展介绍)。在第一个例子中,它更容易基于硬件的实际用量进行扩展(在整个系统中的读/写数量),而Flickr是基于其用户群进行扩展(but forces the assumption of equal usage across users so there can be extra capacity)。而前面的那个例子,任何一个中断或者问题都会降低整个系统功能(例如任何人都没办法执行写操作),而Flickr的一个中断只会影响到其所在碎片的用户数。在第一个例子中,它更容易通过整个数据集进行操作——例如,更新写服务,包括新的元数据或者通过所有的图片元数据进行搜索——而Flickr架构的每个碎片都需要被更新或搜索(或者需要创建一个搜索服务来收集元数据——事实上,他们就是这样做的)。

当谈到这些系统时,其实并没有非常正确的答案,但有助于我们回到文章开始处的原则上看问题。确定系统需求(大量的读或写或者两个都进行、级别并发、跨数据查询、范围、种类等等),选择不同的基准、理解系统是如何出错的并且对以后的故障发生情况做些扎实的计划。

冗余

为了可以正确处理错误,一个Web架构的服务和数据必须具备适当的冗余。例如,如果只有一个副本文件存储在这台单独的服务器上,那么如果这台服务器出现问题或丢失,那么该文件也随即一起丢失。丢失数据并不是什么好事情,避免数据丢失的常用方法就是多创建几个文件或副本或冗余。

同样也适用于服务器。如果一个应用程序有个核心功能,应确保有多个副本或版本在同时运行,这样可以避免单节点失败。

在系统中创建冗余,当系统发生危机时,如果需要,可以消除单点故障并提供备份或备用功能。例如,这里有两个相同的服务示例在生产环境中运行,如果其中一个发生故障或者降低,那么该系统容错转移至那个健康的副本上。容错转移可以自动发生也可以手动干预。

服务冗余的另一重要组成部分是创建一个无共享架构。在这种体系结构中,每个节点都能相互独立运行,并且没有所谓的中央“大脑”管理状态或协调活动其他节点。这对系统的可扩展帮助很大,因为新节点在没有特殊要求或知识的前提下被添加。然而,最重要的是,这些系统是没有单点故障的,所以失败的弹性就更大。

例如在我们的图片服务器应用程序中,所有的图片在另一个硬件上都有冗余副本(理想情况下是在不同的地理位置,避免在数据中心发生一些火灾、地震等自然事故),服务去访问图片将被冗余,所有潜在的服务请求。(参见图3:采用负载均衡是实现这点的最好方法,在下面还会介绍更多方法)

图3 图片托管应用程序冗余

分区

数据集有可能非常大,无法安装在一台服务器上。也有可能这样,某操作需要太多的计算资源、性能降低并且有必要增加容量。在这两种情况下,你有两种选择:纵向扩展或横向扩展。

纵向扩展意味着在单个服务器上添加更多的资源。所以,对于一个非常大的数据集来说,这可能意味着添加更多(或更大)的硬件设备,来使一台服务器能容下整个数据集。在计算操作下,这可能意味着移动计算到一个更大的服务器上,拥有更快的CPU或更大的内存。在各种情况下,纵向扩展可以通过提升单个资源的处理能力来完成。

横向扩展在另一方面是添加更多的节点,在大数据集下,这可能会使用第二服务器来存储部分数据集,对于计算资源来说,这意味着分割操作或跨节点加载。为了充分利用横向扩展,它应作为一种内在的系统架构设计原则,否则修改或拆分操作将会非常麻烦。

当谈到横向扩展时,最常见的做法是把服务进行分区或碎片。分区可以被派发,这样每个逻辑组的功能就是独立的。可以通过地理界限或其他标准,如非付费与付费用户来完成分区。这些方案的优点是他们会随着容量的增加提供一个服务或数据存储。

在我们的图片服务器案例中,用来存储图片的单个文件服务器可能被多个文件服务器取代,每个里面都会包含一套自己独特的图像。(见图4)这种架构将允许系统来填充每一个文件/图片服务器,当磁盘填满时会添加额外的服务器。这样的设计需要一个命名方案,用来捆绑图片文件名到其相应的服务器上。图像名字可以形成一个一致的哈希方案并映射到整个服务器上;或者给每张图片分配一个增量ID,当客户端对图片发出请求时,图片检索服务只需要检索映射到每个服务器上(例如索引)的ID。

图4 图片托管应用程序冗余和分区

当然,跨越多个服务器对数据或功能进行分区还是有许多挑战的。其中的关键问题是数据本地化。在分布式系统中,数据操作或计算点越接近,系统性能就会越好。因此,它也可能是个潜在问题,当数据分散在多个服务器上时。有时数据不是在本地,那么就要迫使服务器通过网络来获取所需的信息,这个获取的过程就会设计到成本。

另一潜在问题是不一致。当这里有多个服务对一个共享资源执行读写操作时,潜在可能会有另一个服务器或数据存储参与进来,作为竞选条件——一些数据需要更新,但是读的优先级高于更新——在这种情况下,数据就是不一致的。例如在图片托管方案中,有可能出现的不一致是:如果一个客户端发送更新“狗”图片请求,进行重新命名,把“Dog”改成“Gizmo”,但同时,另一个客户端正在读这张图片。在这种情况下,标题就是不清楚的。“Dog”或“Gizmo”应该被第二个客户端接收。

当然,在进行数据分区时会产生一些障碍,但是分区允许把每个问题拆分到管理群里——通过数据、负载、使用模式等。这样对可扩展和易管理都是有帮助的,但也不是没有风险的。这里有很多方式来降低风险和故障处理;然而,为了简便起见,并未在本文中详细说明,如果你有兴趣,可以访问我的博客

总结

以上介绍的都是设计分布式系统需要考虑的核心要素。可用性、性能、可靠性、可扩展、易管理、成本这几个原则非常重要,但在实际应用中可能会以牺牲某个原则来实现另外一个原则,在这个过程中就要做好权衡工作,做到因时制宜。

在下面的构建分布式系统实战中,我们将会深入介绍如何设计可扩展的数据访问,包括负载均衡、代理、全局缓存、分布式缓存等。

本文是作者在AOSA一书介绍如何构建可扩展的分布式系统里的内容,我们进行了翻译并分为上下两篇分布分享给大家。在上一篇《构建高可扩Web架构和分布式系统实战》中,我们举例讨论了设计分布式系统需要考虑的核心要素:可用性、性能、可靠性、可扩展、易管理、成本。而在这篇文章中,我们将深入介绍如何设计可扩展的数据访问,包括负载均衡、代理、全局缓存、分布式缓存等。

构建快速可伸缩的数据访问块

在讨论完设计分布式系统的核心考虑因素后,下面让我们再一起讨论难点部分:可扩展的数据访问。

大多数简单的Web应用程序,例如LAMP堆栈应用程序,看起来如图5所示:

图5:简单的Web应用程序

随着系统渐渐扩大,他们将面临两大主要挑战:构建可扩展的应用程序服务器和数据访问机制。在一个高可扩的应用程序设计中,通常最小化的应用程序(或Web)服务往往能体现一种无共享的架构。这就使得应用程序服务器层要进行横向扩展,这种设计的结果就是把繁重的工作转移到堆栈下层的数据库服务和配置服务上,这才是这一层上真正的可扩展和性能挑战。

本文的剩余部分专门讨论一些常见策略和方法来使这些服务可以快速和可扩展,提升数据的访问速度。

图6 过于简化的的Web应用程序

大多数系统可能会简化成图6那样,这是个非常不错的起点。如果你有大量的数据,想要快速便捷地访问,就好比在你书桌抽屉的最上面有一堆糖果。虽然过于简化,但也暗示了两个难题:可扩展存储和快速的数据访问。

为了这个例子,让我们假设有许多太字节(TB)数据,并且允许用户随机访问一小部分数据(见图7)。这与本文图片应用程序里的在文件服务器上定位一个图片文件非常相似。

图7 访问特定数据

这也是个非常大的挑战,把TB级数据加载到内存中的成本比较昂贵,这可以直接转化到磁盘上进行IO。从磁盘上读取要比内存慢的多——内存访问和Chuck Norris一样快,而磁盘的访问速度要比在DMV上慢。这个速度不同于大数据集上的合计,实数内存访问大概要比顺序访问快6倍,或者比随机从磁盘上读取快10万倍(参考The Pathologies of Big Data)。此外,即使是unique ID,想要在较少的数据中查找确切的位置也是一项艰巨的任务。

幸运的是,能找到许多方法让这个问题变的简单,这里提供4个非常重要的方法:缓存、代理、索引和负载均衡器。在下面将会详细讨论这4个内容来提升数据访问速度。

缓存

缓存就是利用本地参考原则:当CPU要读取一个数据时,首先从缓存中查找,找到就立即读取并送给CPU处理;没有找到,就用相对慢的速率从内存中读取并送给CPU处理,同时把这个数据所在的数据块调入缓存中,可以使得以后对整块数据的读取都从缓存中进行,不必再调用内存。它们几乎被用在每一个计算层上:硬件、操作系统、Web浏览器、Web应用程序等。一个缓存就相当于是一个临时内存:它有一个有限的空间量,但访问它比访问原始数据速度要快。缓存也可以存在于各个层的架构中,但经常在离前端最近的那个层上发现,在那里可以快速实现并返回数据,无需占用下游层数据。

那么如何在我们的API例子里利用缓存使数据访问更快呢?在这种情况下,有许多地方可以插入缓存。一种是在请求层节点上插入缓存,如图8所示。

图8 在请求层节点插入缓存

在请求层节点上放置一个缓存,即可响应本地的存储数据。当对服务器发送一个请求时,如果本地存在所请求数据,那么该节点即会快速返回本地缓存数据。如果本地不存在,那么请求节点将会查询磁盘上的数据。请求层节点缓存即可以存在于内存中(这个非常快速)也可以位于该节点的本地磁盘上(比访问网络存储要快)。

 

图9 多个缓存

当扩展到许多节点的时候,会发生什么呢?如图9所示,如果请求层被扩展为多个节点,它仍然有可能访问每个节点所在的主机缓存。然而,如果你的负载均衡器随机分布节点之间的请求,那么请求将会访问各个不同的节点,因此缓存遗漏将会增加。这里有两种方法可以克服这个问题:全局缓存和分布式缓存。

全局缓存

顾名思义,全局缓存是指所有节点都使用同一个缓存空间。这包含添加一台服务器或某种类型的文件存储,所有请求层节点访问该存储要比原始存储快。每个请求节点会以同种方式查询缓存,这种缓存方案可能有点复杂,随着客户机和请求数量的增加,单个缓存(Cache)很容易溢出,但在某些结构中却是非常有效的(特别是那些特定的硬件,专门用来提升全局缓存速度,或者是需要被缓存的特定数据集)。

在图10中描述了全局缓存常见的两种方式。当一个Cache响应在高速缓存中没有发现时,Cache自己会从底层存储中检索缺少的那块数据。如图11所示,请求节点去检索那些在高速缓存中没有发现的数据。

图10 负责检索数据的全局缓存

图11 全局缓存里负责检索的请求节点

大多使用全局缓存的应用程序都倾向于使用第一种类型,利用Cache本身来驱逐和获取数据以防止来自客户端的同一个数据区发出大量的请求。然而,在某些情况下,使用第二种实现反而更有意义。例如,如果该缓存用于存储大量的文件,低缓存的命中率会导致高速缓冲区不堪重负和缓存遗漏,在这种情况下, it helps to have a large percentage of the total data set (or hot data set) in the cache.

分布式缓存

分布式缓存即缓存在分布式系统各节点内存中的缓存数据。如图12所示,每个节点都有自己的缓存数据,所以如果冰箱扮演着缓存杂货店的角色,那么分布式缓存就是把食物放置在不同的地方——冰箱、橱柜和饭盒——当索取的时候,方便拿哪个就拿哪个,而无需特地往商店跑一趟。通常情况下,会使用一致性哈希函数对缓存进行划分,例如,一个请求节点正在寻找一个特定块的数据,在分布式缓存中,它很快就会知道去哪里找,并确保这些数据是可用的。这种情况下,每个节点都会有一小块缓存,然后在向另一个节点发送数据请求。因此分布式缓存的优点之一就是只需向请求池添加节点即可增加缓存空间,减少对数据库的访问负载量。

当然,分布式缓存也存在缺点,例如单点实效,当该节点出现故障或宕机,那么该节点保存的数据就会丢失。

图12 分布式缓存

分布式缓存的突出优点是提高运行速度(前提当然是正确实现)。选择不同的方法也会有不一样的效果,如果方法正确,即使请求数很多,也不会对速度有所影响。然而,缓存的维护需要额外的存储空间,这些通常需要购买存储器实现,但价格都很昂贵。

其中一个非常流行的开源缓存产品:Memcached(即可以在本地缓存上工作也可以在分布式缓存上工作);然而,这里还有许多其他选项(包括许多语言——或者是框架——特定选项)。

Memcached用于许多大型Web站点,其非常强大。Memcached基于一个存储键/值对的hashmap,优化数据存储和实现快速搜索(O(1))。

Facebook采用不同类型的缓存技术来提升他们的网站性能(参考“Facebook caching and performance”)。在语言层面上使用$GLOBALS和APC(在PHP里提供函数调用),这有助于使中间函数调用更快(大多数语言都使用这些类型库来提升网站页面性能)。Facebook使用全局缓存并且通过多台服务器对缓存进行分布(参考“Scaling memcached at Facebook”),这就允许他们通过配置用户文件数据来获得更好的性能和吞吐量,并且还可以有一个中心位置更新数据(这是非常重要的,当运行成千上万台服务器时,缓存实效和一致性维护都是非常大的挑战)。

下面让我们谈谈当数据不在缓存中时,我们该做什么……

代理

简单点讲,代理服务器就是硬件/软件的中间件,接受客户端请求并且将他们转发到后端的源服务器上。通常,代理服务器用于过滤请求、记录请求日志或有时转换请求(通过添加/删除头结点、加密/解密或压缩)。

图13 代理服务器

代理可以优化请求,并且从整个系统的角度来优化请求通信量。一方面,使用代理可以加速数据访问,可以把相同(或相似的)请求重叠压缩成一个请求,然后返回单个结果到请求客户端,这就是压缩转发(collapsed forwarding)。

在一个局域网代理中,例如,客户端无需使用它们自己的IP去连接互联网,对于相同的内容,局域网将压缩来自客户端的请求。它很容易造成混淆,因为很多代理同样也是缓存(它是一个非常合乎逻辑放置缓存的地方),但并非所有缓存都扮演代理这一角色。

图14 使用一个代理服务器压缩请求

使用代理服务器的另一伟大方式是通过压缩请求对空间数据进行加密。采用这种策略最大化数据本地化的请求会导致减少请求的延迟。例如这里有一大串的节点请求B:partB1、partB2等等。我们可以设置代理来识别个人空间的位置请求,把它们压缩成单一的请求并只返回bigB,大大减少了从数据源处读取数据次数(如图15所示)。在高负载的情况下,代理也特别有用,或者当你具有有限的缓存时,它们基本上可以把多个请求批处理成一个。

图15 使用代理对空间数据请求进行压缩

如果你正在为系统寻找代理,这里提供几个供你选择:SquidVarnish,它们都做过非常全面的测试并且被广泛用在许多大型网站上。这些代理解决方案对客户端——服务器端通信提供了许多优化方案。在Web服务器层作为反向代理安装可以大大提高Web服务性能,减少处理传入客户机请求所需的工作量。

索引

使用索引来快速访问和优化数据是一个众所周知的策略,最有名的莫过于数据库索引。

图16 索引

一个索引就是数据库表的目录,表中数据和相应的存储位置的列表。好比是一篇文章的目录,可以加快数据表的。例如让我们来查找一块数据,B中的第二部分——如何发现它的位置?如果你通过数据类型存储了一个索引——例如数据A、B、C——它将告诉你数据B的原始位置。然后你只需去查看B并且根据需要阅读B的数据即可(参考图16)。

这些索引通常存储在内存或者是传入客户端请求的本地中。Berkeley DBs(BDBs)和树数据结构常常被用在有序列表中存储数据,这是访问索引的理想选择。

通常,会把许多层索引作为一个映射,从一个位置移到下一个,以此类推,直到你得到想要的特定块数据(参照图17)。

图17 多层索引

索引也可以对相同的数据创建多个不同的视图。对大型数据集来说,这种方法是非常好的,无需创建多个额外的数据副本就可以定义不同的过滤和排序,

例如,早期的图像托管系统实际上是托管图像书本内容,允许客户端查询这些图像中的内容,输入一个主题,就可以把所有相关的内容搜索出来。此外,采用同样的方式,搜索引擎还允许你搜索出HTML内容。在这种情况下,需要很多的服务器来存储这些文件,查找其中一个页面可能会很麻烦。首先,反向索引查询任意个单词或字元祖都需要可以轻松地访问;再有就是导航到正确的页面和位置,检索到正确的图像结果也是项挑战。因此,在这种情况下,反向索引会映射到一个位置(例如书B),然后书B可能会有一个包含所有内容、位置和各个部分出现次数的索引。

这种中间级索引只包含了Words、位置和书B的信息。与所有的信息不得不存储到一个大的反向索引中相比,这种嵌套的索引架构允许每个索引占用较少的空间。在大型系统中,这是非常关键的,即使采用压缩,这些索引也需要占用相当昂贵的存储空间。

例如,让我们假设这个世界上有——100,000,000本书(参考Inside Google Books官方博客)——每本书只有10页,每页只有250个单词,这也就意味着有2500亿个单词。如果每个单词只有5个字节,每个字节占用8 bits(或1个byte,甚至有些字符占用2 bytes),所以5 bytes/单词,那么一个索引所包含的单词就有可能超过一个TB的存储。此外,索引还有可能包含其他信息,例如元祖单词、数据位置等。

能够快速、轻松地找到数据是非常重要的,而使用索引就可以简单高效的实现。

负载均衡器

分布式系统的另一个关键部分是负载均衡。负载均衡器几乎是每个架构的主要组成部分,他们的角色是负责把网络请求分散到一个服务器集群中的可用服务器上去,通过管理进入的Web数据流量和增加有效的网络带宽,从而使网络访问者获得尽可能最佳的联网体验的硬件设备。

图18 负载均衡器

这里有许多种算法可用于为请求提供服务,包括随机选择一个节点、循环或者甚至是基于某个特定的标准来选择节点,例如内存或CPU利用率。负载均衡器即可以以硬件的方式表现出来,也可以以软件的方式。HAProxy是一个开源的负载均衡器,并且得到了非常广泛的使用。

在一个分布式系统中,负载均衡器通常处于系统的前端位置,所有传入的请求会相应地被路由。在一个复杂的分布式系统中,一个请求被路由到多个负载均衡器上并不常见,如图19所示:

图19 多个负载平衡器

和代理一样,有些负载均衡器也可以基于请求的类型路由到不同的服务器集群上。(技术上来讲,这也被称为反向代理。)

负载均衡器所面临的挑战之一是管理用户特有的会话(user-session-specific)数据。在一个电子商务网站上,当你只有一个客户端时,是很容易让用户把商品放入购物车并且继续访问(这是非常重要的,因为商品很有可能在继续出售,而用户退出时,商品仍然留在购物车里)。然而,如果用户本次会话路由了一个节点,那么当他下次访问的时候会路由一个不同的节点,这样,就很有可能使购物车里的商品不一致,因为新的节点有可能会丢失该用户购物车里原先的商品(当你先放6包Mountain Dew 在购物车里,等到再次登录后发现购物车为空了)。解决这个问题的方法之一是使用sticky sessions,来使用户一直被路由到相同的节点,但它很难利用到可靠性功能,像自动故障转移(automatic failover)。这种情况下,用户的购物车里将会一直有商品,但如果sticky node变的不可用,这就需要特殊情况来处理并且假设购物车里的商品将不再有效(尽管希望这种假设不会被内置于应用程序里)。当然解决这个问题还有许多其他方法,例如本文提到的服务以及不包括(浏览器缓存、cookies和URL重写)。

在一个大型系统里会有各种不同类型的调度和负载均衡算法,包括简单点的像随机选择或循环以及更复杂的机制,例如利用率和容量。所有的这些算法都可以分布流量和请求,并且提供有用的可靠性工具,像自动故障转移或者自动清除一个坏的节点(例如当它无法响应时)。然而,这种高级功能会把问题诊断的复杂。 例如,当遇到高负载情况时,负载均衡器将会移除变慢或超时的节点(因为请求太多,删除节点后会把请求分配到其他节点上),这无疑会加剧其他节点的工作量,即负载加重。这种情况下,大量的监测变的非常重要,因为整个系统流量和吞吐量看起来可能会减少(因为节点服务更少的请求),但可能会累坏个别节点(处理更多的请求)。

负载均衡器也是扩展系统容量的一种简单方式,像文中提到的其他技术,在分布式系统架构中发挥着非常重要的作用。负载均衡器也提供一些重要功能来测试节点的健康状况,例如,如果该节点响应迟钝或过载,它可能就会被删除,然后利用系统中不同的节点冗余。

队列

到目前为止,我们已经讨论了许多方法来加快数据读取速度,但扩展数据层的另一个重要组成部分是如何高效的写入数据。在简单的系统中,进程负载等都比较少,并且数据库比较小,毋庸置疑,写的速度肯定不会慢。然而,在大型复杂的系统里,这个速度就很难把握了,可能会花费很长的时间。例如,数据有可能要写到几个不同的地方,不同的服务器或索引、或者系统正处于高负载情况下。在这种情况,该在哪里进行写?或者其他任何任务都有可能花费很长时间,要想在系统实现性能和可用性需要构建异步。处理这种异步的一种常见的方式就是采用队列。

图20 同步请求

想象在一个系统里,每个客户机都要把请求发送至远程服务器,那么服务器应该尽可能快的接收并完成任务,然后把结果返回到相应的客户端。在小型系统中,一台服务器(或逻辑服务器)传入客户端数据会与客户端发出时一样快,这样就比较完美了。然而,当服务器接收到的请求多余它的处理能力时,那么每个客户端必须排队等待服务器处理其他客户端请求,直到轮到你了,服务器才会处理你的请求,直到最终完成。这就是一个同步请求的例子,如图20所示。

这种同步行为会严重降低客户端性能,客户端被迫等待,而通过添加额外的服务器来满足负载并不能解决问题,即使采用最有效的负载均衡也很难保证分配公平,在大客户端下。进一步讲,如果处理请求的服务器不可用或者瘫痪,那么客户端上游也将失败。有效的解决这个问题需要抽象客户端请求以及服务请求的实际工作。

图21 使用队列来管理请求

队列就像听起来那样简单,一个任务进来,就添加到队列里去,然后the workers挑选有能力处理的下一个任务。(参考图21)这些任务有可能仅是简单的写入,也有可能是复杂的,如把文档生成图像预览。当一个客户端把任务请求提交的队列中时,他们不需要被迫等待结果,相反,他们只需确认请求是否被正确接收。

队列使客户端能够以异步的方式工作,对客户端请求和响应提供战略抽象。另一方面,在一个同步系统中,请求和回应是没有分化的,因此他们不能被分开管理。在异步系统中,客户端发出请求任务,服务器对收到的消息进行响应并确认任务被接收,然后客户端可以定期检查任务状态,一旦任务完成,即可看到结果。当客户端在等待异步请求是否完成时,它还可以自由执行其他任务,甚至是向其他服务器发出异步请求。下面要介绍的是消息和队列在分布式系统中的杠杆作用。

队列也对服务中断或失败提供一种保护机制。例如,它很容易创建一个高度健壮的队列,当服务器瞬间失败时,该队列可以把刚刚失败的请求重新发送至服务器。相比直接暴露客户端来间断服务供应,使用队列来保证服务质量更可取,要求必须有复杂且矛盾性的客户端差错处理。

队列是管理分布式通信与任何大规模分布式系统中各个部分之间的基础,并且有许多实现方式。这里有许多开源的队列,如RabbitMQActiveMQBeanstalkD,但也有一些当做服务使用,如Zookeeper,甚至是用来数据存储,像Redis

总结

设计出一个高效的分布式系统是令人兴奋的事情,尤其是拥有快速的数据访问速度。本文只是讨论了几个实际的例子,希望对你能有所帮助。

分布式应用程序体系结构的选择

 如果您正在设计一个基于 NET 的分布式应用程序则需要考虑本文中讨论的所有问题并对系统体系结构的应有结果得出一些结论一般来讲这比您想像的要容易些但总有一些特殊的情况需要其他的方法以下是您可以进行的某些一般假设可为您简化情况

  首先在默认情况下使用 ASPNET Web 服务它们的执行和使用都很简单可以为客户端平台提供尽可能宽的使用范围而且可以从默认安全性策略下沙箱中运行的代码中调用 ASPNET Web 服务客户端代理代码

  如果要使用较传统的带有 CLR 类型保真度的分布式对象模型不需要与其他平台进行互操作而且由您控制客户端和服务器的配置请考虑使用 NET Remoting如果您选择 NET Remoting最好使 HTTP 通道信道与 IIS 和 ASPNET 集成否则必须建立自己的进程生命周期管理和安全性基础结构假定 NET Remoting 需要 NET 客户端使用二进制格式化程序而不是 SOAP 格式化程序是很有意义的互操作性将不成问题而且性能将显著提高

  最后如果需要公布的事务请使用企业服务 (COM+)如果您执行 ServicedComponents则出于性能方面的考虑默认情况下它们将部署在库应用程序中如果它们需要在远程计算机上运行则将它们部署在服务器应用程序中(如果您需要执行不同的进程安全性令牌 [而不是 aspnet_wpexe 使用的令牌] 的代码即使在相同的计算机上可能也要考虑使用 COM+ 服务器应用程序)

  以下是三个基于这些理念的公共体系结构

    
        图 一个简单的 层体系结构

  图 显示了一个简单的 层体系结构它带有 Web 服务器领域支持一系列不同的客户端所有服务器端的代码都运行在 ASPNET 辅助进程 aspnet_wpexe 中三种不同类型的客户端使用 HTTP 访问服务器领域基于浏览器的客户端调用 ASPNET Web 页面胖客户端(例如Windows 窗体应用程序Microsoft? Visual Basic? 应用程序)和其它 Web 服务使用 ASPNET Web 服务而NET 胖客户端(例如Windows 窗体应用程序)和 Web 服务则像期望的一样通过 HTTP 信道和二进制格式化程序(假设它不在沙箱中)使用 ASPNET Web 服务或 NET Remoting      

      图 一个使用 ASPNET 的 n 层体系结构
  
  一些非常大的应用程序使用一套辅助计算机从 Web 服务器的外层分担工作这一体系结构如图 所示注意在这种情况下第二层也通过 ASPNET 提供功能     

     图 使用企业服务 (COM+) 的 n 层体系结构

  图 显示此体系结构的另一种版本其第二层使用在 COM+ 中部署的 ServicedComponents 提供功能

  显然这些并不是 NET 框架所支持的所有可能的体系结构但是它为您设计自己的系统提供了适当的基础

分布式系统

  分布式系统设计难点在于其组件的异构性开放性安全性可伸缩性故障处理以及组件的并发性和透明性分布式用两种完全不同方式进行协同和合作包括基于实例的协作基于服务的协作

  分布式系统设计基于实例的协作常使用被称为代理的方式某个对象实例调用远程对象时可以只和代理打交道由代理完成和远程对象实例的通信工作创建远程对象提交请求得到结果然后把结果提交给调用的对象实例这种环境叫近连接

  分布式系统设计基于服务的协作常采用层次式架构只提供远程的对象接口用户可以调用这些方法却无法远程创建和销毁远程对象实例这样就减少了交互简化了编程

  基于WEB分布式系统设计传统的B/S架构存在的不足难以进行大规模的实物处理以及大量的实时的用户交互

  分布式对象技术是指采用面向对象技术开发的两个或多个软件互相共享信息主要解决如何在分布系统中集成各组件的问题三大技术分为CORBACOM/DCOMEJB

面向商务智能应用的分布式数据挖掘系统设计

商务智能(business intelligence,BI)是1989年由Gartner Group的Howard Dresner提出,但目前对商务智能还没有统一的定义。一般地,商务智能被认为是将存储于各种商业信息系统中的数据通过智能手段转换成有用信息以帮助企业提高决策能力解决商业问题的概念、方法和技术的集合。商务智能引起了国内外企业界和软件开发界的广泛关注,并成为当前一个热点研究问题。作为商务智能的重要组成部分之一的数据挖掘(dataining,DM)的研究提高到了一个新的高度,在分布式商务智能环境下,采取合适的数据挖掘系统模型和数据挖掘算法尤为重要。

  Agent技术是人工智能的新兴研究课题,是有效解决复杂分布式问题的计算模式之一。基于Agent技术的应用系统不仅具有一般分布式系统所具有易于扩张、灵活性强等特点,而且系统具有很强的智能性和组织能力。本文在分析商务智能分布式环境基础上,介绍了面向商务智能的分布式数据挖掘系统应具有的基本特点,提出了一种面向商务智能应用基于Agent技术的分布式数据挖掘系统,并讨论了系统各组成部分功能特点。

  1 商务智能的分布式环境

  商务智能的发展,先后经历了事务处理系统、高级管理员信息系统、管理信息系统、决策支持系统和专家系统等阶段,最终演变成今天的商务智能。商务智能系统,与这些信息系统相比,主要区别之一是用户不再仅仅局限于企业的领导和决策分析人员,而是扩展到企业组织内外各类人员,即商务智能系统是面向多层次各类用户的应用需要。这些用户往往分布在不同部门或地区,从而使商务智能系统面向分布式应用环境。

  同时,商务智能面向分布式数据环境。商务智能有效地集成企业内外部各种商业数据,并转换成易于理解的商业知识,帮助企业内外部用户进行科学决策,更好地实现商业目的。企业内部数据是指通过企业各种业务信息系统收集到的数据。这些数据可能分布在不同的硬件、数据库、网络环境中,为不同的业务部门服务。外部数据主要是市场信息和外部竞争对手信息,这些数据可以通过网络或市场调研等手段获得。因此,商务智能将肯定面向分布式的应用环境和数据环境。

  2 面向商务智能应用的数据挖掘系统特点

  为了充分利用企业内外流动的大量商业数据,企业商业智能系统必须采用数据挖掘技术实现商务知识的发现。数据挖掘是从大量数据中挖掘出隐含的、未知的、用户可能感兴趣的和对决策有价值的知识和规则。传统的商务智能数据挖掘是采用一种集中式思想,即要求将这些分布存储的数据收集到一个集中的地方,然后才进行知识发现、管理和决策,这样的商务智能要求企业有高速的数据通信网络。商务智能往往需要用户交互以获取参数信息,这无疑增加了集中式商务智能系统的负荷。同时,这种方式也破坏了数据的私有性和安全性。因此,数据的分布式存储、数据的私有性和安全性、用户频繁的信息交互和商务智能的及时性要求等迫切需要深入研究分布式环境下的分布式数据挖掘技术。

  分布式数据挖掘(distributed data mining,DDM)主要涉及到分布式数据挖掘系统模型和分布式数据挖掘算法。一个分布式数据挖掘系统是一个复杂的实体,整个系统必须提供有效的访问分布式数据和计算资源、监控整个挖掘过程和以一定格式将挖掘结果呈现给用户的功能。而且,一个成功的DDM系统应该具有灵活的结构,提供一个简单的更新其组件的方式以适应变化的环境。由此可见,面向商务智能的分布式数据挖掘系统模型应该具有以下特点:①采用模块化设计,保证系统中不同模块可以根据需要进行灵活地增减和配置以及分布式数据挖掘系统的持续可用;②实现分布式移动数据挖掘,满足商务智能系统中多层次用户的多种数据挖掘需要,保证商业数据安全;③采用商务本体知识模型和通用数据描述格式实现各个站点上的分布式数据挖掘以及数据挖掘系统与其他系统的信息交互;④集成多种安全保障技术,满足业务系统安全以及分布式数据挖掘系统自身安全需要。

  3 基于Agent技术的分布式数据挖掘系统

  为了满足分布式数据挖掘的需要,三层客户/服务器结构被应用到系统设计中,如Kensington系统和Intelliminer系统。然而,这些系统采用的体系结构本质上仍没有改变集中式数据挖掘系统的模式,系统缺乏开放性、自主性和智能性。为了提高系统的智能性和开放性,融合不同的数据挖掘技术,许多学者将数据挖掘过程进行功能抽象,并分别由不同的Agent来完成。对于大量分散数据的数据挖掘,更多系统采用基于Agent的分布式结构模型,其中典型的应用系统如JAM系统和BODHI系统。本文充分利用移动Agent的移动性并以Agent为主要组件构造满足商务智能需要的分布式数据挖掘系统。同时,系统中Agent按照FIPA标准设计,这样只要增加消息转换和服务描述注册转换器等部件就可以实现Agent与web services的集成,从而使得系统支持web功能。因此,整个系统具有更大的灵活性、智能性和开放性。

  3.1 分布式数据挖掘系统整体结构

  如图1所示,整个商务智能系统可以整合为4个部分:用户群、用户接口、数据挖掘系统和企业数据库系统。用户通过用户接口提交数据挖掘请求,数据挖掘系统规划数据挖掘任务并创建装配数据挖掘Agent。依据要求,数据挖掘Agent迁移到企业业务数据库系统中进行数据挖掘,并提交结果给协调Agent进行综合且按照用户要求返回结果。

  1)用户接口 是用户与数据挖掘系统互操作的渠道,完成用户参数的输入和数据挖掘结果的显示。例如,用户可以选择挖掘模型(挖掘算法)、数据源、挖掘的预设流程等;用户可以完成系统中Agent的知识和规则的更新,弥补系统知识的不足;用户还可以选择最终的挖掘结果的可视化形式等。

  2)数据挖掘系统 是基于Agent的分布式数挖掘系统的工作流程大致为:①用户Agent将用户通过用户接口提交的数据挖掘任务转变成协调Agent可接受的挖掘任务;②协调Agent综合环境信息规划数据挖掘任务,创建多个装载合适挖掘模型的移动数据挖掘Agent,并将这些移动Agent打包和序列化为数据流进行数据的网络移动。

  3)企业数据库系统主要由移动Agent服务器、业务子系统和数据库等3部分组成。这里,移动Agent服务器作为windows服务而在系统启动时自动启动。

  3.2 数据挖掘系统

  如图1所示,数据挖掘系统由协调Agent、数据挖掘Agent、数据挖掘Agent管理器、用户Agent、挖掘算法Agent和数据站点管理Agent组成。其中,数据挖掘Agent是移动Agent,其他功能Agent

  为静态Agent。这些Agem协调一致地工作,共同完成数据挖掘任务。

  1)用户Agent代表用户向数据挖掘系统提出数据挖掘请求。只需要用户提出相应的要求或者做一系列的选择,用户Agent就可以将用户要求转化为协调Agent能够识别的命令并提交给协调Agent进行任务的计划分配;用户Agent还负责处理通过用户接口输入的系统更新信息等,如数据挖掘特定算法参数、Agent知识和规则等。该Agent除了处理用户输入的信息外,还需要能够保存数据挖掘结果等输出信息以待用户查询或直接提交这些信息给用户拥有的用户接口。

  2)协调Agent主要完成下列3项任务:①任务规划优化,主要完成数据挖掘任务的规划,并选择最优的规划方案。与数据挖掘算法Agent、数据挖掘Agent管理器和数据站点管理Agent交互,得到数据挖掘算法效率功能特点、系统中挖掘Agent的功能状态等信息来确定相应的数据挖掘方案以尽可能满足用户需要。该Agent还具有实时规划能力以满足因某个移动Agent失效而进行任务的重新规划。②依据规划结果,协调Agent创建并命名多个并行协同工作的装载有挖掘模型的移动数据挖掘Agent,并将这些移动Agent的基本信息注册到挖掘Agent管理器中。③数据挖掘整个过程的协作协调,主要协调数据挖掘过程的各个Agent以及维护系统当前的运行状态信息等。同时,协调Agent也是系统中Agent信息交换中心,负责维护Agent之间信息的交互传递等。

  3)数据挖掘Agent是移动Agent,由协调Agent创建,并移动到相应的数据库系统主机上进行数据挖掘,并将自身的,其他功能Agent

  为静态Agent。这些Agem协调一致地工作,共同完成数据挖掘任务。

  1)用户Agent代表用户向数据挖掘系统提出数据挖掘请求。只需要用户提出相应的要求或者做一系列的选择,用户Agent就可以将用户要求转化为协调 Agent能够识别的命令并提交给协调Agent进行任务的计划分配;用户Agent还负责处理通过用户接口输入的系统更新信息等,如数据挖掘特定算法参 数、Agent知识和规则等。该Agent除了处理用户输入的信息外,还需要能够保存数据挖掘结果等输出信息以待用户查询或直接提交这些信息给用户拥有的 用户接口。

  2)协调Agent主要完成下列3项任务:①任务规划优化,主要完成数据挖掘任务的规划,并选择最优的规划方案。与数据挖掘算法Agent、数据挖掘 Agent管理器和数据站点管理Agent交互,得到数据挖掘算法效率功能特点、系统中挖掘Agent的功能状态等信息来确定相应的数据挖掘方案以尽可能 满足用户需要。该Agent还具有实时规划能力以满足因某个移动Agent失效而进行任务的重新规划。②依据规划结果,协调Agent创建并命名多个并行 协同工作的装载有挖掘模型的移动数据挖掘Agent,并将这些移动Agent的基本信息注册到挖掘Agent管理器中。③数据挖掘整个过程的协作协调,主 要协调数据挖掘过程的各个Agent以及维护系统当前的运行状态信息等。同时,协调Agent也是系统中Agent信息交换中心,负责维护Agent之间 信息的交互传递等。

  3)数据挖掘Agent是移动Agent,由协调Agent创建,并移动到相应的数据库系统主机上进行数据挖掘,并将自身的位置信息和状态信息传递给挖掘 Agent管理器,将数据挖掘结果反馈给协调器以进行数据结果的融合。数据挖掘Agent运用自身携带的算法模型或请求协调Agent得到的算法等完成具 体的数据挖掘任务。

  4)数据挖掘Agent管理器主要负责管理所有数据挖掘Agent的相关信息,这样各种Agent通过与管理Agent交互便可以动态获取其他Agent 的属性信息(位置、功能等),从而与其他Agent进行交互,以获取所需要的信息。是实现系统分布式透明性的关键,主要用于收集、管理、统计、查询各种 Agent信息资源,按其功能分类或建立Agent联盟。同时,也担当可信任的安全认证中心,保证各Agent之间的安全通信机制。

  5)数据挖掘算法Agent主要负责维护数据挖掘算法。用户可以注册数据挖掘算法。当算法注册到系统中,算法Agent登记算法的元知识信息及其特点(比 如,名字、版本、输入参数、操作环境描述和输出格式等)。同时,算法Agent将这些信息反馈给协调Agent以便协调Agent根据数据挖掘任务选择最 佳的算法。

  6)数据站点管理Agent 主要负责企业数据库系统Agent服务器的基本信息,如Agent服务器的启动、停止状态信息、位置信息以及数据源信息等。Agent服务器及时将其启 动、停止信息注册到数据站点管理器以便协调器合理规划任务。为了维护Agent服务器和防止Agent服务器出现故障,往往在企业提供同步工作的 Agent服务器。

  数据挖掘系统由6类功能各异协同工作的Agent组成,当接受到用户提交的具体数据挖掘任务后,系统自动有条不紊地工作。在整个系统中,数据挖掘Agent及执其执行环境——移动Agent服务器是数据挖掘任务执行的关键部件。

  3.3 数据挖掘Agent

  数据挖掘Agent是一种移动Agent,每个数据挖掘Agent都是一个相对独立的工作单元,利用其自带的数据挖掘算法和知识库信息等执行任务。数据挖 掘Agent封装了描述其状态的属性以便当Agent移动到另一主机上重新开始工作和向数据挖掘Agent管理器返同信息。

  1)接口模块主要将数据挖掘Agent的内部机制与外界隔离,所有信息交流都通过该接口。该模块包含与协调Agent和Agent服务器进行交互接口和消息的传递接口以及与数据源的JDBC联接接口。

  2)安全控制模块对移动Agent自身提供保护;防止外部环境对数据挖掘Agent的非法访问,以保证数据的正确性和合法性;完成对数据的加/解密、数字签名等任务。

  3)数据挖掘模块首先对业务数据挖掘数据进行预处理,然后依据数据挖掘模型来对已经规范化的数据进行分析,并将分析结果传给协调Agent以实现数据综合。

  4)路由策略确定移动数据挖掘Agent移动到企业数据库系统主机的有序列表。对于明确的任务数据挖掘过程,可采用静态路由策略,即数据挖掘系统中的协调Agent给定移动Agent的移动路径,或采用基于规则的动态路由策略以满足复杂数据挖掘任务的计算。

  数据挖掘Agent中的知识库,由协调Agent进行初始化,主要存放数据挖掘模型和其他一些必备的知识。身份信息用来标明该Agent的身份,包括该 Agent的ID号、Agent的主人、源站点、代码在源站点上的路径以及一些对该Agent权限进行限制的信息,这些信息均是加密的,而且在该 Agent的Java类设计中将这些属性设置为只读的。状态反映的是Agent执行状态信息、原始数据、中间数据以及数据挖掘结果。

  3.4 移动Agent服务器

  为了实现移动数据挖掘Agent在网络中安全可靠地访问不同的业务主机并在本地执行数据挖掘任务,系统中的业务主机必须提供Agent支撑环境为 Agent提供所需要的服务,包括Agent的传输、创建、激活、挂起、删除以及安全管理、通信和事件服务等。

  1)ATP模块提供Agent传输服务,即包括负责发送和接收移动Agent。本系统采用Java环境中的弱迁移方式,采用Agent传输协议 ATP(Agent transfer protocol),并在底层移动平台上采用socket技术。系统在确认符合接受方安全要求的情况下,进行Agent的传输,并在由Agent安全器进 行认证和解密后将其加入到系统Agent库中。在传输过程中,采用Java实现的IAIK SSL加密和认证TCP字节流,实现传输层上的数据加密。

  2)Agent管理器是移动Agent服务器的核心部件。其主要功能是:①为系统中的所有数据挖掘Agent提供生命周期服务(创建、激活、挂起和终 止),协调和监视各个数据挖掘Agent的运行,负责记录Agent的运行状态,进行系统中Agent的队列管理和持续性管理;②将Agent正常运行所 需要的各项服务正确地分配到相应的模块,并协调本系统中各个功能模块,为Agent提供一个完整而顺畅的运行环境,使得Agent安全可靠地完成各自的任 务。

  3)Agent安全管理器提供双向的安全管理机制,一方面保证Agent本身不被恶意Agent以及执行环境的攻击破坏,另一方面也保证主机不受恶意 Agent的攻击,其主要功能:①负责对接收的信息和Agent进行认证与解密,负责对发送的信息与Agent进行签名与加密,同时还负责Agent的权 限控制,其中使用存取控制权限列表ACL(access control list)来描述Agent对资源访问权限;②采用Java线程组和类装载器实现移动审计Agent自身的安全;③Agent访问等安全验证以及授权等。

  4)通信管理器负责同一主机上的Agent之间以及与其他主机上的Agent之间的通信和数据交换。对于服务器中的Agent之间的通信采用Proxy方 式,具体过程为:通过Agent管理器对被请求Agent处于运行状态的确定,通信管理器激活与被请求Agent具有相同接口的Proxy。该Proxy 访问Agent安全管理器,并在确定该请求Agent具有访问权限的情况下为请求Agent提供通信和数据交换服务。对于本地Agent与其他主机上的 Agent之间的通信采用Java提供的远程方法调用RMI(remote method invocation)机制来实现。同时,使用XML来实现Agent之间的数据交互。

  5)事件管理器负责管理发送给Agent或从Agent发出的注册和通知等事件,主要为网络上其他主机上的Agent与本系统中Agent之间的协作和事件的传送提供支持。

  6)接口模块除ATP模块外惟一与外部环境信息交流的接口。该模块包含与协调Agent和Agent服务器进行交互接口和消息的传递接口以及不同Agent服务器中Agent之间通过RMI交互接口。

  7)资源注册器负责系统中资源的注册服务。采用统一资源名字URN(universal resource name)来命名系统中的各种资源,如文件、打印机等。同时,资源注册器保存有本地数据站点的有关信息,如本地数据的形式、数据库管理系统、业务数据库表 结构、数据范围等。本系统中的资源访问控制分两级:

  ①系统级的资源访问控制采用Java提供的安全控制策略,即Security Manager;②应用级资源的访问控制采用基于Proxy的资源访问策略。

  4 结语

  面向商务智能应用基于Agent技术的分布式数据挖掘系统是从多Agent系统的角度来研究商务智能环境下的分布式数据挖掘任务的计算模型。本系统充分利 用移动Agent的移动性,大大降低了系统对网络带宽的依赖且提高了数据挖掘过程中数据安全性,具有良好的扩展性和自组织能力,对构建基于Agent技术 的商务智能数据挖掘系统具有一定的参考意义

高并发服务端分布式系统设计概要

又是快一年没写博客了,2013年也只剩尾巴,也不知道今年都忙了些什么。写这篇文章的目的,主要是把今年以来学习的一些东西积淀下来,同时作为之前文章《高性能分布式计算与存储系统设计概要》的补充与提升,然而本人水平非常有限,回头看之前写的文章也有许多不足,甚至是错误,希望同学们看到了错误多多见谅,更欢迎与我讨论并指正。

我大概是从2010年底起开始进入高并发、高性能服务器和分布式这一块领域的研究,到现在也差不多有三年,但其实很多东西仍然是一知半解,我所提到的许许多多概念,也许任何一个我都不能讲的很清楚,还需要继续钻研。但我们平时在工作和学习中,多半也只能从这种一知半解开始,慢慢琢磨,不断改进。

好了,下面开始说我们今天要设计的系统。

这个系统的目标很明确,针对千万级以上PV的网站,设计一套用于后台的高并发的分布式处理系统。这套系统包含业务逻辑的处理、各种计算、存储、日志、备份等方面内容,可用于类微博,SNS,广告推送,邮件等有大量线上并发请求的场景。

如何抗大流量高并发?(不要告诉我把服务器买的再好一点)说起来很简单,就是“分”,如何“分”,简单的说就是把不同的业务分拆到不同的服务器上去跑(垂直拆分),相同的业务压力分拆到不同的服务器去跑(水平拆分),并时刻不要忘记备份、扩展、意外处理等讨厌的问题。说起来都比较简单,但设计和实现起来,就会比较困难。以前我的文章,都是“从整到零”的方式来设计一个系统,这次咱们就反着顺序来。

那我们首先来看,我们的数据应该如何存储和取用。根据我们之前确定的“分”的方法,先确定以下2点:

(1)我们的分布式系统,按不同的业务,存储不同的数据;(2)同样的业务,同一个数据应存储多份,其中有的存储提供读写,而有的存储只提供读。

好,先解释下这2点。对于(1)应该容易理解,比如说,我这套系统用于微博(就假想我们做一个山寨的推特吧,给他个命名就叫“山推” 好了,以下都叫山推,Stwi),那么,“我关注的人”这一个业务的数据,肯定和“我发了的推文”这个业务的数据是分开存储的,那么我们现在把,每一个业务所负责的数据的存储,称为一个group。即以group的方式,来负责各个业务的数据的存储。接下来说(2),现在我们已经知道,数据按业务拆到group里面去存取,那么一个group里面又应该有哪些角色呢?自然的,应该有一台主要的机器,作为group的核心,我们称它为Group Master,是的,它就是这个group的主要代表。这个group的数据,在Group Master上应该都能找到,进行读写。另外,我们还需要一些辅助角色,我们称它们为Group Slaves,这些slave机器做啥工作呢?它们负责去Group Master处拿数据,并尽量保持和它同步,并提供读服务。请注意我的用词,“尽量”,稍后将会解释。现在我们已经有了一个group的基本轮廓:

一个group提供对外的接口(废话否则怎么存取数据),group的底层可以是实际的File System,甚至是HDFS。Group Master和Group Slave可以共享同一个File System(用于不能丢数据的强一致性系统),也可以分别指向不同的File System(用于弱一致性,允许停写服务和系统宕机时丢数据的系统),但总之应认为这个”File System”是无状态,有状态的是Group Master和各个Group Slave。

下面来说一个group如何工作,同步等核心问题。首先,一个group的Group Master和Group Slave
间应保持强一致性还是弱一致性(最终一致性)应取决于具体的业务需求,以我们的“山推”来说,Group Master和Group Slave并不要求保持强一致性,而弱一致性(最终一致性)即能满足要求,为什么?因为对于“山推”来讲,一个Group Master写了一个数据,而另一个Group Slave被读到一个“过期”(因为Group Master已经写,但此Group Slave还未更新此数据)的数据通常并不会带来大问题,比如,我在“山推”上发了一个推文,“关注我的人”并没有即时同步地看到我的最新推文,并没有太大影响,只要“稍后”它们能看到最新的数据即可,这就是所谓的最终一致性。但当Group Master挂掉时,写服务将中断一小段时间由其它Group Slave来顶替,稍后还要再讲这个问题。假如我们要做的系统不是山推,而是淘宝购物车,支付宝一类的,那么弱一致性(最终一致性)则很难满足要求,同时写服务挂掉也是不能忍受的,对于这样的系统,应保证“强一致性”,保证不能丢失任何数据。

接下来还是以我们的“山推“为例,看看一个group如何完成数据同步。假设,现在我有一个请求要写一个数据,由于只有Group Master能写,那么Group Master将接受这个写请求,并加入写的队列,然后Group Master将通知所有Group Slave来更新这个数据,之后这个数据才真正被写入File System。那么现在就有一个问题,是否应等所有Group Slave都更新了这个数据,才算写成功了呢?这里涉及一些NWR的概念,我们作一个取舍,即至少有一个Group Slave同步成功,才能返回写请求的成功。这是为什么呢?因为假如这时候Group Master突然挂掉了,那么我们至少可以找到一台Group Slave保持和Group Master完全同步的数据并顶替它继续工作,剩下的、其它的Group Slave将“异步”地更新这个新数据,很显然,假如现在有多个读请求过来并到达不同的Group Slave节点,它们很可能读到不一样的数据,但最终这些数据会一致,如前所述。我们做的这种取舍,叫“半同步”模式。那之前所说的强一致性系统应如何工作呢?很显然,必须得等所有Group Slave都同步完成才能返回写成功,这样Group Master挂了,没事,其它Group Slave顶上就行,不会丢失数据,但是付出的代价就是,等待同步的时间。假如我们的group是跨机房、跨地区分布的,那么等待所有Group Slave同步完成将是很大的性能挑战。所以综合考虑,除了对某些特别的系统,采用“最终一致性”和“半同步”工作的系统,是符合高并发线上应用需求的。而且,还有一个非常重要的原因,就是通常线上的请求都是读>>写,这也正是“最终一致性”符合的应用场景。

好,继续。刚才我们曾提到,如果Group Master宕机挂掉,至少可以找到一个和它保持同不的Group Slave来顶替它继续工作,其它的Group Slave则“尽量”保持和Group Master同步,如前文所述。那么这是如何做到的呢?这里涉及到“分布式选举”的概念,如Paxos协议,通过分布式选举,总能找到一个最接近Group Master的Group Slave,来顶替它,从而保证系统的可持续工作。当然,在此过程中,对于最终一致性系统,仍然会有一小段时间的写服务中断。现在继续假设,我们的“山推”已经有了一些规模,而负责“山推”推文的这个group也有了五台机器,并跨机房,跨地区分布,按照上述设计,无论哪个机房断电或机器故障,都不会影响这个group的正常工作,只是会有一些小的影响而已。

那么对于这个group,还剩2个问题,一是如何知道Group Master挂掉了呢?二是在图中我们已经看到Group Slave是可扩展的,那么新加入的Group Slave应如何去“偷”数据从而逐渐和其它节点同步呢?对于问题一,我们的方案是这样的,另外提供一个类似“心跳”的服务(由谁提供呢,后面我们将讲到的Global Master将派上用场),group内所有节点无论是Group Master还是Group Slave都不停地向这个“心跳”服务去申请一个证书,或认为是一把锁,并且这个锁是有时间的,会过期。“心跳”服务定期检查Group Master的锁和其有效性,一旦过期,如果Group Master工作正常,它将锁延期并继续工作,否则说明Group Master挂掉,由其它Group Slave竞争得到此锁(分布式选举),从而变成新的Group Master。对于问题二,则很简单,新加入的Group Slave不断地“偷”老数据,而新数据总由于Group Master通知其更新,最终与其它所有结点同步。(当然,“偷”数据所用的时间并不乐观,通常在小时级别)

 

上篇我们完成了在此分布式系统中,一个group的设计。那么接下来,我们设计系统的其他部分。如前文所述,我们的业务及其数据以group为单位,显然在此系统中将存在many many的groups(别告诉我你的网站总共有一个业务,像我们的“山推”,那业务是一堆一堆地),那么由谁来管理这些groups呢?由Web过来的请求,又将如何到达指定的group,并由该group处理它的请求呢?这就是我们要讨论的问题。

我们引入了一个新的角色——Global Master,顾名思义,它是管理全局的一个节点,它主要完成如下工作:(1)管理系统全局配置,发送全局控制信息;(2)监控各个group的工作状态,提供心跳服务,若发现宕机,通知该group发起分布式选举产生新的Group Master;(3)处理Client端首次到达的请求,找出负责处理该请求的group并将此group的信息(location)返回,则来自同一个前端请求源的该类业务请求自第二次起不需要再向Global Master查询group信息(缓存机制);(4)保持和Global Slave的强一致性同步,保持自身健康状态并向全局的“心跳”服务验证自身的状态。

现在我们结合图来逐条解释上述工作,显然,这个系统的完整轮廓已经初现。

首先要明确,不管我们的系统如何“分布式”,总之会有至少一个最主要的节点,术语可称为primary node,如图所示,我们的系统中,这个节点叫Global Master,也许读过GFS + Bigtable论文的同学知道,在GFS + Bigtable里,这样的节点叫Config Master,虽然名称不一样,但所做的事情却差不多。这个主要的Global Master可认为是系统状态健康的标志之一,只要它在正常工作,那么基本可以保证整个系统的状态是基本正常的(什么?group或其他结点会不正常不工作?前面已经说过,group内会通过“分布式选举”来保证自己组内的正常工作状态,不要告诉我group内所有机器都挂掉了,那个概率我想要忽略它),假如Global Master不正常了,挂掉了,怎么办?显然,图中的Global Slave就派上用场了,在我们设计的这个“山推”系统中,至少有一个Global Slave,和Global Master保持“强一致性”的完全同步,当然,如果有不止一个Global Slave,它们也都和Global Master保持强一致性完全同步,这样有个好处,假如Global Master挂掉,不用停写服务,不用进行分布式选举,更不会读服务,随便找一个Global Slave顶替Global Master工作即可。这就是强一致性最大的好处。那么有的同学就会问,为什么我们之前的group,不能这么搞,非要搞什么最终一致性,搞什么分布式选举(Paxos协议属于既难理解又难实现的坑爹一族)呢?我告诉你,还是压力,压力。我们的系统是面向日均千万级PV以上的网站(“山推”嘛,推特是亿级PV,我们千万级也不过分吧),但系统的压力主要在哪呢?细心的同学就会发现,系统的压力并不在Global Master,更不会在Global Slave,因为他们根本不提供数据的读写服务!是的,系统的压力正是在各个group,所以group的设计才是最关键的。同时,细心的同学也发现了,由于Global Master存放的是各个group的信息和状态,而不是用户存取的数据,所以它更新较少,也不能认为读>>写,这是不成立的,所以,Global Slave和Global Master保持强一致性完全同步,正是最好的选择。所以我们的系统,一台Global Master和一台Global Slave,暂时可以满足需求了。

好,我们继续。现在已经了解Global Master的大概用途,那么,一个来自Client端的请求,如何到达真正的业务group去呢?在这里,Global Master将提供“首次查询”服务,即,新请求首次请求指定的group时,通过Global Master获得相应的group的信息,以后,Client将使用该信息直接尝试访问对应的group并提交请求,如果group信息已过期或是不正确,group将拒绝处理该请求并让Client重新向Global Master请求新的group信息。显然,我们的系统要求Client端缓存group的信息,避免多次重复地向Global Master查询group信息。这里其实又挖了许多烂坑等着我们去跳,首先,这样的工作模式满足基本的Ddos攻击条件,这得通过其他安全性措施来解决,避免group总是收到不正确的Client请求而拒绝为其服务;其次,当出现大量“首次”访问时,Global Master尽管只提供查询group信息的读服务,仍有可能不堪重负而挂掉,所以,这里仍有很大的优化空间,比较容易想到的就是采用DNS负载均衡,因为Global Master和其Global Slave保持完全同步,所以DNS负载均衡可以有效地解决“首次”查询时Global Master的压力问题;再者,这个工作模式要求Client端缓存由Global Master查询得到的group的信息,万一Client不缓存怎么办?呵呵,不用担心,Client端的API也是由我们设计的,之后才面向Web前端。

之后要说的,就是图中的“Global Heartbeat”,这又是个什么东西呢?可认为这是一个管理Global Master和Global Slave的节点,Global Master和各个Global Slave都不停向Global Heartbeat竞争成为Global Master,如果Global Master正常工作,定期更新其状态并延期其获得的锁,否则由Global Slave替换之,原理和group内的“心跳”一样,但不同的是,此处Global Master和Global Slave是强一致性的完全同步,不需要分布式选举。有同学可能又要问了,假如Global Heartbeat挂掉了呢?我只能告诉你,这个很不常见,因为它没有任何压力,而且挂掉了必须人工干预才能修复。在GFS + Bigtable里,这个Global Heartbeat叫做Lock Service。

 

 

现在接着设计我们的“山推”系统。有了前面两篇的铺垫,我们的系统现在已经有了五脏六腑,剩下的工作就是要让其羽翼丰满。那么,是时候,放出我们的“山推”系统全貌了:

前面啰嗦了半天,也许不少同学看的不明不白,好了,现在开始看图说话环节:

(1)整个系统由N台机器组合而成,其中Global Master一台,Global Slave一台到多台,两者之间保持强一致性并完全同步,可由Global Slave随时顶替Global Master工作,它们被Global Heartbeat(一台)来管理,保证有一个Global Master正常工作;Global Heartbeat由于无压力,通常认为其不能挂掉,如果它挂掉了,则必须人工干预才能恢复正常;

(2)整个系统由多个groups合成,每一个group负责相应业务的数据的存取,它们是数据节点,是真正抗压力的地方,每一个group由一个Group Master和一个到多个Group Slave构成,Group Master作为该group的主节点,提供读和写,而Group Slave则只提供读服务且保证这些Group Slave节点中,至少有一个和Group Master保持完全同步,剩余的Group Slave和Group Master能够达到最终一致,它们之间以“半同步”模式工作保证最终一致性;

(3)每一个group的健康状态由Global Master来管理,Global Master向group发送管理信息,并保证有一个Group Master正常工作,若Group Master宕机,在该group内通过分布式选举产生新的Group Master顶替原来宕机的机器继续工作,但仍然有一小段时间需要中断写服务来切换新的Group Master;

(4)每一个group的底层是实际的存储系统,File system,它们是无状态的,即,由分布式选举产生的Group Master可以在原来的File system上继续工作;

(5)Client的上端可认为是Web请求,Client在“首次”进行数据读写时,向Global Master查询相应的group信息,并将其缓存,后续将直接与相应的group进行通信;为避免大量“首次”查询冲垮Global Master,在Client与Global Master之间增加DNS负载均衡,可由Global Slave分担部分查询工作;

(6)当Client已经拥有足够的group信息时,它将直接与group通信进行工作,从而真正的压力和流量由各个group分担,并处理完成需要的工作。

好了,现在我们的“山推”系统设计完成了,但是要将它编码实现,还有很远的路要走,细枝末节的问题也会暴露更多。如果该系统用于线上计算,如有大量的Map-Reduce运行于group中,系统将会更复杂,因为此时不光考虑的数据的存储同步问题,操作也需要同步。现在来检验下我们设计的“山推”系统,主要分布式指标:

一致性:如前文所述,Global机器强一致性,Group机器最终一致性;

可用性:Global机器保证了HA(高可用性),Group机器则不保证,但满足了分区容错性;

备份Replication:Global机器采用完全同步,Group机器则是半同步模式,都可以进行横向扩展;

故障恢复:如前文所述,Global机器完全同步,故障可不受中断由slave恢复工作,但Group机器采用分布式选举和最终一致性,故障时有较短时间的写服务需要中断并切换到slave机器,但读服务可不中断。

还有其他一些指标,这里就不再多说了。还有一些细节,需要提一下,比如之前的评论中有同学提到,group中master挂时,由slave去顶替,但这样一来该group内其他所有slave需要分担之前成这新master的这个slave的压力,有可能继续挂掉而造成雪崩。针对此种情况,可采用如下做法:即在一个group内,至少还存在一个真正做“备份”用途的slave,平时不抗压力,只同步数据,这样当出现上述情况时,可由该备份slave来顶替成为新master的那个slave,从而避免雪崩效应。不过这样一来,就有新的问题,由于备份slave平时不抗压力,加入抗压力后必然产生一定的数据迁移,数据迁移也是一个较麻烦的问题。常采用的分摊压力做法如一致性Hash算法(环状Hash),可将新结点加入对整个group的影响降到较小的程度。

另外,还有一个较为棘手的问题,就是系统的日志处理,主要是系统宕机后如何恢复之前的操作日志。比较常见的方法是对日志作快照(Snapshot)和回放点(checkpoint),并采用Copy-on-write方式定期将日志作snapshot存储,当发现宕机后,找出对应的回放点并恢复之后的snapshot,但此时仍可能有新的写操作到达,并产生不一致,这里主要依靠Copy-on-write来同步。

最后再说说图中的Client部分。显然这个模块就是面向Web的接口,后面连接我们的“山推”系统,它可以包含诸多业务逻辑,最重要的,是要缓存group的信息。在Client和Web之间,还可以有诸如Nginx之类的反向代理服务器存在,做进一步性能提升,这已经超出了本文的范畴,但我们必须明白的是,一个高并发高性能的网站,对性能的要求是从起点开始的,何为起点,即用户的浏览器。

现在,让我们来看看GFS的设计:

很明显,这么牛的系统我是设计不出来的,我们的“山推”,就是在学习GFS + Bigtable的主要思想。说到这,也必须提一句,可能我文章中,名词摆的有点多了,如NWR,分布式选举,Paxos包括Copy-on-write等,有兴趣的同学可自行google了解。因为说实在的,这些概念我也没法讲透彻,只是一知半解。另外,大家可参考一些分布式项目的设计,如Cassandra,包括淘宝的Oceanbase等,以加深理解。

Google搜索引擎的工作原理

PPCblog.com呈现给我们一幅由Jess Bachman(在WallStats.com工作)精心描绘的示意图,这张流程图展示了每天拥有3亿次点击量的Google搜索按钮背后搜索引擎在那不到1秒的响应时间内所进行的处理。

  这是我刚付印的最新示意图,这张流程图演示了在你点击Google搜索按钮后,在Google返回查询结果前那一眨眼的功夫里,Google是如何处理你的搜索请求的?这可是搜索巨人Google年赢利额高达200亿美元的杀手级应用,也是Internet首屈一指的商业和技术神话,大家肯定都想知道Google这棵摇钱树背后的秘密。

Google官方对其搜索技术的叙述
  我们搜索技术的后端软件会在服务器侧触发一系列执行时间不到1秒的并行计算,Google问世前的传统搜索引擎的搜索结果严重依赖于关键词在页面上出现的频度,我们使用了200多个指标信号(其中包括我们拥有专利的PageRank页面等级加权算法)用来检查万维网的链接结构(佩奇和布林最初的想法是把万维网的链接结构用图论的有向无环图来建模)并决定网页的重要程度,我们假定一个网页的重要程度取决于别的页面对它的引用,就像学术论文中的引用指数一样,重要的论文总是会被很多其他论文引用。然后我们再根据搜索条件进行超文本匹配分析(对bot抓取的页面内容进行关键词倒排索引检索)确定跟搜索请求最相关的网页。综合最重要的网页和跟搜索请求最相关的网页两个方面,我们就能按重要程度和用户搜索请求相关程度把查询结果排序后呈现给我们的用户。

数据中心:Google用来索引世界的塔
Google的数据中心高度机密,我们能了解到的不多:

1. 在美国本土有19个以上的数据中心,其余17个数据中心分布在美国以外的世界各地。

2. 每个数据中心有50万平方英尺那么大,建造一个数据中心要花费约6亿美元。

3. Google数据中心是世界上最高效的设施之一,而且也非常环保,几乎没有碳排放。

4. 数据中心使用50到100兆瓦的电力,由于需要冷却,通常建在便于用水的地方。

5. Google服务器安置在一个一组容得下1160台服务器的有房子那么大的标准集装箱容器中。

处理流程
1.你写博客、或在Twitter上推微博、更新站点等诸如此类往Web上添加内容的操作

2.Google bots程序(一种作为搜索引擎构件的智能代理程序)抓取你网页的title和description、keyword等内容

(1)Google爬虫沿着链接路径周游万维网,如果没有超文本路径到你的站点,你的站点将不会被索引

(2)如果你在robots.txt中设置不许索引,Google爬虫程序将不会抓取你的网页

(3)如果链接到你站点的超文本链接上有nofollow标签,Google爬虫将不会从这些链接路径周游到你的站点。

(4)Google也能通过blog软件或xml站点地图找到你的网站

(5)从PageRank越高的网站链接到你的网站的链接越多,你的网站的PageRank就越高。

(6)Google爬虫将周游所有未标注为nofollow的链接

3.一旦被Google爬虫访问到,网页几秒内就被索引了

(1)网页内容被存储在一个倒排索引中

① 网页标题和链接数据被保存在一个索引中,用于广度优先搜索

② 网页内容保存在另一个索引中,以用于检索频率不高的长尾、个性化、深度优先搜索

(2)当你用Google搜索时,你并没有在检索时时更新的万维网,而是在检索Google的缓存,Google定期更新其索引库,在Twitter实时搜索等的竞争下,Google的索引库更新周期趋短。

4.Google基于链接评估域名和网页的总体PageRank值。

5.检查网页以防止作弊行为

(1) Google的搜索质量和反垃圾信息审查和优化算法

(2) 1万多远程测试用户评价搜索结果的质量

(3) Google征请用户对有PageRank讹诈嫌疑的垃圾信息进行举报

(4) Google接到 (美国)数字千年版权法案的通知,要求Google从搜索结果中剔除涉嫌盗版的内容

6.在对页面做了损害分析后,现在每个页面都有很多用于辅助用户搜索的数据片(比如检索关键词)反向引用着它

7.用户发出搜索请求

(1)Google搜索质量工程师Patrick Riley:在大多数Google搜索中,你的搜索处于许多并行的控制过程或Google实验室的创新项目组过程中,可以说每一个查询请求都会参与一些Google的创意实验。

8.Google会用同义词匹配与你的搜索关键词语义相近的查询结果

9.生成初步的查询结果

(1)Google当然能返回成千上万数量无限的查询结果,但一般只显示不到1000条的查询结果,出于“少则得,多则惑”的考虑。

(2)对查询结果做本地化处理,本土站点在查询结果中优先出现

10.对查询结果集按权威性和PageRank进行排序,重复的查询结果被剔除。

(1) Google根据关键词、广告类型、用户所处位置找出相关的被竞价拍卖的关键词广告

(2) 关键词广告必须遵守当地法律条文

① 广告业主的非法广告将被取缔

② 如果关键词的搜索流量过低或关键词广告点击量偏低,则会被自动禁用

③ 出于商业策略,像亚马逊这样的客户会给予优惠折扣。

(3) 关键词相关广告按收益潜力(对关键词进行竞价拍卖后的广告质量不断进行评估)排序

(4) 对广告业主来说广告内容一般都是固定的,但有时使用动态关键词使关键词广告与搜索关键词相关度更高

① 一些广告本身允许增加易变的附属信息,比如网站链接、电话号码、产品链接、地址等

(5) 当广告拥有了相当高的点击率,则会显示在搜索结果列表的上方,以使其更显眼。

(6) 其余的广告依序显示在相应的位置

11.对查询结果进行过滤处理

(1) 对通常的查询(比如在Google首页上发出的搜索请求),Google会把相关的专题性垂直搜索结果(比如新闻、购物、视频、书籍、地图等)也加到返回的查询结果中

(2) 个性化方面:用户访问过的网站在查询结果列表中会更靠上

(3) 大量使用锚点的网站有可能被从查询结果中删除

(4) 搜索结果集的聚簇性:如果网页被其他高PageRank的网站引用,则网页的重要性会大大提高。

(5) 趋势分析:对搜索流量爆增或有大量新闻的搜索关键词,Google会在新的查询结果中增加额外的PageRank权值。(Google有反映关键词搜索流量的Google趋势专题页面)

(6) 同一个域名下的多个网页如果具有相同的PageRank会被归为一组。

12. 最终返回给浏览器端的用户一个人性化的、布局良好的、查询结果和广告泾渭分明的有机查询结果页面。

  所有这些步骤在总共不到1秒的响应时间内完成,每天3亿次的点击量给Google带来了超过200亿美元的年收入。

Google编程学院:分布式系统设计简介

本文内容

1.面向的读者和预备知识

2.基本概念

3.实现方式

4.远程过程调用

5.分布式设计原则

6.练习

7.参考资料

—————————————————————————————————————–

 

一、面向的读者和预备知识

本教程覆盖了分布式系统设计的基本概念。预备的知识包括一定的编程经验(C++,JAVA,etc)、网络知识的基本了解,以及数据结构和算法。

二、基本概念

什么是一个分布式系统?在其他相关定义未明朗之前这个概念很难定义。这里,给出一个“渐进”式的定义。

1.程序:你所写的代码

2.进程:运行中的程序

3.消息:进程间通信的媒介

4.包:消息在网络介质上传输的片段

5.协议:关于消息格式的正式化描述以及两个进程交换消息时需要遵循的规则

6.网络:连接计算机、工作站、终端、服务器等设备的基础设施。它包括由通信线路连接的路由器。

7.组件:可以是一个进程,也可以是用以提供进程运行、支持进程通信、存储数据的硬件设备

8.分布式系统

一个应用,该应用运行一组协议以协调网络中的多个进程,从而使所有组件共同完成一个或多个相关的任务。

那为什么我们需要建立一个分布式系统呢?这里有很多的好处,其中包括通过一种开放和可扩展的方式,连接远程用户以获得远程资源。当我们提到“开放”的时候,我们是指组件之间可以不断开放地交互。至于“可扩展”,我们是指系统可以容易地根据用户、资源和计算实体的数目变化而进行相应的改变。因此,我们可以看出,相对于把单机系统组合起来的方式,通过分布式组件的连接,分布式系统能获得更大的规模和更强的能力。要使分布式系统有实际用处,它必须是可靠的,然而这个目标并不容易实现,因为要同时运行的组件之间进行交互具有一定的复杂性。要使分布式系统真正地可靠,它必须拥有以下特性:

1.容错:系统能在组件出错时恢复并一直执行正确的指令。

2.高可用性:系统能对操作进行恢复,从而使得它在遇到组件出错的情况,也可以重新开始正在提供的服务。

3.自我恢复:错误修复以后,组件能自我重启并重新加入系统。

4.一致性保持:系统能协调多个组件的运作,而这些组件是并发运行的且随时会出现故障。这些问题都会使分布式系统出现“非分布式”的情况(译者注:即不同的组件不能保持一致性,各个组件好像单机的情况,维护不同的数据,即“非分布式”)

5.可扩展性:即使是系统的规模增大,它也能正确地运行。例如,我们可能想增大一个正在运行的系统的规模。对于一个不可扩展的系统,通常这些都会增加系统的损耗或使系统退化。类似地,对于一个可扩展的系统,我们想增加用户或服务器的数目,或系统的负载,这些变更不会因此明显的改变。

6.可预计的性能:系统能提供预期的响应时间。

7.安全:系统对数据和服务的访问提供认证。

以上是一些比较高的标准,实现具有一定的挑战性。其中最困难的可能是分布式系统必须在一些组件出现故障的情况下继续正常运行。在下面的一段对话摘录了Ken Arnold对这个问题的看法。他是Sun公司的研究者,同时参与建立了Jini的构建,另外,他也是CORBA架构团队的成员。

—————————————————————————————————————–

“在本地编程和分布式编程里,‘故障’的含义有所不同,在设计分布式系统的时候,必须考虑到故障的情形。设想一下,问一个人,如果某事件发生的可能性是10的13次方分之一,那它究竟多久发生一次呢?一般的回答是,从不发生。在人类的眼光看来,这是一个无限大的数。但如果你问一个物理学家,他可能会说,一直都在发生,在一立方尺的空气里,这种事情一直都在发生。所以,当你设计一个分布式系统的时候,你必须认为,‘故障’是一直发生的。所以‘故障’问题是你首要考虑的问题。然后,究竟故障问题的该怎么看待呢?一个典型的描述是“部分故障”。例如我发一个消息给你,然后网络出错,接着又两种可能的结果。一是你收到消息后网络出错了,我得不到响应。二是你并没有收到消息因为在这之前网络已经出错,这种情况下,我也得不到响应。

所以假如我没有收到一个响应,我怎么知道是哪一种结果呢?只有找到你我才能知道。这样需要修复网络或者你要出现,因为这也可能不是网络的问题而是因为你不在网络中。这些情况使得我们在设计的时候有什么不同呢?首先,它使得设计的复杂性增大了。我和你的交互越多,我需要对恢复交互的方法做更多的考虑。”

—————————————————————————————————————–

在分布式系统的设计中,故障(failure)处理是一个重要的主题。明显地,故障可以分为两种类型,软件故障和硬件故障。在80年代后期之前,硬件故障是一个主要考虑的因素,但从那以后,硬件的可靠性已经显著地提高。发热的减少、更微型的电路、更少的芯片外连接以及更高质量的生产技术都提高了硬件的可靠性。现在,问题主要出现在网络连接线路以及机械设备上,例如网络连接故障和驱动的故障。

而软件故障则是一个显著的问题。即使在严格的测试后,软件的bug(错误、漏洞)仍然带来一部分非预期宕机时间(约占总宕机时间25%-35%)。在一些成熟的系统里面,软件的bug可分为两种类型。

Heisenbug:这种bug在被察觉或研究的时候会消失或改变特性。一个典型的例子是,bug出现在程序的发行版编译模式(release mode),但在调试模式(debug mode)下并不出现。”Heisenbug”是根据”Heisenberg 不确定理论”命名的。该理论属于量子物理学的范畴,常被用来描述观察者的观察方式对于测量结果的影响。

Bohrbug:根据Bohr 原子模型命名。与Heisenbug相反,这种bug在观察研究的时候并不消失或改变特性。Bohrbug在一些定义好的条件下表现稳定。

相对于本地系统,Heisenbug更多地出现在分布式系统。其中一个原因是,程序员很难对并行进程的交互过程有全面和一致的认识。

下面,我们更具体地看一下,在分布式式系统里有那些类型的故障情况。

停机故障:即一个组件突然停止运行。除了设定超时,没有其他方式能检测这种故障。超时一般是指组件不能定期发送心跳消息或不能响应请求。例如,你的计算机死机或失去响应就是一种停机故障。

停止运行故障:与停机故障不同的是,这种故障发生的时候会通知其他组件。例如,一个网络文件服务器通知其客户端它将要停机,这就是一种停止运行故障。

丢失故障:发送或接收消息时,因为缓存不足导致消息被丢弃,而且发送方和接收方都不知情的故障。例如,路由器过载就会出现这种故障。

网络连接故障:网络连接线路中断。

网络分割故障:网络分割成两个或多个互不连接的子网络,子网络内部能进行消息通信,子网络之间则不行。当网络连接线路中断的时候会发生这种故障。

时序故障:系统的时序特性并扰乱。例如,不同计算机上的时钟不同步或消息延时超过时限。

Byzantine故障:恶意程序或网络错误等引起的数据污染、丢失或其他不合规范的行为,从而导致的错误。(译者注:这里取名于Byzantine Generals’ Problem,拜占庭帝国军队的将军们必须全体一致的决定是否攻击某一支敌军。问题是这些将军在地理上是分隔开来的,并且将军中存在叛徒。因此如何达到一致性是一个严峻的问题。

我们的目标是设计一个分布式系统,具有之前提到的特性(容错、高可用性、可恢复性等),那样就意味着我们必须针对故障的情况进行设计。过程中,我们必须非常谨慎,不能对系统组件的可靠性抱有假设。然而几乎每一个人,在第一次建立分布式系统的时候都会有以下假设。因为这些假设已经被广泛的认识到,所以一般称之为“8大谬论(8 fallacies)。”

8大谬论如下:

1.延时为0.

2.无限带宽

3.网络是安全的。

4.网络拓扑不变。

5.传输消耗为0.

6.网络是同构的。

下面是以上一些名词的简单解释。

延时:初始化一个对数据的请求到数据真正开始传输的时间。

带宽:描述信道通信能力的参数。带宽越大,信道能承载更多的信息。

网络拓扑:网络建立的形式,例如环状、总线型、星型或网状。

同构网络:网络中运行单一的协议。

三、实现方式

在不可靠的通信网络之上建立一个可靠的系统似乎是不可能的。我们必须处理这些不确定性。一个进程知道自己的状态,也知道其他进程之前最近的状态。但进程间无法互相知道对方现时的状态。他们缺乏类似于共享内存这样的媒介。他们也缺乏一个准确的方法去检测错误,或辨别出一个故障是本地故障还是通信故障。

明显地,分布式系统设计是一项挑战性的工作。我们怎么能在不允许任何假设且有这么多复杂性的情况下完成这项工作呢?我们以一些限定规模的情况作为开始。我们接下来看看一个具体的分布式系统设计,它使用客户端-服务器(C/S)模型和标准的协议。这些标准的协议在低网络可靠性的情况为系统的运行提供了很大的帮助,这样会使我们的工作更简单。接下来我们看看C/S技术和这些协议。

在C/S架构的应用中,服务器提供诸如数据查询、发送现时股价等服务。客户端使用服务器提供的服务,为用户显示数据查询结果或提供股票购入建议。这两者通信的过程必须是可靠的。那意味着数据不能丢失而且数据必须按照服务器发送的顺序到达客户端。

在分布式系统里,有多种类型的服务器。例如文件服务器管理文件系统所在的磁盘存储单元。数据库服务器管理数据库以及使它们能被客户端访问。网络名字服务器维护一个进程,该进程实现符号名称或服务描述与IP地址及端口号的对应。

在分布式系统里,可能会有多个同一类型的服务器,例如多个文件服务器或网络名字服务器。这里使用“服务”一次表示同一类型的一组服务器。同时绑定也是个特别的概念。当一个进程需要访问一个服务时,这个进程就和提供该服务的服务器发生了绑定。有很多绑定的策略来定义怎样选择某一台特定的服务器进行绑定。例如,策略可以基于邻近性(一个UNIX NIS客户端会从本机器上开始寻找服务器);或者策略也可以基于负载平衡(一个CICS客户端的绑定则采用随机的方式)。

分布式系统可能会引入数据冗余存储,在这种情况下,服务必须维护多份数据的副本使得在数据在多个地方可以被访问,或者从而增加数据的可用性,即使某一服务器的进程崩溃了数据也能被访问。“缓存”则是一个相关概念,而且它广泛地被运用在分布式系统。如果一个进程维护一个数据的副本,使得它再次被使用时能被迅速地获取,那么,我们就说一个进程有缓存数据。同时,如果请求能从缓存中获取数据而不是原始的服务数据,我们就说缓存被命中了。例如,浏览器使用文档缓存来加速常用文档的访问。

缓存跟冗余存储比较相似。但缓存数据的实时性没那么好。所以,在使用一个缓存之前,我们可能需要一个策略来进行验证。如果一个缓存经常被原始的服务更新,那缓存就和冗余的副本一致。

正如刚才所提到的,服务器和客户端之间的通信需要可靠性。你很可能听说过TCP/IP。网际协议(IP)是一套运行于互联网和很多商业网络的通信协议。而传输控制协议(TCP)则是这其中一个核心协议。使用TCP协议,客户端和服务器能够互相创建连接,在这些连接的基础上能通过包交换数据。这个协议保证了通信的可靠性和双方之间数据传输的正确顺序。而IP协议体系则可以被看作一个分层的体系,每一个有各自的特点,而且它只使用下层的服务,只向上层提供服务。一个系统分层地实现了协议的行为,我们可以称之为协议栈。协议栈可以在软件或硬件实现,或者两者的混合。一般来说,较低的层在硬件上实现,较高的层在软件上实现。

————————————————————————————————————–

相关材料:TCP/IP的历史反映了互联网的发展。这里是对TCP/IP历史的简要概括。

—————————————————————————————————————

在网际协议的体系里,分为四层,分别是应用层、传输层、网络层、链路层。

应用层:这里主要是需要进行网络通信的应用程序。数据在程序里按照特定的格式发送到下一层,然后被封装到传输层。例如HTTP,FTP,Telnet就处于应用层。

传输层:传输层负责端到端的消息传输,传输过程独立于下层网络。同时,传输层也负责错误控制、数据分块和流控制。在传输层里,端到端的传输可分为两种类型:面向连接的传输(TCP)和无连接的传输(UDP)。TCP连接比UDP连接更为丰富,可以提供可靠的传输。首先,TCP确保接受方处于准备接收的状态。它使用三次握手机制保证通信双方都准备好进行通信。第二,TCP确保数据到达了发送的目标。如果接受方没有返回一个确认包,TCP就会自动地重新传输数据包(一般情况下会进行3次重传)。如果需要的话,TCP也可以把一个大的数据包分成小的包来使数据能在通信双方之间稳定地传输。此外,TCP协议可以自动地丢弃重复的包,以及在接收方按照正确的顺序对包进行重新排列。

UDP和TCP很相似,因为它也是负责数据包在网络里进行传输的协议。但这两者有很大的差别。首先,UDP是无连接的。这意味着一个程序可以向网络中的另一方发送数据包,但这两者并不建立连接关系。后者可能会向前者返回一些消息,而前者也可能继续发送数据包,但这个过程中没有一个固定的连接。第二,UDP并不保证接收方按照正确的顺序接受数据包。保证的只是数据包的内容。因为没有额外的包检错开销,所以UDP比TCP效率更高。因此,在线游戏往往使用这个协议。在游戏里,如果一个用于更新画面的包丢失了,玩家只会有一下子的不愉快。另一个包将很快地对画面进行更新,虽然这会使画面出现抖动,但影响不大。

 

虽然TCP比UDP更可靠,但在很多时候仍然面临着故障的问题。TCP使用确认和重传即时来检测和修复错误。但如果传输损耗很大,出现连接长时间断开的时候,会导致重传机制的失效。一般地,最大的连接失效时间规定在30-90秒之间。超出个时间,TCP会发出一个故障信号,然后放弃重传。虽然TCP已经提供了一些策略减轻这个问题,但这只是TCP故障中的其中一个例子。

网络层:在定义之初,网络层就负责实现数据包在单一网络中的传输。随着网络互连的需要,网络层承载了更多的功能,即负责使数据从一个网络送达另一个网络。这主要包括把一个数据包在互联网中路由。IP协议负责数据包传出的基本任务。

链路层:链路层处理数据在物理介质上的传输。通常也负责在数据包上加上帧头和帧尾使得数据能在物理网络和组件间进行传输。

—————————————————————————————————————–

相关材料:更多关于IP体系的内容,访问这里

—————————————————————————————————————–

四、远程过程调用(Remote Procedure Calls ,RPC)

以前,很多分布式系统的组件通信是直接在TCP/IP体系的基础上建立起来的。逐渐地,一种使得客户端和服务器有效地进行交互的方式被提出来了,称之为RPC,即远程过程调用。它扩展了原来的本地过程调用,使得被调用的过程不仅仅处于调用者的地址空间里。这两个进程可以处于同一个系统,或网络中的不同系统。

RPC类似于函数调用。跟函数调用相似的是,当进行PRC的时候,调用者需要把参数传递到远程过程,然后等待响应。在下面的例子里,客户端向服务器发出一个RPC请求,然后客户端等待直到消息返回或超时。而当请求到达服务器以后,服务器调用一个分配的程序执行请求的服务然后把返回消息发送到客户端。当RPC完成以后,客户端继续往下执行。

在基于RPC的分布式系统里,线程的使用非常普遍。在服务器端,对于每个到达的请求将产生一个新的线程去执行。而在客户端,当线程发出一个RPC后则进入阻塞状态。当接收到返回后,客户端的线程继续执行。

程序员编写基于RPC的代码时,应该完成三项工作。

1.规定客户端和服务器之间的通信协议。

2.开发客户端程序。

3.开发服务器程序。

通信协议是通过多个stub规定和建立的。stub是通过协议编译器(译者注:例如rpcgen就是一个协议编译器)产生的程序,它不具体完成工作,而是做了一个函数名称以及参数的声明。它仅包含使编译和链接能够进行的代码。

客户端和服务器的程序必须通过协议规定的过程和数据类型进行通信。服务端注册的过程可以被客户调用,然后返回必要的数据。客户端发送需要的参数,调用远程的过程,然后接受返回的数据。

所以,一个RPC程序使用stub产生器产生的类来执行RPC。而程序员需要提供服务器端的类,它包含了PRC的处理逻辑。

RPC也引入了一些在本地过程编程里面不会出现的问题。例如,当服务器不在运行的时候会出现绑定错误;客户端和服务器程序版本不同的时候会出现版本不匹配;当服务器崩溃,出现网络问题或客户端计算机出现问题的时候会产生超时。

一些PRC程序把某些错误看做是不能恢复的。然而,一个容错的系统,在主服务器和备份服务器上,为了应对重要的服务和故障,应该有多个可切换的服务源。

其中一项挑战性的错误处理工作发生在以下的情况。当服务器发生故障以后,客户端需要了解请求的结果以进行继续的工作。而在这种情况下,不正确的交互时有发生。例如,客户端向服务器发送一个Carnegie Hall管弦乐区域的订票请求。如果这座位是空的话,服务器记录这个请求和订票记录。可是请求却超时了。那那到底这个座位是空的且订票被记录下来了吗?即时有一个备份服务器使得请求可以重来,但这个客户会不会被记录为订了两张票呢?在Carnegie Hall里,这可是价格不菲的。

在下面的一些条件之下会发生错误,我们需要对这些情况进行处理:

1.网络数据丢失需要重传:一般地,系统最多只尝试重传一次。在最坏的情况下,如果进行了多次重传,我们需要尽力减少数据多次被接受造成的不良影响。

2.在执行PRC的时候,服务器崩溃了:如果服务器再它完成任务之前崩溃,系统一般能正确地恢复,因为客户端会重新初始化一个请求;如果服务器在完成任务以后发送RPC返回消息之前崩溃,客户端的重试会导致多个请求的处理。

3.客户端在接收响应之前崩溃了:客户端重启,且服务端丢弃响应数据。

五、分布式设计原则

根据本文上述提到的内容,我们可以定义分布式系统设计的一些基本原则。可能其中有些比较简单和明显,但刚开始做这方面的设计,列出来总是有好处的。

1.正如Ken Arnold所说,“在设计分布式系统的时候你必须考虑故障的情况。”避免对系统里任何部件的状态做假设。一个典型的场景是一个进程向另外一台机器上的进程发送数据。第一台机器的进程获得返回数据后进行处理,然后在不知道第二台机器是否就绪的情况下,把结果发给第二台机器。其实在这个过程中,很多情况可能发生,而发送的进程必须对可能的故障进行预测。

2.明确地定义故障的场景和它们发生的可能性。确保你的代码基本考虑到了一些较有可能发生的情况。

3.客户端和服务器都能够对无响应的发送方/接收方进行处理。

4.仔细考虑你需要在网络中传送多少数据。尽可能地减少网络传输。

5.延时是指初始化一个数据请求道数据真正开始传输之间的时间。最小化延时有时候会归根到一个问题:你是需要使用很多个小数据量的请求/响应还是一个大数据量的请求/响应。实验可以帮助你做出决定,做一些小的测试可以寻找出最好的折中方案。

6.不要假设数据经过传输后和他发送时保持一致(即使是机架上硬盘之间的传输)。校验和或有效性检验可以帮助检测数据是否有更改。

7.缓存和冗余存储是处理组件间状态问题的方法。我们尝试使组件拥有的状态最少,但这非常困难。状态是被存储于一个地方的数据,它代表另外一个地方的某个进程,它不能由其他部件重建。如果它能够重建的话,那它就是一个缓存。缓存有助于减轻维护部件间状态的风险。(译者注:这段想不明白状态究竟代表什么?有待高手解决。原文如下:Caches and replication strategies are methods for dealing with state across components. We try to minimize stateful components in distributed systems, but it’s challenging. State is something held in one place on behalf of a process that is in another place, something that cannot be reconstructed by any other component. If it can be reconstructed it’s a cache. Caches can be helpful in mitigating the risks of maintaining state across components. )但缓存不能保持更新,所以在使用之间需要一个策略去验证。

如果一个进程存储了一些不可重建的信息,问题就出现了。例如,这里会产生单点失效问题吗?要处理这个问题,可以使用冗余存储。冗余存储在减轻这类问题时非常有效。但仍然存在一些挑战,例如一致性问题。在怎样维护状态以及何时使用缓存和冗余的问题中,有很多需要权衡。但在这些场景中进行实验测试非常困难,因为每种机制的开销又有所不同。

8.注意速度和性能。认真考虑一下你的系统中那些部分对性能影响较大,哪里是性能瓶颈,为什么是性能瓶颈。采用一些测试来评价其他可能的方案,对这些方案进行测试和归纳。和你的同事讨论这些方案和结果,得出一个最佳的选择。

9.应答是相对耗费资源的,在分布式系统应尽量避免。

10.重传也是耗费资源的。你需要通过一些实验调节延时的范围来对重传的机制进行优化。

六、练习(略)

七、参考资料

[1] Birman, Kenneth. Reliable Distributed Systems: Technologies, Web Services and Applications. New York: Springer-Verlag, 2005.

[2] Interview with Ken Arnold

[3] The Eight Fallacies

[4] Wikipedia article on IP Suite

[5] Gray, J. and Reuter, A. Transaction Processing: Concepts and Techniques. San Mateo, CA: Morgan Kaufmann, 1993.

[6] Bohrbugs and Heisenbugs

分布式系统设计注意点

今天看了陈硕前辈的博客,对分布式系统设计提出的注意几点非常受益匪浅,做点读后笔记。

注意:

1.设计集群要方便重启,能自动连接,重启速度快

2.不要使用生命周期比进程多得ipc,比如共享内存

3.不要使用父子进程传递数据,不如pipe

4.DNS解析不要用,使用自己的名字服务,可用elang编写,使用paxos算法

5.建立维修通道,节点状态,服务进程等

6.分布式系统是以进程为基本单位的。

7.通信模型的编程风格,以消息为事件,采用事件驱动分发,reactor

8.考虑对方失去响应的情况

9.心跳包:包括进程的启动时间和负载,不要单独起心跳线程,而是在工作线程中启动

10.消息格式不要用C STRUCT或是bit fields,避免协议版本,建议使用google protocol buffers,apache thrift的消息打包格式

11.采用正则表达式在运行期间替换,避免重启时编译,部署

12.信号量不要用,读写锁基本不用。

13.建议用一个线程一个消息循环非阻塞模式

14.用心跳包实现重启

15.进程间通信用tcp

16 使用四元组标志唯一进程:ip(hostname):port:start_time_pid

分布式Web系统设计准则

大型分布式web系统设计中,主要需考虑一下几个准则

可用性:
系统是否可用,对于大多数互联网公司来讲,都是一个重要的衡量标准。特别对于大型的零售商业站点来讲,几分钟的当机,可能导致数百万元交易额的损失;大型门户若出现超半小时的当机,也大都将被登到第二天的IT新闻头版。
因此,保证系统的持续可用性和失败的可恢复性,不仅仅是技术所应遵循的基本准则,更是一个商业业务准则。
在分布式系统中,高可用性需要关注以下几点: 关键业务的冗余, 局部系统错误的快速恢复,以及出现问题发生时还可有损的支持服务。
以银行系统来说,存取款交易,必须保证冗余。某一套系统或数据发生错误时,能使用另一套备份系统快速替代;并争取自行恢复其错误系统;若实现大故障,导致交易无法进行,也需要支持有限服务,比如仅支持余额查询操作,而暂不支持取现交易。
性能:
性能是衡量网站的另一个重要标准。网站的打开速度,影响到用户的使用率,用户体验,甚至是搜索引擎排名。他与网站的收益与用户流失率也有重要关系。
因此,请优化您网站的相应时间,保证用户较低的等待时间.
可靠性:
系统应具有可靠性。当用户提交数据后,需要让用户感知到,系统已经正确的保存了他的数据,并能在需要的时候重新进行获取。
可伸缩性:
说到分布式系统,不得不提到的就是系统的可伸缩性。
系统的可伸缩性关心以下几个问题:目前系统还能承受多少流量?是否能够方便的增加更多的资源?增加资源后,又能多支撑多少流量?
可管理性:
设计一个方便管理的系统。方便管理等价于运维上的可扩展性:维护和更新。
可管理性主要关注的问题点在于:错误发生时,是否能方便地定位到问题所在?系统升级或修改是否方便?系统的常规维护是否方便?
成本:
成本是另一个考虑因素.它包含硬件、软件成本。
主要需考虑部署和维护成本、系统开发成本、 系统运行时需要的硬件资源成本、甚至是系统的使用,开发培训成本。
广义的成本可概括为系统所有者所需要付出的所有的钱。
以上准则仅从各方面做了一个罗列,但其个准则间可能是有冲突的。比如通过系统的可靠性,增加硬件设备后,将导致系统成本更高更难以管理。

实际设计过程中,需要根据业务类型,在各准则中进行重要度衡量,必要时可能需要牺牲某些准则。

谷歌Jeff Dean阐述分布式系统设计模式

导读:在6月举行的ACM SOCC 2010大会上,Google公司的Fellow、也是最杰出的云计算专家之一Jeff Dean做了主题演讲(PPT链接,视频链接,观看视频需要安装Silverlight或者Monolight),介绍了分布式系统的一些设计模式与未来的挑战。比利时Outerthought公司的Bruno Dumon参加了这次会议,并对Dean的演讲做了笔记,记录了演讲的要点。CSDN云计算频道特约专家吴朱华在博客”人云亦云”中翻译了这篇笔记,并加了少量评点。全文如下:

分布式系统设计模式

1. 系统失败是很平常的事情:每年有1-5%的硬盘会报废,服务器每年会平均宕机两次,报废几率在2-4%几率。

2. 将一个大而复杂系统切分为多个服务:而且服务之间依赖尽可能的少,这样有助于测试,部署和小团队独立开发。例子:一个google的搜索会依赖100多个服务。吴注:需要一套机制来确保服务的fault-tolerant,不能让一个服务的成败影响全局。

3. 需要有Protocol Description Language:比如protocol buffers。吴注:这样能降低通信方面的代码量。

4. 有能力在开发之前,根据系统的设计来预测性能:在最下面有一些重要的数字。

5.在设计系统方面,不要想做的很全面,而是需要抓住重点。

6. 为了增量做设计,但不为无限做设计。比如:要为5-50倍的增量做设计,但超过1000倍了,就需要重写和重新设计了。

7. 使用备份请求来降低延迟:比如一个处理需要涉及1000台机器,通过备份请求这个机制来避免这个处理被一台慢机器延误。吴注:这个机制非常适合MapReduce。

8. 使用范围来分布数据,而不是Hash:因为这样在语义上比较简单,并且容易控制。吴注:在大多数情况下语义比性能更重要,不要为了20%的情况hardcode。

9. 灵活的系统,根据需求来伸缩:并且当需求上来的时候,关闭部分特性,比如:关闭拼写检查。

10. 一个接口,多个实现。

11. 加入足够的观察和调式钩子(hook)。

12. 1000台服务器只需单一Master:通过Master节点来统一指挥全部的行动,但客户端和Master节点的交互很少,以免Master节点Crash,优点是,在语义上面非常清晰,但伸缩性不是非常强,一般最多只能支持上千个节点。

13. 在一台机器上运行多个单位的服务:当一台机器宕机时,能缩短相应的恢复时间,并且支持细粒度的负载均衡,比如在BigTable中,一个Tablet服务器会运行多个Tablet。

PPT中列出的重要数据(图)

未来的挑战

全球级(world-wide)系统的适应性方面:如何自动地分配和放置数据和计算来降低延迟和成本。
在弱一致性(weakly consistent)的存储上搭建应用:如何轻松使用抽象来解决多版本之间的冲突。
分布式系统的抽象:如何用同一个抽象来统一多个分布式系统。
【CSDN小百科】

JeffDean

Jeffrey Dean Google Fellow。2009年当选美国工程院院士。他是Google公司最具才华的工程师之一。众多Google的核心产品都有他的重大贡献,包括设计和实现了Google广告服务系统的最初版本,Google爬虫、索引和查询服务系统的五个版本,Adsense最初版本,Protocol Buffers,Google News的服务系统,MapReduce,BigTable,等等。当然,也包括搜索排序算法的许多方面,Google Translate和Google Code Search的部分开发。

1996年获得华盛顿大学计算机科学博士学位,课题是面向对象语言中的全程序优化技术。1990年毕业于明尼苏达大学,获得计算机科学和经济学学士学位。1999年加入Google之前,曾效力于DEC研究中心。本科毕业后曾经在世界卫生组织艾滋病项目工作,开发了艾滋病传染的统计建模、预测和分析软件。

他的一个人生目标是在所有大洲打篮球和橄榄球。

IE5,IE6,IE7多版本共存JavaScript调试工具Multiple IE

web标准很重要的一点也是最烦的一点,特别是要兼容各版本浏览器,随着微软IE7的推出,在网页设计时需要同时考虑兼容ie7前面的IE5/IE5.5/IE6版本,之前一直没有找到好的方法,最近终于找到IE版本合集软件包Multiple_IE。严重推荐给大家,大家升级到IE7后也可方便测试网页的兼容性。

过多的浏览器给 Web 开发造成了困难,颇有抱怨的味道。不过抱怨归抱怨,对于安装有 IE7 的电脑,除非卸载 IE7,否则只能靠虚拟机的方式来运行 IE 的其它版本。有了 Multiple IE 就可以在现有的系统上同时运行 IE 3 – IE7。这对网站设计者来说好处是不言而喻的。

Multiple IE 的安装非常简单,在装有 IE7 的计算机上,下载这个安装包,安装即可。目前已知的问题是 IE4 不能正常工作,不过我们开发时主要测试的 IE 版本是 5.5,6 和 7,IE 4 其实可以不考虑了。

Mutiple IE 确实是一个非常好的测试工具,强烈推荐的同时感谢

前一段听了MS忽悠,说IE7多么好用,多么安全,于是与时俱进,就装了一个,一看界面,是比IE6好看,但一打开正在开发的项目就发现不对,简直不堪入目,在网上一查说在IE7下开发的项目在IE6里也是一塌糊涂。
对于一般的用户来说,问题还不算太大,但对于我们做WEB开发的人员来说就麻烦大了,现在大部分的人都在使用IE6,而IE7也也在慢慢的成为一种趋势,所以现在的项目首先在兼容IE6,然后再在兼容IE7,可IE又是WINDOWS的核心程序,所以MS就不让IE的多个版本共存,这下项目调试就麻烦了,不可能一个人用两机器吧?这时就想到了一句老话:人有多大胆,地有多大产!在这种精神的鼓舞下,终于在网在找到了一个叫Multiple IEs的小软件,才10.3M,里面包括从IE3.0到IE6.0的所有版本,安装时可以选择,而且和IE7也没有冲突,对WEB开发人员来说这可是一个好工具,不敢独享,拿出来和大家分享一下。

官方网站地址:http://tredosoft.com/Multiple_IE
下载地址如下:http://tredosoft.com/files/multi-ie/multiple-ie-setup.exe

推荐免费好用的Web在线Office(Word)编辑控件

DSOFramer下载+开发接口

当前版本:V2.2.0.2 2006-11-29

DSOFramer2.2.0.8
dso接口说明

版本修改记录:
V2.2.0.2修改:
修改了HttpPost相对路径的一些问题。
V2.2.0.0增加:
[id(0x00010041), helpstring(“Get Rev Index”)]
HRESULT GetRevCount( [out,retval] long * pbool);

[id(0x00010042), helpstring(“Get Rev Index Info”)]
HRESULT GetRevInfo([in] long lIndex, [in] long lType, [out,retval] BSTR* pbool);

[id(0x00010043), helpstring(“Set Doc Prop”)]
HRESULT SetValue([in] BSTR strValue, [in] BSTR strName, [out,retval] long* pbool);

[id(0x00010044), helpstring(“Set Doc Variable”)]
HRESULT SetDocVariable([in] BSTR strVarName, [in] BSTR strValue,[in] long lOpt, [out,retval] long* pbool);

[id(0x00010045), helpstring(“Save page To Doc”)]
HRESULT SetPageAs([in] BSTR strLocalFile, [in] long lPageNum, [in] long lType,[out,retval] long* pbool);

———————————————————————————————————————————————————————-

LoadDso.js

var s = “”
s += “