本期的架构周报主要关注 MapReduce 框架 Yarn、HBase 写数据过程分析、用增量更新算法为 web 应用节省流量以及.NET 应用架构设计中如何进行面向查询服务的参数化查询设计等。
技术选型
MapReduce 框架 Yarn 分析
博文“ Yarn 详解”总结了 MapReduce 框架 Yarn 的产生背景,与之前框架的优势对比,以及运行机制。
Yarn 是一个分布式的资源管理系统,用以提高分布式的集群环境下的资源利用率,这些资源包括内存、IO、网络、磁盘等。其产生的原因是为了解决原 MapReduce 框架的不足。最初 MapReduce 的 committer 们还可以周期性的在已有的代码上进行修改,可是随着代码的增加以及原 MapReduce 框架设计的不足,在原 MapReduce 框架上进行修改变得越来越困难,所以 MapReduce 的 committer 们决定从架构上重新设计 MapReduce, 使下一代的 MapReduce(MRv2/Yarn) 框架具有更好的扩展性、可用性、可靠性、向后兼容性和更高的资源利用率以及能支持除了 MapReduce 计算框架外的更多的计算框架。Yarn/MRv2 最基本的想法是将原 JobTracker 主要的资源管理和 job 调度 / 监视功能分开作为两个单独的守护进程。有一个全局的 ResourceManager(RM) 和每个 Application 有一个 ApplicationMaster(AM),Application 相当于 map-reduce job 或者 DAG jobs。ResourceManager 和 NodeManager(NM) 组成了基本的数据计算框架。ResourceManager 协调集群的资源利用,任何 client 或者运行着的 applicatitonMaster 想要运行 job 或者 task 都得向 RM 申请一定的资源。ApplicatonMaster 是一个框架特殊的库,对于 MapReduce 框架而言有它自己的 AM 实现,用户也可以实现自己的 AM,在运行的时候,AM 会与 NM 一起来启动和监视 tasks。
有关 Yarn 框架的实现原理,读者可以查看较早之前的一篇文章“ Hadoop 新 MapReduce 框架 Yarn ”。
用增量更新算法为 web 应用节省流量
该文章主要讲解如何利用 HTML5 的localstorage 和增量更新算法实现JavaScript 的本地化,并在版本更新的时候基本做到修改多少内容就下载多少内容,为网站和用户节省90% 以上的JavaScript 流量,尤其适合快速迭代开发的手机网站使用。
传统的 JavaScript 资源存放方式一般就是通过 CDN 方式存放,缓存方面通过增加 maxage、Last-Modified,etag 等方式依靠 HTTP Cache 相关协议进行缓存。这种方式的问题主要是缓存命中率不是很高,另外在快速迭代的产品中,由于代码经常需要修改,虽然很多时候只是修改很小的一部分内容,但是还是需要用户全量下载整个 JavaScript 文件,造成流量上的耗费。除了传统方式的存放和加载 JavaScript,HTML5 给我们提供了另一种 JavaScript 资源缓存的方式,即 HTML5 的离线存储或 application cache. 通过给 manifest 头文件定义资源的本地存放方式,我们可以完全实现静态数据本地存储,减少了大量网络请求,减少网络流量。但是这种方式同时也有他致命的缺点:appcache 机制定义了在更新离线存储版本的时候,用户的首次进入页面并不会启用最新的资源文件,而是由一个后台程序先把资源下载到本地,用户需要刷新或者再次进入页面时才会启用新的资源文件,当然这个问题可以通过监听离线缓存的更新完成时间,在更新完成的时候程序去刷新页面以启用新的静态资源,但是这个方式带来了一个致命的不佳的用户体验,就是用户进来后会看到浏览器自己刷新了一下页面,对一些网站来说这显然不能接受。对于引入了离线存储的页面,是没有办法去掉离线存储的,这给一些首页是动态页面的网站造成了极大的困扰。一些灰度发布的策略无法很好的实施。
架构技巧
面向查询服务的参数化查询设计
该博文以.NET 应用架构设计为前提,通过运用”关注点分离“通用设计思想来对查询服务在服务端的强耦合进行分解,将强耦合从服务端迁移出来通过策略性的配置将关注点放入各自的客户端,从而有效的解决服务不再臃肿的问题。
按照作者的案例,有一个SOA 商品(Item)查询接口,这个接口很通用,主要用来支撑日常很多其他系统的大量关于Item 的查询,尤其是在高峰期间该服务的压力是很大的;我们站在SOA 的角度看这个接口,这个通用的接口解决了众多的查询业务,确实不错,但是我们切换一下角度,站在每一个调用接口的访问端看似乎并不是很满意或者说牺牲了部分性能上的代价,因为我们无法干净利落的只获取当前这个业务点需要的数据项,这个Item 服务接口所返回的数据项必须同时满足所有调用它的业务点,哪怕这次调用我只需要用到Item 的三分之一的数据字段都不行,每次都会把不需要的字段都查询出来,不管是返回的性能、查询的性能,其实都是可以通过调整设计来避免的。
以往我们的思路都是集中在服务端,常规做法都是提供了一个能够容纳。所有查询客户端需求的数据实体,客户端可选择的余地很有限,无法只获取自己所需要的几个数据项,甚至各个业务点在不同的情况下都有可能需要两到三个数据返回实体;总而言之,面向数据查询的服务接口如果要向着SOA 方向发展那就必须包含SOA 设计上的相关原则,如这里的面向查询为主的服务设计其实就是缺少SOA 原则中的”服务应具有策略性“一原则。
为什么以往一直没有暴露出这个问题呢,是因为以往都是在本地直接调用“查询引擎”,如:SQLSERVER,在“查询引擎”的最后一层就是应用程序,而应用程序中可以编写很多彼此类似的查询方法,每个方法可能只有一两个字段的差异性,或者通过“企业应用架构模式—查询对象模式”来将不同的方法合在一起通过一个可以调整查询字段的对象来配置本次需要的查询字段;由于现在我们已将查询服务化,就不太可能再去为了所有客户端在去适应性的去扩充类似没有太大价值的接口,但是客户端又需要将自己所需要的查询字段让服务知道,所以这里的解决方案可以称为面向SOA 的“企业应用架构模式—查询对象模式”。
HBase 写数据过程分析
除了使用 HBase,深入了解和分析 HBase 的实现原理也是非常有效的,既可以加深对 HBase 的理解和运用,又可以借鉴其出色的设计方法。该博文以HBase0.94.12 为基础,从Client 和Server 端两个角度分析了写数据过程。
比如,对于写操作,HBase 内部就是多线程,线程数量与批量提交的数据涉及的region 个数相同,通常情况下不需要再自己写多线程代码,自己写的多线程代码主要是解决数据到HTable 的put 这个过程中的性能问题,数据进入put 的缓存,当达到writeBufferSize 设定的大小后才会真正发起写操作(如果不是自己控制flush),这个过程的线程数与这批数据涉及的region 个数相同,会并行写入所有相关region,一般不会出现性能问题,当涉及的region 个数过多时会导致创建过多的线程,消耗大量的内存,甚至会出现线程把内存耗尽而导致OutOfMemory 的情况,比较理想的写入场景是调大writeBufferSize,并且一次写入适量的不同regionserver 的region,这样可以充分把写压力分摊到多个服务器。
欢迎读者朋友推荐或者投稿架构相关的文章,联系邮箱为 editors@cn.infoq.com ,标题请注明“架构专栏”。
评论