听了一场跟Flash存储有关的演讲

没有全部听懂,但有段关于latency的内容记住了:   1. 内存的latency: 纳秒级   2. 普通硬盘:    毫秒级   3. Flash存储:      微秒级 也就是说,Flash存储的性能虽比不上内存,但比普通硬盘强太多了; 有的I/O密集型应用如数据库,如果对某些数据使用Flash存储,对性能的提升会非常明显。 

jmockit中的诡异陷阱

以前用过一些Mock框架;使用jmockit时,我以为mock框架都差不多,jmockit也不例外。吃了一些苦头才发现,jmockit是多么的与众不同(中性词). 在我看来,jmockit作者的思维方式是扭曲的,jmockit的用法也是诡异的。要想充分利用jmockit的强大功能,你必须容忍它与众不同的地方。本文给出一些最近收集到的诡异点: Mock行为中的诡异点 看起来只Mock了一个对象,实际上Mock了整个类 @Mocked //看起来只Mock了bean这个对象 private SimpleBean bean; @Test public void test_MockedInstance() throws Exception { bean.throwException(); // 不会报异常,因为bean已经被mock了. 这个好理解. } @Test public void test_AnotherInstance() throws Exception { SimpleBean bean2 = new SimpleBean(); bean2.throwException(); // 也不会报异常,因为SimpleBean的任何一个实例都被Mock了。想不到吧? } 看起来只Mock了本类,实际上这个类的父类及所有祖先类都被Mock了 @Mocked private SubSimpleBean bean; //看起来只Mock了SubSimpleBean类 @Test public void test_AnotherInstance() throws Exception { SimpleBean bean2 = new SimpleBean(); //SubSimpleBean的父类 bean2.throwException(); …

jmockit中的诡异陷阱 Read More »

根据Module的特性决定单元测试的覆盖率要求

业务应用的单元测试覆盖率可以低一些,框架和工具箱的覆盖率必须要很高   单元测试的覆盖率高当然很好,但高的覆盖率意味着要写很多测试;而写测试本身是件费脑费时的事情,代价不低。 所以要在收益和代价之间取得平衡。   可以着眼现在,放眼未来,具体考量四个因素:     1. 对代码正确性的要求。 要求越高,覆盖率就应该越高。     2. 能否人肉测试。 如果用人肉可以做测试,则自动化运行的单元测试代码可以少一些     3. 代码逻辑的易变性。    代码逻辑越易变,写的测试被浪费的概率越大,对低覆盖率的容忍度越大     4. 发现bug时的易改性。 出现bug的代码越容易改,对低质量的容忍度越大,对低覆盖率的容忍度也越大       以互联网为例,在业务应用层面,可以为了迅速推出新产品而容忍一定的bug,可以用鼠标进行人肉测试,产品易变代码逻辑也易变,发现bug时也容易改,所以这一块的测试覆盖率可以低一些   而框架、工具箱等Module的代码正确性必须非常高,否则会影响很多上层模块和系统;没有界面因此不能人肉测试; 代码逻辑不怎么变,单元测试被浪费的概率不高;一旦出现bug,受牵连的上层模块和系统可能会非常多,因此不能轻易让bug出现。 所以,框架、工具箱等Module的测试覆盖率必须非常高。

《构建高性能Web站点》笔记:11 性能监控

性能监控   1.要监控什么?   a. linux服务器的性能指标:如cpu,内存,I/O等,可以使用nmon, snmp等工具或手段   b. 各种服务的性能:    i.apache: mod_status模块 + 模块提供的http请求接口    ii.mysql: Cacti, mysqlreport等    iii.Memcached: Nagios等   c. web页面的响应时间  通过工具模拟浏览器从外网访问你的一些页面,如www.jiankongbao.com就提供了这样的服务     2.在哪里查看监控结果?       a.服务器上直接查看    可以用Nmon在本地实时监控,实时刷新各种指标,包括内核状态、NFS等;也可以定期采集数据,并通过Nmon Analyser生成报表       b.远程监控:     i.服务器上运行监控代理,在其他机器上另建监控中心与代理交互    ii.可以让代理主动采集数据,定期上报给监控中心   iii.也可以让代理被动响应数监控中心的请求    iv.代理采集数据的方式有:      1. 调用shell命令,获得输出,基于正则表达式进行解析      2. 可以把SNMP服务器直接用作监控代理      3. 有些服务端软件自己提供了监控接口,如Nginx就提供了一个http请求接口     v.监控中心可以用开源产品Cacti来搭建,它的绘图能力很强

《构建高性能Web站点》笔记:10 异步计算与并行计算

要解决的问题:并发能力没问题,但处理单个请求比较耗时,比如,获得一个统计值需要查多次数据库导致很长的响应时间   解决办法:      1.同步改异步,给用户的反馈也改成异步         前端程序把任务丢到后台消息队列后,立即告诉用户:“正在为你统计,请稍候刷新页面”         消息队列的消费者,即worker,在算出统计值后,再把结果丢进数据库         用户刷新页面时,前端程序去数据库查询到结果,再反馈给用户         这就叫异步计算         2.串行变并行,减少总体响应时间,用户可以同步等待反馈         把任务拆分成多个小任务,后台多个worker各领一个小任务并同时处理,响应时间立刻缩短         并行处理完后,再将结果合并起来,返回给用户         如果总体时间够短,对用户的反馈机制就没必要做成异步;用户可同步等等反馈          这叫做并行计算   具体技术:      1. 同步改异步:用消息队列,如MemcacheQ      2. 并行计算:采用Map/Reduce         a. Map代表拆分,Reduce代表汇总         b. 兼做监控、容错、可用性、负载均衡等事情         c. Map/Reduce框架本身不是开放的(Google的内部产品),但思想是开放的;你可以用Hadoop来做Map/Reduce, 也可以自己编码实现

《构建高性能Web站点》笔记:9 数据库的扩展

数据库的扩展问题可以拆为两个子问题:   1. 高并发时,单台数据库服务器会不堪重负       2. 大数据量时,针对一个大数据集进行数据读写,响应时间会比较长   前端和后端 出于分而治之的理念,可以想到一种理想的方案:把DBMS拆分为数据服务系统和数据存储系统,前者作为用户请求的入口,后者则存储数据,两者的关系类似于web服务器和文件服务器;分离之后,再分别采用针对性的扩展方案,比如说数据服务系统可以像web服务器一样简单地集群,数据存储系统则可以采用类似于分布式文件系统的方案,然后再让“前端”调用“后端”,整体上像一个分布式系统   遗憾的是,传统的DBMS内部似乎并不支持这种分布(TBD)。一个DBMS进程中,一般都既要响应请求,又要存取数据;这种感觉就像,文件只能存放在web服务器里,如果要复制web服务器,就一定要考虑文件怎么处理。 接受“拆不开”这个事实后,再来看目前流行的各种方案。 首先,可以看看如何提高并发能力。 1.搞N个一模一样的数据库服务器,每个服务器分摊一部分用户请求   每台服务器都提供服务,每台服务器上都包含全量数据,这是最直接的方案       问题在于,实时同步各台服务器上的数据本身比较耗费性能:一台服务器上来了新数据,就立即广播出去,然后大家都从它那里复制数据;还没复制完,又有一台服务器广播了一点更新消息。。。如果大家总是忙于跟它人搞双向复制,就没有余力处理用户的请求了 2.服务器之间单向复制数据,并使用单个数据更新源    单向复制比双向复制快的多。一台服务器更新数据后顺便更新一下自己的日志(主服务器),其余服务器只根据这个日志更新自己的数据(从服务器),不再接收用户的数据更新请求。那“从服务器”对高并发又起什么作用呢?他们可以为用户的数据查询请求提供服务。 这就是读写分库,把读和写分摊掉了。     这种方案的问题在于,如果写请求很多,主服务器忙不过来,加再多从服务器也没有用。 3.分摊写请求     主服务器本身可以根据数据更新的目的地的不同,而分拆成不同的结点。 如果一个服务器中有两个库,可以把其中一个库挪到另一台服务器中去(前提是A库和B库基本不join),这样一来,对不同库的数据更新请求就落到了不同的服务器上(当然,读的请求也分摊了)。你也可以把库中不同的表放到不同的服务器上。 这种分法叫垂直分区。 但垂直分区仍有个极限:如果某张表只有一个字段,而且对这张表的请求非常多,你怎么办? 4.再次分摊写请求     可以把1万行的表,拆成10张1000行的子表,放到十台服务器中,这样可以再次分摊请求; 而且理论上说,这种分摊没有极限,数据每增多一点,服务器就多加一台。 这种分法叫做水平分区。其实水平分区不仅可以分摊请求,同时也可以对付大数据集的问题。夸张地说,以前表里有1万行数据,按索引查数据都很慢;现在表里只有1000行数据,全表扫描都无所谓了 大数据量的问题     上面提到的水平分区,已经可以用来对付大数据量的问题。这里就不再提了 最后罗列一下上述各种方案中可以运用的技术或工具  1. 实时、双向同步数据: mysql clustering(TBD) 2. 读写分离: mysql主从复制可以实现读写分离,mysql proxy则可以前置于主从服务器前面,自动决定读、写的归属,为应用程序提供透明的服务 3. 水平分区:     如果要根据user.user_id对user及其相关表分区,对于一个user_id,应该放到哪个区呢?    a. 取模:  5,10,15…放A区, 6, 11, 16…放B区 … …

《构建高性能Web站点》笔记:9 数据库的扩展 Read More »

《构建高性能Web站点》笔记:8 共享式/分布式文件系统

共享文件系统    主流实现:NFS          a.直接基于RPC协议,client/server通信模型 b.client再把服务端的ip映射为本地磁盘 c.RPC默认使用UDP,在应用层即RPC协议里做差错重传等操作 d.服务端采用多进程模型 e.NFS的性能天花板非常低,在高并发环境下(比如前置10台web服务器,每个web服务器100个并发数)非常容易成为瓶颈 f.唯一的好处就是简单 g.支持异步写。server接到异步写的请求后会立即返回"OK",然后再在后台把文件写入磁盘 文件系统中影响性能的因素:     1.磁盘吞吐率     2.服务器的并发处理能力     3.网络带宽 ========================== 共享文件系统很容易成为瓶颈; 替代方案是要让web服务器上也有文件,同时又保证服务器中各个节点上都有相同文件的副本。那怎么创建这些副本呢?   1. 可以主动地将文件传给别的服务器   a. 可以通过高级语言里的SSH Client模块 (具体的SSH应用是SCP和SFTP),将文件复制出去   b. 也可以走Http WebDAV协议(subversion走的就是WebDAV) 2. 也可以被动地轮循别的服务器,下载这段时间内更新过的文件; linux下的rsync就是这样一个工具。 3.另外,文件的复制可以用多级的方式实现。如中心服务器把文件复制到各省服务器,各省再复制到各市等等 ========================== 分布式文件系统    这里的文件的概念不是操作系统中的文件的概念,目录也不是操作系统里的目录(MogileFS甚至没有目录的概念);分布式文件系统自己定义了一套文件的概念,它运行在用户态。 分布式文件系统的作用:    1.在使用者眼里,一个分布式文件系统是一个单一的整体    2.但实际上,一个分布式文件系统确可以跨越多台服务器,并在服务器之间进行自动的文件复制 原理:    1.文件存储在Storage Node中    2.Storage Node之间可以根据规则自动复制文件    3.Tracker放在Nodes前端,作为客户端的入口      …

《构建高性能Web站点》笔记:8 共享式/分布式文件系统 Read More »

一个应用为什么要拆成多个jar包,并且包要有版本?

都说应用大了就要拆,有人建议拆成多个应用并构成分布式系统,有人建议维持一个应用但拆成多个包(指jar包,不是package);本文讨论的是后一种分拆法。    下面从多个方面来讲述分包的好处。我将主要使用“反证法”,论述大应用如果不分包可能导致的后果,反过来证明分包的合法性。 本文的观点和论据大部分来自于Robert C. Martin的《敏捷软件开发》,有兴趣的朋友可以细阅这本书的第20章-“包的设计原则”     从软件开发的多个方面看待分包问题:     系统的可理解性      分包可以提供模块化的视图,帮助了解系统的组织结构    如果一个大公司没有分部门,你是不是很难说出这家公司哪些人有哪些职能? 如果一个系统的java代码都堆在一起,你是不是也很难说清这个系统大概有哪几块? 如果系统的结构不清晰,对大家理解系统、系统架构的未来规划都有很大的坏处。    而系统分包就是系统模块化的过程;结合包依赖图,可以帮助了解系统的组成部分以及模块之间的依赖关系   编译      a. 分包可以避免一次编译所有代码,减少编译时间      如果代码不分包,则意味着每次编译都要编译整个应用;对于一个大的应用,这个编译时间会很长;如果应用是c/c++写的,这个编译时间可能会达到无可容忍的地步。    分包之后,大部分代码已经编译掉、作为jar文件引入了,此时你要编译的代码只是你正在改的那一小部分。这种情况下,编译时间可大大缩短。      b. 分包后,服务提供者的接口变更不会立即导致消费者代码编译失败      如果代码不分包,且大家都在同一份代码库里开发,会有这样一种场景:A写完FooService准备提交时,发现它依赖的BarService已经被删掉了! 这时A就没办法提交代码了。如果A是一个冒进的人,他在提交之前不会更新代码库,而是直接提交FooService,然后下班;比他下班晚的人如果签出最新代码,就会发现代码无法编译。      如果大家各搞一个私有分支,可以避免这种问题;大家在各自的分支里进行开发,最后再合到一起。但最后合并的工作量可能很多,因为要解决的代码冲突可能会很多或者很难搞,上面说的BarService被删掉的问题就很难搞;应用越大,这种冲突的概率就越高。      把应用拆成带版本的jar包可以解决这个问题:把BarService放到bar-1.0.jar里,FooService依赖bar-1.0.jar. 如果BarService被删掉,删它的人会打出一个bar-1.1.jar; FooService则仍然使用bar-1.0.jar,不会出现编译问题。   3.代码的正确性      分包后,服务消费者不必使用服务提供者的最新版本,避免出现未经验证的代码组合      如果大家都在同一份代码库里开发,A写完FooService自测并提交,然后B把它依赖的BarService改掉,FooService和新的BarService共同运行时可能就会导致错误的结果,因为对于这个Foo+Bar组合,还没人测过。      把应用拆成带版本的jar包可以解决这个问题:把BarService放到bar-1.0.jar里,FooService依赖bar-1.0.jar. …

一个应用为什么要拆成多个jar包,并且包要有版本? Read More »

从“构建、发布java程序”的角度看待OO Principles

这里的OO Principles是指Robert C. Martin提出的那一系列原则,如SRP,OCP等; 本文旨在从构建、发布java程序的角度理解这些原则,更具体一点,就是 要找出违背这些原则会对java程序的构建、发布等过程造成什么困扰。 首先可以先罗列一下构建、发布过程中可能出现的坏现象: 1. 编译、打包时间过长 2. 打出的包过大,导致网络传输久、应用启动慢、虚拟机方法区空间被浪费 3. 打出的包里有相同的库,如spring 2.5和spring2.0同时出现 4. 频繁发布应用,浪费时间,影响可用性 5. 做太多的回归测试 6. 发布前夕发现错误,不得不回滚 待续。。。 接下来进入正题, 逐个看下违反OO Principle的后果 (我们会用 caller代表模块的调用者, callee代表被调用的模块) 1. 违反SRP 场景: callee有两个互不相关的职责r1, r2; caller1依赖r1, caller2依赖r2. 后果:    a. caller因为callee中的无关代码而浪费构建自己的时间和空间。caller1与callee共同编译时,也需要编译r2相关的代码;但r2部分的代码caller1并不需要,这就意味着编译时间和打包空间的浪费  — 当然,这个浪费可能很小    b. caller因为callee中的无关第三方库,而浪费构建自己的时间和空间。 如果r2依赖了一些第三方库,而这些库又依赖了另一些第三库,打包caller1时就要把这些只能r2相关的第三方库统统纳入进来,造成打包时间过长、打出的包过大    c. 第三方库太多增加了callee包中库冲突的几率。如果r1依赖了spring 2.5, r2依赖了spring 2.0,callee在打包前就必须进行依赖调解,即两个spring只能留一个;你要么手动确定留哪个,要么通过Maven之类的依赖管理工具来处理;但不管怎么搞,都会比较繁琐,并且带来风险(maven环境中,一个极端情况就是,r1使用了官方的spring坐标,而r2使用了一个山寨的spring坐标)    d. callee的部分升级导致无关caller的回归测试。为了满足caller2的新功能,r2被升级了,这也意味着整个callee的升级;如果callee没有版本机制,则callee的更新会导致caller1不得不接受新的callee,并不得不做一下回归测试    e. callee的部分升级导致无关caller在callee发布时不可用。如果callee是一个web …

从“构建、发布java程序”的角度看待OO Principles Read More »