放翁(文初)的一亩三分地

  BlogJava :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理 ::
  210 随笔 :: 1 文章 :: 320 评论 :: 0 Trackbacks

#

    下周一应该是我今年最后一次参加内部培训了,所要讲的内容也是我这大半年来都在专注的技术:Open API&SIP。由于文章要在程序员1月刊发表,因此文章暂时不能放在Blog上,不过下周一的培训PPT还是可以分享一下的。有兴趣的集团的同学也可以来阿里软件听。同时也很高兴的看到在csdn的blog在年底冲过了10w,希望明年有更多的分享能够贡献出来^_^



Uploaded on authorSTREAM by cenwenchu
posted @ 2008-12-10 10:23 岑文初 阅读(3941) | 评论 (1)编辑 收藏

    今天,有一个使用我优化的Memcached cache Client给我发了邮件问到一个参数的作用,觉得还是比较重要的一个参数,因此也说一下,同时也在这里说一下,当前优化过的客户端已经作了几次小的升级,修复了一些边界数据的问题,大家如果在使用的话,最好能够升级。(http://code.google.com/p/memcache-client-forjava/

   邮件如下:

   你好:
<socketpool name="pool0" failover="true" initConn="5" minConn="5"
  maxConn="250" maintSleep="5000" nagle="false" socketTO="3000"
  aliveCheck="true" >
  <servers>10.0.0.16:11111</servers>
  <weights>10</weights>
 </socketpool>
能解释下maintSleep这个意思么?我看有的资料说吧它设置为0性能更好,能给些建议么?谢谢!!我们现在的PV大约每天500万。

 

    这个参数是对此连接池维护线程的检查间隔时间的配置,如果配置小于等于0,则将不会有后台线程维护此连接池,参数单位为毫秒,下面解释一下维护连接池的含义,其实就和其他的资源池一样,资源池的目的就是为了解决资源的申请和释放的开销增加系统压力的问题,将资源通过池的方式回收重用,有利于系统性能的提高。memcached cache client 其实是通过socket来和服务端进行通信,建立socket连接也是比较消耗时间的工作,因此配置了池的初始连接数(initConn),最小连接数(minConn),最大连接数(maxConn)。这三者关系如下图,维护他们之间状态转移的就是后台线程。

 

    后台进程维护资源池的作用就是将有限资源回收,例如数据库连接,如果一台oracle只有500个连接数可以支持,那么如果一个应用都占用了50个闲置,那对于其他需要资源的应用来说无疑是一种浪费。但如果配置了资源管理,但由于应用属于忙时和闲时交替比较频繁的情况,那么如果时间配置的不是很合适,就会达不到原来资源池的作用,资源反复回收和申请。所以对于这个参数的配置,个人觉得一定要配,配置的值需要注意,初始化和最小的值可以是自己预估平时平均并发处理的均值,最大的连接数当然依赖于资源的总数,而维护时间间隔则最好是能够根据闲时和忙时的情况来考虑配置,这样既不会浪费资源,同时也不会使资源池时效。

    顺带说一句,如果对于数据丢失要求不是很苛刻,然后网络情况也不错的时候,可以将aliveCheck设置为false,因为如果是true,在每一次发送任何数据操作之前都会去做心跳检查,这个未来也会考虑去优化。

posted @ 2008-11-21 15:28 岑文初 阅读(1930) | 评论 (0)编辑 收藏

    一句话:“不要为做别人已经作过的事情而沾沾自喜,要做就做别人没有做或者做不到的”。原话可能不是这句了,但是意思差不多,这是上次架构委员会开会的时候,阿里集团新来的首席架构师王坚和我们说的一句话。原因就是集团内或者公司内部资源重复去做一些工作,包括我在内很多程序员就整天津津乐道的去重复做一些工作,对于别人的成果(国外开源除外),总是有些排斥,特别是一些关键性技术,但其实真正的架构师应该关注如何能够找到合适的方法正确高效的解决问题,如何积累技术,而不是重复建设,这点很多人都很清楚,但是真的遇到一些情况的时候,就忘记了这些准则。

    一个人,我们阿软的首席架构师赵进。说到对人能力的佩服,我想对于赵进作为首席架构师的能力,我自己真的是很实实在在的佩服。远了不说,就说最近的关于阿里软件自己的基础组件Cache,当前除了SIP以外,其他两个自主产品的cache都采用的我维护的cache组件,这次做外贸重构,其他的架构师作了一个新的Cache,赵进知道后觉得这个关系到未来的整体基础架构统一性的问题,因此反复找我们几个人谈了很久,当然我也很理解架构师为了项目需求不愿意切换或者改变现有成型代码,但是如果作为一种长远的负责的规划,的却是需要统一起来。期间的困难可想而知,赵进最后找了我们的老大来拍板,结果我老大的一句话把赵进打入冷库,连我们老大都因为担心项目影响的风险而不是很赞成,我可以看出当时赵进的失落,但是在他弃而不舍的精神下,我真的算是感动了,大家一起在作了分析和讨论,最后总算确定了一个不算最满意,但也算是达到目的的一个解决方案。

    那么回顾一下我对赵进的感受,那么就能够体现出如果要成为一个架构师,或者是一个首席架构师应该具有的能力,首先就是微笑,其次就是倾听,再则就是引导,最后就是坚持。微笑可以化解敌意,倾听可以找出问题,引导可以商讨解决方案,坚持可以达到目标。当然双赢之类的就不说了。其实说到能力,在我看来技术方面的能力是可以培养的,要成为技术上的能人,需要专注,坚持和勤奋,但是要成为一个架构师那么最重要的还是胸怀和眼界,能够容纳别人才会让别人接受你,这些说起来都很容易,但是做起来却是很难,因为这和个性也有关系,改变自己的个性需要勇气和时间。

    我现在MSN的名字叫做海纳百川,时时告诉自己有容乃大,整天为了一些细枝末节的重复劳动而沾沾自喜,只会变成井底之蛙,要做就要做别人没有做或者做不到的,多了解一些,多学习一些,站在不到巨人的肩膀上也站到石头上,看得更高才会走得更远。

    一年过去之际,勉励自己改变自己。

    有架构师的能力,却没有宽广的胸怀,那么永远只会停留在一个代码编写者阶段。

    有宽广的胸怀,却只有程序员的能力,那么只要努力就会成为架构师甚至首席架构师。
    (打个广告^_^,年底关于Open API的文章由于要发表在杂志上,因此无法在这里贴了,不过到了一月份应该就可以贴了,这篇关于Open API的文章是自己沉淀自己大半年工作的一份总结,也希望能够分享给大家)

posted @ 2008-11-20 08:46 岑文初 阅读(2492) | 评论 (9)编辑 收藏

    昨天集团架构委员会(虚拟组织)作了第二次交流,各个子公司都说了当前的一些进度,问题和想法,我也大致讲了一下阿里软件的服务集成平台的一些进展和自己的一些思考,这里先贴一下PPT的图片,后面想整理以下关于当前Open API的一些想法以及对Open API Framework的一些思路。

 

幻灯片1

 

幻灯片2

 

幻灯片3

 

幻灯片4

 

幻灯片5

 

幻灯片6

 

幻灯片7

 

幻灯片8

 

幻灯片9

 

幻灯片10

 

幻灯片11
posted @ 2008-10-31 09:53 岑文初 阅读(1963) | 评论 (4)编辑 收藏

       SIP5.0以后服务的请求量爆发性增长,因此也暴露了原来没有暴露出来的问题。由于过去一般一个新版本发布周期在一个月左右,因此如果是小的内存泄露,在一个月之内重新发布以后也就看不出任何问题。

因此这阵子除了优化Memcache客户端和SIP框架逻辑以外其他依赖部分以外,对于内存泄露的压力测试也开始实实在在的做起来。经过这次问题的定位和解决以后,大致觉得对于一个大用户量应用要放心的话,那么需要做这么几步。

1.       GC输出的环境下,大压力下做多天的测试。(可以在 JAVA_OPTS增加-verbose:gc -XX:+PrintGCDetails -XX:+HeapDumpOnOutOfMemoryError

2.       检查GC输出日志来判断是否有内存泄露。(这部分后面有详细的实例说明)

3.       如果出现内存泄露问题,则使用jprofiler等工具来排查内存泄露点(之所以不一开始使用,因为jprofiler等工具对于压力测试有影响,使得大压力无法上去,也使问题不那么容易暴露)

4.       解决问题,并在重复2步骤。

这里对SIPjdk1.5jdk1.6下做压力测试的GC 日志来做一个实际的分析对比,通过对比来大致描述一下如何根据输出情况能够了解应用是否存在内存泄露问题。(这里的内存泄露问题就是在以前blog写过的jdkconcurrent包内LinkedBlockingQueuepoll方法存在比较严重的内存泄露,调用频率越高,内存泄露的越厉害)

两次压力测试都差不多都是两天,测试方案如下:

开始50个并发,每个并发每次请求完毕后休息0.1秒,10分钟后增长50个并发,按此规律增长到500并发。

旧版本SIP是在JDK1.5环境下完成的压力测试,

新版本SIPJDK版本是1.6

压力机和以前一样,是10.2.226.40DELL19508CPU8G内存。

压力机模拟发出对一个需要签名的API不断的调用请求。

看看两个Log的具体内容(内容很多截取部分做分析)

先说一下日志输出的结构:(1.61.5略微有一些不同,只是1.6对于时间统计更加细致)

[GC [<collector>: <starting occupancy1> -> <ending occupancy1>, <pause time1> secs] <starting occupancy3> -> <ending occupancy3>, <pause time3> secs]

<collector>GC收集器的名称

<starting occupancy1> 新生代在GC前占用的内存

<ending occupancy1> 新生代在GC后占用的内存

<pause time1> 新生代局部收集时jvm暂停处理的时间

<starting occupancy3> JVM Heap GC前占用的内存

<ending occupancy3> JVM Heap GC后占用的内存

<pause time3> GC过程中jvm暂停处理的总时间

Jdk1.5 log

启动时GC输出:

[GC [DefNew: 209792K->4417K(235968K), 0.0201630 secs] 246722K->41347K(498112K), 0.0204050 secs]

[GC [DefNew: 214209K->4381K(235968K), 0.0139200 secs] 251139K->41312K(498112K), 0.0141190 secs]

一句输出:

新生代回收前209792K,回收后4417K,回收数量205375KHeap总量回收前246722K回收后41347K,回收总量205375K。这就表示100%的收回,没有任何新生代的对象被提升到中生代或者永久区(名字说的不一定准确,只是表达意思)。

第二句输出:

按照分析也就只是有1K内容被提升到中生代。

运行一段时间后:

[GC [DefNew: 210686K->979K(235968K), 0.0257140 secs] 278070K->68379K(498244K), 0.0261820 secs]

[GC [DefNew: 210771K->1129K(235968K), 0.0275160 secs] 278171K->68544K(498244K), 0.0280050 secs]

第一句输出:

         新生代回收前210686K,回收后979K,回收数量209707KHeap总量回收前278070K回收后68379K,回收总量209691K。这就表示有16k没有被回收。

第二句输出:

         新生代回收前210771K,回收后1129K,回收数量209642KHeap总量回收前278171K回收后68544K,回收总量209627K。这就表示有15k没有被回收。

比较一下启动时与现在的新生代占用内存情况和Heap使用情况发现Heap的使用增长很明显,新生代没有增长,而Heap使用总量增长了27M,这就表明可能存在内存泄露,虽然每一次泄露的字节数很少,但是频率很高,大部分泄露的对象都被升级到了中生代或者持久代。

又一段时间后:

[GC [DefNew: 211554K->1913K(235968K), 0.0461130 secs] 350102K->140481K(648160K), 0.0469790 secs]

[GC [DefNew: 211707K->2327K(235968K), 0.0546170 secs] 350275K->140921K(648160K), 0.0555070 secs]

第一句输出:

         新生代回收前211554K,回收后1913K,回收数量209641KHeap总量回收前350102K回收后140481K,回收总量209621K。这就表示有20k没有被回收。

         分析到这里就可以看出每一次泄露的内存只有10K,但是在大压力长时间的测试下,内存泄露还是很明显的,此时Heap已经增长到了140M,较启动时已经增长了100M。同时GC占用的时间越来越长。

后续的现象:

         后续观察日志会发现,Full GC的频率越来越高,收集所花费时间也是越来越长。(Full GC定期会执行,同时局部回收不能满足分配需求的情况下也会执行)。

[Full GC [Tenured: 786431K->786431K(786432K), 3.4882390 secs] 1022399K->1022399K(1022400K), [Perm : 36711K->36711K(98304K)], 3.4887920 secs]

java.lang.OutOfMemoryError: Java heap space

Dumping heap to java_pid7720.hprof ...

         出现这个语句表示内存真的被消耗完了。

Jdk1.6 log

 

启动时GC的输出:

[GC [PSYoungGen: 221697K->31960K(229376K)] 225788K->36051K(491520K), 0.0521830 secs] [Times: user=0.26 sys=0.05, real=0.05 secs]

[GC [PSYoungGen: 228568K->32752K(229376K)] 232659K->37036K(491520K), 0.0408620 secs] [Times: user=0.21 sys=0.02, real=0.04 secs]

第一句输出:

         新生代回收前221697K,回收后31960K,回收数量189737KHeap总量回收前225788K回收后36051K,回收总量189737K100%被回收。

运行一段时间后输出:

[GC [PSYoungGen: 258944K->2536K(259328K)] 853863K->598135K(997888K), 0.0471620 secs] [Times: user=0.15 sys=0.00, real=0.05 secs]

[GC [PSYoungGen: 259048K->2624K(259328K)] 854647K->598907K(997888K), 0.0462980 secs] [Times: user=0.16 sys=0.02, real=0.04 secs]

第一句输出:

         新生代回收前258944K,回收后2536K,回收数量256408KHeap总量回收前853863K回收后598135K,回收总量255728K680K没有被回收,但这并不意味着就会产生内存泄露。同时可以看出GC回收时间并没有增加。

在运行一段时间后输出:

[GC [PSYoungGen: 258904K->2488K(259264K)] 969663K->713923K(1045696K), 0.0485140 secs] [Times: user=0.16 sys=0.01, real=0.04 secs]

[GC [PSYoungGen: 258872K->2448K(259328K)] 970307K->714563K(1045760K), 0.0473770 secs] [Times: user=0.16 sys=0.01, real=0.05 secs]

第一句输出:

         新生代回收前258904K,回收后2488K,回收数量256416KHeap总量回收前969663K回收后713923K,回收总量255740K676K没有被回收,同时总的Heap也有所增加。

         此时看起来好像和1.5的状况一样。但是查看了一下Full GC的执行还是400-500GC执行一次,因此继续观察。

运行一天多以后输出:

[GC [PSYoungGen: 257016K->3304K(257984K)] 1019358K->766310K(1044416K), 0.0567120 secs] [Times: user=0.18 sys=0.01, real=0.06 secs]

[GC [PSYoungGen: 257128K->2920K(258112K)] 1020134K->766622K(1044544K), 0.0549570 secs] [Times: user=0.19 sys=0.00, real=0.05 secs]

可以发现Heap增长趋缓。

运行两天以后输出:

[GC [PSYoungGen: 256936K->3584K(257792K)] 859561K->606969K(1044224K), 0.0565910 secs] [Times: user=0.18 sys=0.01, real=0.06 secs]

[GC [PSYoungGen: 256960K->3368K(257728K)] 860345K->607445K(1044160K), 0.0553780 secs] [Times: user=0.18 sys=0.01, real=0.06 secs]

发现Heap反而减少了,此时可以对内存泄露问题作初步排除了。(其实在jdk1.6环境下用jprofiler来观察,对于concurrent那个内存泄露点的跟踪发现,内存的确还是会不断增长的,不过在一段时间后还是有回收,因此也就可以部分解释前面出现的情况)

总结:

         对于GC输出的观察需要分两个维度来看。一个是纵向比较,也就是一次回收对于内存变化的观察。一个是横向比较,对于长时间内存分配占用情况的比较,这部分比较需要较长时间的观察,不能仅仅凭短时间的几个抽样比较,因为对于抽样来说,Full GC前后的区别,运行时长的区别,资源瞬时占用的区别都会影响判断。同时要结合Full GC发生的时间周期,每一次GC收集所耗费的时间作为辅助判断标准。

         顺便说一下,Heap YoungGen,OldGen,PermGen的设置也是需要注意的,并不是越大越好,越大执行收集的时间越久,但是可能执行Full GC的频率会比较低,因此需要权衡。这些仔细的去了解一下GC的基础设计思想会更有帮助,不过一般用默认的也不错。还有就是可以配置一些特殊的GC,并行,同步等等,充分利用多CPU的资源。

         对于GC的优化可以通过现在很多图形工具来做,也可以类似于我这样采用最原始的分析方式,好处就是任何时间任何地点只要知道原理就可以分析无需借助外部工具。原始的总是最好的^_^

posted @ 2008-10-22 16:36 岑文初 阅读(4821) | 评论 (5)编辑 收藏

         从去年到今年,开放这个词也在互联网上炒得火热,自己一年多的工作也让自己对开放这个词有了自己的一些理解和认识。

开放的平台

         去年到今年自己的工作也随着公司的战略改变不断的发生着变化。最早公司定位致力于为中小企业提供商务管理软件,让中小企业能够通过使用在线软件轻松搞定电子商务贸易管理。随后公司又致力于提供开放的在线软件运营平台,为众多ISV和中小企业建立一个软件交易平台,中小企业可以随需定制管理软件。到今年年初,提出了服务集成平台,ISV的应用开发不再是封闭的开发模式,可以基于ISP提供的服务定制出更加丰富的应用。其实这种转变也是对平台的开放的思想不断成熟的一个过程。

         独自实现在线管理软件和传统软件其实没有太大的差别,唯一的差别就是把应用由客户的机器拉到了软件提供商的服务器上,对于维护,更新和商业模式可能有部分的变化,但是根本上来说软件的封闭性还是和传统软件一样。互联网软件的最大特点就是个性化需求强烈以及需求变更周期短,要适应行业客户的需求,仅仅靠一个公司的几杆枪几号人的创意远远不够。Web2.0的热潮其实能够给开发人员最大的启示就是参与才是力量的源泉,其实软件开发也是一样,如果能够集合互联网上众多ISV的思想和创意,那么满足用户需求并不是一件难事,同时及时响应用户需求也不再是火烧屁股的事情。同时,看看互联网应用开发的今天,国外Open API前几年就已经兴起,Amazon,Google,Yahoo,FaceBook,MySpace等等,将自己的数据,存储,计算通过API的方式提供给第三方,让第三方开发者能够通过使用这些服务有机会实践自己的创新和创意,互联网应用的开发也有了新的开放式开发模式。服务集成平台其实就是为ISV提供了创建应用的一个资源平台,ISV可以通过服务集成平台获取到各个ISP(例如淘宝)API,在其基础上开发出在线应用,然后直接挂接到应用运营平台为终端用户提供应用服务。这很类似于传统行业的产业链,服务集成平台就好比原料交易市场,应用运营平台就好比商品交易市场。回过头来看,阿里系的各个子公司,其实都是在以这种思路做事,从加入公司到现在,给我印象最深刻的一句话就是:“凡事不要先想着如何赚到别人的钱,让别人先赚到钱,别人自然很乐意的和你分享”,这种双赢的思想在开放中能够得到最好的实践。

开放的框架

         一个公司技术是需要积累的,如果纯粹让每一个开发人员根据自己的能力去合作开发企业的产品和平台,对于企业,对于产品都是不利的。Java吸引人就在于它的开源世界,每一个开发人员可以去获得自己想要的,或者去贡献给他人自己创造的。现在很多公司应聘的过程就是一个开源知识问答,其实是否用过能说明什么问题呢,关键是没用过如何去学习和了解并且快速上手,如果能力再强一点,那就知道如何定制和扩展,我想这样的才可以叫做企业需要的人才。

         从公司成立那时起,内部就有一个应用开发框架,作用就是为了快速开发应用,尽最大可能降低开发者对于开发技术的学习,集中精力致力于业务开发。(当然看到这里估计98%的开发人员都会皱起眉头)。我也为此贡献了自己2Q的工作时间,当时我主要负责后台重构,需要建立起一个服务框架,开始参考了OSGI(因为它的模块化和动态载入机制),发现并不是很合适,然后接触了SCA框架(可扩展,模块化,SOA的支持),最后决定在开源项目Tuscany0.91版本的基础上再次开发和封装,实现了内部的ASF(应用服务框架)ASF作为我们开发框架的后台基础框架被广泛使用在了我们的多条产品线以及基础平台上,但是ASF的质疑就一直没有停过,性能,学习成本,调试困难度等等。虽然自己竭力去写了厚厚的一套文档,一组单元测试工具,一系列的问题查找工具,作了多次的压力测试,学习普及,但是还是得不到一些架构师的支持。

         其实,自己在后面也做过一些思考,其实对于ASF来说,它的可扩展性没有什么好怀疑,他不像其他开源项目,我可以封装Hessian组件,REST组件等等,随需载入,开发者只需要配置一下标签,即可使用,因此这样的框架下,不会随着技术的发展和自己的封闭而腐烂。但是,有一点就导致推广产生了那么多问题,那就是参与。我记忆很深的就是我们的首席架构师在今年招开会议评估ASF的问题时地邮件中说的:“ASF不是岑文初一个人的ASF,也不是平台一部的ASF,而是大家的ASF”。其实那时候我已经不再专职负责ASF,当今年因为一个项目进度由于开发受到影响时再次提出ASF的质疑地时候,我自己真的觉得比较沮丧,很多架构师和开发者从来就没有看过文档,没有用过调试工具,没有看过Q&A,一出问题就觉得无所适从,要找人解决框架问题,我曾经说是否Spring用的时候出现问题,第一想法就是去找Spring的开发者,还是先会看看文档,调试一下。我想这应该是两方面的原因,但如果能够让每个人都参与进来,那么就不会是今天一人独挡的局面。

         因此未来自己的工作中,不论是内部的基础组件还是基础平台都会多邀请一些参与者,毕竟自己的肩膀有限,蚂蚁就算在大力也需要有伙伴的支持。

开放的心态

         这点其实是做人的基本要素,有一个宽阔的胸怀才会有更多的机会,才会成长的更快。但是自己这点的却做得很不够。开发人员都有一个相同的特点就是热衷于技术钻研,今天搞一个东西比你快一点,明天做一个东西比他功能多一点,总是在技术方面去寻找满足。其实老大一直和我们也在说,现在公司内部的架构师并不是一个“全专”,也不一定是一个写代码高手,但是在某一个领域会有深入的研究,同时接触其他领域也能够胜任。没有什么技术人员是绝对的高手,其实随着工作重心的不断变化,所接触的领域也会不断发生变化,因此不可能有所谓的“全才”。

         有时候自己也会用技术的眼光去看待人或者事,其实这样只会让自己看不到自己的不足,也忽略了别人的优点,更重要的就是失去了一次进步的机会。其实经常给自己换换思路会对自己有很大的帮助,就好比最近忙于写了一阵子代码,那么就给自己一个机会去看看一些关于搜索领域的知识。开发了一个阶段的服务集成平台,去了解一下所有的国外网站Open API的风格,结构,流程。用惯了Java后,去学习学习Php,Ruby等等。这样换换脑子对自己来说会有新的收获。

         开放的心态理解容易,但是要让他不仅仅写在MSNtitle中,而写在心里却需要不断地督促和付出。不过知道自己有问题好过觉得自己没有问题。

         写了那么些,其实思路比较乱,我想从随笔里面也看得出来,但是还是想记录一下自己的一些思考,起码以后回过头来可以看到自己成长的过程。

posted @ 2008-10-07 13:24 岑文初 阅读(1705) | 评论 (5)编辑 收藏

    昨天贴了这个帖子以后,有同学说我是不是写错了,Memcached Cache应该是分布式的Cache,怎么变成集中式了。

    这里把我另外一部分的内容贴出来。

    Memcached是一种集中式Cache,支持分布式横向扩展。这里需要有点说明,很多开发者觉得Memcached是一种分布式Cache,但是其实Memcached服务端本身是单实例的,只是在客户端实现过程中可以根据存储的主键作分区存储,而这个区就是Memcached服务端的一个或者多个实例,如果将客户端也囊括到Memcached中,那么可以部分概念上说是集中式的。其实回顾一下集中式的构架,无非两种情况:1.节点均衡的网状(JBoss Tree Cache),利用JGroup的多播通信机制来同步数据。2.Master-Slaves模式(分布式文件系统),由Master来管理Slave,如何选择Slave,如何迁移数据,都是由Master来完成,但是Master本身也存在单点问题。

总结几个它的特点来理解一下它的优点和限制。

         Memory:内存存储,不言而喻,速度快,对于内存的要求高,不指出的话所缓存的内容非持久化。对于CPU要求很低,所以常常采用将Memcached服务端和一些CPU高消耗Memory低消耗应用部属在一起。(作为我们AEP正好有这样的环境,我们的接口服务器有多台,接口服务器对于CPU要求很高(由于WS-Security),但是对于Memory要求很低,因此可以用作Memcached的服务端部属机器)

         集中式Cache:避开了分布式Cache的传播问题,但是需要非单点保证其可靠性,这个就是后面集成中所作的cluster的工作,可以将多个Memcached作为一个虚拟的cluster,同时对于cluster的读写和普通的memcached的读写性能没有差别。

         分布式扩展:Memcached的很突出一个优点,就是采用了可分布式扩展的模式。可以将部属在一台机器上的多个Memcached服务端或者部署在多个机器上的Memcached服务端组成一个虚拟的服务端,对于调用者来说完全屏蔽和透明。提高的单机器的内存利用率,也提供了scale out的方式。

         Socket通信:传输内容的大小以及序列化的问题需要注意,虽然Memcached通常会被放置到内网作为Cache,Socket传输速率应该比较高(当前支持Tcp和udp两种模式,同时根据客户端的不同可以选择使用nio的同步或者异步调用方式),但是序列化成本和带宽成本还是需要注意。这里也提一下序列化,对于对象序列化的性能往往让大家头痛,但是如果对于同一类的Class对象序列化传输,第一次序列化时间比较长,后续就会优化,其实也就是说序列化最大的消耗不是对象序列化,而是类的序列化。如果穿过去的只是字符串,那么是最好的,省去了序列化的操作,因此在Memcached中保存的往往是较小的内容。

         特殊的内存分配机制:首先要说明的是Memcached支持最大的存储对象为1M。它的内存分配比较特殊,但是这样的分配方式其实也是对于性能考虑的,简单的分配机制可以更容易回收再分配,节省对于CPU的使用。这里用一个酒窖比喻来说明这种内存分配机制,首先在Memcached起来的时候可以通过参数设置使用的总共的Memory,这个就是建造一个酒窖,然后在有酒进入的时候,首先申请(通常是1M)的空间,用来建酒架,酒架根据这个酒瓶的大小分割酒架为多个小格子安放酒瓶,将同样大小范围内的酒瓶都放置在一类酒架上面。例如20cm半径的酒瓶放置在可以容纳20-25cm的酒架A上,30cm半径的酒瓶就放置在容纳25-30cm的酒架B上。回收机制也很简单,首先新酒入库,看看酒架是否有可以回收的地方,如果有直接使用,如果没有申请新的地方,如果申请不到,采用配置的过期策略。这个特点来看,如果要放的内容大小十分离散,同时大小比例相差梯度很明显,那么可能对于使用空间来说不好,可能在酒架A上就放了一瓶酒,但占用掉了一个酒架的位置。

         Cache机制简单:有时候很多开源的项目做的面面俱到,但是最后也就是因为过于注重一些非必要性的功能而拖累了性能,这里要提到的就是Memcached的简单性。首先它没有什么同步,消息分发,两阶段提交等等,它就是一个很简单的Cache,把东西放进去,然后可以取出来,如果发现所提供的Key没有命中,那么就很直白的告诉你,你这个key没有任何对应的东西在缓存里,去数据库或者其他地方取,当你在外部数据源取到的时候,可以直接将内容置入到Cache中,这样下次就可以命中了。这里会提到怎么去同步这些数据,两种方式,一种就是在你修改了以后立刻更新Cache内容,这样就会即时生效。另一种是说容许有失效时间,到了失效时间,自然就会将内容删除,此时再去去的时候就会命中不了,然后再次将内容置入Cache,用来更新内容。后者用在一些时时性要求不高,写入不频繁的情况。

         客户端的重要性:Memcached是用C写的一个服务端,客户端没有规定,反正是Socket传输,只要语言支持Socket通信,通过Command的简单协议就可以通信,但是客户端设计的合理十分重要,同时也给使用者提供了很大的空间去扩展和设计客户端来满足各种场景的需要,包括容错,权重,效率,特殊的功能性需求,嵌入框架等等。

         几个应用点:小对象的缓存(用户的token,权限信息,资源信息)。小的静态资源缓存。Sql结果的缓存(这部分用的好,性能提高相当大,同时由于Memcached自身提供scale out,那么对于db scale out的老大难问题无疑是一剂好药)。ESB消息缓存。

posted @ 2008-09-26 11:45 岑文初 阅读(3270) | 评论 (2)编辑 收藏

     摘要:   Author:文初 Email: wenchu.cenwc@alibaba-inc.com Blog: http://blog.csdn.net/cenwenchu79/            MemCached Cache在大型网站被应用得越来越广泛,不同语言的客户端也都在官方网站上...  阅读全文
posted @ 2008-09-25 16:34 岑文初 阅读(6773) | 评论 (3)编辑 收藏

越是忙,杂七杂八的事情越多,最近正在优化Memcache的客户端代码,这时候SIP突然出现OOM的问题(Out of Memory),作开发最头痛就是这种问题,压力测试都作过,早期的几个版本都没有出现这样的问题,因此怀疑可能是最近一次发布修改引起的。借助JProfiler在测试环境搭了一套系统,开始做压力测试,来分析Memory到底流到了哪里去了。

问题一:连接池泄漏

       看到这个问题,我想很多人都说,都什么年代了,使用开源的现成连接池,怎么还会有这样的问题,又不是那些使用jdbc的年代了。那来看看现象吧。

场景:测试部用loadRunner往死里压,发现很多业务对象不断增长,但是按照业务场景来说,这些业务对象处理以后就自动释放了。(在本地的开发环境验证了是会自动释放的)

JProfiler截图:

       上图中可以看到有很多业务对象已经累积占用了不少内存,在让测试部同学停掉压力测试以后,等待了一会儿,然后用JProfiler主动发起垃圾回收,也看到了Jboss后台有GC回收的记录输出以后,发现这些对象依然存在,也就是说这些对象成为了Memory泄漏的诱因之一。但是就如我所说的,在本地测试以及白盒测试来看,这些对象在一次请求以后,处理完毕一定会被释放,没有被其他MapReference,然后通过JProfiler看了看这些对象的Allocation Call Tree,就是我们处理请求的Servlet作为源头的,但为什么Servlet没有被清理掉呢?接着来看看后面二张图

       既然知道对象存在并且被Hold了,那么就去看看线程运行的状况,这一看发现有很多线程都处于Wait的状态(其实在serverdump也可以看到),这张图上就可以看到,我选择了其中一个wait的线程它处于等待状态的原因就是在ibatisThrottleincrement的时候处于等待状态,看了看ibatis的代码,这部分代码其实是ibatis连接池的一段代码,在连接池被占满以后,处于等待释放的状态,也就是说程序把连接池耗尽了。

       为了验证是否是耗尽了,让DBA老大光辉给我看了看MySql(这部分当天的日志数据都保存在MySql中)的连接情况,发现只有8个连接,看来不是真的耗尽,应该是连接池泄露了。光辉告诉我,这八个连接都在做同一个查询,就是统计某一个API的访问记录次数和流量。在当前的业务流程中对于MySql主要做了两类操作:

       1.访问控制计数器创建的统计查询。

由于要对Open API访问控制,采用了Memcache计数器方式来实现。当发现此类API没有创建过计数器,那么就分析MySql中的数据,创建计数器,后续的访问记录除了插入数据库以外还需要累加计数器,这样访问控制可以高效使用集中式计数器而不需要查询数据库。

2.日志批量异步写入。

对于Open API的记录采用了线程池中每一个线程维护一个内存分页,当页满或者到了刷新间隔时,一次性批量写入数据库,缓解数据库写入压力,这里采用了事务来批量提交。

       对于第一种操作,由于设计中MySql就只会保留当天的数据量,因此只有系统启动的时候做一次统计,对于数据库压力和Sql执行来说应该没有太大的压力,但是由于压力测试是从昨天下午就开始做的,里面的数据已经有上千万,因此这次重新启动开始做压力测试,导致了这个创建计数器的Sql执行很慢。同时日志的批量写入采用的是事务方式来提交,对于MySql其实自己还不是很深入,但是感觉上来说,问题应该出现在这里,由于查询的缓慢在加上事务批量的提交,可能会造成事务失败,同时没有正确的将释放资源的信号传递给ibatis,导致了看起来的连接资源耗尽。

       我将数据库中的记录全部删除,然后重新启动,开始压力测试,问题不存在了,对象都及时得到回收。后续还会去跟进这个问题,在ibatis早期版本,同样是这个类出现了死锁的问题,后来升级得到了解决,但是也看到很多国外的朋友说道2.22.3其实还是有死锁的问题,不过我个人觉得可能还是和数据库也有一定关系。

疑问:

       这个问题的背后我还有一点疑问,对于我们来说,如果一个普通的http请求,当超时以后肯定就会自动中断,但是在这个场景中,我足足等了1个小时还是没有释放,也就是说客户端其实已经断开了,但是JBoss好像并不会释放这些处理请求的事务,导致资源被卡。

问题二:LinkedBlockingQueue惹祸

       自从Jdk1.5以后concurrent包为大家提供了很多便利高效的开发新模式,我在不少地方用到了LinkedBlockingQueue,作为消费者和生产者之间的数据通道,消费者们等待在LinkedBlockingQueue门口守候生产者提供数据,获取数据后就开始并行处理。这里我会采用queue.poll(100,TimeUnit.MILLISECONDS)这种方式来半阻塞的获取数据。其实在昨天已经听说LinkedBlockingQueue可能存在着内存泄露的问题,看了看很多网上的人也都提到了这个问题,在1.5种没有得到解决,在1.6中会去fix这个问题,但是没有证据,也不好乱加断定。在问题一搞好以后,然后继续查找潜在bug,这时候不经意的发现有一个对象随着时间的推移始终在增加,但是由于单个对象占的内存不大,因此没有很明显的体现出来,但是对象实例的增加却是很明显的,看看下面两张图:

      

这两张图的间隔时间2小时左右,可以发现这个对象的instance已经有了很大的增长,同时内存也吃了不少,看了看创建这个对象的Tree,发现就是poll这个方法,也就是我线程池中线程周期性扫描的结果。这期间没有任何访问,仅仅就是放着不动,就有如此大量的增长。我尝试将poll(100,TimeUnit.MILLISECONDS)换成poll()全阻塞方式,对象增长依旧。因此可以看出来服务器的Memory Leak很大程度上由这部分引起,早先没有发现,因为是SIP上线不久,没有太多用户,而这阵子用户越来越多,加上API中的更新类请求比较吃内存,就容易发现此类问题。

       那么是否1.6就解决了这个问题呢,开始使用机器上1.6_01的版本,发现问题依旧,去sun下载了最新的1.6_07,发现的却会回收,但是回收和增长都存在,具体数据描述举例如下:

1.       1000 instance   31k

2.       200 instance    6k (回收了一部分)

3.       1500 instance   46k(发现增长的比以前还多)

4.       300 instance    9k (回收了一部分)

5.       2000 instance   62k (发现增长的比以前还多)

也就是说,回收时有发生,但是总体趋势还是在上升,这个真的还需要好好测试,有兴趣的同学也可以试验一下我的测试方式,就仅仅只需要使用一个LinkedBlockingQueue,然后定时的去pool1.5绝对增长的不小。

       对于这个问题,我只能再去验证,如果发现真的暂时不可避免,那么只有考虑替代方案了。

这是今天作了Memory Leak的一些分享,希望也能给其他遇到或者将会遇到问题的同学一个分享,说一句,如果有条件的话用JProfiler去分析性能绝对是不错的,没有条件么就dump,gc输出来查找问题。
   刚刚作了测试现在的场景可以用take来替换poll,原来是看中了poll的timeout方式,take完全没有问题,看来如果要在1.5版本用,还是老老实实用take。

posted @ 2008-09-18 22:14 岑文初 阅读(3900) | 评论 (4)编辑 收藏

 

       集团内部很多团队都使用Memcache来提高应用性能,最近的一次工作汇报中提及了MemcacheHash算法需要研究来满足一些需求,同时提高Memcache的利用效率。讨论了一下最后自己总结了这么几点是对Hash算法需要着重考虑的。

问题:

1.       存储数据如何均匀分散。如何把数据尽可能的散开存储,这样对于Memcache的可扩展性才会有充分利用,试想如果算法每次都会把数据定向到某几台机器,那么就会导致集群机器之间利用率的不均衡,无法发挥出集群效应。

2.       增减机器减小对原有数据存取的影响。由于业务量的增长势必需要对后端的服务器有所扩容,但是增加或者减少机器如何尽可能小的影响已有的缓存数据,这点直接影响业务处理以及应用的效率。

3.       提高Memcache效率。Memcache在压力测试下也会暴露出对于网络资源的消耗问题,毕竟也是网络间的Socket数据交互。

解决的一些思路和方法:

1.       Consistent Hashing是一种比较好的解决思路。可以参看一下:http://tech.idv2.com/2008/07/24/memcached-004/ 其中主要两个亮点就是稀释节点以及环状分区段管理。稀释节点就是将原来的节点再复制几十倍,使得离散度更高,数据更加分散。环状分区段管理,就能够将数据分区管理,在加入和减少节点时对数据产生影响最低,最好的类比就是解放前的地下工作者单线联系,如果被捕不会涉及到所有的地下党同志。

2.       集群的机器使用Memcache最好结合本地Cache,这里我们自己写了一个本地的类似于Memcache有超时时间Cache,两者结合一起使用缓存信息,在压力测试下提高了20%左右的性能。这里和我们的系统也有关系,我们对于Memcache有比较大的依赖,虽然已经对于每一个请求处理都防止重复获取信息,将必要信息放在线程上下文中,但是在运行期间还是会有不少的请求。

存储到Memcache中的数据类型:

1. 一次写入多次读,很少更新。这种数据系统启动以后构建,在非命中情况下不采用从后备数据源中获取数据来填充Memcache。(也是提高效率,同时防止一些攻击性的请求)

2. 多次写入多次读取。这类数据往往是在运行期被构建,非命中下会从后备数据源中获取,或者是某一种计算结果的缓存。

对于第一类数据来说,增加机器需要重新构建,如果采用分区分段,那么只需要构建某一部分的数据,或者是移动数据。对于第二类数据,增加机器如果采用简单的Hash算法也问题不大,最多存储多份,命中率降低,但是如果采用分区,也可以降低命中率下降的情况。

       这里只是抛出问题,后续如何解决请各位看官各抒己见了。当然这里自己也会考虑这方面的实现和设计。
posted @ 2008-08-14 10:36 岑文初 阅读(3408) | 评论 (1)编辑 收藏

仅列出标题
共12页: First 上一页 4 5 6 7 8 9 10 11 12 下一页