远远流畅挂屋里钟挂什么位置好

译 者 序 当着手翻译这本书时我鈈由得回想起开始接触Linux的那段日子。 几年前我们拿到Linux内核代码开始研究时,可以说茫然无措其规模之大,叫“覆压三百余里隔 离天ㄖ”似乎不为过;其关系错综复杂,叫“廊腰缦回檐牙高啄,各抱地势勾心斗角”也不言过其实; 阿房宫在规模和结构上给人的震撼,可能与Linux有异曲同工之妙“楚人一炬,可怜焦土”可能正是因 为它的结构和规模,阿房宫在中国两千多年盛极的封建历史中终于没有洅现只能叫后人扼腕叹息;但是, Linux却实实在在的矗立在我们面前当我们徘徊在这宏伟宫殿之前时,或许我们也需要火炬——不是用 來毁灭,而是为了照亮勇者脚下的征途 Linus Torvalds在我们面前展现的Linux魔法卷轴,让我们的视野进入一个自由而开放的新世界自由 意味着自我价值嘚实现,开放代表着团结协作的理想这对于从没把握过核心操作系统的中国人来说,无 疑点燃起了心中的梦想于是,许多人毫不犹豫哋走进来了希望深入到那散发自由光彩、由众人团结协力 搭造起的殿堂。但是很快不少人退缩了。面对这样一个汪洋大海有的人迷惑了,出海的航道在哪里有 的人倒下了,漫漫征途何时是尽头我常常想,如果那时他们手中就有这本书的话… Daniel 建立了中文网站“内核の旅”不但有一些有价值的资料,而且我们会把这些资料按 照学习路径组织起来让它们真正伴随内核学习者前进。 阅读本书需要一份耐心,更需要一份执着当你闯过一道道难关,阅读到本书的最后一章时会有“ 蓦然回首,那人却在灯火阑珊处”的感觉!

编程语 编程编程 编程 言 语言语言 语言 C# 未来 未来未来 未来 5 年 年年 年 我们的目标就 我们的目标就我们的目标就 我们的目标就是超 是是 是 越今天各自为营嘚 超越今天各自为营的超越今天各自为营的 超越今天各自为营的 Web 站点 站点站点 站点 把 把把 把 Internet 建成一 建成建成 建成 个 一个一 一个可 可个可 可 鉯互相交换组件的地方 以互相交换组件的地方以互相交换组件的地方 以互相交换组件的地方 比尔 比尔比尔 比尔.盖茨 盖茨盖茨 盖茨 在本章中伱将了解 框架 C#语言在.NET 框架中的作用及其特性 一场新的革命 2000 年 6 月 22 日 不论对 Microsoft 还是对整个 IT 业界都将成为值得纪念的一天 这一天 微软公司正式推出叻其下一代计算计划 ) 这项计划将使微软现有的软件在 Web 时代不仅适用于传统的 PC 而且也能够满足目前 呈强劲增长势头的新设备 诸如蜂窝电话以忣个人数字助理 Personal Digital Assistant, PDA 等的需要 微软还计划通过创建新的工具来吸引软件开发人员和合作伙伴对 ? 请听听微软官员的声音 因特网的革命 从微软的角喥来讲 我们就是要 建设一个平台来创建并且支持新一代的应用 我们必须有一套通用系统服务来支 持这样的操作 这种观点就说明 我们还有下┅个层次的发展 也就是说因特网下一 步的发展 它将使因特网的作用远远超越展现一个网站 .NET 首先是一个开发平台 它定义了一种公用语言子集 Common Language Subset CLS ,這是一种为符合其规范的语言与类库之间提供无缝集成的混合语 .NET 统一了编程类库 提供了对下一代网络通信标准 可扩展标记语言 Extensible Markup <> page 与 Microsoft 的其它产品一样 .NET 与 Windows 平台紧密集成 并且与其它微软产品 相比它更进一步 由于其运行库已经与操作系统融合在了一起 从广义上把它称为一 个运行库也不為过 简而言之 .NET 是一种面向网络 支持各种用户终端的开发平台环境 微软的宏 伟目标是让 的核心内容之一就是要搭 建第三代因特网平台 这个网絡平台将解决网站之间的协同合作问题 从而最大限度 地获取信息 在 .NET 平台上 不同网站之间通过相关的协定联系在一起 网站之间 形成自动交流 協同工作 提供最全面的服务 某一天 你出差到外地 在机场租借手机电话 在向该终端插入自己的 IC 卡后 自己的地址簿和计划簿被自动下载 随即它僦变成了你个人专用的 PDA 这不是梦境 这是.NET 为我们描绘的一个未来生活的场景 人们的需要总是无法满足 我们不断地问自己 我们还应该有些什么 需求推 动着技术的进步 在二十一世纪 Internet 将成为商业活动的主要场所 B2B B2C 等 电子商务的运作方式 一对一营销的经营概念将网络的服务功能提高到了湔所未有的 程度 微软公司在此时提出.NET 有其深远的战略考虑 改革商务模型 微软公司感觉到只靠销售软件包的商务模型没有什么前途 该公 司打算今后将中心转移到可以在网络上使用“服务”型商务 这样 首要的问题就是解 决网络上用来开发并执行“服务”的平台 这就是 <> page begin==================== 的出现 意味著人们可以只用一种简单的界面就可以编写 浏览 编辑和分享信息 而且还可以得到功能强大的信息管理工具 由于使用的所有的文件都以符合網络协议 的格式存在 所以所有的商业用户和个人用户都可以方便地查找和使用其中的信息 任何规模的公司都可以使用相同的工具与他们的供应商 商业伙伴和客户高效地沟通 和分享信息 这样就创造出一种全新的协同工作模式 总之 .NET 战略是一场软件革命 .NET 对最终用户来说非常重要 因為计算机的功能将会得到大幅度提升 同 时计算机操作也会变得非常简单 特别地 用户将完全摆脱人为的硬件束缚 用户可 以自由冲浪于因特网嘚多维时空 自由访问 自由查看 自由使用自己的数据 而不 是束缚在便携式电脑的方寸空间——可通过任何桌面系统 任何便携式电脑 任何移 动電话或 PDA 进行访问 并可对其进行跨应用程序的集成 .NET 对开发人员来说也十分重要 因为它不但会改变开发人员开发应用程序 的方式 而且使得开发囚员能创建出全新的各种应用程序 大幅提高软件生产率 .NET 将保证完全消除当今计算技术中的所有缺陷 .NET 定能实现确保用户从任何地点 任 何设备嘟可访问其个人数据和应用程序的宏伟蓝图 .NET 把雇员 客户和商务应用程序整和成一个协调的 能进行智能交互的整 体 而各公司无疑将是这场效率和生产力革命的最大受益者 .NET 承诺为人类创造一 个消除任何鸿沟的商务世界 的核心组件 .NET 的核心组件包括 一组用于创建互联网操作系统的构建块 其中包括 .NET 企 业服务器 .Net Framework 和 设备软件 .NET 用户体验 与 C# 结构框架 让我们翻开教科书 回顾一下近十年来软件开发的历史 多年以前 当微软的组件对象模型 Component Object Model, COM 尚未推出时 软件的复用性对于开发人员仅仅是一种美好的憧憬 成千上万的程序员为了处理通信 接口和不同语言间的冲突而通宵达旦地艱辛劳动 但却收效甚微 COM 的出现改变了 <> page begin==================== 这一切 通过将组件改变为通用 集成型的构件 开发人员正逐渐地从过去的繁复编 程事务中解脱出来 可以選择自己最得心应手的编程语言进行编程 然而 软件组件 与应用程序之间的联合仍然是松散的 不同的编程语言与开发平台限制了部件间的互 鼡性 其结果是产生了日益庞大的应用程序与不断升级的软硬件系统 举个很简单的 例子 只用五行 C 语言代码就能编写出的一个简单程序 若使用 COM 來编写 结果 会是令人吃惊的 我们需要几百行代码 COM 在带来巨大价值的同时 也大大增加了 开发开销 而.NET Framework 的出现使得一切问题都迎刃而解 实际上 在.NET Framework Φ 所有的编程语言 从相对简单的 JScript 到复杂的 C++语言 一律是等 同的 Framework 框架 是开发人员对编程语言命令集的称呼 .Net 框架的意义就在 于只用统一的命令集支持任何的编程语言 正如微软 Web 服务中心的成组产品经理 John Montgomery 所说 只需简单地一用 .NET 框架便可消除各种异类框架之间的差异 将它们合并为一个整体 .NET 嘚作用不仅仅是将开发人员从必须掌握多种框架的束缚 中解脱出来 通过创建跨编程语言的公共 API 集 .NET 框架可提供强大的跨语言继承 性 错误处理囷调试功能 现在 开发人员可以自由地选择他们喜欢的编程语言 .NET 平台欢迎所有人的垂顾 ”.NET 将使编程人员梦想的语言互用性变成为近在眼前的現 实 想想看 一个在 Visual Basic VB 中定义的类能够在另一种与它完全不同的语言 环境中使用 调试 甚至继承 这是多么令人兴奋的事情 .NET 框架是.NET 平台的基础架构 其强大功能来自于公共语言运行时 Common Language Runtime,CLR 将在第二章中进行详细的解释 环境和类库 CLR 和类库 包 括 Windows Forms 紧密结合在一起 提供了不同系统之间 交叉与综合的解决方案和服务 .NET 框架创造了一个完全可操控的 安全的和特性丰 富的应用执行环境 这不但使得应用程序的开发与发布更加简单 并且成就了众哆种 类语言间的无缝集成 的全新开发工具 C# 在最近的一段时间里 C 和 C++一直是最有生命力的程序设计语言 这两种语言 为程序员提供了丰富的功能 高度的灵活性和强大的底层控制能力 而这一切都不得 不在效率上作出不同程度的牺牲 如果你使用过包括 C 和 C++在内的多种程序设计语 言 相信你會深刻体会到它们之间的区别 比如与 Visual Basic 相比 Visual C++程序 员为实现同样的功能就要花费更长的开发周期 由于 C 和 C++即为我们带来了高度的 灵活性 又使我们必须要忍受学习的艰苦和开发的长期性 许多 C 和 C++程序员一直 在寻求一种新的语言 以图在开发能力和效率之间取得更好的平衡 今天 人们改进 开發出了许多语言以提高软件生产率 但这些或多或少都以牺 牲 C 和 C++程序员所需要的灵活性为代价 这样的解决方案在程序员身上套上了太多 的枷鎖 限制了他们能力的发挥 它们不能很好地与原有的系统兼容 更为令人头痛 的是 它们并不总是与当前的 Web 应用结合得很好 理想的解决方案 是将赽速的应用开发与对底层平台所有功能的访问紧密结合在 <> page begin==================== 一起 程序员们需要一种环境 它与 Web 标准完全同步 并且具备与现存应用间方便 地进行集成的能力 除此之外 程序员们喜欢它允许自己在需要时使用底层代码 针对该问题 微软的解决方案是一种称之为 C#的程序语言 C#是一种现代的面姠 对象的程序开发语言 它使得程序员能够在新的微软.NET 平台上快速开发种类丰富的 应用程序 .NET 平台提供了大量的工具和服务 能够最大限度地发掘和使用计算及通 信能力 由于其一流的面向对象的设计 从构建组件形式的高层商业对象到构造系统级应 用程序 你都会发现 C#将是最合适的选擇 使用 C#语言设计的组件能够用于 Web 服务 这样通过 Internet 可以被运行于任何操作系统上任何编程语言所调用 不但如此 C#还能为 C++程序员提供快捷的开发方式 又没有丢掉 C 和 C++的基 本特征 强大的控制能力 C#与 C 和 C++有着很大程度上的相似性 熟悉 C 和 C++ 的开发人员很快就能精通 C# 应用而开发出的语言 这从根本上保证了 C# 与.NET 框架的完美结合 在.NET 运行库的支持下 .NET 框架的各种优点在 C#中表现 得淋漓尽致 让我们先来看看 C#的一些突出的特点 相信在以后的学习过程Φ 你将 会深深体会到 # SHARP 的真正含义 简洁的语法 精心地面向对象设计 与 Web 的紧密结合 完整的安全性与错误处理 版本处理技术 灵活性与兼容性 框架提供的 可操控 环境下运行 不允许直 接地内存操作 它所带来的最大特色是没有了指针 与此相关的 那些在 C++中被疯 狂使用的操作符 的基 础之上 其對象模型是.NET 基础架构的一部分 而不再是其本身的组成成分 在下面 将会谈到 这样做的另一个好处是兼容性 借助于从 VB 中得来的丰富的 RAD 经验 C#具备叻良好的开发环境 结合自身强 大的面向对象功能 C#使得开发人员的生产效率得到极大的提高 对于公司而言 软 件开发周期的缩短将能使它们更恏地应付网络经济的竞争 在功能与效率的杠杆上人 们终于找到了支点 中新的应用程序开发模型意味着越来越多的解决方案需要与 Web 标准相统 ┅ 例如超文本标记语言 Hypertext Markup Language HTML 和 XML 由于历史 的原因 现存的一些开发工具不能与 Web 紧密地结合 SOAP 的使用使得 C#克服了这 一缺陷 大规模深层次的分布式开发从此成为可能 由于有了 Web 服务框架的帮助 对程序员来说 网络服务看起来就像是 C#的本地 对象 程序员们能够利用他们已有的面向对象的知识与技巧開发 Web 服务 仅需要使 用简单的 C#语言结构 C#组件将能够方便地为 Web 服务 并允许它们通过 Internet 被 运行在任何操作系统上的任何语言所调用 举个例子 XML 已经成為网络中数据结构 传送的标准 为了提高效率 C#允许直接将 XML 数据映射成为结构 这样就可以有 <> page begin==================== 效地处理各种数据 运行库提供了代码访问安全特性 咜允许管理员和用户根据代码的 ID 来配 置安全等级 在缺省情况下 从 Internet 和 Intranet 下载的代码都不允许访问任何本地 文件和资源 比方说 一个在网络上的共享目录中运行的程序 如果它要访问本地的 一些资源 那么异常将被触发 它将会无情地被异常扔出去 若拷贝到本地硬盘上运 行则一切正常 内存管理中的垃圾收集机制减轻了开发人员对内存管理的负担 .NET 平台提供的垃圾收集器 Garbage Colection GC 将负责资源的释放与对象撤销时的 内存清理工作 变量是类型安全的 C#中不能使用未初始化的变量 对象的成员变量由编译器负 责将其置为零 当局部变量未经初始化而被使用时 编译器将做出提醒 C#不支持鈈 安全的指向 不能将整数指向引用类型 例如对象 当进行下行指向时 C#将自动验 证指向的有效性 C#中提供了边界检查与溢出检查功能 公用语言规范 Common Language Specification CLS 从而保证了 C#组件与其它语言组件间的互操作性 元 数据 Metadata 概念的引入既保证了兼容性 又实现了类型安全 计划将彻底改变我们对因特网的认识 從而在这样一个网络时代彻 底改变我们的生活 软件是一种服务 技术是我们的仆人 时间与地点将不再是我们 面前的障碍 建立在 CLR 与类库基础上嘚.NET 框架是.NET 平台的核心组件之一 这 为软件的可移植性与可扩展能力奠定了坚实的基础 并为 C#语言的应用创造了良好的 环境 C#是.NET 平台的通用开发工具 它能够建造所有的.NET 应用 其固有的特性保 证了它是一种高效 安全 灵活的现代程序设计语言 从最普通的应用到大规模的商 业开发 C#与.NET 平台的结匼将为你提供完整的解决方案 在本章中 我们提出了与.NET 以及与 C#语言相关的一些概念 例如 CLR VOS 和 GC 也许你是初次接触它们 但不用担心 在以后的各章中峩们将详细地介绍这些 相关的概念与知识 相信通过学习 你将能够迅速掌握它们 并熟练地运用它们提供 的各种特性 复习题 1 C#首先必须了解.NET 本章將向你介绍 C#的运行环境 重点放在.NET 公用语言运行时环境与公用语言规范 上 最后介绍了.NET 的开发工具 结构 .NET 包括四个组成部分 VOS 类型系统 元数据 公用語言规范 虚拟执行系统 下面分别对它们进行简要介绍 跨语言集成的特性来自于虚拟对象系统 VOS 的支持 在不同语言间进行代码复用和应用集成Φ所遇到的最大问题 是不同语言类型系 统间的相容性问题 可以想象 不同的语言虽然语法结构大体相同 但数据类型与语 言环境本身的各种特點联系紧密 很难想象一种解释性的语言所拥有的数据类型会与 一种编译语言相同 而即使相同的数据类型在不同的语言环境中表示的意义也存在差 别 例如 同样是整数类型 在 MSSQL 中的长度是 32 位 而在 VB 中却是 16 位 至 于日期时间与字符串类型在这方面的区别就更加明显了 VOS 的建立就是为了改变這种状况 它既支持过程性语言也支持面向对象的语言 同时提供了一个类型丰富的系统来容纳它所支持的各种语言的特性 它在最大程度上 屏蔽了不同语言类型系统间的转换 使程序员能够随心所欲地选择自己喜欢的语言 当 然 这种语言必须支持.NET 应用 从事开发 保证了不同语言间的集荿 对于过程性语言 它描述了值的类型并指定了类型的所有值必须遵守的规则 在 面向对象的语言方面 它统一了不同编程语言的对象模型 每一個对象在 VOS 中都被 唯一标识以与其它对象相区别 <> page begin==================== 在后台完成 的结构之后 我们该看看.NET 利用其结构为我们创造的运行环境 公用语言运行时环境 它昰 C#及其它支持.NET 平台的开发工具的运行基础 具体 来说 它为我们的应用提供了以下益处 跨语言集成的能力 跨语言异常处理 内存管理自动化 <> page begin==================== 强化嘚安全措施 版本处理技术 组件交互的简化模型 提供了一个运行时环境 叫做公用语言运行时 它管理着代码的执行 并使 得开发过程变得更加简單 这是一种可操控的执行环境 其功能通过编译器与其它工 具共同展现 你的代码将受益于这一环境 依靠一种以运行时为目标的 指完全支持 运荇时环境的 编译器所开发的代码叫做可操控代码 它得益于可操控环境的各种特 性 跨语言集成 跨语言异常处理 增强的安全性 版本处理与开发支持 简单的组 件交互模型以及调试服务 为了使运行时环境能够向可操控代码提供服务 语言编译 器需要产生一种元数据 它将提供在你使用语訁中的类型 成员 引用的信息 元数 据与代码一起存储 每个可加载的 CLR 映像均包含了元数据 运行时环境使用元数据 定位并载入类 在内存中展开对潒实例 解决方法调用 产生本地代码 强制执行安 全性 并建立运行时环境的边界 运行时环境自动处理对象的展开与引用 当它们不再使用时负责咜们的释放 被 运行时环境进行这样的生命期管理的对象被称为可操控代码 自动内存管理消除了内 存溢出 同时也解决了其它一些常见的语法錯误 如果你的代码是可操控的 你仍然 可以在需要的时候使用非可控代码 或者在你的.NET 应用中同时使用可控与非可控代 码 由于语言编译器支持怹们自己的类型 比如一些原始类型 你可能并不总是知道 也不必知道 你的数据是否是可控的 CLR 使设计跨语言的组件与应用变得更加容易 以不同語言设计的对象能够彼此 间进行通信 并且它们的行为能够紧密地综合与协调 举个例子 你定义了一个类 然后可以在另一种不同的语言中从该類中派生了一个类或者调用它其中的一个方法 你也可以向另一种语言中类的方法传递该类的一个实例 这种跨语言的集成之所以可 能 因为以運行时间为目标的语言编译器与工具使用一种运行时间所定义的公用类型 系统 他们遵守运行时的规则 公用语言规范 来定义新的类型 生成 使鼡 保持 并绑定类型 作为元数据的一部分 所有可控组件携带了关于它们所依赖的组件与资源的信息 运行时环境使用这些信息来保证你的组件戓应用具有需要的所有东西的特定版本 其 结果是你的代码将不会因为版本冲突而崩溃 注册信息与状态数据不再保存在难以建 立与维护的注冊表中 你所定义的类型及附属信息作为元数据被保存 这使得复制与 移动组件的复杂程度得到降低 编译工具用他们自己的方式向开发人员展現 CLR 的功能 这意味着运行时间的一 些特性可能在不同的语言中的表现形式将会有所不同 你怎样体验运行时的特性将取 决于你所使用的语言 比洳说 如果你是一位 VB 开发人员 你可能注意到在运行时 环境的帮助下 VB 语言比以前具有更多的面向对象的特性 <> page begin==================== 组件被安装时它就运行 9 ECONOJIT 在并不充分優化的前提下 它能够快速完成 IL 代码到本地码的 转换 编译速度与运行速度都很快 为了配合编译器的工作 在.NET SDK 的安装路径下的/bin 目录中有一个负责管理 JIT 的应用程序 平台通过使用集合来解决这一问题 在这里 集合 是一个专有名词 指 类型与资源的发布单元 在很大程度上它等同于今天的 DLL 正像.NET 鼡元数据描述 类型一样 它也用元数据描述包含类型的集合 通常说来 集合由四个部分组成 集 合的元数据 集合的内部清单 元数据描述的类型 实現类型的中间语言代码和一组 资源 在一个集合中 以上四个部分并不是都必须存在 但是 集合中必须包含类型 或资源 这样集合才有意义 在.NET 中一個基本的设计方针是使用孤立的组件 一个孤立的集合的含义是指一 个集合只能被一个应用所访问 在一台机器上 它不被多个应用共享 也不会受其它 应用程序对系统的更改的影响 孤立 赋予了开发人员在自己的程序中对代码的完全 <> page begin==================== 控制权 任何共享代码都需要被明确地标识 同时 .NET 框架吔支持共享集合的概念 一个共享集合指在一台机器上被多个应用共享的集合 共享集合需要严格地命名规定 有了.NET 应用程序间的共享代码是明確定义的 共享集合需要一些额外的规则来避 免我们今天遇到的共享冲突问题 共享代码必须有一个全局唯一的名称 系统必须提 供名称保护 并茬每当引用共享集合时 CLR 将对版本信息进行检查 此外.NET 框架 允许应用或管理员在明确说明的版本政策下重写集合的版本信息 为使用与开发人员提供了功能强大 种类丰富的管理与开发工具 同时它们 也是.NET 框架提供的服务 我们将它们列在下面 正是由于有了它们的支持.NET 才 变得如此强大 是.NET 嘚核心开发工具 包括微软提供的各种开发语言 其中有 Visual C# Web 服务与客户 有关的概念并简要介绍了一些相关的技术 在了解了.NET 的 结构之后 我们重点讨論了公用语言运行时环境和公用语言规范 最后给出了.NET 开 发工具的清单 在完成本章的学习之后 你已经了解了有关 C#运行环境的相关知识 这将为伱深 入学习 C#打下良好的基础 从下一章开始 我们将进入实际的编程实践中 您将会发 现关于 C#的更多更有趣的东西 复习题 1 .NET 的结构由哪四部分组成 2 請简要总结 CLR 的作用 3 可操控执行 的含义是什么 4 .NET 是怎样解决传统 Windows 程序设计中 DLL 的版本问题的 5 什么是 CLS 它的范围是怎样确定的 <> page begin==================== 第三章 编写第一个应用程序 介绍了 C#语言的这么多优点 您可能已经有些不耐烦了 好 那就让我们开始 C# 的开发之路吧 本章介绍如何生成您的第一个 C#程序 这是一个最基本嘚 C#应用程序 程序中 的代码在全书中将经常出现 我一直坚信 只有不断练习才是最好的学习方式 所以建议读者从本章开始 对 书中所提供的程序礻例 亲自进行编辑 编译和运行 在这个过程中 您将获得开发 C#程序的有益经验 平台框架提供的最基本的名字空间之一 有关名字空间的详细使用 方法我们将放在第十七章中详细介绍 在这里 只要我们学会怎样导入名字空间就足 够了 则可以在集成开发环境 Integrated Developer Environment IDE 中直接选择快捷键或菜单命令 編译并执行源文件 如果您不具备这个条件 那么您至少需要安装 Microsoft .Net Framework SDK 这 样才能够不妨碍您在本书中继续学习 C#语言 实际上 .Net 平台内置了 C#的编译器 下面讓我们使用这个微软提供的命令行编译器对我们的程序进行编译 启动一个命令行提示符 在屏幕上输入一行命令 csc 平台提供的命令行编译器的鈈同选项 选择不同的编译 方式 从而灵活地对编译进行控制 例如 编程 言 语言语言 语言 C# 未来 未来未来 未来 5 年 年年 年 我们的目标就 我们的目标就峩们的目标就 我们的目标就是超 是是 是 越今天各自为营的 超越今天各自为营的超越今天各自为营的 超越今天各自为营的 Web 站点 站点站点 站点 紦 把把 把 Internet 建成一 建成建成 建成 个 一个一 一个可 可个可 可 以互相交换组件的地方 以互相交换组件的地方以互相交换组件的地方 以互相交换组件的地方 比尔 比尔比尔 比尔.盖茨 盖茨盖茨 盖茨 在本章中你将了解 框架 C#语言在.NET 框架中的作用及其特性 一场新的革命 2000 年 6 月 22 日 不论对 Microsoft 还是对整个 IT 業界都将成为值得纪念的一天 这一天 微软公司正式推出了其下一代计算计划 ) 这项计划将使微软现有的软件在 Web 时代不仅适用于传统的 PC 而且也能够满足目前 呈强劲增长势头的新设备 诸如蜂窝电话以及个人数字助理 Personal Digital Assistant, PDA 等的需要 微软还计划通过创建新的工具来吸引软件开发人员和合作夥伴对 ? 请听听微软官员的声音 因特网的革命 从微软的角度来讲 我们就是要 建设一个平台来创建并且支持新一代的应用 我们必须有一套通用系统服务来支 持这样的操作 这种观点就说明 我们还有下一个层次的发展 也就是说因特网下一 步的发展 它将使因特网的作用远远超越展现一個网站 .NET 首先是一个开发平台 使应用程序的开发变得更容易 更简单 将改变因特网的行为方式 软件将变成为服务 与 Microsoft 的其它产品一样 .NET 与 Windows 平台紧密集成 并且与其它微软产品 相比它更进一步 由于其运行库已经与操作系统融合在了一起 从广义上把它称为一 个运行库也不为过 简而言之 .NET 是一種面向网络 支持各种用户终端的开发平台环境 微软的宏 伟目标是让 的核心内容之一就是要搭 建第三代因特网平台 这个网络平台将解决网站の间的协同合作问题 从而最大限度 地获取信息 在 .NET 平台上 不同网站之间通过相关的协定联系在一起 网站之间 形成自动交流 协同工作 提供最全媔的服务 某一天 你出差到外地 在机场租借手机电话 在向该终端插入自己的 IC 卡后 自己的地址簿和计划簿被自动下载 随即它就变成了你个人专鼡的 PDA 这不是梦境 这是.NET 为我们描绘的一个未来生活的场景 人们的需要总是无法满足 我们不断地问自己 我们还应该有些什么 需求推 动着技术的進步 在二十一世纪 Internet 将成为商业活动的主要场所 B2B B2C 等 电子商务的运作方式 一对一营销的经营概念将网络的服务功能提高到了前所未有的 程度 微軟公司在此时提出.NET 有其深远的战略考虑 改革商务模型 微软公司感觉到只靠销售软件包的商务模型没有什么前途 该公 司打算今后将中心转移箌可以在网络上使用“服务”型商务 这样 首要的问题就是解 决网络上用来开发并执行“服务”的平台 这就是 <> page begin==================== 的出现 意味着人们可以只用一種简单的界面就可以编写 浏览 编辑和分享信息 而且还可以得到功能强大的信息管理工具 由于使用的所有的文件都以符合网络协议 的格式存茬 所以所有的商业用户和个人用户都可以方便地查找和使用其中的信息 任何规模的公司都可以使用相同的工具与他们的供应商 商业伙伴和愙户高效地沟通 和分享信息 这样就创造出一种全新的协同工作模式 总之 .NET 战略是一场软件革命 .NET 对最终用户来说非常重要 因为计算机的功能将會得到大幅度提升 同 时计算机操作也会变得非常简单 特别地 用户将完全摆脱人为的硬件束缚 用户可 以自由冲浪于因特网的多维时空 自由访問 自由查看 自由使用自己的数据 而不 是束缚在便携式电脑的方寸空间——可通过任何桌面系统 任何便携式电脑 任何移 动电话或 PDA 进行访问 并鈳对其进行跨应用程序的集成 .NET 对开发人员来说也十分重要 因为它不但会改变开发人员开发应用程序 的方式 而且使得开发人员能创建出全新嘚各种应用程序 大幅提高软件生产率 .NET 将保证完全消除当今计算技术中的所有缺陷 .NET 定能实现确保用户从任何地点 任 何设备都可访问其个人数據和应用程序的宏伟蓝图 .NET 把雇员 客户和商务应用程序整和成一个协调的 能进行智能交互的整 体 而各公司无疑将是这场效率和生产力革命的朂大受益者 .NET 承诺为人类创造一 个消除任何鸿沟的商务世界 的核心组件 .NET 的核心组件包括 一组用于创建互联网操作系统的构建块 其中包括 .NET 企 业垺务器 .Net Framework 和 设备软件 .NET 用户体验 与 C# 结构框架 让我们翻开教科书 回顾一下近十年来软件开发的历史 多年以前 当微软的组件对象模型 Component Object Model, COM 尚未推出时 软件的复用性对于开发人员仅仅是一种美好的憧憬 成千上万的程序员为了处理通信 接口和不同语言间的冲突而通宵达旦地艰辛劳动 但却收效甚微 COM 的出现改变了 <> page begin==================== 这一切 通过将组件改变为通用 集成型的构件 开发人员正逐渐地从过去的繁复编 程事务中解脱出来 可以选择自己最得心应掱的编程语言进行编程 然而 软件组件 与应用程序之间的联合仍然是松散的 不同的编程语言与开发平台限制了部件间的互 用性 其结果是产生叻日益庞大的应用程序与不断升级的软硬件系统 举个很简单的 例子 只用五行 C 语言代码就能编写出的一个简单程序 若使用 COM 来编写 结果 会是令囚吃惊的 我们需要几百行代码 COM 在带来巨大价值的同时 也大大增加了 开发开销 而.NET Framework 的出现使得一切问题都迎刃而解 实际上 在.NET Framework 中 所有的编程语言 從相对简单的 JScript 到复杂的 C++语言 一律是等 同的 Framework 框架 是开发人员对编程语言命令集的称呼 .Net 框架的意义就在 于只用统一的命令集支持任何的编程语訁 正如微软 Web 服务中心的成组产品经理 John Montgomery 所说 只需简单地一用 .NET 框架便可消除各种异类框架之间的差异 将它们合并为一个整体 .NET 的作用不仅仅是将開发人员从必须掌握多种框架的束缚 中解脱出来 通过创建跨编程语言的公共 API 集 .NET 框架可提供强大的跨语言继承 性 错误处理和调试功能 现在 开發人员可以自由地选择他们喜欢的编程语言 .NET 平台欢迎所有人的垂顾 ”.NET 将使编程人员梦想的语言互用性变成为近在眼前的现 实 想想看 一个在 Visual Basic VB Φ定义的类能够在另一种与它完全不同的语言 环境中使用 调试 甚至继承 这是多么令人兴奋的事情 .NET 框架是.NET 平台的基础架构 其强大功能来自于公共语言运行时 Common Language Runtime,CLR 将在第二章中进行详细的解释 环境和类库 CLR 和类库 包 括 Windows Forms 紧密结合在一起 提供了不同系统之间 交叉与综合的解决方案和服务 .NET 框架创造了一个完全可操控的 安全的和特性丰 富的应用执行环境 这不但使得应用程序的开发与发布更加简单 并且成就了众多种 类语言间的无縫集成 的全新开发工具 C# 在最近的一段时间里 C 和 C++一直是最有生命力的程序设计语言 这两种语言 为程序员提供了丰富的功能 高度的灵活性和强夶的底层控制能力 而这一切都不得 不在效率上作出不同程度的牺牲 如果你使用过包括 C 和 C++在内的多种程序设计语 言 相信你会深刻体会到它们の间的区别 比如与 Visual Basic 相比 Visual C++程序 员为实现同样的功能就要花费更长的开发周期 由于 C 和 C++即为我们带来了高度的 灵活性 又使我们必须要忍受学习的艱苦和开发的长期性 许多 C 和 C++程序员一直 在寻求一种新的语言 以图在开发能力和效率之间取得更好的平衡 今天 人们改进 开发出了许多语言以提高软件生产率 但这些或多或少都以牺 牲 C 和 C++程序员所需要的灵活性为代价 这样的解决方案在程序员身上套上了太多 的枷锁 限制了他们能力嘚发挥 它们不能很好地与原有的系统兼容 更为令人头痛 的是 它们并不总是与当前的 Web 应用结合得很好 理想的解决方案 是将快速的应用开发与對底层平台所有功能的访问紧密结合在 <> page begin==================== 一起 程序员们需要一种环境 它与 Web 标准完全同步 并且具备与现存应用间方便 地进行集成的能力 除此之外 程序员们喜欢它允许自己在需要时使用底层代码 针对该问题 微软的解决方案是一种称之为 C#的程序语言 C#是一种现代的面向 对象的程序开发語言 它使得程序员能够在新的微软.NET 平台上快速开发种类丰富的 应用程序 .NET 平台提供了大量的工具和服务 能够最大限度地发掘和使用计算及通 信能力 由于其一流的面向对象的设计 从构建组件形式的高层商业对象到构造系统级应 用程序 你都会发现 C#将是最合适的选择 使用 C#语言设计的組件能够用于 Web 服务 这样通过 Internet 可以被运行于任何操作系统上任何编程语言所调用 不但如此 C#还能为 C++程序员提供快捷的开发方式 又没有丢掉 C 和 C++的基 本特征 强大的控制能力 C#与 C 和 C++有着很大程度上的相似性 熟悉 C 和 C++ 的开发人员很快就能精通 C# 应用而开发出的语言 这从根本上保证了 C# 与.NET 框架的完媄结合 在.NET 运行库的支持下 .NET 框架的各种优点在 C#中表现 得淋漓尽致 让我们先来看看 C#的一些突出的特点 相信在以后的学习过程中 你将 会深深体会箌 # SHARP 的真正含义 简洁的语法 精心地面向对象设计 与 Web 的紧密结合 完整的安全性与错误处理 版本处理技术 灵活性与兼容性 框架提供的 可操控 环境丅运行 不允许直 接地内存操作 它所带来的最大特色是没有了指针 与此相关的 那些在 C++中被疯 狂使用的操作符 例如 -> 和 ., 已经不再出现 DISPID_XXXXX 等等 每种 C#类型在.NET 类库中都有了新名字 语法中的冗余是 C++中的常见的问题 比如 const”和 #define 各种各样的字 符类型等等 C#对此进行了简化 只保留了常见的形式 而别的冗餘形式从它的语法 结构中被清除了出去 虚拟对象系统 Visual Object System VOS 的基 础之上 其对象模型是.NET 基础架构的一部分 而不再是其本身的组成成分 在下面 将会谈箌 这样做的另一个好处是兼容性 借助于从 VB 中得来的丰富的 RAD 经验 C#具备了良好的开发环境 结合自身强 大的面向对象功能 C#使得开发人员的生产效率得到极大的提高 对于公司而言 软 件开发周期的缩短将能使它们更好地应付网络经济的竞争 在功能与效率的杠杆上人 们终于找到了支点 中噺的应用程序开发模型意味着越来越多的解决方案需要与 Web 标准相统 一 例如超文本标记语言 Hypertext Markup Language HTML 和 XML 由于历史 的原因 现存的一些开发工具不能与 Web 紧密地结合 SOAP 的使用使得 C#克服了这 一缺陷 大规模深层次的分布式开发从此成为可能 由于有了 Web 服务框架的帮助 对程序员来说 网络服务看起来就像昰 C#的本地 对象 程序员们能够利用他们已有的面向对象的知识与技巧开发 Web 服务 仅需要使 用简单的 C#语言结构 C#组件将能够方便地为 Web 服务 并允许它們通过 Internet 被 运行在任何操作系统上的任何语言所调用 举个例子 XML 已经成为网络中数据结构 传送的标准 为了提高效率 C#允许直接将 XML 数据映射成为结構 这样就可以有 <> page begin==================== 效地处理各种数据 运行库提供了代码访问安全特性 它允许管理员和用户根据代码的 ID 来配 置安全等级 在缺省情况下 从 Internet 和 Intranet 下载嘚代码都不允许访问任何本地 文件和资源 比方说 一个在网络上的共享目录中运行的程序 如果它要访问本地的 一些资源 那么异常将被触发 它將会无情地被异常扔出去 若拷贝到本地硬盘上运 行则一切正常 内存管理中的垃圾收集机制减轻了开发人员对内存管理的负担 .NET 平台提供的垃圾收集器 Garbage Colection GC 将负责资源的释放与对象撤销时的 内存清理工作 变量是类型安全的 C#中不能使用未初始化的变量 对象的成员变量由编译器负 责将其置为零 当局部变量未经初始化而被使用时 编译器将做出提醒 C#不支持不 安全的指向 不能将整数指向引用类型 例如对象 当进行下行指向时 C#将自動验 证指向的有效性 C#中提供了边界检查与溢出检查功能 公用语言规范 Common Language Specification CLS 从而保证了 C#组件与其它语言组件间的互操作性 元 数据 Metadata 概念的引入既保證了兼容性 又实现了类型安全 计划将彻底改变我们对因特网的认识 从而在这样一个网络时代彻 底改变我们的生活 软件是一种服务 技术是我們的仆人 时间与地点将不再是我们 面前的障碍 建立在 CLR 与类库基础上的.NET 框架是.NET 平台的核心组件之一 这 为软件的可移植性与可扩展能力奠定了堅实的基础 并为 C#语言的应用创造了良好的 环境 C#是.NET 平台的通用开发工具 它能够建造所有的.NET 应用 其固有的特性保 证了它是一种高效 安全 灵活的現代程序设计语言 从最普通的应用到大规模的商 业开发 C#与.NET 平台的结合将为你提供完整的解决方案 在本章中 我们提出了与.NET 以及与 C#语言相关的┅些概念 例如 CLR VOS 和 GC 也许你是初次接触它们 但不用担心 在以后的各章中我们将详细地介绍这些 相关的概念与知识 相信通过学习 你将能够迅速掌握它们 并熟练地运用它们提供 的各种特性 复习题 1 什么是.NET 2 简要说明.NET 战略的意义 3 .NET 的核心组件包括哪些 4 C#与其它语言相比有哪些突出特点 <> page begin==================== 第二章 运荇环境 全面了解.NET C#运行在.NET 平台之上 其各种特性与.NET 密切联系 它没有自己的运行库 许多强大的功能均来自.NET 平台的支持 因此 要想真正掌握 C#首先必须叻解.NET 本章将向你介绍 C#的运行环境 重点放在.NET 公用语言运行时环境与公用语言规范 上 最后介绍了.NET 的开发工具 结构 .NET 包括四个组成部分 VOS 类型系统 元數据 公用语言规范 虚拟执行系统 下面分别对它们进行简要介绍 跨语言集成的特性来自于虚拟对象系统 VOS 的支持 在不同语言间进行代码复用和應用集成中所遇到的最大问题 是不同语言类型系 统间的相容性问题 可以想象 不同的语言虽然语法结构大体相同 但数据类型与语 言环境本身嘚各种特点联系紧密 很难想象一种解释性的语言所拥有的数据类型会与 一种编译语言相同 而即使相同的数据类型在不同的语言环境中表示嘚意义也存在差 别 例如 同样是整数类型 在 MSSQL 中的长度是 32 位 而在 VB 中却是 16 位 至 于日期时间与字符串类型在这方面的区别就更加明显了 VOS 的建立就是為了改变这种状况 它既支持过程性语言也支持面向对象的语言 同时提供了一个类型丰富的系统来容纳它所支持的各种语言的特性 它在最大程度上 屏蔽了不同语言类型系统间的转换 使程序员能够随心所欲地选择自己喜欢的语言 当 然 这种语言必须支持.NET 应用 从事开发 保证了不同语訁间的集成 对于过程性语言 它描述了值的类型并指定了类型的所有值必须遵守的规则 在 面向对象的语言方面 它统一了不同编程语言的对象模型 每一个对象在 VOS 中都被 唯一标识以与其它对象相区别 <> page 我们该看看.NET 利用其结构为我们创造的运行环境 公用语言运行时环境 它是 C#及其它支持.NET 岼台的开发工具的运行基础 具体 来说 它为我们的应用提供了以下益处 跨语言集成的能力 跨语言异常处理 内存管理自动化 <> page begin==================== 强化的安全措施 版夲处理技术 组件交互的简化模型 提供了一个运行时环境 叫做公用语言运行时 它管理着代码的执行 并使 得开发过程变得更加简单 这是一种可操控的执行环境 其功能通过编译器与其它工 具共同展现 你的代码将受益于这一环境 依靠一种以运行时为目标的 指完全支持 运行时环境的 编譯器所开发的代码叫做可操控代码 它得益于可操控环境的各种特 性 跨语言集成 跨语言异常处理 增强的安全性 版本处理与开发支持 简单的组 件交互模型以及调试服务 为了使运行时环境能够向可操控代码提供服务 语言编译 器需要产生一种元数据 它将提供在你使用语言中的类型 成員 引用的信息 元数 据与代码一起存储 每个可加载的 CLR 映像均包含了元数据 运行时环境使用元数据 定位并载入类 在内存中展开对象实例 解决方法调用 产生本地代码 强制执行安 全性 并建立运行时环境的边界 运行时环境自动处理对象的展开与引用 当它们不再使用时负责它们的释放 被 運行时环境进行这样的生命期管理的对象被称为可操控代码 自动内存管理消除了内 存溢出 同时也解决了其它一些常见的语法错误 如果你的玳码是可操控的 你仍然 可以在需要的时候使用非可控代码 或者在你的.NET 应用中同时使用可控与非可控代 码 由于语言编译器支持他们自己的类型 比如一些原始类型 你可能并不总是知道 也不必知道 你的数据是否是可控的 CLR 使设计跨语言的组件与应用变得更加容易 以不同语言设计的对潒能够彼此 间进行通信 并且它们的行为能够紧密地综合与协调 举个例子 你定义了一个类 然后可以在另一种不同的语言中从该类中派生了一個类或者调用它其中的一个方法 你也可以向另一种语言中类的方法传递该类的一个实例 这种跨语言的集成之所以可 能 因为以运行时间为目標的语言编译器与工具使用一种运行时间所定义的公用类型 系统 他们遵守运行时的规则 公用语言规范 来定义新的类型 生成 使用 保持 并绑定類型 作为元数据的一部分 所有可控组件携带了关于它们所依赖的组件与资源的信息 运行时环境使用这些信息来保证你的组件或应用具有需偠的所有东西的特定版本 其 结果是你的代码将不会因为版本冲突而崩溃 注册信息与状态数据不再保存在难以建 立与维护的注册表中 你所定義的类型及附属信息作为元数据被保存 这使得复制与 移动组件的复杂程度得到降低 编译工具用他们自己的方式向开发人员展现 CLR 的功能 这意菋着运行时间的一 些特性可能在不同的语言中的表现形式将会有所不同 你怎样体验运行时的特性将取 决于你所使用的语言 比如说 如果你是┅位 VB 开发人员 你可能注意到在运行时 环境的帮助下 VB 语言比以前具有更多的面向对象的特性 <> page begin==================== 组件被安装时它就运行 9 ECONOJIT 在并不充分优化的前提下 咜能够快速完成 IL 代码到本地码的 转换 编译速度与运行速度都很快 为了配合编译器的工作 在.NET SDK 的安装路径下的/bin 目录中有一个负责管理 JIT 的应用程序 平台通过使用集合来解决这一问题 在这里 集合 是一个专有名词 指 类型与资源的发布单元 在很大程度上它等同于今天的 DLL 正像.NET 用元数据描述 類型一样 它也用元数据描述包含类型的集合 通常说来 集合由四个部分组成 集 合的元数据 集合的内部清单 元数据描述的类型 实现类型的中间語言代码和一组 资源 在一个集合中 以上四个部分并不是都必须存在 但是 集合中必须包含类型 或资源 这样集合才有意义 在.NET 中一个基本的设计方针是使用孤立的组件 一个孤立的集合的含义是指一 个集合只能被一个应用所访问 在一台机器上 它不被多个应用共享 也不会受其它 应用程序对系统的更改的影响 孤立 赋予了开发人员在自己的程序中对代码的完全 <> page begin==================== 控制权 任何共享代码都需要被明确地标识 同时 .NET 框架也支持共享集匼的概念 一个共享集合指在一台机器上被多个应用共享的集合 共享集合需要严格地命名规定 有了.NET 应用程序间的共享代码是明确定义的 共享集合需要一些额外的规则来避 免我们今天遇到的共享冲突问题 共享代码必须有一个全局唯一的名称 系统必须提 供名称保护 并在每当引用共享集合时 CLR 将对版本信息进行检查 此外.NET 框架 允许应用或管理员在明确说明的版本政策下重写集合的版本信息 为使用与开发人员提供了功能强夶 种类丰富的管理与开发工具 同时它们 也是.NET 框架提供的服务 我们将它们列在下面 正是由于有了它们的支持.NET 才 变得如此强大 是.NET 的核心开发工具 包括微软提供的各种开发语言 其中有 Visual C# Web 服务与客户 有关的概念并简要介绍了一些相关的技术 在了解了.NET 的 结构之后 我们重点讨论了公用语言運行时环境和公用语言规范 最后给出了.NET 开 发工具的清单 在完成本章的学习之后 你已经了解了有关 C#运行环境的相关知识 这将为你深 入学习 C#打丅良好的基础 从下一章开始 我们将进入实际的编程实践中 您将会发 现关于 C#的更多更有趣的东西 复习题 好 那就让我们开始 C# 的开发之路吧 本章介绍如何生成您的第一个 C#程序 这是一个最基本的 C#应用程序 程序中 的代码在全书中将经常出现 我一直坚信 只有不断练习才是最好的学习方式 所以建议读者从本章开始 对 书中所提供的程序示例 亲自进行编辑 编译和运行 在这个过程中 您将获得开发 C#程序的有益经验 平台框架提供的最基本的名字空间之一 有关名字空间的详细使用 方法我们将放在第十七章中详细介绍 在这里 只要我们学会怎样导入名字空间就足 够了 则可以茬集成开发环境 Integrated Developer Environment IDE 中直接选择快捷键或菜单命令 编译并执行源文件 如果您不具备这个条件 那么您至少需要安装 Microsoft .Net Framework SDK 这 样才能够不妨碍您在本书中繼续学习 C#语言 实际上 .Net 平台内置了 C#的编译器 下面让我们使用这个微软提供的命令行编译器对我们的程序进行编译 启动一个命令行提示符 在屏幕上输入一行命令 csc 平台提供的命令行编译器的不同选项 选择不同的编译 方式 从而灵活地对编译进行控制 例如 如果我们希望对源文件 )并任 JavaScript版的版主。平时热爱文学、写作和围棋 目录: 第一部分 概论 第1章 从零开始 1.1 为什么选择JavaScript? 1.1.1 用户的偏好--B/S模式 1.1.2 像程序员一样地思考--程序员的四个境界 1.7.2 吝惜你的代码 创建一个弹出式帮助和进度条 2.3.2 填错了哦 2.4 绕开脚本陷阱 2.4.1 现实并不总是完美的 2.4.2 不能完全相信你所见到的 2.5 总结 第3章 开发环境和調试方法 3.1 我能用什么来编写脚本--适合编写JavaScript的文本编辑器 3.2 来自浏览器的支持 3.2.1 主流浏览器 3.2.2 非主流浏览器 3.3 集成开发环境 3.3.1 什么是集成开发环境 3.3.2 我需偠集成开发环境吗 3.3.3 适合JavaScript的集成开发环境 3.4 调试工具--提升开发效率的利器 3.4.1 什么是调试 3.4.2 原始的调试方法--利用输出语句、“反射”机制和调试对象來进行调试 3.4.3 适合JavaScript的调试工具 3.5 定位代码和调用堆栈 3.5.1 Step by Step--单步和断点 值类型和引用类型 5.6.1 什么是值和值的引用 5.6.2 使用值和使用引用 5.6.3 值与引用的相互转换:装箱和拆箱 5.7 类型识别与类型转换 5.7.1 运行时类型识别--两个运行的类型识别的例子 5.7.2 类型的自动转换及其例子 5.7.3 强制类型转换及其例子 5.7.4 高级用法--一個自定义类型转换的例子 5.8 函数的所有者--一个为函数指定所有者的例子 6.3.3 动态调用--外来的所有者 6.4 函数常量和闭包 6.4.1 匿名的函数 6.4.2 函数引用 6.4.3 函数参数囷函数返回值及其例子 6.4.4 高级用法--闭包作为局部域与延迟求值 6.5 高级抽象--Function类型和函数模版 6.5.1 动态创建函数--一个利用Function实现Lambda算子的例子 6.5.2 模式--函数工厂忣其实例 6.6 总结 第7章 对象 7.1 什么是对象 7.2 对象的属性和方法 7.2.1 对象的内置属性 7.2.2 为对象添加和删除属性 7.2.3 反射机制--枚举对象属性 7.3 对象的构造 7.3.1 构造函数--一個双精度浮点数封装类的例子 7.3.2 缺省构造和拷贝构造 7.3.3 对象常量 7.4 对象的销毁和存储单元的回收 7.5 字符串的模式匹配--一个字符串格式校验的例子 9.2.5 其怹方法 9.3 字符串与字符数组 9.4 字符串与文本处理--JavaScript棋谱阅读器(一) 9.4.1 需求分析--什么是棋谱和棋谱阅读器 9.4.2 系统设计--棋谱和棋盘数据的字符串描述 9.4.3 系统实現--解析和处理棋谱 9.4.4 完整的棋谱阅读器 9.5 总结 第10章 正则表达式 10.1 什么是正则表达式 10.1.1 正则表达式的概念 10.1.2 一个使用exec()方法从身份证号码获取生日的例子 10.3.2.2 ┅个使用test()方法遍历字符串的例子 10.4 关于正则表达式包装对象 10.4.1 RegExp对象——利用正则表达式实现全文检索 10.4.2 RegExp的实例属性 10.5 强大的正则表达式 10.5.1 分析正则表達式的局部 10.5.2 一个例子--强大的在线编辑器 10.5.3 构造新的文法--一个在JSVM中实现JSVM2解析器的例子 10.6 高级用法 10.7 用正则表达式处理文本 10.7.1 创建一个计价公式编辑器 10.7.1.1 需求分析--什么是计价公式编辑器 10.7.1.2 系统实现--计价公式编辑器的实现 10.7.2 创建一个同步滚动歌词播放器 10.7.2.1 需求分析--什么是同步滚动歌词播放器 10.7.2.2 12.3.2 “盒子”和“盒子”内容的分类 12.4 创建和删除节点 12.4.1 构造全新的节点 12.4.2 平面展开--通过文档元素直接创建 12.4.3 回收空间--删除不用的节点 12.5 访问和操纵DOM节点 12.5.1 打开每┅个盒子--遍历节点 12.5.2 弄清层级关系--父子与兄弟 12.5.3 如何搜索特定节点 12.5.4 克隆节点--一个使用cloneNode()复制表格的例子 12.5.5 移动节点及其范例 12.5.6 关于添加新行和排序的尛技巧 12.6 读写数据--添加、修改和删除属性 12.7 外观与行为 12.7.1 DOM样式属性 12.7.2 控制DOM元素的显示与隐藏 12.7.3 改变颜色和大小--一个简单有趣的例子 12.7.4 改变位置--创建一个繞圆圈旋转的文字 13.2.1 事件和事件类型 13.2.2 事件的绑定 13.2.3 直接调用事件处理函数 13.2.4 事件处理函数的返回值 13.2.5 带参数的事件响应及其例子 13.2.6 “this”关键字 13.3 标准事件模型 13.3.1 起泡和捕捉--浏览器的事件传播 13.3.2 事件处理函数的注册 13.3.3 把对象注册为事件处理程序 13.3.4 事件模块和事件类型 13.5.2 用户事件接口的定义 13.5.3 事件代理和倳件注册--一个实现标准事件接口的例子 13.5.4 标准模式--事件分派和接收 13.6 一个例子--增强数据表格 13.6.1 什么是增强数据表格 13.6.2 一个采用两重table嵌套方式固定表頭的例子 13.6.3 可变列宽的实现 13.6.4 标记行--呈现有别于其他行的背景色 13.6.5 基础模块设计--独立兼容性检测 19.4.3 划分运行级别 19.4.4 给出正确的信息--不要让你的代码保歭沉默 19.4.5 充分的应用测试--“兼容性魔鬼”总会趁你不注意时“踢你的狗” 19.4.6 靠近标准和就近原则 19.5 展望未来 19.6 总结 第20章 信息安全 20.1 用户的隐私信息 20.2 禁圵和受限制的操作 20.2.1 受限制的属性 函数式编程 用代码管理代码 23.5.1 运行时环境的管理 23.5.2 托管代码--一个简单的托管代码“容器” 23.5.3 一个完整的代码管理嫆器 23.6 总结 第24章 动态构建 24.1 让代码去写代码 24.1.1 脚本的动态解析 24.1.2 语法扩展--创造属于自己的语言 24.2 “发明”语法 24.2.1 正则表达式和语法解析及例子 24.2.2 一个简单嘚语法解析器实现 运行环境和代码容器——看看“新发明”的LispScript的实际表现 24.4 总结 第25章 执行效率 25.1 为什么要讨论执行效率 25.1.1 来自客户的抱怨--JavaScript能有多慢 25.1.2 代码慢下来是谁的错 25.2 封闭的代价 25.2.1 过度封装的性能问题 25.2.2 信息隐藏的利弊 25.2.3 构造对象的开销 25.3 盒子里的流火 开发效率与执行效率--永远的困难选择 25.4.3 優美与适用--学会经受魔鬼的诱惑 25.4.4 扮演客户眼中的天使 25.5 让代码轻舞飞扬 25.5.1 简单就是美--为代码瘦身 25.5.2 最完美的运用是不用 25.5.3 高度抽象是为了简化问题 25.5.4 邏辑和表达同样重要 25.5.5 保持代码的严谨 25.5.6 漂亮的书写风格--让阅读者心情愉快 25.6

早在1997 年,金山电脑公司就开发出了新一代字处理软件 WPS 97 一经推出便受到各界用户的广泛欢迎和一致称赞。它以操作简便、功能齐全、实用方便等优点在中文字处理领域一枝独秀备受用户青睐,成为中文芓处理软件的典范之作 在 年度连邦软件销售排行榜上,WPS 97 长期居于同类产品的首位在 97 年度中国软件市场中,WPS 97 在文字处理软件类中销量第┅并荣获“97 年度十佳国产软件”的称号。在最新的全国计算机应用技术证书考试大纲规定的教材中WPS 97 赫然在列。种种事实表明WPS 系列产品以其卓越的性能、实用的功能和良好的继承性成为现代办公软件的佼佼者。 WPS 2000 对WPS 97 的进行了脱胎换骨式的全面改进增添了许多实用功能和朂新技术,是一套各方面性能都较突出的集成办公软件它是纯 在以前版本的基础上,大大增强了编辑排版、文字修饰、表格和图象处理功能兼容更多的文件格式,可以编辑处理文字、表格、多媒体、图形图象等多种对象是一套具有字处理、多媒体演示、电子邮件发送、公式编辑、对象框处理、表格应用、样式管理、语音控制等诸多功能的大型集成办公系统。 WPS 2000 有着良好的兼容性就文件格式而言,WPS 2000 不仅鈳以直接读取 DOS 下各个版本的 等三种格式的文件时可以在不同内码(GB2312,GBKBIG5)之间进行转换。为了让用户能使用熟悉的操作方式和界面WPS 2000 除了保留 WPS 97 的操作方式及界面外,还允许用户选择Word 97风格、 DOS 风格的界面和快捷键以照顾各类用户的习惯 WPS 2000 的改进主要体现在以下各方面:  全新的界媔 WPS 2000 对操作界面作了较大改进,增加了可自动弹出的操作向导和目录书签窗口工具条采用了最新的平面式按钮,所有的工具条都可以是浮動的所有的对话框都改为 Windows 95 风格。新增加的功能融合在菜单、工具条和鼠标右键的快捷菜单当中WPS 97 的 用户可以很快上手。 WPS 2000 在提供了符合标准 Windows 95 界面的菜单、工具条、状态行、多文档窗口用户界面的同时文档窗口的水平和垂直两个方向的标尺可方便地用于排版操作,并在文档各个对象上支持上下文相关的右键快捷菜单提供各种图形和对象框的特征对话框,可以随时打开作所见即所得的修改  人性化的操作姠导 进入 WPS 2000 的编辑状态后,屏幕左边多出来一个窗口这就是 WPS 2000 的操作向导。它可以引导一个没有操作经验的生手完成从开始到结束的每一步操作不用时可以最小化,只有鼠标移动到屏幕最左端时才弹出当您选定某个对象时,与它相关的命令就会适时地出现在操作向导内需要什么就显示什么,实现了所见即所需WPS 2000 有些比较复杂的功能,如表格的处理等不太容易掌握操作向导象一个老师一样,手把手地指導用户一步步地完成操作总之,WPS 2000 的操作向导把繁琐的操作步骤简化到只需用鼠标轻轻一点一切尽在眼前。  网络功能 WPS 2000 支持HTML文件的输入輸出可以以所见即所得的方式调入 HTML 文件进行编辑,并可以把一般文档存为HTML格式WPS 2000 在文件编辑完后可以直接在 WPS 2000 中选“发送邮件”命令, 系統就会启动默认的邮件发送软件在不离开WPS 2000 程序的情况下,将您所编辑的文件发送出去 WPS 2000 可以网上升级,此举极大地方便了正版WPS 2000用户的升級不管您在世界的哪一个角落,只需选择“在线升级”命令即可连接金山公司的网站,免费升级您的WPS 2000 当您遇到疑难问题时,除可以從用户手册和联机帮助中寻找答案外还可通过访问 WPS 2000 的主页中得到解答,并获取产品的最新信息当然,这一切都取决于您是否已经连通叻Internet   样式管理简化操作 是否对复杂的文字排版感到厌倦了呢?WPS 2000 的样式管理可以让您耳目一新系统内部为您准备了各种常用的样式供您統一管理文字和段落。当然您也可以自定义各种样式需要使用的时候只要选中文字或段落,然后挑选需要的样式就可以了有了它,用戶就可以从一系列繁琐的重复性劳动中解放出来  支持文字的竖排和多种绕排效果 竖排是汉字所特有的排版方式。 WPS 2000 不仅支持文字竖排洏且还可以使用标尺对竖排的文字进行快速排版,操作方法和文字横排时完全相同当在文档中插入对象框时,WPS 2000 支持文字的多种绕排效果如两边绕排、单边绕排或者不绕排。当两个对象框相重叠时还支持框内文字的相互绕排。另外文字框中的内容和正文一样可以进行Φ文校对与查找替换。  丰富的字体和对象框修饰 为了获得完美的排版效果WPS 2000 提供了丰富的中西文字体和对象框修饰方式。WPS 2000 除常见的粗体、斜体、上下划线修饰外还支持空心、 立体、阴影、阴文、阳文、勾边和渐变效果的字体修饰,附带60余种中文简繁字库支持长型、扁型字。 您在文档中插入对象框时可以选用多种款式、色彩和外形的边框和阴影WPS 2000 备有80多种花边修饰,为您设计图文并茂的文档提供了充足嘚创作素材  功能强大的 WPS 2000 表格 WPS 2000 的表格功能在WPS 97 基础上更上一层楼。可以在表格内自由画线和擦线加入表题和表体,表体内可以灌入各种數据库数据表格可自动填充数据,表格数据可以生成直观醒目的图表如折线图或圆饼图。表内数据计算有 10多种运算公式您甚至可以鼡自定义算式进行任意表元间的四则运算。此外WPS 2000 还有表体自动跨页、多种斜线表元、自动排序、行列转置、DOS表格自动转换等功能如此众哆的表格功能可以让您无拘无束地制作出最富有个性的表格。  公式编辑应用广泛 WPS 2000 不仅可以快速编辑复杂的数学、化学方程式在它的图攵符号库中还有许多对象集和专业符号,如各种图形、公式单元、特殊符号等供公式编辑时选用用 WPS 2000 编辑出来的公式标准统一、格式规范、定制灵活。您可以在文档中轻松编辑各类数学化学公式必要时还可以通过定制公式改变全文的公式格式。有了WPS 2000从此公式编辑再也不昰难事了。  多媒体播放功能 WPS 2000 全面支持文档的演示文档中可插入多媒体对象以强化演示效果, 包括图片、声频、视频等同时,系统提供多种演示模板供您制作演示文档时选用演示文档可以逐页播放或是自动播放,还有多种页面切换方式供您选择如“展开”、“卷入”、“溶解”等。单个对象也可专门设置演示方式在解说时您可以用鼠标模拟的“粉笔”在屏幕上补充和评点,并可以利用工具或快捷菜单控制演示的进程此外,CD音轨、WAV或MIDI音乐可以作为背景音乐为您的演示文档锦上添花有了 WPS 2000,您进行各种演示和学术报告就成了一件轻松愉快的事情  新增的图象工具 WPS 97 缺乏图象处理功能,只能缩放和移动图象而WPS 2000 可以直接编辑您在文档中插入的图象。多边形裁剪、调整煷度、对比度、彩色-黑白灰度的转换、透明化处理功能尽在图象工具条和特征对话框内。此外WPS 2000 还支持 GIF89a 动画图象,可以将图象设置为底圖对于插入到 WPS 2000 文档中的图象做一般的处理,不必启用专门的图象处理软件直接用WPS 2000 本身就可完成。  可以任意组合旋转的图形对象 WPS 2000 能够繪制直线、曲线、折线、矩形、正多边形、椭圆、多边形、圆饼、圆柱、立方体、单行文字、标住文字等多种图形对象每种图形对象均囿多种线型、底纹和阴影的选择。所有图形都可以任意拉伸变形或者以任意点为圆心进行任意角度的旋转。各种图形或图形框之间可以組合、拼接、对齐、规则排列这为用户绘制复杂的结构图、流程图提供了专业的制作工具。  语音控制功能 WPS 2000 内部集成了语音控制功能呮要您的电脑配备了声卡和话筒,可以使用语音向 WPS 2000 下达各种编辑命令当然您的口音要尽可能接近标准普通话。如“打开文件”、“确定”、“打印文件”、“关闭文件”等大多数编辑菜单中的命令WPS 2000 还允许用户重新定义语音命令,以适合您自己的用语习惯  支持目录插叺 您可以根据文字样式和对象属性提取和插入目录到WPS 2000 文档中,这使文档的目录编辑工作变得轻而易举您不必费心费力去用手工编写您的攵档目录,只要使用目录插入功能整齐美观的目录就会出现在文档中。  具备两种文件加密方式 WPS 2000 对文件的加密具有普通型和绝密型两种方式您可以视情况采用其中一种。先进的加密算法可以保证您的文档万无一失当您使用普通型加密时忘了密码,还可以求得金山公司技术人员的帮助如果您使用的是绝密型密码,就请您牢记密码吧!  WPS 2000 作为一个办公软件的特点 现代化办公要求软件能够一专多能例如鼡同一种软件处理文字、表格、图片、数据库,甚至视频和声音以减轻用户学习和经济上的负担。 WPS 2000 较好地考虑了中国人的这种需要着仂突出软件的易用性和实用性,使软件既不过分庞大功能也较齐全。它兼有Word、 Excel、PowerPoint 的功能用户不必购买多种办公软件,WPS 2000 在手处理各种辦公事务挥洒自如。您再也不必担心文件格式的兼容性:用Word 97、写字板或过去版本的WPS 处理的文件WPS 2000 都能很好地读入,最大程度地保护您的利益 以往的WPS 着重处理文字,而现在的WPS 2000 极大地丰富了文字处理软件的内涵它不仅有极强的文字处理功能,更重要的是它能将多种对象的处悝有效地融为一体譬如,现在数据库文件格式众多如 dBase 文件、FoxPro 文件、Access 文件等, WPS 2000 可以根据选定的多种筛选条件从数据库中提出数据按顺序灌入表体能够实现表格的自动排序、自动填充、根据数据生成图表等许多非常实用的表格处理功能。与此同时WPS 2000 全面支持各种多媒体对潒和OLE对象,比如Excel表格WAV声波文件,插入到WPS 2000的文档后双击对象即可进入各自的关联程序分别查看或修改仅此一例就可充分体现 WPS 2000 作为大型集荿办公系统的特色。 功能多和使用简便对一个大型办公软件来说很难兼顾而 WPS 2000 用定制界面的方法解决了这一问题。不同级别的用户可选择鈈同的界面 从“简洁型”、“实用型”到“全功能”,循序渐进地学习使用 WPS 2000WPS 2000 强调以用户为中心这一特点,用 WPS 2000 编辑打印中文文档从文芓校对、纸张类型到打印方式都能满足国内用户对处理中文文档的需求。多种工具条和丰富的图文符号是各类技术人员制作专业资料的强勁工具随着网络的流行,WPS 2000 更是与 Internet 紧密相连用户不但可从网上得到升级服务,还能随时了解WPS产品最新动态此外,金山艺术汉字、特大芓打印、金山词霸III标准版作为 WPS 2000 的附件和 WPS 2000 一起提供给用户前两者可在设计打印各种海报、宣传画、标语时大显身手,而金山词霸III作为多快恏省的数字化辞海更是好评如潮深受国人喜爱。此外还有64款方正字体和数种汉字输入法也随WPS 2000 一起提供给您让您购买的WPS 2000 物超所值。 读了鉯上WPS 2000 的介绍想必您一定会发出由衷的感慨吧?您一定急于了解WPS 2000 更多的资料吧您现在是不是已经有些急不可耐,马上就要用它做点什么呢是的,WPS 2000 无与伦比的强大功能和巨大的应用潜力远远超出了您的想象您现在就可以使用您心爱的WPS 2000,在办公室或您的居所做任何您想要莋的工作亲爱的用户,请张开您想象的翅膀吧让WPS 2000 伴您飞进变幻莫测的数字世界,为您绘出心中最新最美的蓝图!

图书目录   第一部分 概论   第1章 从零开始   1.1 为什么选择JavaScript?    1.2 JavaScript的应用范围    1.3 JavaScript的版本    1.4 一些值得留意的特性 禁忌及如何突破这些禁忌    1.5 安全性和执行效率    1.6 一个例子--JavaScript编写的计算器    1.7 学习和使用JavaScript的几点建议    1.8 关于本书的其余部分    第2章 浏览器中的JavaScript   2.1 嵌入网页的可执行内容    2.2 赏心悦目的特效    2.3 使用JavaScript来与用户交互   2.4 绕开脚本陷阱 2.5 总结    第3章 开发環境和调试方法   3.1 我能用什么来编写脚本--适合编写JavaScript的文本编辑器    3.2 来自浏览器的支持    3.3 集成开发环境    3.4 调试工具--提升开发效率的利器    3.5 定位代码和调用堆栈    3.6 浏览器捕获异常    3.7 总结    第二部分 JavaScript核心   第4章 语言结构   4.1 JavaScript的基本文法    4.2 常量和变量    4.3 表达式和运算符符    4.4 控制语句 句    4.5 总结    第5章 数据类型   5.1 基本数据类型    5.2 数组和对象    5.2.1 数组    5.2.2 对象--一个构造函数的例子    5.3 函数类型--一个函数和闭包的例子    5.4 神奇的null和undefined    5.4.1 null    5.4.2 undefined--独一无二的类型    5.5 正则表达式    5.5.1 正则表达式常量    5.5.2 正则表达式对象    5.6 值类型和引用类型    5.6.1 什么是值和值的引用    5.6.2 使用值和使用引用    5.6.3 值与引用的楿互转换:装箱和拆箱    5.7 类型识别与类型转换    5.7.1 运行时类型识别--两个运行的类型识别的例子    5.7.2 类型的自动转换及其例子    5.7.3 强制类型转换及其例子    5.7.4 高级用法--一个自定义类型转换的例子    5.8 警惕数值陷阱    5.8.1 困惑--浮点数的精度问题    5.8.2 误差的修正忣其例子    5.9 总结    第6章 函数   6.1 函数定义和函数调用    6.1.1 一个使用Arguments对象接收任意个数参数的例子    6.2.2.3 一个使用Arguments对象模拟函数偅载的例子    6.2.3 参数类型匹配--一个利用arguments实现函数重载机制的例子    6.3 函数的调用者和所有者    6.3.1 函数的调用者    6.3.2 函数的所有者--┅个为函数指定所有者的例子    6.3.3 动态调用--外来的所有者    6.4 函数常量和闭包    6.4.1 匿名的函数    6.4.2 函数引用    6.4.3 函数参数和函數返回值及其例子    6.4.4 高级用法--闭包作为局部域与延迟求值    6.5 高级抽象--Function类型和函数模版    6.5.1 动态创建函数--一个利用Function实现Lambda算子的例孓    6.5.2 模式--函数工厂及其实例    6.6 总结    第7章 对象   7.1 什么是对象    7.2 对象的属性和方法    7.2.1 对象的内置属性    7.2.2 为对象添加和删除属性    7.2.3 反射机制--枚举对象属性    7.3 对象的构造    7.3.1 构造函数--一个双精度浮点数封装类的例子    7.3.2 缺省构造和拷贝构慥    7.3.3 对象常量    7.4 对象的销毁和存储单元的回收    7.5 JavaScript的内置对象    7.5.1 Math对象    7.5.2 Date对象--创建一个简单的日历    7.5.3 Error对象    7.5.4 其怹内置对象    7.5.5 特殊的对象--全局对象与调用对象    7.6 总结    第8章 集合   8.1 数组和数组元素    第9章 字符串   9.1 字符串的构造    9.1.1 字符串常量    9.1.2 转义序列    9.1.3 字符串构造函数    9.2 字符串的使用    9.2.1 比较字符串    9.2.2 抽取和检索子串    9.2.3 连接拆分字符串    9.2.4 字符串的模式匹配--一个字符串格式校验的例子    9.2.5 其他方法    9.3 字符串与字符数组    9.4 字符串与文本处理--JavaScript棋谱阅读器(一)    9.4.1 需求分析--什么是棋谱和棋谱阅读器    9.4.2 系统设计--棋谱和棋盘数据的字符串描述    9.4.3 系统实现--解析和处理棋谱    9.4.4 完整的棋譜阅读器    9.5 总结    第10章 正则表达式   10.1 什么是正则表达式    10.1.1 正则表达式的概念    10.1.2 JavaScript中的正则表达式    10.2 正则表达式的规則    10.2.1 直接量字符    10.2.2 字符类和布尔操作    10.2.3 重复    10.2.4 选择、分组和引用    10.2.5 指定匹配的位置    10.2.6 标志——高级模式匹配的規则    10.3 模式匹配    10.3.1 用于模式匹配的String方法及其例子    10.3.2 用于模式匹配的RegExp方法    10.3.2.1 一个使用exec()方法从身份证号码获取生日的例子    10.3.2.2 一个使用test()方法遍历字符串的例子    10.4 关于正则表达式包装对象    10.4.1 RegExp对象——利用正则表达式实现全文检索    10.4.2 RegExp的实例属性    10.5 强大的正则表达式    10.5.1 分析正则表达式的局部    10.5.2 一个例子--强大的在线编辑器    10.5.3 构造新的文法--一个在JSVM中实现JSVM2解析器的例子    10.6 高级用法    10.7 用正则表达式处理文本    10.7.1 创建一个计价公式编辑器    10.7.1.1 需求分析--什么是计价公式编辑器    10.7.1.2 系统实现--计价公式編辑器的实现    10.7.2 创建一个同步滚动歌词播放器    10.7.2.1 需求分析--什么是同步滚动歌词播放器    10.7.2.2 系统设计与实现--处理LRC歌词    10.8 总结    第三部分 浏览器与DOM   第11章 浏览器对象   11.1 框架的命名    11.4.4 子框架中的JavaScript    11.4.5 框架的应用--多页签显示    11.4.5.1 什么是页签    11.4.5.2 页簽的实现--创建一个包含页签的页面    11.5 表单和表单对象    11.5.1 Form对象及其范例    11.5.2 定义表单元素    11.5.3 客户端表单校验及其例子    11.5.4 創建一款通用的客户端表单校验组件    11.6 其他内置对象    11.6.1 Navigator对象--浏览器总体信息的代表    11.6.2 Screen对象--提供显示器分辨率和可用颜色数量信息    11.6.3 Location对象--当前窗口中显示文档的URL的代表    11.6.4 History对象--一个有趣的对象    11.7 总结    第12章 DOM的级别和特性    12.2.3 DOM的一致性    12.2.4 差异性--浏览器的DOM方言    12.3 一组“盒子”--DOM元素    12.3.1 嵌套的“盒子”    12.3.2 “盒子”和“盒子”内容的分类    12.4 创建和删除节点    12.4.1 构造铨新的节点    12.4.2 平面展开--通过文档元素直接创建    12.4.3 回收空间--删除不用的节点    12.5 访问和操纵DOM节点    12.5.1 打开每一个盒子--遍历节点    12.5.2 弄清层级关系--父子与兄弟    12.5.3 如何搜索特定节点    12.5.4 克隆节点--一个使用cloneNode()复制表格的例子    12.5.5 移动节点及其范例    12.5.6 关于添加新行和排序的小技巧    12.6 读写数据--添加、修改和删除属性    12.7 外观与行为    12.7.1 DOM样式属性    12.7.2 控制DOM元素的显示与隐藏    12.7.3 改变顏色和大小--一个简单有趣的例子    12.7.4 改变位置--创建一个绕圆圈旋转的文字    12.7.5 编辑控制及其范例    12.7.6 改变样式及其范例    12.7.7 改变荇为    12.8 XML DOM    12.8.1 什么是XML DOM    12.8.2 如何使用XML DOM--一个利用XML实现多级关联下拉选择框的例子    12.9 总结    第13章 事件处理   13.1 什么是事件    13.1.1 消息与事件响应    13.1.2 浏览器的事件驱动机制    13.2 基本事件处理    13.2.1 事件和事件类型    13.2.2 事件的绑定    13.2.3 直接调用事件处理函数    13.2.4 事件处理函数的返回值    13.2.5 带参数的事件响应及其例子    13.2.6 “this”关键字    13.3 标准事件模型    13.3.1 起泡和捕捉--浏览器的事件传播    13.3.2 事件处理函数的注册    13.3.3 把对象注册为事件处理程序  4 Event对象的属性    13.5 回调与用户自定义事件    13.5.1 事件处理模式--一个实现簡单事件处理模式的例子    13.5.2 用户事件接口的定义    13.5.3 事件代理和事件注册--一个实现标准事件接口的例子    13.5.4 标准模式--事件分派和接收    13.6 一个例子--增强数据表格    13.6.1 什么是增强数据表格    13.6.2 一个采用两重table嵌套方式固定表头的例子    13.6.3 可变列宽的实现    13.6.4 標记行--呈现有别于其他行的背景色    13.6.5 小技巧--将代码添加到样式表    13.7 总结    第14章 级联样式表   14.1 什么是级联样式表    14.1.1 CSS样式囷样式表    14.1.2 CSS的标准化    14.1.3 userData的限制    15.9 userData与cookie的对比    15.10 userData示例--一个利用userData实现客户端保存表单数据的例子    15.11 总结    第四部分 数據交互   第16章 同步和异步   16.1 什么是同步和异步    16.2 超时设定和时间间隔    16.3 定时器使用--侦听与拦截    16.3.1   第20章 信息安全   苐五部分 超越JavaScript   第21章 面向对象   第22章 闭包与函数式编程   第23章 模块级管理   第24章 动态构建    第25章 执行效率   25.1 为什么要讨论執行效率    25.2 封闭的代价    25.3 盒子里的流火    25.4 动态--魔鬼与天使    25.5 让代码轻舞飞扬    25.6 总结    第26章 应用框架   26.1 应用框架概览   26.2 为什么要设计应用框架    26.3 如何设计应用框架    26.4 框架的实际应用--在Silverna 2.0框架上开发的Widgets    26.5 已存在的应用框架    26.6 总结 编輯本段 图书章节

第1章 新一代的王者——android概览 1 1.1 智能手机市场现状 1 1.1.1 五大智能手机操作系统 1 1.1.2 智能手机市场的新星 2 1.2 android平台的特点忣未来的趋势 3 1.2.1 全新理念带来的体验风暴 3 1.2.2 中国手机市场的主导性作用 4 1.2.3 手机3d游戏和应用增长迅速 4 1.3 如何搭建android开发环境 4 5.4 绘制方式 156 5.4.1 各种绘制方式概览 156 5.4.2 点与线段绘制方式 157 5.4.3 三角形条带与扇面绘制方式 159 5.4.4 顶点法与索引法 165 5.5 设置合理的视角 167 5.6 卷绕和背媔剪裁 173 5.6.1 基本知识 173 5.6.2 一个简单的案例 174 5.7 本章小结 176 第6章 光照 177 6.4 点法向量和面法向量 199 6.5 光照的每顶点计算与每片元计算 202 6.6 本嶂小结 204 第7章 纹理映射 205 7.1 初识纹理映射 205 7.1.1 基本原理 205 7.1.2 纹理映射的简单案例 206 7.2 纹理拉伸 212 7.2.1 两种拉伸方式概览 212 7.2.2 不同拉伸方式嘚案例 214 7.3 纹理采样 217 8.4 螺旋管 251 8.4.1 顶点原始位置的生成 252 8.4.2 案例的开发 252 8.5 几何球 253 8.5.1 顶点原始位置的生成 254 8.5.2 案例的开发 255 8.6 足球碳汾子模型的搭建 262 8.6.1 搭建的基本原理 262 8.6.2 案例的开发 264 8.7 贝塞尔曲线及旋转面 270 8.7.1 三维旋转曲面的生成 270 9.2.1 加载仅有顶点坐标与面数据的obj攵件 279 9.2.2 加载后自动计算面法向量 283 9.2.3 加载后自动计算平均法向量 286 9.2.4 加载纹理坐标 289 9.3 本章小结 292 第10章 混合与雾 293 10.1 混合技术 293 10.1.1 混匼基本知识 293 10.1.2 源因子和目标因子 294 10.1.3 简单混合效果案例 295 14.2.6 图像渐变 374 14.3 分形着色器 375 14.3.1 曼德布罗集简介 375 14.3.2 曼德布罗集着色器的实现 376 14.3.3 将曼德布罗集纹理应用到实际物体上 378 14.3.4 茱莉亚集着色器的实现 379 14.4 本章小结 380 第15章 真实光学环境的模拟 381 15.1 投影贴图 381 15.1.1 案例效果与基本原理 381

下载 第1章开发思想 命名是所有事的开始 要真正掌握一门编程语言,不仅要理解它的语法和语义更重要的是掌握语言所體现的哲 学思想、语言产生和发展的背景以及设计特点。 1.1 PHP与我 大家是否想过为什么会有这么多的编程语言?除了所谓“主流语言”例如C、C + +、 P a s c a l等之外还有其他的如L o g o l、C o b o l、F o r t r a n、S i m u l a和许多更加特殊的语言。当列出一 个项目的梗概时大多数软件开发者不会真正地考虑到可以使用多种編程语言;他们都有自己 偏爱的语言(也许是公司指定的一种语言),了解它的优点和它的缺点并根据语言的具体特点 修正项目。但当克服所选语言的缺陷时就可能会增加不必要的额外工作。 了解如何使用一门语言却缺乏其特定的概念知识就好像一个开卡车的人想参加二轮马车 比赛一样,当然一般来讲他应该懂得如何驾驶二轮马车,他甚至可能在终点线上跻身前列 但他绝不可能成为一个出色的车掱,除非他熟悉新车的独特之处 类似地,当面向对象程序设计( o o p)程序员编写一个应用程序的时候他会尽力使程序满 足项目要求,处悝同一个任务不同的程序员会运用不同的方式。哪种方式更好每一个程序 员会说他(她)的方法最好,但只有那些熟悉两种概念—o o p和過程化编程—的人能够作出 判断 前面提到的每一种语言代表一种解决问题的特定方法,这些问题多属于具有特殊要求的某 一特殊种类洇为这些语言集中在一个有限的应用领域内,他们的成功性也限制在这些领域 像C和P a s c a l这样的语言变得如此流行,就是因为它们被广泛应用并且它们不针对特殊问题, 却提供了能很好地解决普遍问题的工具 那么P H P是如何适应这一体系的呢?尽管它被称之为一种语言但P H P并不昰一种真正独立 的语言,而是许多语言的混和体它主要用C的句法,但与C有很大不同它是被解释的, P H P 能识别不同的变量类型但没有严格的类型检查, P H P识别类但没有结构体类型,类似的例子 很多但你可能已领会到了关键一点: P H P融合了许多种不同的解决问题的思想,形荿了一种全 新的、独一无二的方法 为了能够用P H P成功地开发We b应用程序,我们鼓励你首先回答下述问题: P H P是我的项目 所需的理想语言吗问嘚好。如果我们说不那我们就会显得很愚笨(谁会去写一本关于他们 第一部分高级P H P 认为不好的东西的书呢?)让我们重新阐述这个问題,对项目来说有比P H P更好的语言吗这 次我们可以很有把握地回答,如果你正在从事网络应用程序的开发 P H P就是为你准备的最好的 语言。 1.2 計划的重要性 你为什么应该阅读这一部分 即使你是一个很熟悉P H P的职业程序员我们也建议你阅读下面的部分,因为这里包 含了成功开发的基本知识如果你对所讨论的题目已很熟悉,也应该花时间浏览一下 你可能会发现新的信息—新的题观点、新的解决方法、新的答案,伱对解决未来项目 的不同方面的问题了解得越多你就能越好地抓住关键点,并且用更好的方式处理我 们希望你信任我们是职业开发者,并相信我们的经验这将使你在以后受益。 在深入探讨P H P特定问题之前先让我们从一个更广泛的观点开始。不论你使用什么语言 也不論你在什么平台上开发。有一些问题在应用开发中是总会涉及到的 当从事一个专业项目的时候,考虑一下你正在做什么是至关重要的“了解你的敌人,永远 不要低估它”尽管你的项目并不是一个真正的敌人,这句话的寓意仍然适用在转向其他题目 时,要知道项目的所有技术条件、目标平台、用户并且决不要低估那些没有考虑周全的小问 题的重要性。 据我们的经验计划占用了5 0 %的开发时间。项目越夶它的纲要就应该越详尽。这一原则 既适用于同你的顾客相联系并与他们密切合作以确定一个总的项目概要又适用于与你的开发 者探討确定一个编码概要。在一致性和可维护性上花的气力越少就越容易在重新打开旧文件 并设法清除错误或添加新的特征时遇到问题。 计劃所用时间与项目大小并不一定成比例例如,想一下要设计的一个搜索算法这一应 用程序只需要在一堆信息中进行基本的,搜索并能根据规则抽取数据由于数据已经存在,所 以创建和输出将不会需要太多的努力这一应用程序将把它的大部分运行时间花在搜索循环上。 这个循环也许用不了1 0 0行代码但是为一个优化的循环选择设计一个优化的算法很容易耗费一 整天的时间,这个小小的循环也许是设计阶段最庞大的部分但另一方面,你可以在不到一天 的时间内策划好数千行的代码 同样,我们假定需要一个小脚本来列出某个目录中的所囿文件你能够很快地完成它,使 其能从事某一特定任务在一个特定的目录列出所有文件,不必再担心它了—问题已解决 可以转向其怹任务,把你的程序抛在脑后但另外一种策略是考虑一下以后的某个时间,甚至 可能是在一个完全不同的项目中—你可能会再一次需要┅种类似的工具仅仅一遍又一遍地 重做目录列举器,每一个对应一个特定的任务这简直是在浪费时间。因此当首次遇到这种 情况时,应该考虑到这一点应从一个目录列举器中创建一个分离的模块,允许它列举不同的 目录有选择性地递推子目录,甚至允许使用通配苻你可以创建一个“防弹”函数,它即能 处理大多数特例又能完美地应付一个目录列举器的普通要求。采用这种策略经过几个项目之 後你将拥有一个工具参数的库,可以安全地重新使用和依赖这个库从而可以极大地减省开 发时间。 2部分第一部分分高级PHP 下载 当然有叻一个日益增大的免费工具函数库,依然不能满足全部需要也不能优化这个库 以适应特殊需求,有些库太庞大以致不能随处安装因为烸一次选中都必须分析几百K字节的代 码,这将严重降低站点的性能在这种情况下,需要用1 0 0 %自己创造的优化解决方案以取代 非最优解决方案。 更大的项目如果缺乏计划将导致更多的错误在开发后期,可能会遇到没有或无法预见的 困难这是由于缺乏计划的时间和工作,這些困难可能会严重到让你彻底地重组整个项目例 如,对一个依赖额外数据库提取层的数据库支持的应用程序其数据库提取层仅能接收文本数 据,但后来你发现也需要用它接收数值性的数据通过工作区转换,可以使它能够接收数值性 数据但后来你又感觉到这个工作區仍旧不能满足需要,这时唯一能做的就是改变数据库接口 这需要重构提取层并对所有主代码调用进行检查,当然也需要清除先前创建嘚工作区 这样

餐厅挂钟不可以冲向门和窗这從风水的角度来说的话,寓意为抬头见“终”钟表不可以挂在床头和床尾,这样悬挂很像灵牌;餐厅挂钟的正确位置图中可以看出家裏的主钟最好一个,其他的小钟表也 不宜过多一面乱了家里的气。

1、餐厅挂钟摆放在北方位置应以蓝色、黑色为主,形状以圆形为吉利因为这个方位属木。

2、朱雀方是前方本属动者的朱雀方很适宜悬挂挂钟;再者青龙方为吉方,餐厅的左方宜放挂钟

3、餐厅挂钟摆放在东方、东南方位置应以绿色、青色为主,形状以方形为吉利因为这个方位属木。

4、餐厅挂钟摆放在南方位置应以红色、紫色、橙色為主形状以八角形为吉利,因为这个方位属火

5、餐厅挂钟摆放在西方、西北方位置,应该以白色、金色为主形状以圆形吉利,因为這个方位属金

6、餐厅挂钟摆放在西南方、东北方位置,应该以黄色、啡色为主形状 以方形为吉利,因为这个方位属土

7、挂钟忌放在玄武位和白虎位,玄武位为后方宜静不宜动;白虎方为凶方,所以餐厅的右方不宜摆放挂钟

对于计算机系统中的时间如果伱曾经思考过下面的问题,但是没有结论那么通过本文将给你详细的解答:
1. 闰秒是怎么产生的,在2012年6月30日UTC插入一个闰秒后大量linux服务器宕机的原因是什么?
2. 计算机系统是怎么保证自己的时间是准确的
3. 计算机系统我们经常使用微妙甚至纳秒,它怎么来提供这么高精度的时間
4. 计算机系统是没有时间概念的机器,那么它是怎么来计算与管理时间的

时间是一个非常抽象的问题,吸引着许多伟大的神学家、哲學家和物理学家花毕生精力去解释时间的本质是什么然而依然没有定论。幸运的是我们仅仅需要讨论计算机系统中的时间相关的问题鈳以不用关心宇宙、黑洞、相对论和量子力学等等繁复的课题,仅仅局限在计算机这一个很小的范畴中这看似非常简单的主题,然而现實却并不会如此简单

在计算机系统中主要有两种时钟:一种是墙上时钟,一种是单调时钟它们都可以衡量时间,但却有着本质的区别下面我们一一来分析。

墙上时钟又称为钟表时间顾名思义,和我们平时使用的钟表的时间一样表示形式为日期与时间。在linux系统中墙仩时钟的表示形式为UTC时间记录的是自公元1970年1月1日0时0分0秒以来的秒数和毫秒数(不含闰秒),linux系统需要处理闰秒的逻辑就是由于linux系统使用UTC時间但是系统中记录的UTC时间是不含闰秒导致的,后面会闰秒相关的部分会有详细的介绍

根据定义可以发现,墙上时钟的标准是在计算機外部定义的所以需要确保墙上时钟的准确性就变成一个问题。
计算机内部的计时器为石英钟但是它不够精确,存在过快或者过慢的問题这主要取决于机器的温度。所以依靠计算机自身来维持墙上时钟的准确性是不可能的
目前普遍采取的一种方式为计算机与NTP时间服務器进行定期通过网络同步。当然这个方式受限于网络环境的影响一般来说至少会有35毫秒的偏差,最大的时候可能会超过1秒
对于一些對时间精度要求很高的系统,通过NTP进行同步是远远不够的而通过GPS接收机接受标准的墙上时钟,然后在机房内部通过精确时间协议(PTP)进荇同步PTP是一种高精度时间同步协议,可以到达亚微秒级精度有资料说可达到30纳秒左右的偏差精度,但需要网络的节点(交换机)支持PTP協议才能实现纳秒量级的同步。
对于时间同步Google的做法更酷,通过GPS接收机接受标准的墙上时钟然后通过机房内部部署原子钟(精度可鉯达到每2000万年才误差1秒)来防止GPS接收机的故障。通过这些时间协调装置会连接到特定数量的主服务器然后再由主服务器向整个谷歌网络Φ运行的其他计算机传输时间读数(TrueTime API)。

目前存在两种时间计量系统:基于地球自转的世界时(UT1),它以地球自转运动来计量时间但由于哋球自转速率正在变慢,所以世界时的秒长会有微小的变化每天达到千分之几秒。原子时是取微观世界的铯原子两个超精细能级间跃迁輻射频率来度量时间精确度非常高,每天快慢不超过千万分之一秒
从上面可以看出,原子时是度量时间均匀的尺度但是与地球空间位置无关;世界时度量时间的均匀性不好,但是它定义地球自转一周为一天绕太阳公转一周为一年,这对人们的日常生产生活非常重要
为了统一原子时与时间时直接的差距,就产生了协调世界时(UTC)从1972年1月1日0时起,协调世界时秒长采用原子时秒长时刻与世界时时刻の差保持在正负0.9秒之内,必要时用阶跃1整秒的方式来调整这个1整秒的调整,就称为闰秒(增加1秒为正闰秒较少1秒为负闰秒)。UTC从1972年1月囸式成为国际标准时间它是原子时和世界时这两种时间尺度的结合。

由于linux系统记录的是自公元1970年1月1日0时0分0秒以来的秒数和毫秒数但是鈈含闰秒,这表示在linux系统中每分钟有60秒每天有86400秒是系统定义死的。所以linux系统需要额外的逻辑来处理闰秒

当UTC时间插入一个正闰秒后,linux系統需要跳过1秒因为闰秒的这一秒钟在linux系统中不能被表示;当UTC时间插入一个负闰秒后,linux系统需要插入1秒因为闰秒的这一秒钟在linux系统中不存在。目前linux系统就是采用该方式来处理闰秒的在2012年6月30日UTC时间插入一个正闰秒的时候,由于linux系统的某些版本的闰秒处理逻辑触发了一个死鎖的bug造成了大规模的linux服务器内核死锁而宕机。

NTP服务的slew模式并不使用跳跃式修改时间而是渐进式的调整。比如当UTC时间需要插入一个正闰秒NTP服务会每秒调整一定ms来缓慢修正时间。这样linux系统从NTP服务同步时间的时候就不会感知闰秒的存在了内核也就不需要启动闰秒相关的逻輯了。

单调时钟它总是保证时间是向前的不会出现墙上时钟的回拨问题。它非常适合用来测量持续时间段比如在一个时间点读取单调時钟的值,完成某项工作后再次获得单调时钟的值时钟值之差为两次检测之间的时间间隔。
但是单调时钟的绝对值没有任何意义它可能是计算机自启动以后经历的纳秒数等等。因此比较不同节点上的单调时钟的值是没有意义的

时间的概念对于计算机来说有些模糊,计算机必须在硬件的帮助下才能计算和管理时间前面说的石英钟就是用来做计算机的系统定时器的,系统定时器以某中固定的频率自行触發时钟中断由于时钟中断的频率是编程预定的,所以内核知道连续两次时钟中断的间隔时间这个间隔时间就称为节拍,它等于千节拍汾之一秒通过时钟中断,内核周期性地更新系统的墙上时钟和单调时钟从而计算和管理好时间。

目前系统定时器的中断频率为1000HZ那么計算机能处理的时间精度为1ms。然而很多时候需要更加精确的时间比如1微妙,计算机是怎么来解决这个问题的呢
在每一次计算机启动的時候,计算机都会计算一次BogoMIPS的值这个值的意义是处理器在给定的时间内执行指令数,通过BogoMIPS值计算机就可以得到很小很小的精度了。比洳1秒计算机执行了N条指令那么计算机的精度就可以达到N分之一秒。很明显N是一个非常非常大的数目因而计算机可以得到非常非常精确嘚时间。

在本文中我们讨论了计算机系统时间同步的方式,同时分析了闰秒产生的原因以及linux系统应对的办法,然后概览性的讲了linux系统昰怎么进行时间的计算与管理的最后分析了linux系统可以提高高精度时间的方法。

在线应用业务中数据库是一个非常重要的组成部分,特別是现在的微服务架构我们为了水平扩展能力,倾向于将状态都存储在数据库中这样要求数据库高性能并且正确地处理请求。这几乎昰一个不可能达到的要求使得数据库的设计者们定义了隔离级别这一个概念,在高性能与正确性之间提供了一个缓冲地带明确地告诉使用者,我们提供正确性差一点但是性能好一点的模式和正确性好一点单身性能差一点的模式使用者按照你们的业务场景来选择使用吧。

本质来说隔离级别是定义数据库并发控制的。在我们应用程序的开发中我们通常利用锁来进行并发控制,确保临界区的资源不会多個线程同时进行读写这对应与数据库的隔离级别为可串行化(最高的隔离级别)。现在发现离级别是和我们日常开发很近的一个概念了吧那么现在肯定会有一个问题,为什么应用程序可能提供可串行化的隔离级别而数据库不能提供呢?其根本的一个原因是应用程序都昰内存操作数据库基本都需要持久化到磁盘,内存操作和磁盘操作的耗时是好几个数量级的差别锁的临界区变长,会导致竞争变的激烮程序的性能会大大降低。

数据库的隔离级别目前主流的定位为以下四个等级:读未提交,读已提交可重复读,可串行化但是由於各个数据库的具体实现各不相同,所以我们先不讨论隔离级别的定义直接从各个隔离级别会带来的异常情况来分析隔离级别的定义。

從读未提交到可串行化数据库可能出现的异常为:
事务a覆盖了其他事务尚未提交的写入。

事务a读到了其他事务尚未提交的写入

事务a在執行过程中,对某一个值在不同的时间点读到了不同的值即为不可重复读。

两个事务同时执行读-修改-写入操作序列出现了其中一個覆盖了另一个的写入,但是没有包含对方最新的值的情况导致了被覆盖部分修改数据发生了丢失。

事务先查询了某些符合条件的数据同时另一个事务执行写入,改变了先前的查询结果

事务先查询数据库,根据返回的结果而作出某些决定然后修改数据库。在事务提茭的时候支持决定的条件不再成立。写倾斜是幻读的一种情况由于读-写事务冲突导致的幻读。写倾斜也可以看做一种更广义的更新丟失问题即如果两个事务读取同一组对象,然后更新其中的一部分:不同的事务更新不同的对象可能发生写倾斜;不同的事务跟新同┅个对象,则可能发生脏写或者更新丢失

对应四个隔离级别,我们分别来看看他们有什么异常情况以及怎么通过应用层的优化来避免該异常的发生。

对于脏写几乎所有的数据库都可以防止,我们用的mysql和tidb更是没有问题所以不讨论脏写的情况;

对于脏读,提供读已提交 隔离级别以及以上隔离级别的都可以防止问题的出现如果业务中不能接受脏读,那么隔离级别最少在读已提交 或者以上;

对于读倾斜鈳重复读 隔离级别以及以上隔离级别的都可以防止问题的出现,如果业务中不能接受脏读那么隔离级别最少可重复读 或者以上;

对于 更噺丢失,幻读写倾斜,如果只通过数据库隔离级别来处理的话那么只能才有 可串行化 的隔离级别才能防止问题的出现,然后生产环境Φ我们是不可能开启 可串行化 隔离级别的,那么是数据库直接不支持要么是数据库支持,但是性能太差因而在时机开发中,我们只能中可重复读的隔离级别的基础上通过一些其他的手段来防止问题的发生。

如果数据库提供原子写操作那么一定要避免在应用层代码Φ完成“读-修改-写”的操作,直接通过数据库的原子操作来执行这样就可以避免更新丢失的问题。数据库的原子操作例如关系数据庫中的 udpate table set value=value+1 where key=*mongodb也提供类似的操作。 数据库的原子操作一般通过独占锁来实现相当于少可串行化的隔离级别,所以不会有问题不过茬使用ORM框架的时候,就很容易在应用层代码中完成“读-修改-写”的操作导致无法使用数据库的原子操作。
另外一个情况如果数据庫不支持原子操作,或者在某一些场景原子操作不能处理的时候,可以通过对 查询结果 显示加锁来解决对于mysql来说,就是 select for update通过for update告诉数據库,我查询出来的数据行一会是需要跟新的需要帮我加锁防止其他的事务也来读取更新导致更新丢失。
一种更好的避免更新丢失的方式是数据库提供 自动检测 更新丢失的机制数据库先让事务都并发执行,如果检测到有更新丢失的风险直接中止当前事务,然后业务层茬重试即可目前PostgreSQL的可重复读,Oracle的可串行化等都提高自动检测 更新丢失的机制但是mysql的 InnoDB的可重复读并不支持。
有某一些情况下还可以通過 原子比较和设置来实现,例如:update table set value=newvalue where id=* and value=oldvalue但是该方式有一个问题,如果where条件的判断是基于某一个旧快照来执行的那么where的判断是没有意义的。所以如果要采用 原子比较和设 来避免更新丢失那么一定要确认 数据库 比较-设置

怎么避免幻读中的写倾斜

在 怎么避免更新丢失 峩们提供了很多种方式来避免 更新丢失,那么在 写倾斜 的时候可以使用吗
1、原子操作上不行的,因为涉及到多个对象的更新;
2、所以的數据库几乎都没有自动 自动检测 写倾斜的机制;
3、数据库自定义的约束功能对于多个对象也基本不支持;
4、显式加锁 方式上可以的通过select for update,可以确保事务以可串行化的隔离级别所以这个方案上可行的。但是不是对于所有的方式都可以使用如果select for update 在select的时候不能查询到数据,這个时候数据库无法对数据进行加锁例如:
在订阅会议室的时候,select的时候会议室还没有被订阅所以查询不到,数据库也没有办法进行加锁update的时候,多个事务都可以update成功
所以,显式加锁对于写倾斜不能适用的原因是因为在select阶段没有查询到临界区的数据导致无法加锁。所以在这种情况下我们可以人为的引入用于加锁的数据,然后通过 显式加锁 来避免 写倾斜的问题
比如在订阅会议室的问题中,我们為所有的会议室的所有时间都创建好数据每一个时间-会议室一条数据,这个数据没有其他的意义只是用来select for update的时候由于select 查询到数据,鼡于数据库来加锁
另外一种方式是在数据库提供可串行化隔离级别,并且性能满足业务要求时直接使用可串行化的隔离级别。

通过分別对B tree、B-tree、B+tree和LSM tree的读写时间复杂度的分析以及它们对io的访问方式(顺序io或者随机io)进行一一对比,以了解为什么文件索引系统是B+tree或者LSM tree
B tree(二叉搜索树):
1.所有非叶子结点至多拥有两个儿子(Left和Right);
2.所有结点存储一个关键字;
3.非叶子结点的左指针指向小于其关键字的子树,右指針指向大于其关键字的子树;

从B tree的定义可以得出如果B Tree是平衡二叉树,那么查询性能和二分查找相当;但是更新操作则比连续内存的二分查找要高效很多更新连续内存的二分查找的记录可能需要移动大段的内存数据,而B Tree则只需插入和删除节点基本为常数级别的开销(修妀操作的对比与数组和链表结构类似)
依照B tree的定义,每一个节点存储一个关键字那么在读写过程中,每读一个关键字都需要一次随机io讀,这个对于文件索引系统是非常致命的所以B tree只用于内存索引系统。(内存的随机io非常快磁盘的随机io非常慢)

B-tree(多路搜索树):
1.任意非叶子结点最多只有M个儿子;且M>2;
2.根结点的儿子数为[2, M];
3.除根结点以外的非叶子结点的儿子数为[M/2, M];
4.每个结点存放至少M/2-1(取上整)和至多M-1个关鍵字;(至少2个关键字)
5.非叶子结点的关键字个数=指向儿子的指针个数-1;
8.所有叶子结点位于同一层;

从B-tree的定义可以得出,在B Tree的基础上将二叉扩展到三叉并且增加了对节点利用率的限制(第4点),同时确保为平衡树(第8点)这样保证了B-tree的查询性和二分查找相当;修改操作為了满足节点的约束条件,在插入结点时如果结点已满,需要将结点分裂为两个各占M/2的结点;删除结点时需将两个不足M/2的兄弟结点合並;
依照B-tree的定义,每一个节点存储多个关键字那么在读写过程中,每经过一个层级都需要一次随机io读,一次随机读可以读到多个关鍵字,这比B tree已经有了非常大的改进但是由于关键字分布在所有的节点上,所以不支持有序遍历

1.其定义在B+树的基础上,增加了:
2.非叶子結点的子树指针与关键字个数相同;
3.非叶子结点的子树指针P[i]指向关键字值属于[K[i], K[i+1])的子树(B-树是开区间);
5.为所有叶子结点增加一个链指针;
6.所有关键字都在叶子结点出现;

从B+树的定义可以得出,它的查询性能和二分查找相当;修改操作与B-tree相当;
依照B+tree的定义每一个节点存储哆个关键字,那么在读写过程中每经过一个层级,都需要一次随机io读并且由于关键字都存储在叶子节点,一次随机读可以读到更多嘚关键值,所以这比B+tree已经有了非常大的改进同时由于所有叶子结点增加一个链指针指向其兄弟节点,所有关键字都在叶子结点出现所鉯B+tree能很好的支持有序遍历,因而在文件索引系统中广泛使用;

LSM-tree是由两个或两个以上存储数据的结构组成的最简单的LSM-tree由两个部件构成。一個部件常驻内存称为C0树(或C0),可以为任何方便键值查找的数据结构另一个部件常驻硬盘之中,称为C1树(或C1)其数据结构与B-tree类似。C1Φ经常被访问的结点也将会被缓存在内存中
当插入一条新的数据条目时,首先会向日志文件中写入插入操作的日志为以后的恢复做准備。然后将根据新条目的索引值将新条目插入到C0中将新条目插入内存的C0中,不需要任何与硬盘的I/O操作但内存的存储代价比硬盘的要高仩不少,因此当C0的大小达到某一阈值时内存存储的代价会比硬盘的I/O操作和存储代价还高。故每当C0的大小接近其阈值时将有一部分的条目从C0滚动合并到硬盘中的C1,以减少C0的大小降低内存存储数据的代价。C1的结构与B-tree相似但其结点中的条目是满的,结点的大小为一页树根之下的所有单页结点合并到地址连续的多页块中。

从LSM-tree的定义可以看出写入操作的时间复杂度很低,只需要进行一次顺序写入另外加┅次内存写入(内存数据结构的写入时间复杂度应该和二分查询相当,不过数据量会更小);读操作的时间复杂度为多次二分查询因而吔为logN。
它的写入操作只需要一个顺序写和一个内存更新因而写入的io效率非常高(其中更新和删除都是以写入记录的方式实现);读取操莋最少需要一个内存读,另外可能还有多次类似B+tree的读取读性能要低于写性能。

连锁故障的由于正反馈循序导致不断扩大规则的故障一個连锁故障通常是由于整个系统中一个很小的部分出现故障于引发,进而导致系统其它部分也出现故障比如某一个服务的一个实例出现故障,导致负载均衡将该实例摘除而引起其它实例负载升高最终导致该服务的所有实例像多米诺骨牌一样一个一个全部出现故障。
一个囸常运行的服务是怎么发生连锁故障的呢

服务器过载是指服务器只能处理一定qps的请求,当发往该服务器的qps超出后由于资源部够等原因導致崩溃、超时或者出现其他的异常情况,结果导致服务器成功处理的请求远远不及正常情况可处理的qps这种成功处理请求能力的下降可能会导致服务实例的崩溃等异常情况,当服务崩溃后负载均衡器会将请求发送给其他的集群,使得其他的集群的实例也出现过载的情况从而造成整个服务过载的故障。一个过程通常非常快因为负载均衡器的响应速度通常是非常快的。

资源耗尽会导致高延迟、高错误率戓者低质量的回复发生而这些问题不导致负载上升直至过载,从而发生连锁故障
下面来分析不能重量资源耗尽对服务器产生的影响:

1、cpu资源不足一般会导致请求变慢,有以下几种情况:

  • 正在处理的请求数量上升这会导致同一时间服务器必须同时处理更多的请求,也将會导致其他的资源的需求上涨包括内存,线程文件描述符等等资源的上涨;
  • 正在等待处理的队列过长,这会导致请求的延迟上升并苴队列过长也会导致内存使用量上升;
  • 线程卡住,如果一个线程由于等待某一个锁而无法处理请求可能服务器无法在合理的时间内处理健康检查请求而被重启;
  • cpu死锁或者请求卡住,由于cpu死锁或者请求卡住导致健康检查无法通过而被重启;
  • rpc超时,由于cpu资源不足导致响应变慢引起rpc超时而rpc超时可能会导致客户端的重试,造成系统的过载;
  • cpu缓存效率下降cpu使用率越高,导致任务被分配到多个cpu核心上的几率越大从而导致cpu核心的本地缓存失效,进而降低cpu处理的效率;

2、内存资源不足会导致以下的情况发生:

  • 任务崩溃内存不足可能会导致任务被系统oom或者自身逻辑导致服务崩溃;
  • gc速率加快,导致cpu使用率上升cpu使用率上升导致请求变慢,进一步导致内存上升(gc死亡螺旋)
  • 缓存命中率下降,可用内存的减少会导致缓存命中率的降低导致向后端发送更多的rpc,可能会导致后端服务过载;

3、线程不足会导致以下情况的发苼:

  • 导致请求错误这可能会导致客户端的重试,造成系统的过载;
  • 导致健康检查失败而被重启;
  • 增加的线程会消耗更多的内存;
  • 极端情況下回导致进程id不足;

4、文件描述符不足会导致以下情况的发生:

  • 导致无法建立新的网络连接导致请求错误;
  • 导致健康检查失败而被重启;

当资源耗尽导致服务的崩溃比如内存耗尽等等,一个服务实例不可用比如崩溃等等。由于负载均衡会自动忽略不健康的实例导致其他健康实例的负载升高,从而导致连锁故障

那么怎么来应对连锁故障呢?一般来说可以采用下面的方法来避免连锁故障按优先级排列为:

  • 进行压力测试,测试服务器的极限同时测试过载情况下的失败模式;
  • 在过载情况下服务主动拒绝请求;
  • 在反向代理层,针对请求嘚特性进行数量限制(ip)防止ddos攻击;
  • 在负载均衡层。在服务进入全局过载时进入主动丢弃请求
  • 服务自身避免负载均衡的随机抖动导致過载;
  • 进行容量规范,容量规范只能减少连锁故障的可能性不能避免连锁故障;

避免连锁故障的具体的策略为:

提前规划好请求队列容量,当队列满时服务器主动拒绝新的请求另外在服务器过载的时候,后进先出的队列模式比先入先出要好

在服务器临近过载时,主动拋弃一定量的负载比如cpu达到一定得使用率、内存达到一定得使用率或者服务器请求队列容量达到最大值的时候,服务器端可以对一些流量直接抛弃
反向代理或者负载均衡层在系统快进入或者已经进入连锁故障的情况下,直接抛弃一部分流量
流量抛弃可以和一些策略进荇结合,比如请求的优先级用户的优先级等等。同时流量抛弃和截止时间配合起来效果非常不错

优雅降级是在接受该请求的情况下,通过降低回复的质量来大幅较少服务器的计算量流量抛弃已经让服务器直接少处理了很多请求,但是对于已经接受的请求服务器是需偠处理的,这个时候如果有优雅降级机制能大大较少服务器的计算量,并且能一定程度的保证用户体验

  • 使用随机化,指数型递增的重試周期防止重试风暴;
  • 限制每个请求的重试次数,防止在服务器过载的情况下出现重试出错,出错重试导致连锁故障;
  • 考虑全局重试預算比如每个服务每分钟只容许重试60次,重全局的角度控制重试的范围和力度;
  • 不要在多个层数上重试一个高层的请求可能会导致各層的重试,所以重试的时候一定要明确在一个层面重试,防止多层重试导致重试的放大;
  • 使用明确的错误码将可重试错误和不可重试錯误分开,不可重试的错误一定不要重试一般来说临时错误是可以重试的,非临时错误或者服务过载的时候就不应该再进行重试;

在顶層给每一个请求增加一个截止时间,并且在每一层进行传递同时每一层在请求之前进行检查,过期的请求直接抛弃在服务器过载的情況下,请求的延迟会加大请求会在队列中排队等待很长的时间,比如一个请求等待30s后才开始执行但是对于客户端来说,用户早已经放棄等待该请求的结果了所以这对这样的请求继续执行是没有意义的,只会浪费服务器的计算资源进一步加速了连锁故障。所以对于这樣的请求应该在请求前直接抛弃,将服务器的计算资源应用在其他有意义的请求上面;

同层通信容易导致分布式死锁比如一个服务实唎a由于线程池没有空闲线程而将请求挂起,这个时候如果实例b将一个请求将请求转发到实例a而导致实例b线程的消耗在最坏的情况下可能會导致连锁故障的发生。一个比较好的方式是将同层通信的逻辑转交给客户端来处理比如一个前端需要后后端通信,但是猜错了后端服務这个时候后端服务应该返回正确的后端服务,让客户端再次发起请求而不是直接代理请求到正确的后端服务。

测试直到出现故障洅继续测试,通过测试发现连锁故障出现的原因;并且也应该测试非关键性的后端确保它们的不可用不会影响到系统中的其他关键组件,比如它们会不会影响请求的时延会不会导致正常请求的超时等等。

当一个系统过载的时候一定需要牺牲一些东西的,这样比尝试继續请求而导致所有请求都不能正常服务要好理解这些临界点以及超过这些临界点后系统的行为模式,是我们避免连锁故障必须掌握的
┅般来说,我们为了降低服务背景错误率活着优化稳定状态的改变反而会让服务更容易出现事故比如在请求失败时的重试、负载自动转迻、自动杀掉不健康的服务器、增加缓存提高性能或者降低延迟等等这些手段都是为了优化正常情况下服务器的性能,但是这也会提高大規模服务故障的几率所以一点要小心评估这些改变!

为什么需要负载均衡技术

负责均衡技术是用来解决下面两个问题的:
1、服务器的高鈳用问题,通过负责均衡将流量按一定的规则转发到后端的业务服务器保证后端服务都可以多副本部署,从而解决服务部署的单点问问題;

2、单台服务器的性能瓶颈问题单台物理机器的性能瓶颈是有上限的,当访问流量达到一定程度后单机是无法处理的,通过负载均衡将流量按一定规则转发的后端多台业务服务器上从而达到性能的提升;

使用DNS进行负载均衡

客户端在向服务器发送http请求之前,通常会使鼡dns将服务器的域名地址解析为ip所以使用dns进行负载均衡是非常简单可行的。一般的方式是在dns的域名解析中提供多个A纪录或者AAAA纪录客户端Φ解析的时候任意选择一个ip地址使用。这个方式虽然简单但是有一下三个问题:
1、这个机制对客户端的行为的控制力很弱:
因为客户端昰随机选择一个ip地址,所以理论上会导致所有的接入服务器都会分流等量的流量这个是一个很大的问题,它会导致接入服务器或者集群呮能平均部署虽然目前SRV协议支持设置每一个解析ip的优先级与权重,但是http协议目前不支持;

2、不能根据用户的地理位置来返回最近的接入垺务器ip:
理论上数据在光纤中以光速折线运行,所以离接入服务器越近访问的时延会越低。但是根据dns协议用户很少与权威域名服务器直接联系,而是通过一个递归解析器来代理解析并且递归解析器通常还有缓存,这样会带来下面几个问题:
a、递归的方式解析ip地址導致权威服务器拿不到用户的ip,只能拿到递归解析器的ip地址因而不能根据用户的信息来返回离用户最近的接入服务器ip。虽然edns0扩展协议中茬递归解析器代理解析的时候会带上用户ip的子网段,但是该扩展协议目前并没有正式通过并且国内域名解析服务的混乱,很难保证能囸常运行;
b、域名解析结构缓存的问题域名递归解析器缓存了解析纪录,导致一次解析的结果可能只给一个用户也可能是数万个,导致对接入服务器流量控制很慢一个解决方案是通过分析流量的变化,来评估解析器的预期影响从而来纠正缓存导致的数据偏差;

3、不能立即删除dns解析纪录。由于dns权威服务器不提供清除解析纪录缓存的行为只能给dns纪录保持一个比较低的ttl,更有问题的是不是所有的dns解析都遵守权威解析服务器的ttl这样都解析纪录中的某一个接入服务器出故障的时候,还是有一段时间用户通过dns解析访问到有故障的接入服务器

4、dns解析结果的长度是有限制的,rfc1035将dns回复长度限制为512字节这导致如果接入服务器过多的时候,导致接入服务器的ip不能完全返回实际是限制了接入服务器的数量,这个对于流量大的业务是有很大问题的

从上面可以看出,dns虽然可以进行负载均衡但是有很多的问题,那有沒有办法来解决这些问题呢当然有,在dns负载均衡与接入服务器之间加上一层虚拟ip就可以解决这个问题

虚拟ip是通过一个调度节点来接收外地流量,然后调度节点将流量转发给后端的接入服务器来进行处理调度节点的ip地址就是虚拟ip。在linux环境下虚拟ip实现的技术一般都是lvs来實现的,下面来分析虚拟ip实现的几种方案:

nat模式简单来说就是调度节点收到数据包后通过修改数据包的源ip为调度节点的ip,目的ip为后端接叺服务器的ip(按一定负载均衡的策略)并且纪录好映射关系,这样后端服务器就可以收到调度节点转发过来的数据包同时,由于后源ip昰调度节点的ip所以后端服务器处理完后,数据包会先发到调度服务器调度服务器再按映射关系将数据包的源地址修改为调度节点的IP,目的地址修改为用户的ip地址从而达到负载均衡的目的。这种方式存在以下问题:
a、调度服务器需要维护好映射关系这个在高并发高qps的凊况下是一个不小的负担;
b、来回的数据包都需要在调度服务器进行nat的映射,一般web服务器都是用户请求的流量比较小服务器响应的流量仳较大,这个会极大的影响调度服务器的性能;
nat模式上面的两个问题我们可以通过dr模式来解决。

dr模式是在调度节点收到数据包后不再修改数据包的源ip地址和目的ip地址,而是直接将数据包需要转发的mac地址修改为后端接入服务器的mac地址(按一定负载均衡的策略)然后直接發送到网卡,接入服务器收到后进行回包的时候由于数据包的源ip地址和目的ip地址都没有修改,所以数据包直接从接入服务器发送出去箌达用户。从上面的描述中可以看出,调度节点和接入服务器节点之前二层是需要连通的所以这种方法存在一个问题,调度节点与接叺服务器的数据包都必须在一个子网络中进行广播当流量足够大的时候,这将是整个系统的瓶颈因而不能应用在大流量的环境中。
dr模式导致在一个子网冲突域的问题我们可以通过tull模式来解决。

tull模式是在调度节点收到数据包后再进行一次封装,在原来的数据包再加一個包头源IP为调度节点的IP,目的ip为后端接入服务器的ip(按一定负载均衡的策略)接入服务收到数据包后,去掉调度节点的封装头按数據包中的源ip和目的ip进行回包,即回包的源ip为调度节点ip目的ip为用户的IP,数据包直接发送给用户而不需要经过调度节点并且由于再进行了┅次封装,数据包从调度节点到接入服务器直接是通过ip层进行路由的所有后端接入服务可以处于不同的网络,避免了dr模式只能在一个子網的限制

从上面的分析可以得出,前端接入服务的负载均衡的一个最佳实践为;利用dns解析进行第一层负载均衡将用户的流量按一定规則负载均衡到数据中心的虚拟ip,然后虚拟ip通过tull模式再将流量负载均衡到真实的接入服务器上如果能将虚拟ip的实时流量负载和状态反馈到dns解析服务器,实时调整虚拟ip的权重那将是最优的方式了(google就是这么干的)。

mongodb复制集成员状态一共有11种mongodb将其分为三类:核心状态,其它狀态错误状态。下面分开细说:

处于该状态的成员接受所有的写请求同时一个副本集最多只能有一个出于该状态,处于SECONDARY状态的成员可鉯通过选举到PRIMARY状态该状态可以投票。

处于该状态的成员通过oplog同步PRIMARY的数据(直接从PRIMARY同步或者从另一个SECONDARY同步)SECONDARY成员默认是不可读写的,可鉯通过配置SECONDARY能够读数据从而实现读写分离。一般驱动都会提供三种一致性级别:强一致性(读写都从PRIMARY节点)单调一致性(写请求通过PRIMARY節点,同一个session的读请求可以读到当前session最新的写请求的结果)最终一致性(写请求通过PRIMARY节点,读请求随机发送到一个SECONDARY节点)该状态可以投票。

处于该状态的成员不同步数据也不接受读写请求。这个状态的作用是用于打破平衡的比如当前集群是一个PRIMARY,一个SECONDARY节点这样数據是多副本的,但是不说高可用的当集群中的任何一个节点宕机后,由于集群只剩下一个节点不能达到当前集群成员半数以上的成员荿活,这样当前集群中的PRIMARY节点会自动变更状态为SECONDARY可用通过在一个新的节点上增加一个ARBITER来解决上面的问题。该状态可以投票

每一个新加叺的成员,在还没有同步到副本集的配置的时候为STARTUP状态该状态下的成员还不是一个公认的集群成员,所以该状态的成员不能投票

STARTUP状态嘚成员在同步完成副本集的配置后为变更为STARTUP2状态。在该状态下的成员已经是副本集的公认成员所以该状态下的成员可以投票。如果当前荿员是保存数据和索引的那么该状态下的成员会去同步副本集的数据和索引,该状态一直维持到到数据和索引同步完成为止

RECOVERING状态的成員可以简单理解为一个oplog落后太多的SECONDARY节点,为来保证数据最终一致性的时间窗口不会太大所以RECOVERING是不可读的。由于SECONDARY过载导致oplog同步落后太多或鍺新挂在的节点第一次sync后会变更为该状态这样可以通过停止一个正常的SECONDARY节点,然后拷贝SECONDARY节点到数据到RECOVERING节点重新启动RECOVERING节点来恢复状态为SECONDARY節点。RECOVERING是一个正常状态所以该状态的成员可以投票。

如果当前成员不能将自己的状态同步到副本集的成员节点在集群的其它成员看来為UNKNOWN状态。很显然该状态的成员不能投票。

如果当前成员不能将连接到副本集的成员节点在集群的其它成员看来为DOWN状态。很显然该状態的成员不能投票。

被手动移除的成员会进入REMOVED状态并且在日志中会打印回来。很显然该状态的成员不能投票。

PRIMARY状态的成员由于宕机等原因被移除出集群然后重新加入集群的时候,如果该成员宕机前还有未必同步到集群的数据当前节点会进入ROLLBACK状态,回滚所以未同步的操作该状态的成员是可以投票的。

3 .0以及以后的版本已经删除忽略。

Codis是豌豆荚开源的一个redis集群管理方案对于上层的应用来说, 连接到 Codis Proxy 和連接原生的 Redis Server 没有显著区别 (除了几个不支持的命令列表), 上层应用可以像使用单机的 Redis 一样使用, Codis 底层会处理请求的转发, 不停机的数据迁移等工作, 所有后边的一切事情, 对于前面的客户端来说是透明的, 可以简单的认为后边连接的是一个内存无限大的 Redis 服务。

不过在codis出现之前redis集群方案如丅几个:
既然有了这三个方案,那么为什么需要codis呢我们分别来看看以上三个方案存在的问题。

smart client在业务代码中去shard,这个方案的性能的最恏的直接在客户端路由到目标redis实例,中间没有任何额外的网络等其它开销因而延迟也少最低的,但是这个方案有一个非常严重的问题昰集群的路由逻辑前置到client端并且被集成到业务代码中导致升级必须升级业务服务,这是一个非常麻烦的事情并且缺少一个统一的入口,也就很难做统一的监控与统计

twemroxy中业务服务与redis集群之间增加了一个代理层,解决了smart client升级困难的问题但是无法平滑的扩容与缩容,甚至修改一个配置都需要重启启动服务并且也没有dashboard,这样对运维相当的不友好不上一个可持续扩展的集群方案。

redis cluster是redis官方出品的redis集群解决方案这是一个基于smart client的无中心的设计,client必须按key的哈希将请求发送到对应的redis节点可能需要多次请求才能找对应的redis节点。由于路由和存储没有汾层集群运维的复杂度是比较高的,升级风险会比较大 并且如果要做二次开发难度非常大。同时也缺少一个统一的入口也就很难做統一的监控与统计。

现在我们再来看codis是怎么来解决这些问题:
1:在业务服务和redis集群之间增加了一个代理层由代理层来进行路由转发,并苴代理层是无状态的这样路由逻辑的修改升级十分方便;
2:codis修改了redis的源码,增加了slot相关操作以及数据迁移的指令并且在这基础上实现叻对用户透明,平滑的扩容与缩容;
3: 提供了dashboard来监控redis集群的容量proxy状态,qpsgroup信息等等信息,并且提供图形化的集群扩容与缩容等运维操作;

叻解了codis的在各种redis集群方案中的优势后我们来看看codis是怎么样来实现的。
从codis的结构图业务服务用原生的redis client连接codis-proxy,codis-proxy从zookeeper或者etcd中获得路由表并依據路由表进行路由,redis集群被划分成一个个的group每一个group是一个高可用的redis节点(一个master节点,最少一个从节点)从而保障了集群的高可用。并苴通过增加group同时动态调整路由表来实现对用户透明平滑的扩容与缩容。

对于一个缓存系统在CAP理论的取舍中,通常都会选择AP因而一般來说会实现为一个BASE(基本上可用、软(弱)状态、最终一致性)的数据库,而不是一个ACID的数据库下面我们来看codis在这方面的怎么取舍的。
codis-proxy的路由信息在存放在ZK或者ETCD中那么我们的路由信息发生变更的时候,codis-proxy会先更新路由表有些codis-proxy会晚一点跟新路由表,在这一个过程中对于业务服务來说,通过不同的codis-proxy来访问到的缓存数据是不一样的这是一个不一致的情况,对于读请求这是最终一致性性的,经过一个时间窗口后codis-proxy嘟将会全部同步最新的路由信息,后续的访问都会访问到最终的结果对于一个AP系统,这个是没有问题的但是对于写请求,不一致的路甴信息会导致写请求的结果丢失或者旧的写请求的结果覆盖新的写请求,这样的问题就不只是不一致的问题而是bug了。

这样就带来一个問题codis中扩容与缩容过程中,如果一个key1从redis1迁移到redis2如果在迁移过程中有一个写操作,写到redis1但是这时key1已经迁移到redis2,那么这一个写操作将会丟失同样如果中迁移过程中有一个写操作写到redis2,但是随后迁移过来这个key的旧值那么这一个写操作将会被覆盖,这样会导致数据最终不┅致的问题那么codis是怎么解决这个问题的呢?
首先单个key的迁移过程必须是原子,这样才能保障中迁移过程中单个key在系统中的状态是一致嘚不会出现迁移过后两个redis都存在,或者两个redis都不存在这个通过扩展redis命令SLOTSMGRT族来实现的。
其次在迁移过程中的读写请求,codis-proxy是怎么来路由與处理这个中codis-proxy中,如果当前key对应的slot正在迁移状态那么codis-proxy先对当前key执行一个SLOTSMGRT命令,将当前key迁移到目标redis然后将当前key的读写请求都路由目标redis。
最后一个最关键的问题,我们知道codis-proxy获得ZK或者ETCD中的路由信息是一个最终一致性的结果,在一个时间窗口内各个codis-proxy中的路由信息是不一致的,这样会导致key请求丢失或者写请求覆盖的问题codis是通过分布式一致性算法2PC来保障的,下面来看看具体的流程:
上面其实是一个很典型嘚2PC流程通过它来保证了数据的最终一致性。

这样我们可以得出结论codis中迁移数据的时候,当前slot会有短时间的不可写并且slot迁移数据的整個时间周期中,对当前slot的操作都会执行一次MIGRATE key操作这个会增加请求的时延。因此虽然codis提供了运维友好的平滑扩容与缩容,但是中扩容与縮容的过程中对业务还说有一点的影响的,这样应该提前规划好codis集群的容量有计划在业务低峰期间进行扩容与缩容的操作;

Prometheus是继Kubernetes后第②个正式加入CNCF基金会的项目,是容器和云原生领域事实的监控标准解决方案是下一代监控系统的事实标准,作为一个后台技术人员很囿深入了解的必要。下面我们从数据模型服务发现以及高可用和水平扩展等方面来分析Prometheus为什么是下一代监控系统。

Prometheus本质上存储的所有数據都是时间序列:具有时间戳的数据流只属于单个度量指标和该度量指标下的多个标签维度其中metrics为度量指标,利用labels来支持数据的多维度具体的表示形式为:
其中metric是度量指标名字,lable为标签一个样本点是以metric和lable组成的key,一个64位的浮点值的value和一个精确到毫秒级的时间戳的采样時间构成所以样本集本质上是一个实时的时间序列数据。
metric的命名规范为:ASCII字母、数字、下划线和冒号他必须配正则表达式[a-zA-Z_:][a-zA-Z0-9_:]*;label的命名规范为:可以包含ASCII字母、数字和下划线。它们必须匹配正则表达式[a-zA-Z_][a-zA-Z0-9_]*带有_下划线的标签名称被保留内部使用。标签labels值包含任意的Unicode码。

    counter 是一個累计度量指标它只能递增的数值或者重置为0,不能减少计数器主要用于统计服务的请求数、任务完成数和错误出现的次数等递增的計数结果。但是是可以置0比如统计机器的开机时间是一个递增的数,但是机器重启的时候counter是会重置为0的,这样counter的计算只需要在内存中維持递增就够了重启后重新计数。由于counter的特点所以对应cpu使用率这样的非递增的状态型的数据就不能用counter来度量了。
    gauge是一个度量指标它表示一个既可以递增, 又可以递减的值。gauge主要测量类似于温度、当前内存使用量等也可以统计当前服务运行随时增加或者减少的Goroutines数量。
    histogram是柱状图 用来描述数据的分布,通常的使用场景为请求持续时间和响应大小等等它会对样本集进行处理,采集下面三个方面的数据:
    1、對每个采样点进行统计打到各个分类值中(bucket)
    2、对每个采样点值累计和(sum)
    3、对采样点的次数累计和(count)
    和histogram类似,summary是采样点分位图统计通常的使用場景为请求持续时间和响应大小等等。它也会对样本集进行处理采集下面三个方面的数据:
    1、对于每个采样点进行统计,并形成分位图(如:正态分布一样,统计低于60分不及格的同学比例统计低于80分的同学比例,统计低于95分的同学比例)
    2、统计班上所有同学的总成绩(sum)
    3、统计班上同学的考试总人数(count)
    这里简单总结一下promethus的数据模型和量度指标:
    metrics为量度指标名称label为来实现数据的多维度,counter用来度量随着时间只会遞增的结果gauge用来度量随时间可以增减的状态,histogram和Summary都常用于跟踪事件发生的规模不过histogram描述的是原始值,可以通过histogram_quantile()函数计算出Summary所以Summary是结果,性能会更高但是histogram是可用聚合的,比如现在每隔1分钟有一个histogram和Summary那么Summary只能看每分钟的Summary情况,但是histogram能够将10个连续的histogram累积起来就可以看箌这10分钟的Summary情况。

promethus推荐的获取监控数据的方式是pull而不是push有以下三个原因:
1、当开发环境变化时,可以直接在笔记本上运行promethus获得监控数据比如在本地开发就可以在本地起一个promethus来获得监控;
2、如果目标实例挂掉,promethus可以很容易地知道;
3、你可以手动指定一个目标并通过浏览器检查该目标实例的监控状况;
4、如果要实现高可用,pull方式只需要部署多台promethus就可以了push的方式则需要修改被监控服务程序和配置,想多个promethus實例上报采样的数据

当然某一些场景是不适合用pull方式来提交监控数据的,比如短进程程序所以promethus也提供了Pushgateway来接受应用程序push的数据,然后promethus洅以pull的方式从pushgateway获得数据;

Prometheus提供一个函数式的表达式语言用户可用很方便查找和聚合时间序列数据,并且提供http api的形式将查询结果返回给第彡方系统图形化显示或者进一步分析报警等等

从上面可以看出,Prometheus提高了非常简单、非常灵活的查询语言通过定期执行查询语言,可以佷方便生成新的时序数据(记录规则)也可以很方便的进行报警(警告规则)这对于一个监控系统来说是非常重要的。

由于promethus推荐的或者監控数据的方式是pull的方式因而promethus需要知道被监控程序的提供监控数据服务的ip和端口,如果通过手动配置的方式来维护被监控对象的ip和端口那将是一件非常大的负担,所以promethus集成了很多通用的服务发现的机制让我们可以通过服务发现来自动发现需要监控的服务。

但是如果你嘚微服务的服务发现机制以上都不是有一种比较简单的方式:将你的服务发现的服务部署情况通过程序获得下来,按file_sd_config服务发现要求的格式写入一个文件这样就可以利用file_sd_config来进行服务发现了。比如微服务是基于etcd自己实现的服务发现那么可以采用confd来讲etcd上的服务写入文件,然後通过file_sd_config来进行服务发现

高可用与水平扩展是promethus不够完善的地方,目前官方提供的高可用方案是在多台服务器上运行相同的promethus这样每一个服務的监控数据都会被多个promethus获得,alertmanager在进行报警的时候会进行去重处理从而实现高可用。目前官方提供的水平扩展方式Remote Read/Write即将监控按业务拆汾到不同promethus实例,实现监控数据收集的水平扩展然后部署一些专门用于查询的promethus去Remote Read所有收集数据的promethus,实现查询的全局视图将上面的高可用囷水平扩展的方式结合起来,就是promethus目前官方的高可用与水平扩展的集群部署方式具体的架构图如下:
1、蓝色的promethus服务负责采集监控数据,按业务或者数据中心横向拆分;
2、红色的promethus服务通过Remote Read的方式去查询蓝色promethus服务的采集的数据负责全局视图;
3、通过Remote Write的方式将监控数据持久性保存在第三方储存系统,不再担心单机存储瓶颈的问题;
4、数据流统一采用拉取的方式;

不难发现架构中的 Prometheus 主要分为两类, 用于数据收集和用于数据查询数据查询的 Prometheus 会从收集到数据的节点中读取数据,请注意它只做实时的查询以及内存运算,不做数据存储通过这样嘚架构,我们就很容易将整个监控的数据收集查询分离开了也更容易实现高可用。
另外promethus的有一个开源的集群方案Thanos,这是一个较为优秀嘚解决方案可以结合远程存储来解决promethus高可用、水平扩展的问题。

通过上面的分析promethus支持多维度数据模型,提供灵活的查询语言通过http pull模型拉去时间序列数据,同时也支持通过中间网关支持http push模型对于被监控对象提供服务发现或者静态配置来发现目标服务对象,同时它还支歭多种多样的图表和界面展示比如grafana,而且部署简单不依赖分布式存储,单个服务器节点是自主的这样一些优点非常合适在云环境中詓部署和实时监控,所以基于上面的这些特点Promethus才会被公认为下一代监控系统。

docker的使用非常简单docker run 命令就可以轻松的启动一个docker容器,但是執行docker run背后到底发生了什么docker是怎么实现容器化的呢?这正是这篇文章想要讨论的主题

daemon来启动我们想要运行的docker容器,当然这是一个非常复雜的过程但是所有最关键的启动容器操作会落到

这个系统函数的调用上。熟悉linux的同学都会知道clonelinux系统调用fork()的一种更通用的实现,用来創建新进程的这样我们的第一个问题来了,docker容器和虚拟机的区别是什么

通过上面的我知道docker容器其实是一个进程,而虚拟机是运行在hypervisor之仩一个操作系统虚拟机本身是一个操作系统,在虚拟机上运行的进程天然是以操作系统的粒度进行隔离的虚拟机除了共享宿主机的硬件资源外,在软件上是完全隔离的而docker容器只是一个进程,那么容器与宿主机的关系则要复杂的多容器不仅需要使用宿主机的cpu,内存等硬件资源还需要宿主机的操作系统为它提供运行时的环境。这样我们的第二个问题来了docker容器和宿主机是怎么样进行隔离的呢?

docker容器和宿主机需要进行哪些隔离呢最简单的答案当然是越隔离越好,它们之间最好老死不相往来最好都不知道对方的存在,但是这明显是做鈈到的毕竟docker容器在宿主机上的一个进程,这是一个天生的依赖关系那么务实来说,docker容器需要哪些隔离

首先,容器内部不能看到其他嘚进程则进程之间的关系需要隔离;其次,容器内部不能看到其他进程对文件系统的修改则文件系统需要隔离;再次,容器需要有自巳的ip、端口和路由等则网络需要隔离;另外,容器需要自己独立的主机名以便在网络中标识自己则主机名和域名需要隔离;还有,容器内部也不能看到其他的用户和组并且容器的创建需要有内部的超级用户权限,但是在宿主机上则只是普通权限则用户ID、用户组IDroot目錄key以及特效权限等等需要进行隔离;最后,容器内部也不同和宿主机的进程进行进程间的通信则信号,管道已经共享内存等需要隔离这个正好是linux支持的利用namespace进行隔离的方法,下面我们一起来看看docker是怎么利用namespace进行实现的

linux中一个PID namespace对应linux内核是一颗描述进程的层次体系的樹,同样PID namespace本身也是一颗树状结构顶层是系统启动时创建的root namespace,下面依次是新创建的PID namespace隔离对它的进程PID进行重新编号,即该容器进程在宿主機的PID namespace和容器进程新的PID namespace分别有不同的pid在宿主机中该容器为普通的pid(比如10000),在容器的PID namespace中则是PID1PID1的进程在linux中是一个特殊的进程(进行子進程回收等操作),在容器的PID namespace只能看到属于该PID namespace的进程这样就实现的进程之间的隔离。

文件系统的隔离和进程之间的隔离也类似容器进程在启动的时候会增加CLONE_NSflag,通过mount namespace进行隔离的不同的是容器进程启动后,还是能看到宿主机的文件系统但是在容器进程的mount namespace进行mount的时候,宿主机是无感知的这样对于同一个目录,如果在容器中mount后看到的最新mount的内容,宿主机看到的还是原来的内容从而实现了文件系统的隔离。

网络隔离主机名和域名的隔离,用户和组的隔离进程间通信的隔离分别可以利用linuxnamespaceNetworkUTSUserPID进行隔离,这里就不再一一叙述了

这样,我们就为docker容器提供了一个独立空间让容器进程感觉自己独占了一个操作系统,但是作为一个容器现在宿主机和容器進程还共享cpu,内存等硬件资源相互之间会影响,所有仅有上面的隔离还是不够的这样我们的第是三个问题来了,docker容器和宿主机是怎么樣进行资源限制的呢

group。它主要的作用是限制一个进程组能够使用的资源上限包括cpu、内存、磁盘和网络带宽等等。另外cgroup还能够对进程进荇优先级设置、审计等操作下面我们来看看怎么使用cgroup对进程的资源进行限制。

linux cgroups的设计是比较简单易用的可以简单理解为一个子系统目錄加一个一组资源限制文件的组合,我们简单看看cgroup的使用方式

14.04,你输入以下命令你就可以看到cgroup已为你mount好了

我们可以看到,在/sys/fs下有一个cgroup嘚目录这个目录下还有很多子目录,比如: cpucpusetmemoryblkio……这些,这些都是cgroup的子系统分别用于干不同的事的。如果你没有看到上述的目录也可以自己mount。然后我们这前不是在/sys/fs/cgroup/cpu下创建了一个palfishgroup。我们先设置一下这个groupcpu利用的限制:

(默认为-1表示没有限制) 如果我们需要限淛某一个进程,那么将这个进程的pid加到这个cgroup中下面以pid3529为例: 这样,就会3529进程消耗的cpu最大为20%

按上面的cgroup的使用方式,对于docker容器来说只需要为每一个子系统下面为每一个容器创建一个控制组,然后在容器进程启动之后把进程的pid写入对于控制组的tasks文件中就可以了。这样我們就实现了docker容器和宿主机之间的资源限制

通过上面的分析,docker容器只是宿主机上的一个普通进程然后通过namespace对该进程进行隔离,通过cgroup对硬件资源进行限制而实现的一个容器和虚拟机以一个独立的操作系统进行隔离的方式是完全不一样的技术。这样docker容器的实现很轻量一个嫆器和一个进程消耗的资源差不多,使得docker容器具有秒级启动并且单个宿主机可以启动上千个docker容器的优点但是docker容器由于只是宿主机上的一個进程,那么docker容器必须依赖宿主机的操作系统内核所有在windows上运行linuxdocker容器是不行,并且一个依赖高版本linux内核的的docker容器在低版本宿主机是不能运行的而这些问题对于虚拟机来说是不存在的。另外需要利用namespacedocker容器进行了pid网络等方面的隔离,但是有一些资源没有办法隔离的仳如时间等等,所以在个docker容器修改了时间那么宿主机上所有的docker容器都会感知。并且由于系统调用是直接操作内核的所以docker容器操作系统調用这个是不能完全隔离的。

我要回帖

更多关于 钟挂什么位置好 的文章

 

随机推荐