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

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

2008年1月22日 #

     摘要: Beatles小记(三)-分布式数据流分析中Master的横向扩展  阅读全文
posted @ 2012-01-17 13:21 岑文初 阅读(5149) | 评论 (2)编辑 收藏

     摘要: Beatles小记-分布式数据流分析框架(二),局部代码设计和实现分享  阅读全文
posted @ 2011-12-09 16:44 岑文初 阅读(4718) | 评论 (4)编辑 收藏

     摘要: 分布式流式数据分析设计和代码分析  阅读全文
posted @ 2011-12-07 16:46 岑文初 阅读(9562) | 评论 (7)编辑 收藏

     摘要: java优化设计实现细节分享  阅读全文
posted @ 2011-09-23 14:03 岑文初 阅读(5035) | 评论 (1)编辑 收藏

     摘要: 两个开放平台内部组件开放   阅读全文
posted @ 2011-07-12 11:54 岑文初 阅读(3750) | 评论 (2)编辑 收藏

     摘要: 讨论一下并发消息下行的设计方案和实现  阅读全文
posted @ 2011-06-23 12:16 岑文初 阅读(4507) | 评论 (0)编辑 收藏

     摘要: Jetty内部透明简单实现  阅读全文
posted @ 2011-06-22 17:03 岑文初 阅读(3990) | 评论 (0)编辑 收藏

     摘要: 慢连接&LazyParser  阅读全文
posted @ 2011-06-20 23:47 岑文初 阅读(5324) | 评论 (0)编辑 收藏

     摘要: PipeComet测试  阅读全文
posted @ 2011-06-08 23:58 岑文初 阅读(6914) | 评论 (0)编辑 收藏

     摘要: 一段代码,几句话  阅读全文
posted @ 2011-04-13 23:11 岑文初 阅读(4570) | 评论 (1)编辑 收藏

     摘要: 开放平台的技术问题  阅读全文
posted @ 2011-03-31 00:43 岑文初 阅读(4812) | 评论 (4)编辑 收藏

     摘要: Web容器测试模型选择  阅读全文
posted @ 2011-03-31 00:40 岑文初 阅读(3372) | 评论 (0)编辑 收藏

     摘要: 十年  阅读全文
posted @ 2011-03-08 23:46 岑文初 阅读(2902) | 评论 (6)编辑 收藏

     摘要: 模拟登录看前端门外汉学习  阅读全文
posted @ 2011-03-03 23:26 岑文初 阅读(5529) | 评论 (10)编辑 收藏

     摘要: 逻辑划分线程池  阅读全文
posted @ 2011-03-01 00:32 岑文初 阅读(5148) | 评论 (4)编辑 收藏

     摘要: OAuth2的一些改变  阅读全文
posted @ 2011-02-28 23:01 岑文初 阅读(3403) | 评论 (0)编辑 收藏

     摘要: “淘宝的”开放平台  阅读全文
posted @ 2011-02-23 23:39 岑文初 阅读(5073) | 评论 (4)编辑 收藏

     摘要: 交流分享  阅读全文
posted @ 2011-02-20 23:58 岑文初 阅读(4960) | 评论 (7)编辑 收藏

     摘要: ask & answer  阅读全文
posted @ 2011-01-12 23:22 岑文初 阅读(3862) | 评论 (0)编辑 收藏

     摘要: 耗内存应用优化实际案例  阅读全文
posted @ 2010-12-22 23:40 岑文初 阅读(4296) | 评论 (0)编辑 收藏

     摘要: Local Cache的小TIP   阅读全文
posted @ 2010-12-14 22:34 岑文初 阅读(3399) | 评论 (4)编辑 收藏

     摘要: SD开放平台技术分享  阅读全文
posted @ 2010-12-13 20:35 岑文初 阅读(3190) | 评论 (2)编辑 收藏

     摘要: Facebook优化分享后记  阅读全文
posted @ 2010-12-12 19:43 岑文初 阅读(3440) | 评论 (4)编辑 收藏

     摘要: 这篇文章将会从问题,技术背景,设计实现,代码范例这些角度去谈基于管道化和事件驱动模型的Web请求处理。建议从头看,能够从概念上更多的去理解和碰撞,其中的一些描述和例子也许不是很恰当,也希望得到更多的反馈。  阅读全文
posted @ 2010-11-25 14:44 岑文初 阅读(4102) | 评论 (7)编辑 收藏

     摘要: 这篇文章将会从问题,技术背景,设计实现,代码范例这些角度去谈基于管道化和事件驱动模型的Web请求处理。建议从头看,能够从概念上更多的去理解和碰撞,其中的一些描述和例子也许不是很恰当,也希望得到更多的反馈。  阅读全文
posted @ 2010-11-24 01:26 岑文初 阅读(3358) | 评论 (4)编辑 收藏

     摘要: 图片是大纲,先抛出来,后续会有更详细的文章分享  阅读全文
posted @ 2010-11-17 01:00 岑文初 阅读(2640) | 评论 (2)编辑 收藏

     摘要: 如果关注开放平台或者关注平台的一些内容,这篇文章应该有点内容可看  阅读全文
posted @ 2010-10-11 23:42 岑文初 阅读(2914) | 评论 (1)编辑 收藏

     摘要: 美国JavaOne之行内容,需要看直播请关注微博  阅读全文
posted @ 2010-09-22 15:55 岑文初 阅读(1666) | 评论 (1)编辑 收藏

     摘要: 代码背后的点滴,通过一些设计理念来分享技术的积累  阅读全文
posted @ 2010-09-09 02:05 岑文初 阅读(4288) | 评论 (8)编辑 收藏

     摘要: 面试有感  阅读全文
posted @ 2010-09-02 11:31 岑文初 阅读(2394) | 评论 (4)编辑 收藏

     摘要: 对同学性能优化总结的一点回复  阅读全文
posted @ 2010-08-23 16:58 岑文初 阅读(2280) | 评论 (0)编辑 收藏

     摘要: ppt分享  阅读全文
posted @ 2010-08-10 07:48 岑文初 阅读(3629) | 评论 (2)编辑 收藏

     摘要: 在概念篇介绍完以后,开始实际的对TOP开始做技术改造。(这篇东西更像是对短期工作的总结和汇报,写的不是很详实,后续会有一个ppt来深化异步化的一些思想)下面将第一阶段的工作做个总结,第一阶段主要做了以下几个方面的事情  阅读全文
posted @ 2010-08-06 00:38 岑文初 阅读(4206) | 评论 (0)编辑 收藏

     摘要: 淘宝一年陈  阅读全文
posted @ 2010-07-24 00:34 岑文初 阅读(2830) | 评论 (7)编辑 收藏

     摘要: Web服务的重放攻击的一点想法  阅读全文
posted @ 2010-07-07 00:40 岑文初 阅读(3179) | 评论 (0)编辑 收藏

     摘要: Web服务请求异步化介绍  阅读全文
posted @ 2010-06-30 08:41 岑文初 阅读(5225) | 评论 (4)编辑 收藏

     摘要: Web服务请求异步化测试  阅读全文
posted @ 2010-06-13 14:35 岑文初 阅读(4413) | 评论 (9)编辑 收藏

     摘要: 访问TOP链接超时和重置问题  阅读全文
posted @ 2010-06-09 13:34 岑文初 阅读(1706) | 评论 (1)编辑 收藏

     摘要: 对TOP高并发的一点回答  阅读全文
posted @ 2010-06-07 21:22 岑文初 阅读(1755) | 评论 (0)编辑 收藏

     摘要: TOP的价值所在  阅读全文
posted @ 2010-06-01 08:49 岑文初 阅读(3523) | 评论 (5)编辑 收藏

     摘要: 开放平台两三点感悟(下)  阅读全文
posted @ 2010-06-01 02:53 岑文初 阅读(3280) | 评论 (4)编辑 收藏

     摘要: 开放平台两三点感悟  阅读全文
posted @ 2010-05-28 02:29 岑文初 阅读(4361) | 评论 (6)编辑 收藏

http://t.sina.com.cn/fangweng

posted @ 2010-05-24 21:54 岑文初 阅读(1281) | 评论 (0)编辑 收藏

     摘要: ModJK与tomcat消息传递出现的串消息问题  阅读全文
posted @ 2010-05-11 20:00 岑文初 阅读(2763) | 评论 (0)编辑 收藏

     摘要: 异步模式下的Web请求(技术介绍篇)  阅读全文
posted @ 2010-04-20 08:50 岑文初 阅读(4231) | 评论 (1)编辑 收藏

     摘要: Q1技术点滴  阅读全文
posted @ 2010-04-02 02:26 岑文初 阅读(3113) | 评论 (5)编辑 收藏

     摘要: 普通程序员的2009  阅读全文
posted @ 2010-01-29 01:34 岑文初 阅读(2145) | 评论 (4)编辑 收藏

 

优化杂谈

Author :放翁

Bloghttp://blog.csdn.net/cenwenchu79/

         当应用遇到规模化问题的时候,就是考虑性能优化的时候了。今天同事和我聊起了NIO在客户端的使用与BIO有什么优势,也勾起了我前一阵子和其他同学交流优化的一些想法,纯粹个人的一点想法。

CPU利用率和Load

         在过去做压力测试的时候,我们经常会关注两个指标,CPULoad。有同学觉得CPU利用率上去了Load肯定也上去了,Load上去了CPU利用率同样会上去。但是在一些需要优化的场景下,常常会看到Load很高,CPU利用率却可能比较低(多核更是可能出现分配不均的情况)。Load其实就是等待处理的任务队列,当你的应用在等待同步消息返回处理的同时,CPU还是会将时间切片分配给这些线程,而真正需要CPU的线程,却不得不在到了时间片以后暂时放弃工作被挂起。因此在程序设计的时候就要考虑如何利用好CPU的这个资源,如何均匀的将压力分摊到各个CPU上(有时候就一个线程在不断循环,导致单个CPU负荷很高)。

NIO在客户端的使用

         Http消息设置keepalive和采用NIO的方式复用信道、BIO结合连接池的方式,最基本的目的就是降低建立TCP产生握手的成本,最大限度的复用已有的资源,但是否NIO就只有复用信道这点呢?

         NIOBIO在数据传输和处理的模式上有不同,NIO采用的是BufferPacket+Channel的模式,这其实和操作系统本身的传输模式很类似,而BIOStream的模式是Java自己独特的模式。在采用NIO的这种数据传输模式以后,可以充分利用操作系统本身对传输的优化,因此这是一方面好处。另一方面异步和事件机制的使用,可以降低对于昂贵的资源申请,在高并发下提高处理能力。

NIO客户端的编程模型最大特点:依赖反置,松耦合带来性能提升。在请求流程协议中支持“票根”,也就是我们说的回执。例如,你今天面试完了,不需要你在阿里巴巴前台等着结果,直接留个电话,有消息就会直接通知,电话就是通知结果和服务请求者的关联手段。(此时阿里巴巴前台和会议室就会有足够的空间给其他人来面试,这就是资源)

         服务端使用NIO就不多说了,这里主要说一下在客户端的使用场景。两者是否真的有很大的差别,是否NIO有绝对的优势,其实还是和场景有关。简单说来就一个判断标准:应用对于通道的利用率是否够高。下面列了4种场景:

1. 一次请求数据量很少,服务处理速度很快。

2. 一次请求数据量很多,服务处理速度很快。

3. 一次请求数据量很少,服务处理速度很慢。

4. 一次请求数据量很多,服务处理速度很慢。

场景1,传输效率很高,服务处理速度很快,一次请求很快就被完成,采用NIOBIO,在性能优势上除了操作系统对NIO的优化以外,BIO连接池不输于NIO。在易用性上,BIO更加容易处理。(NIO的异步机制,就要求消息传输协议需要有会话码来提供异步处理入口选择如何处理)

场景2,传输过程比较长,消耗时间比较多,服务处理速度很快,因此交互的时间大部分都还是在数据通道传输上,由于NIO在传输过程中依然是串行化的,因此BIO的连接池优于NIO,同时NIO一个客户端只有一个通道,因此BIO开的连接池越大,并行处理能力越强,因此BIO效率比较好一些。

场景3,传输量比较少,服务处理比较慢,很明显这是通道利用率低的表现,NIO有绝对的优势,特别是在高并发下。信道和服务端客户端资源被充分利用。

场景4,传输量比较多,服务处理也比较慢,这时候可以发现信道利用率取决于服务事件和传输消耗时间的比例,这类场景某些情况下BIO也会优于NIO

单线程和多线程

         在使用多线程来优化程序的时候,是否考虑过多线程的使用场景,多线程不是万能药,在某些情况下还可能是毒药。使用多线程的过程中,需要考虑这么几个因素:

1. 资源竞争,复杂度增加。

为什么前面提到的NIO客户端在处理数据流发送和读取的时候都是采用单线程,数据流的发送和读取都是在一个数据通道上的,而读取和发送本身时间消耗是固定的(不论是多线程还是单线程),同时增加了复杂度(需要处理数据包整合问题)。这其实就是在资源上的串行化操作直接导致了任务的串行化,因此任务多线程反而起到了反作用。

2. 是否是关键路径的工作,占关键路径的比例。

首先,在优化以前需要考虑优化的内容是否是关键路径的工作,如果不是,那么增加复杂度实现的多线程模式,就没有价值。其次就是看是否是在关键路径中占有比较大的比例,同样的,还是投入产出比例(多线程带来的复杂度以及在高并发下的一些资源保护措施都需要很多的维护成本)。

3. 任务的合理切分。

NIO的客户端,接受数据的事件将会写得很轻量级,但是接受到数据然后分析数据还原成业务对象,则会通过线程池的方式来分别处理。就好比监听连接到来,和实际的去建立连接分成了两个阶段的任务,让事件型的任务单纯,快速执行,让与业务相关的部分通过多线程并行的方式提高处理效率。总的来说就是把任务划分成为系统性的任务和业务性的任务,前者消耗时间少,设计尽量简单高效,采用单线程处理即可,后者通常情况下在处理流程和资源上不冲突的情况可以通过多线程并行提高效率。

         优化应用关注点:

A.关键路径是否可以优化,关键路径的任务拆分。

B.关键路径上的单个任务是否可以拆分并行执行。(是否有资源竞争,是否会有流程上的前后依赖,是否增加复杂度引入新的不稳定因素)

C.系统资源和依赖外部系统是否会成为瓶颈。(单机的CPU,IO都会在一定的压力下成下降趋势,并行执行反而降低了处理能力)

因此,可以看到不论是MapReduce设计下的Hadoop,还是Erlang语言级别的特性,都尽量的希望任务之间可以并行执行,相互之间低耦合,通过异步事件消息通知方式来交互,同时数据没有共享,防止资源竞争导致无法并行高效处理。系统设计还是要根据场景来判断使用什么方式优化,越简单越好。

posted @ 2010-01-27 01:45 岑文初 阅读(3657) | 评论 (1)编辑 收藏

     摘要: 基于MapReduce的配置型日志分析组件  阅读全文
posted @ 2010-01-12 21:58 岑文初 阅读(3851) | 评论 (5)编辑 收藏

     摘要: TOP团队招贤纳士  阅读全文
posted @ 2009-12-11 15:52 岑文初 阅读(1899) | 评论 (0)编辑 收藏

    中午左右收到一个看我blog的朋友的邮件,最近他在研究mapreduce,然后想用hadoop来做一些工作,不过遇到了一些问题,我这边也贴一下他的几个问题,同时觉得自己把自己的一些看法分享一下,当然只是自己的一些想法,也许对新学习的同学有帮助。

   问题:

  1. 从Map(K,V)的方式来看,难道mapreduce只能做统计?
  2. 目前我想除了日志分析之类的功能外,还想做一个全文检索的功能,类似windows查询一下,通过关键字查询文件的位置即可(可能还要根据匹配度做排序),这个我很迷茫不知道怎么下手,痛苦ing
  3. 你的实践是一个单机模式,如果用户把一个1G的log已经上传到hdfs了,此时分割工作已经完成,只需要从client那里得到文件基本信息和块的location就可以了,那mapreduce怎么进行下去呢?

   我给回复的邮件内容:

   首先,MapReduce的思想和Hadoop的MapReduce的架构不是一个概念,说的具体一点也就是Hadoop的架构设计只是MapReduce的一个子集思想的实现。每个人都可以根据自己对MapReduce的理解去实现业务处理,简单来说多线程处理就是MapReduce的一种最简单的实现,复杂来说多机协调工作就是一种复杂的实现。

   MapReduce的思想里面最值得借鉴的:

   a.问题分而治之。(找到流程的关键路径,优化可以并行处理的工作)

   b.计算靠近数据。(这也是hdfs存在的最重要的特点,计算的转移往往要比数据转移廉价,特别是对海量数据的处理)

   c.数据规模化随着并行处理成数量级递减。

   剩下的内容就是各个框架对于非业务性需求的处理,例如容灾,如何尽量少穿数据协调处理等等。

   针对他提出的三个问题:

    1. Hadoop的mapreduce从架构上来说最适合的就是统计分析计算。做其他方面的工作需要考虑是否适合,而不是为了技术而技术,先有需求再有技术选型。
    2.  对于你这个需求直接用搜索技术实现就可以了,不一定要硬套在mapreduce上。
    3. 对于海量数据是否一定要到hdsf上,或者就简单得数据物理或者逻辑切割来直接处理,根据自己业务场景选择。hdfs的特点就是对文件切割,容灾,数据逻辑存储和物理存储无关性(便于扩容管理,同时也是计算靠近数据的技术保证)。

    是否使用MapReduce框架,HDFS存储关键还是看你是否真的需要,当现有框架对自己来说并不合适的时候可以对小规模问题定制MapReduce的处理,最简化就是你去多线程或者多进程处理问题,需求决定技术选型。

  

posted @ 2009-12-09 13:09 岑文初 阅读(2585) | 评论 (1)编辑 收藏

Author:放翁(文初)
Email:fangweng@taobao.com
Blog:http://blog.csdn.net/cenwenchu79 

 

当前问题:

1.       不小比重的Rest请求都是无效请求,全部接纳数据消耗比较多的时间。

2.       Multipart类型的大文件流请求无法做到合理快速过滤。(参数错误请求,数据文件过多请求,文件大小过大请求)

归结来说,TOP平台处理的服务在解析参数时比较消耗时间和带宽(客户端网络速度慢导致传输字节流比较慢,文件比较大导致带宽占用严重)

处理方式:

通过自行解析字节流方式来lazy化处理请求,减少无效请求对于解析参数时间消耗(导致web容器连接消耗)及带宽消耗。

优化目标:

         Get由于内容长度有限不列入在优化范围。

         优化Post方式的请求(普通的和Multipart),要求优化后:在正常请求处理上两者处理速度不低于传统方式,非正常请求在策略命中情况下(后面会谈到什么情况下优化失效),性能有明显提高。

具体实现:

        由于现在用的是传统IO模式,因此可以用流的方式来lazy解析和处理请求(NIOchannel + buffer package就无法lazy了)。




         一共有三个组件角色:

1. 请求处理配置策略:配置在解析参数时,优先的规则(参数可以从header,uri,post body中获取,相互之间的优先性),异常抛出规则(字节流长度,文件大小,文件个数限制等),字节流解析模块的参数配置(字节流解析的窗口大小,超时时间等)。

2. 线程上下文:用来保存处理过的请求参数。一来复用,二来也是由于请求字节流处理不可逆(不保存字节流副本),必须保留。

3. Http请求字节流解析模块。根据具体的配置以及解析策略来解析字节流,同时将解析结果保存在线程上下文中。主要的实现代码在于对Post消息体逐步解析部分(普通的Postmultipart

压力测试结果:

    正常请求场景( 100并发用户,multipart 文件大小300k,当前业务场景这个值已经满足了):

普通post的处理能力1000TPS。(servlet方式处理差不多,不过有波动)  

  multipart处理能力610TPS。(apache开源项目fileupload,处理能力400TPS左右)

错误请求场景

         异常情况的处理有了很大提高,对于远程客户端传输较慢或者是大流量图片的错误请求都有很大的优化。

优化存在问题:

1. 参数缺失导致优化失效。

2. sign类似的交验,导致获取所有的参数。

3. 当前图片限制在300k,由于考虑处理速度快,就都没有设置超过阀值存储到本地,因此在高并发大流量的情况下也会有内存问题,当然已经做了部分保护。

针对上面的两个问题,作了部分的协议限制,对于API2.0希望将所有的系统参数和业务参数区分开,放入到Http header中或者url中,这样可以避免系统参数缺失导致优化失败,同时大量过滤系统参数出现问题的无效请求。

Sign类似的交验放在流程最后,避免过早获取所有参数。

作安全保护,设定简单丢弃或者io交互来缓解这个问题。

         这部分内容还有很多可以做得工作,其实最初的目的就是为了防止系统对于无效请求的处理消耗,我想在很多系统都会有这样的问题,利用缓存设置黑名单防止攻击也是这样的初衷。因此这点可以考虑在很多系统设计的时候都作一样的优化,对正常的不能优化,起码对错误的可以做一些优化,防止在异常请求高涨的时候,系统被击垮.

posted @ 2009-12-08 01:51 岑文初 阅读(2237) | 评论 (2)编辑 收藏

Author:放翁(文初)
Email:fangweng@taobao.com
Blog:http://blog.csdn.net/cenwenchu79


其实想说这句话很久了
,和很多同事接触,有时候或多或少的都会发现大家会陷入在自己的一亩三分地里面.

         主要表现得症状

1.       PD的需求就是目标,踏实的实现,不懂的就猜。

2.       经验盖过一切,设计系统就是要够完备够复杂。

从开发人员角度来看,第一种人多半比较有自己的想法,同时也有不少的工作经验,同时可能对技术比较着迷。另一种人多半是刚刚工作或者经验不足,要么就是习惯性把工作当任务,而不是爱好,写程序也就是一份赚钱的活。但看起来其实各自都在自己的一亩三分地上捣鼓,忘记了作为一个开发人员最基本的原则:“满足客户需求”。

先说1类型吧,在我们的Team有一个刚毕业一年多的同学,很勤奋,不论从学习以及工作,实实在在,踏踏实实。我们这边来需求,通常大需求我们都会全体过一下,一些小点的需求他就自己考虑一下就作了。那天正要上线,突然说了一下设计修改的内容,发现不仅满足不了PD原有的需求,而且给系统带来了缓存暴增的隐患。然后找来PD一谈,其实他要的功能已经在现有系统中已经实现,只是需要做部分的修改,而不需要新的去建立一套机制。这样的情况其实在前前后后出现了不少次数了,但其实一直没有和他细谈。后来我下班时候和他一起回家的时候说:“很多时候, PD为了让你理解,从开发的角度想要去描述一个需求,但其实最终失去了他自己想要的东西。因此对你来说第一步不是急忙的去考虑如何实现PD的想法或者和他争论他的设计是否合理,而是需要先问他:你想要什么,想要实现的东西最终目的是什么,能满足客户的什么需求?当他能够说清楚他想要什么,也知道要的东西能给客户带来什么价值的时候,我们再回过头来看,究竟应该怎么做?”这其实和我每次和同学分享一些设计的时候步骤是一样的,首先为什么要这么做,然后才是考虑如何从我的目标去寻找行动的方法方式,不然你会发现你和别人讨论了许久的东西,实现出来的时候已经背离了你的目标很远。因此在做任何需求或者设计的时候第一个问题就要问自己为什么要做,作的过程中时刻要记得我的目标是什么。这让我想起了我在离开阿软的那些日子和王坚博士谈话以及听他的一些对于设计的理念,很多时候还没有到规模化的情况下,先解决客户的需求,在解决客户需求以后,逐步的去考虑规模化问题的设计。(当然不是说第一版设计就可以随便作,良好的基础能够提升后续改进的速度)。

二类型的就比较多了,其实是很多开发人员的通病,包括有时候我自己也会陷入这样的误区。通常情况下有两种场景会陷入这样的误区,同时当事人却又不愿意改变。第一种情况就是觉得自己有不少的经验,同时对技术很执着,希望设计出来的都是很完美的,一次发布就可以满足个12年,但其实从这些年的设计角度来看,首先系统都是不断迭代进化的,因此一步到位的说法基本上不靠谱(除非就是一模一样的场景代码重复使用),其次系统的架构要做的足够灵活,通常情况就需要先做核心功能,预留出足够的空间和切入点,这样对未来扩展和需求变化有足够的适应度。从这两点来看,其实设计初期就是要求找到客户最想要的,扩展可以实现客户可能要的,防范客户没有估量到的。但这其实就需要和我们的产品设计师有充分的交流,好的产品设计师不会告诉你你怎么去实现,但是他会告诉你我想要的是什么,这些能给客户带来什么,这时候你可以告诉他我能够通过什么方式来满足你的需求。这样的开发和产品设计交流的结果才是技术化的产品,大家各司其职,同时也通晓对方领域的一些情况,对对方领域的只能给出建议,不是指导,这点在TOP我很庆幸有很好的黑羽同学,我们的交流就是这样产生良性互动。这有点撤远了,刚才说了第一种场景,然后说说第二种场景,就是初期其实大家都没有明确细节,但是在实施过程中开发人员会根据自己的接触面来选择一些技术和架构设计,最后看起来很复杂,很完美,但其实越是复杂的设计背后有越多的隐患。但是此时因为已经设计好了,就不愿意再去简化,也不愿意听任何人的意见,其实这是很危险的。我过去也犯过类似的错误,但是其实当你冷静下来,想想那句话,我们的目标是什么:“满足客户需求”,这时候你就会考虑,这么复杂的系统会不会给客户带来更多的不稳定以及复杂度,其实客户不关心你背后如何实现的,但是你需要满足客户的最基本的需求,用起来方便,高效,实实在在提供了解决问题的手段。

今天下午面试了一个外部的同学,工作年限比我长,看了简历也经历了很多项目,同时在描述的时候写了对高并发,分布式等等都很熟悉和热衷,我开始看了简历就担心,可能我这边不一定要他,因为我怕他开口就是说一大堆如何做高并发和分布式的内容。在我看来如果你没有搞清楚你什么时候要用牛刀,什么时候要用剪刀的人,和你谈论牛刀的构造其实没啥意思,因为在我看来,技术只要你肯花时间去学,没什么学不到的,但是做事方式和项目设计经验却是长时间积累的。幸好今天和他一谈,他对于技术的态度以及架构设计的思想都和我想的比较接近,不是为了技术而技术,不是为了过程而过程,了解如何从简如繁,再从繁入简,最终能够找到自己的目标。当然后来还是谈了很多技术细节的问题,毕竟干活还是要一个好手,作了那么多年如果没有经验和技术积累也是很可怕的事情。最后我问了他两个问题:1.你学习一个新技术的过程是怎么样的?2.你和你同事如果在设计方案上有冲突你怎么解决?他告诉我他学习新技术首先会去考虑这个技术的特点是什么,和其他技术的差别,他的擅长领域是什么,这样才能够用到实处。第二个问题他和我说就是开会讨论,最后大家群体决定。我对他第一个问题感到很满意,因为我就需要这样的同事,第二个问题我给了他一个建议,其实在很多时候,将别人的架构设计的优点融入到自己的设计中,不再以方案作为边界,那么大家最终就很容易达成一致,因为你在接受别人的思想时其实能够看到自己的不足,同时对待别人不是用否定的态度,会让你更容易得到认可和接受。(这点作起来需要不断的改变程序员自身的好胜个性,我起码还是出于变化中

我记得我小时候上政治课的时候,老师给我们划分了三种人:有能力但是没有道德的人是危险的人,没有能力但是有道德的人是对社会无害的人(觉得像葛优说的那个对社会无害的海龟一个概念),有能力同时也有道德的人是对社会有益的人。我觉得其实程序员也就可以从两个纬度看:

1.       有能力,有经验,对技术有追求。

2.       对产品化和客户没有任何感觉。

拥有了素质1但是没有素质2,那么最多也就只能说是试验室的花朵,在大学搞搞研究还不错,实际要做出产品来可能就是纸上谈兵,好钢始终用不到刀刃上,有力没地使。

素质1有所欠缺,素质2很明晰,对自己目标不断追求,其实这样的人,有时候笨鸟也会飞的比聪明的鸟更高。

拥有12的人,当然就是最好的人,只需要学会做人那么就可以发挥自己的能量。(程序员有时候就是很难改变自己的个性,去学会如何沟通和理解)
         最后一类就是自以为有12的人,这类人最怕就是面试的时候被考官通过,那么后续的问题就大了。

说了怎么多,其实也无非想说出一个程序员这些年的经历,从做开发到做基础平台,到做业务平台,该怎么踏实做事,该在什么时候找到自己的瓶颈,该在什么时候改变自己的状态,都需要自己好好的让自己冷静下来想想。做基础平台需要耐得住寂寞,同时也要知道自己是有客户的,服务不好客户,那么基础组件平台就是玩具。做业务平台需要学会去分析和沟通,需要去了解每一个层次的设计如何协作,同时在兼顾业务需求的同时满足隐性需求(稳定性,可用性,响应速度,规模化等等)。但归根到底,能给开发人员不断能量的不是技术本身,而是你用技术给你的客户带来的价值,对你的认可是长期做事的一个最基本的动力,因为当你现在觉得纯做技术能够支持你不断向前走的时候,其实在不远的将来你会体会到原来过程和目标是同样重要的。走出自己的一亩三分地,给自己多一点的空间,会让自己看得更远,走的更高。

posted @ 2009-12-08 00:54 岑文初 阅读(4228) | 评论 (6)编辑 收藏

   今年blog更新的速度比去年慢很多,当然最大的原因就是工作的转变。当选择留在云公司还是去淘宝,自己做了很快的抉择,去淘宝。其实在阿软的后面这一年,对自己来说是一个技术提升的阶段,工作任务不紧,技术预研范围较大,但对于自己这么一个已经到了30的人来说,应该是把技术转变为产品的时候了,因此义无反顾地选择了TOP作为我新的开端。

    其实每个人都会有自己不同的阶段,任何阶段都有自己的目标,同时当你发现在一个阶段停留很久,都没有什么突破,或者渐渐失去目标的时候,那么就需要考虑如何找到新的起点。对我来说,技术追求和提升是没有止尽的,但是需要真正的将所学的作出一点实在的产品,同时在参与产品团队的过程中,学会沟通,交流,分析问题,全面地看问题,这些也是不可缺少的成长经验,如果仅仅局限在狭隘的某一个技术立领域,那么就和普通的学生无异。

    到了TOP,自己的工作分成了三大块:1.救火及防火。2.整体架构支持。3.核心代码的编写。前期花了不少时间在1上,同时和各个Team交流,参与各个团队的关键性设计评审,以及对平台的统一规划,让我实实在在的作了一点2的事情。(说道实实在在,记得在阿软很多团队都抱怨我所在的架构组整天派一个人挂个名字,然后就算是架构支持了,当然这有很多原因造成,并不一定是负责架构的同学的问题)。对于3这点当然是自己最乐意做的,也是自己一直告诫自己要不断提升的,不论自己有多少理由说自己忙碌,写代码是我们这种人的生命所在,不然就会漂浮在空中,渐渐的走向“另一个世界”。 但自己觉得其实还少了一块,就是对业界的发展深入了解,这会让我看的不够远(幸好我们的产品经理黑羽同学总还会给我一些新的思路),到了年底将会多花一点时间作这部分内容。

    去年年底我写了关于对于Open API的思考和探索的一篇文章作为年底总结,今年一样,对于当前自己的工作将会有一份总结和规划,即是对今年平台发展的一个回顾,也是对平台未来的一点思考,大致已经列了一个纲要,对外可能部分内容不能全写出来,不过就算不写细节也会将一些思路写一下,大家可以相互探讨一下。这部分内容也将会成为我12月份参加淘宝内部淘宝大学讲课的内容,希望能够将今年新进淘宝的同学吸引到TOP来,为TOP增加人气。

   下面是一个mind 图,大致描述了一些内容:

posted @ 2009-11-27 00:58 岑文初 阅读(2930) | 评论 (2)编辑 收藏

     摘要: 常用模式的细节问题看设计稳定性  阅读全文
posted @ 2009-11-10 01:52 岑文初 阅读(2866) | 评论 (4)编辑 收藏

在自己的blog上做个招聘广告,TOP平台架构Team欢迎各位资深或者刚毕业的对TOP有兴趣的同学加入,可以直接给我留言或者发mail到fangweng@taobao.com,非诚勿扰^_^,同事可能比老婆相处的时间都要长。对了,请附加上你的简历,方便继续沟通。
posted @ 2009-10-30 15:51 岑文初 阅读(1328) | 评论 (2)编辑 收藏

     摘要: Author:放翁(文初) Email:fangweng@taobao.com Blog:http://blog.csdn.net/cenwenchu79   闲话:(如果图片看不清楚可以看另一个blog,因为图片在家,这里上传就只能转贴了)          为什么又叫做什么…的点滴,...  阅读全文
posted @ 2009-10-30 12:27 岑文初 阅读(3658) | 评论 (6)编辑 收藏

     摘要: 上海校招回来  阅读全文
posted @ 2009-10-13 21:27 岑文初 阅读(1455) | 评论 (4)编辑 收藏

     摘要: 客户端NIO实践分析  阅读全文
posted @ 2009-09-24 08:57 岑文初 阅读(3370) | 评论 (7)编辑 收藏

     摘要: 应用架构设计“防火”经验分享  阅读全文
posted @ 2009-08-27 00:59 岑文初 阅读(3165) | 评论 (5)编辑 收藏

   今天是转岗到淘宝的第七天,也算是一周吧,期待来这个团队已经有快大半年了,这次阿软的重组给了一个机会,过去的就过去吧,不再回首有任何的抱怨和遗憾,需要面对的是新的将来。

    很奇怪,来到淘宝,都是熟人,Boss是早就相识的菲青,TOP团队的自雪,凤先,秀芳及我不认识但是认识我的其他同学都很热情,运营,PD,OST都是以前阿软的老同学,还有其他几个团队的朋友,感觉回到了家,而不是离开了家。

    原先来淘宝是比较坚决的,同时也得到王博士的支持,心里还是比较有底的,不过就是担心过来以后和淘宝已有的团队合作可能会有磨合期,因为担心有“小圈子”。结果却是很出乎我的意料,TOP的人就和做的事情一样,是一批开放的人,自雪,凤先,张三各个都很放的开的和我聊,对于架构,对于技术,对于未来的发展,这些人坐在一起什么都可以说,自己觉得自己早先是用老思维来看待这个团队了。这个团队很年轻,很有活力和创造力,缺少的只是一些经验,而我经验是有一些,但是那些斗志已经在去年一年被磨砺的差不多了,正好是我回炉好好再热一热的时候了。来之前就和黑羽有过接触,也看过他对于TOP的一些构想,在我的计划中就有和他交流的部分,上周找了一个时间碰了一下,果然有很多和我一致的想法,同时还有一些比我更加深入的idea,特别是对于大淘宝未来的一个构想。其实来到TOP我所要做的就是在技术的架构上找到商业的感觉,让商业驱动技术,技术沉淀积累来支持商业的畅想。

    这七天过的很快,全身心投入的工作,时间总是过的很快,而且过去那种沉闷的心情和处事的态度在这里得到了改变。明天基本上就看完了TOP的大部分代码,整理了一些review的建议,同时昨天还花了一些时间去看了看google appengine,写了几个小应用,看了看源码(部分反编译),因为要给boss对于小应用hosting方面的一些想法。

   总的来说还是和我原先的计划一样,商业上和PD运营交流,了解未来TOP商业发展方向,以及对技术架构的一些需求。架构上从代码和文档看起,文档不是很多,所以就只好每个工程看过来,也不错,看到自雪同学写的代码还是不错的,同时也看到了淘宝的基础组件的推广力度之大,这比在阿里软件强的多,其实也是我一直希望看到的,人人都是技术牛人,都在做重复的事情,但是却没有技术沉淀,其实大家完全可以吧自己的构想增强在别人的基础之上,而不是什么都自己搞一套,淘宝的技术应该来说在政策上得到了支持,技术积累效果还是不错的,这里还不得不提到我的淘宝同学毕玄同学的服务基础框架HSF,虽然现在还没有接触,但是应该已经发展的挺好的。

   有两个能够用人,担得起起技术团队发展的Boss,有这么一些年轻有冲劲的小同学,有这么一些乐于倾听分享协作的老同学,有这么一些很有商业feeling的非技术团队同学,要做好TOP,我想只有三个字:“没问题”。这是我在入职七天写的随记,一年后再来回看我今天说的这些话,在来看看这个团队创造的价值。

   附:在淘宝申请好了花名:放翁。陆游的字,武侠小说的人就连扫地的都没有了,历史名人也没有了,不过诗人倒是没有人用,指不定还开创了淘宝同学入职的花名新取法。

   好好工作,天天向上,为了TOP,为了家里的BB,为了自己的一点理想,踏踏实实的走自己的路,让别人开车去吧,^_^

 

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/cenwenchu79/archive/2009/08/12/4440248.aspx

posted @ 2009-08-12 23:16 岑文初 阅读(1160) | 评论 (1)编辑 收藏

   昨天是去淘宝工作的第一天,最近最头痛的就是花名,在我儿子出生的时候我就知道起名字是最麻烦的事情,而起花名更是痛苦,因为你的选择余地更小,同时还不能和前人重复,好不容易找到两个还不错的,结果一个给其他部门的老大保留了,一个因为拼音和一个同学相似而无法使用。想用文初,结果还给一个淘宝的活跃用户使用了,问了HR不取花名是否可以,回答说,不可以,太折腾了。

   昨天开了一整天的会,主要还是协调两个平台之间将来的合作模式,同时也梳理了双方的现有功能,将未来双方的边界做了初步定夺,同时也对将来的一些需求做了初步的规划,系统的模块化也提上了最近的日程。

  今天会化一些时间看看已有的代码熟悉一下Top的情况,同时也看看一些流程性的文档,希望能够尽快的对Top全方位的了解,这样便于从细节实现到整体架构设计都能给出自己的意见。

  初来乍到不容易,很多需要从新开始的,不过对我来说合作的人,做的事情还是有一定的基础,因此只是需要一周左右的过渡期,后续应该会走的更加顺畅。

 

 
posted @ 2009-08-06 05:12 岑文初 阅读(1020) | 评论 (0)编辑 收藏

     摘要: Author : 岑文初 Email: wenchu.cenwc@alibaba-inc.com Blog: http://blog.csdn.net/cenwenchu79 Date: 2009-5-26 目录 需求转而学习 “软”负载均衡 LVS (Linux Virtual Server) Virtual Server三种模式介绍 Virtual...  阅读全文
posted @ 2009-08-04 22:32 岑文初 阅读(2267) | 评论 (1)编辑 收藏

     摘要: “软”负载均衡学习点滴  阅读全文
posted @ 2009-08-04 22:30 岑文初 阅读(2075) | 评论 (0)编辑 收藏

Author : 岑文初

Email: wenchu.cenwc@alibaba-inc.com

Blog: http://blog.csdn.net/cenwenchu79

Date: 2009-5-26

目录

需求转而学习

“软”负载均衡

LVS Linux Virtual Server

Virtual Server三种模式介绍

Virtual Server三种模式的比较

Virtual Server三种模式实践

三种模式下的简单压力测试

HA-Proxy

HA-Proxy安装和使用

HA-Proxy的压力测试结果

负载学习心得

需求转而学习

         很多时候不少做开发的同学都认为技术更新的快,新技术、新概念层出不穷,大家乐此不疲的去跟随着所谓的“技术趋势”走在风头浪尖上,但其实往往忘记了一个最重要的问题“满足客户需求”。其实技术就是为满足需求服务的,用最小的代价来满足用户的需求,以最简单高效的方式来达到目标,就是每个开发者应该追求的。(不要因为自己的架构很简单就脸红拿不出手,只要你在满足用户当前需求的基础上对未来有所考虑,那么化繁为简就是一种能力的表现)

         SIP(服务集成平台)5.7版本中对于未来多个服务提供商,多种类型的服务,在每日几亿的调用压力下,需要找到一个解决方案:可以分流不同服务提供商的服务,分流不同类型的服务,服务隔离化来减少服务相互之间影响以及服务提供商之间的影响。

         当前SIP的前端是通过硬件F5作负载均衡,因此是无状态无差别的服务负载,这也使得无法区分不同的服务提供商的服务请求和不同类型的服务请求,导致服务提供商之间的服务会产生相互影响(旺旺即时通信类API在峰值占用了大部分的服务处理资源,淘宝宝贝上传类API占用了大量的带宽)。近期还有更大的两类API将会接入,因此寻找一个服务可分流的方案势在必行。(当然过去也考虑通过三级域名配置在负载均衡上来解决这些问题,但是这样首先对于开发者来说不透明,其次也是一种比较僵化的设计方案,扩展和维护也有一定的难度)

         在过去也尝试过ApacheWeb容器自己的一些load balance特性,当然效果不是很好,和硬件基本无法比拟,而一些专有的“软”负载均衡方案和开源项目也没有深入的去了解,因此借着这次机会,好好深入的挖一挖“软”负载均衡。

“软”负载均衡

         作为互联网应用,随时都需要做好用户量突然增大,访问量突然上升的准备。今年热门的词汇“云”我就不多说了,这里就简单说说服务器的横向扩展。其实和DB,文件系统等一样,当资源成为瓶颈的时候,就需要考虑如何通过扩展或者提升资源能力来满足用户的需求,这就是我们常说的横向扩展和纵向扩展。(对于横向扩展和纵向扩展的优劣大家应该都很清楚了,这里也不做赘述)横向扩展中就会要求使用负载均衡的能力,如何根据资源能力不同以及资源在运行期负荷动态变化将负载合理分配是判断负载均衡优劣的标准。

         软件负载均衡一般通过两种方式来实现:基于操作系统的软负载实现和基于第三方应用的软负载实现。LVS就是基于Linux操作系统实现的一种软负载,HA Proxy就是基于第三应用实现的软负载。(后面会详细介绍这两种方式的使用)

         最早期也是最原始的软负载均衡:“Round Robin DNS”,通过轮询方式在DNS绑定多个IP的情况下,将用户对于同一个域名的请求分配到后端不同的服务节点。这种方案的优点:配置简单,负载分配效率高。缺点:无法知晓后端服务节点服务情况(是否已经停止服务),无法保证在一个Session中多次请求由一个服务节点服务,每一个节点都要求有一个外网IP

         另一种较为常见的就是基于分发器的Load balance。服务使用者通过向分发器发起请求获得服务,分发器将请求分发给后端实际服务处理的节点,给客户提供服务,最常说的反向代理模式就是典型的分发器Load Balance。这类负载均衡处理可以基于应用级转发,也可以基于IP级别转发,当然基于应用转发效率和损耗比较大,同时分发器本身也会成为瓶颈。

LVS Linux Virtual Server

         LVS是在Linux操作系统基础上建立虚拟服务器,实现服务节点之间的负载均衡。LVS主要是处理OSI模型中的4层消息包,根据一定的规则将请求直接转发到后端的服务处理节点,有较高转发效率。

         Virtual ServerLoad Balancer和一组服务器的逻辑组合统称,使用服务者只需要与Virtual Server进行交互就可以获得高效的服务。真实服务器和Load Balancer通过高速LAN进行交互。Load Balancer能够将请求分发到不同的服务端,在一个虚拟IP下并行处理多个请求。

Virtual Server三种模式介绍

Virtual Server有三种基于IP级别的负载均衡实现方式:IP address translationNAT)、Direct routingIP Tunneling

         NAT(Network address translation)由于IPV4的某些缺陷和安全原因,某些网段例如(10.0.0.0/255.0.0.0, 172.16.0.0/255.240.0.0 and 192.168.0.0/255.255.0.0)不能被用于互联网,因此常常被用作内部局域网,通过网络地址翻译的方式可以让这些网段的服务器访问互联网或者被互联网访问。网络地址翻译主要作用就是将一组ip地址映射到其他的一组ip地址,当映射比例为1:1的时候通常称作静态映射,而当映射地址为M:N(M>N)的时候(M为被映射地址数量,通常是内部ip),则成为动态映射。而对于Virtual ServerNAT模式来说,就是利用了NAT的特性,将内部的一组服务器通过映射到一个虚拟的IP,然后以一个外网虚拟服务节点的身份对外提供服务。

         上图是一个实际的NAT范例,对外的服务IP202.103.106.5,内部建立了虚拟IP172.16.0.1,然后将内部其他两台实际服务的服务器172.16.0.2172.16.0.3映射到172.16.0.1这个虚拟IP。客户端向202.103.106.5发起请求服务,Load Balancer查看请求数据包,如果是请求目标地址是注册的虚拟IP及监听端口的时候,那么通过NAT按照一定算法选择某一台实体服务器,再重写报文目标地址,转发请求到实际的目标服务器,当目标服务器处理完毕以后,将处理结果返回给Load Balancer,由Load Balancer修改源地址,返回给客户端。

         IP TunnelingIP管道技术是在IP报文上再次封装IP报文协议的一种技术。允许将一个目标为AIP数据报文封装成为目标为BIP数据报文,在特定的IP 管道中传输。

         上图就是IP Tunneling模式的运作原理。首先客户端还是通过访问对外的一个服务IP请求服务,当Load Balancer接受到请求以后,检查VIP注册信息,然后根据算法选择实际的一台后台服务器,通过IP管道封装技术对IP报文再次封装,然后将消息通过IP管道转发到实际的服务器,实际的服务器通过解包处理请求,然后根据包体内实际的服务请求地址,将处理结果直接返回给客户端。

         Direct routing利用Load Balancer和实际服务器共享同一VIP,简单的通过修改消息报体目标MAC地址,转发请求,然后再通过实际服务器配置VIP为本地回环,直接处理消息报文,而不再转发,当处理完以后,直接将处理结果返回给客户端。

 

         上图就是Direct Routing的运作流程,当外部请求到Load Balancer时,通过查找VIP注册信息,直接选择一台后端服务器作为新的目标地址,修改消息报文中的目标地址Mac地址,转发到目标服务器,目标服务器由于配置VIP在本地网卡回路中,因此直接处理消息,将处理完的结果直接返回给客户端。

Virtual Server三种模式的比较

         下表是官方整理出的关于Virtual Server三种不同模式的区别:

NAT

TUNNEL

DR

服务器要求

无要求

需要支持IP管道

arp组件(当前也有补丁)

网络要求

Private

LAN/WAN

LAN

可支持后端服务器节点数

较少(10-20

较多

较多

服务网关

Load Balancer

本身

本身

NAT:根据其实现原理,可以知道这种模式对于操作系统,网络都没有太多的要求和约束,但是由于消息需要打解包,同时消息的响应都必须经过Load Balancer,因此Load Balancer自身成为了瓶颈,这样一个Load Balancer能够支持的后端服务节点数量就有限了。当然可以采用混合模式来解决这个问题,也就是通过TUNNEL或者DR模式作为前端模式串联起多个NAT模式Balancer

TUNNEL:这种模式要求操作系统支持IP Tunnel,通过对IP报文再次封装转发,达到负载均衡的目的。设计这种模式的初衷是考虑,对于互联网很多服务来说,服务请求数据量和返回数据量是不对称的,返回的数据往往要远远大于请求的数据量,因此如果请求和返回都走Load Balancer会大量占用带宽,影响处理能力。IP Tunnel设计中请求是通过Load Balancer,但是返回是直接返回到客户端的,因此节省了返回的带宽,提高了请求处理的能力。

DR:这种模式要求Load Balancer和后端服务器处于同一个局域网段。DR模式处理消耗最小,消息转发和回复基本没有损耗,因此效率应该是最高的,但是约束是相对来说最多的。

posted @ 2009-08-04 22:24 岑文初 阅读(3378) | 评论 (2)编辑 收藏

    小A,30,所在公司在去年的经济危机中没有倒下,但是在今年却倒下了。小A觉得能够把一个公司混倒闭了,也算是人生的一点经历。

    公司是没了,但是工作还要继续,生活还要继续,现在将要面对一个新的环境,环境很陌生,但也比较熟悉,工作职责很清晰,但也充满了挑战。人过30,有了孩子,真的成熟了很多,知道了什么叫做责任感,知道了未来真的需要好好规划,需要一个机会,需要一个平台来找到自己,实现自己的价值,不让这黄金时代就这么过去。

   小A将要面对的挑战在心里面已经做好了准备,也有了自己的一套短期的规划及工作安排,要成长有时候就要有压力。在小A即将离开原来团队的时候,和手下的一个同学发了火,因为在这阵子调整过程中,同学的心态一直变的很差,但是小A已经竭尽全力去分析他的未来,虽然听进去,但是过几天依然又开始放弃自己,这种态度让小A原本很看好他发展的心情变得很沉重,最后就在那个探讨会上说了他一些比较重的话,虽然说完以后自己也有些后悔,可能我对他和对我自己一样,要求太高了吧,就像博士说的,如果对一个人没有想法了,就恭维几句即可,大家你好我好大家好,只有当对这个人还存在一定的期望的时候才会表现出这种比较急切的感觉。

   新的开始,新的挑战,新的环境,新的机遇,新的难题,新的称呼

   好的心态,好的沟通,好的未来

   一切都需要小A用自己的能力去证明,走自己的路,让自己走的更好。

posted @ 2009-08-03 09:58 岑文初 阅读(868) | 评论 (0)编辑 收藏

    转眼到了7月份了,今年的blog更新的很慢很慢。写点东西记录自己的生活和工作状态。
   生活:
   儿子提早10天在六月八号来到我们这个小家庭,每个好友在祝福我的同时告诉我,辛苦的日子刚刚开始。不过和大家的感觉一样,辛苦但快乐着,在别人忙着在互联网上种花种草,养猪养鸡的时候,我开始扛起培养祖国新一代的责任。睡觉基本上很难保证连续性,早晨的运动也移到了晚上给儿子洗好澡以后。以前觉得就算到30岁还是觉得自己比较年轻,但是在那个23:25分儿子出来的一瞬间,自己觉得自己真的老了,需要成熟一点了,对儿子,对老婆。

    工作:
    其实今年年初的时候就有些彷徨,自己一手培养出来的SIP和原来的目标渐行渐远,7月份我在产品会议上提出了SIP6(第一阶段最终版),功能,性能,可扩展性都能够满足到明年中旬。虽然日访问量就快突破1亿,年底可能会到几个亿,但是这些数字对我来说只能证明这个架构还可以,但是SIP原有的目标已经被抛弃,成为了一个内部的服务集成平台。
   下个阶段会在做一些中心来满足团队的需要,但在我看来其实这些东西对我对团队的价值有限,创新有限,但这就是工作。
    公司内部有些变化,当然是好是坏不得而知,不过作为我们这些level已经处于地面的人来说也没啥影响。

   文章:
   最近的文章素材其实不少,但是受到内部技术专利申请,外部投稿的影响,能够写出来直接贴的越来越少,有时候也是这样,分享固然好,但是有些时候有些东西只能够小范围分享。

   睡觉,睡觉,中午的休息是很宝贵的,一觉醒来还继续自己的路。(走自己的路,让自己无路可走。没写错,呵呵,觉得这样挺搞笑的)
posted @ 2009-07-09 12:38 岑文初 阅读(744) | 评论 (0)编辑 收藏

 

         这篇blog的问题不能算是解决,仅仅只是一种分析和猜测,后续的一些行动可能会证明一些猜想,也可能什么都解决不了。如果有和我相同情况的同学,也知道是什么问题造成的,请不吝赐教。

问题:

上周周末,没有和同事们出去Outing,在家管孩子,去生产环境观察了一下集群机器的当前运行状态,发现应用在这些多核机器上压力极端不均匀。

         Top一下大致状态如下:



         峰值的时候,单CPU的使用率都到了80%,这种情况对于多核服务器来说是很不正常的使用。对于Java的开发者来说,多线程编程是无法控制线程如何在CPU上分配的,因为Java本身不实现线程机制,说是跨平台的语言,但是性能及特性会根据操作系统的实现有很大的差异,因此Java调优有时候需要对系统配置甚至内核作调优。

分析:

         首先在测试环境下作了多次同样的压力测试,尝试了与线上一样的操作系统版本,相似的配置,但测试结果却是负载分配很均匀。

   
     

         此时重新启动了一台问题机器,发现负载降下来了,同时也很均衡,也就是说在当前的压力下不应该有这样高的cpu消耗,同时也排除了硬件或者操作系统的一些配置问题。

         CPU满负荷的情况下,很多时候会认为应该是循环造成的,对于单个CPU的消耗更是。通过Top H查看具体到底哪一个线程会长时间消耗CPU

         可以看到PID13659的线程是“罪魁祸首”,但13659究竟在干什么,是应用的线程还是系统的线程,是否是陷入了死循环,不得而知。接着就按照Java的土办法,Kill -3 pid,然后看看输出日志。

         根据线程号来查找dump出来的日志中nid,发现这个线程是VM Thread,也就是虚拟机线程。(这里作一下转换,将13659转换成为16进制就是0x355b



         pstack看了一下这个线程的工作,结果如下:

Thread 2074 (Thread 1846541216 (LWP 13659)):

#0 0x0659fa65 in ObjectSynchronizer::deflate_idle_monitors ()

#1 0x065606e5 in SafepointSynchronize::begin ()

#2 0x06613e83 in VMThread::loop ()

#3 0x06613a6f in VMThread::run ()

#4 0x06506709 in java_start ()

#5 0x00aae3cc in start_thread () from /lib/tls/libpthread.so.0

#6 0x00a1896e in clone () from /lib/tls/libc.so.6

         搜索了一下ObjectSynchronizer::deflate_idle_monitors,发现了sunbug库中有bug关于jdk1.6中由于这个方法导致运行期问题的说法:http://bugs.sun.com/bugdatabase/view_bug.do;jsessionid=803cb2d95886bffffffff9a626d3b9b28573?bug_id=6781744

         然后就直接去openjdk官方网站去查找这个类的代码,大致了解一下他的作用,具体的代码链接如下:http://xref.jsecurity.net/openjdk-6/langtools/db/d8b/synchronizer_8cpp-source.html
主要工作应该是对资源对象的回收,在加上pstack的结果,应该大致知道是对线程资源的管理。但具体代码就没有进一步分析了。

接着就分析一下自己的应用:

         压力测试(高强度、长时间)都做过,没有发现什么异常。

         本身应用是否会存在的缺陷导致问题呢。有人说VM Thread兼顾着GC的工作,因此内存泄露,对象长期积压过多也可能影响,但其实在dump的结果可以看到,GC有单独的工作线程,同时我也观察到GC这些线程的工作时间长度,因此由于GC繁忙导致CPU上去,基本上来说可以排除。

         其次在SIP项目中使用了JDK的线程池(ExecutorService)LinkedBlockingQueue。后者以前的文章里面提到在1.5版本里使用poll方法会有内存泄露,到1.6虽然没有内存泄露,但是临时锁对象增长的很快,会导致GC的频度增加。

行动:

         上面零零散散的一些分析,最终让我决定有如下的行动:

1.       升级某一台服务器的JDK,当前是1.6.0_10-b33,打算升级到1.614版本。比较观察多台机器的表现,看是否升级了JDK可以解决问题。

2.       去除LinkedBlockingQueue作为消息队列,直接由生产者将生产结果按照算法分配给消费者线程,避免竞争,锁的消耗,同时也防止LinkedBlockingQueue带来的资源消耗。

3.       测试环境继续作长时间的压力测试,同时可以结合Jprofile之类的工具来分析长时间后可能出现的问题。

后话:

         这年头真的啥都要学一点,求人不如求己。

SA,DBA,测试都需要能够去学习一些,起码在初期排查问题上自己能够做点啥,要不然别人也忙,自己又无从下手。就好比这次压力测试好不容易排上队,但是还是满足不了及时上线的需求,因此自己去LoadRunner压,好歹给出一个零时的报告先大家看着。应用的异常有时候是应用本身设计问题,也可能是开发语言的问题,也可能是操作系统的问题,因此要去定位这种比较复杂的问题,真的需要有耐心去好好的学习各种知识,现在看来知识还是匮乏啊,要不然就可以分析出openjdk中可能存在的问题。

posted @ 2009-07-09 11:59 岑文初 阅读(4404) | 评论 (3)编辑 收藏

 

         昨天在看Cache Client代码的时候,发现在从资源池中获取SocketIO部分代码在高并发情况下效率不高,因此考虑通过一些变通的方式来提高效率,下面说的内容仅仅是当前自己琢磨出来可以部分提高效率的方法,希望看了这篇文章的同学能够有更好的方式或者算法来提高效率。

情景:

       Cache Client SocketIO资源池是一个两级的Map,具体定义为:ConcurrentMap<String, ConcurrentMap<SockIO, Integer>>。第一级MapHost作为Key,第二级MapSockIO本身作为Key,三种SockIO状态(可用,占用,废弃)作为value。之所以采用一个Pool来存储三种状态主要是考虑到在高并发下,多个池之间保持原子性的复杂。

每一次获取可用的SocketIO的操作需要经历:1.遍历Host所在的Map2.逐个比较状态。3.原子方法获取可用SocketIO。(并发问题所要求的,具体代码可以下载:http://memcache-client-forjava.googlecode.com/files/alisoft-xplatform-asf-cache-2.5.1-src.jar )。

在修改过去的版本里面,首先遍历的过程是一个固定顺序的过程(keyset),这样会导致在高并发的情况下,越来越多的资源申请命中率会下降,因为压力总是落在keyset靠前的那些SockIO上(重复比较)。需要考虑通过什么手段可以提高在高并发下的申请命中率。

思考:

1. 资源申请的越早,被释放的可能性越高,因此是否可以考虑采用更新SockIO最后申请时间来作为后续申请的初步依据。(本身复杂度带来的耗时可能会超过命中率降低带来的损耗)

2. 采用随机数的方式来确定keyset的起始游标,也就不是每次都从keyset第一位开始(可以把keyset看作一个首尾相接的数组)。

3. 在每次资源回收的时候纪录下该资源为可用(当前为每一个Host就记录一个可能可用的资源,简单化操作),作为申请的首选尝试。(尝试不成功在去遍历)。

当前实现了2,3组合,发现效果明显,在500个并发下,每个线程200次操作(一系列动作),压力测试结果如下:

Cache test consume(cache测试总共耗时)average boundle consume(每个线程总耗时),average per request(每个线程每次操作总耗时)

没有作任何改动以前的测试结果:

cache test consume: 11507741, average boundle consume: 57538, average per request :115

采用了2策略以后的测试结果:

cache test consume: 10270512, average boundle consume: 51352, average per request :102

采用了23策略以后的测试结果:

cache test consume: 9140660, average boundle consume: 45703, average per request :91

posted @ 2009-05-07 17:15 岑文初 阅读(1951) | 评论 (0)编辑 收藏

 

       服务集成平台5.6的性能测试进入尾声,这期的优化也算告一段落。这次主要的优化工作还是在三个方面:应用服务器(Apache,JBoss)配置,业务流程,Cache Client包(http://code.google.com/p/memcache-client-forjava/ )。这里把过去和这次优化对于Cache的使用作一个经验分享,希望大家能够用好Cache,提速你的应用。

       这里还是通过一些点滴的启示来介绍优化的一些心得,很多时候还是要根据具体情况来判断如何去具体实施,因此这里所说的仅仅是在一些场景下适用,并非放之四海皆准的教条。同时也希望看此文的各位同学,如果有更好的思路可以给我反馈,技术在交流中才会有发展。

积少成多,集腋成裘

       性能提不上去,多半是在一些容易成为瓶颈的“暗点”(IO,带宽,连接数,资源竞争等等)。Memcached Cache现在已经被大家广泛使用,但是千万不要认为对Cache的操作是低损耗的,要知道这类集中式CacheSocket连接数(会牵涉到linux操作系统文件句柄可用数),带宽,网络IO都是有要求的,有要求就意味着会有损失,因此积少成多,集腋成裘。服务集成平台是一个高速的服务路由器,其大部分的业务数据,访问控制策略,安全策略以及对应的一些控制阀值被缓存在Cache服务端,因此对于Cache的依赖性很强。每一次对于客户端的性能提升,总会给服务集成平台性能带来不小的影响,但是每一次优化速度后,客户端可以优化的空间越来越小,这时候需要一些策略来配合,提升应用整体性能。当前主要采用了以下几点策略:

1.  从数据获取角度来做优化,采用本地数据缓存。(因为大家的应用需要能够线形扩展,支持集群,所以才不使用应用服务器本地缓存,但是在某些缓存数据时间性不敏感或者修改几率较小的情况下,可以采用本地缓存结合集中式缓存,减少对远端服务器访问次数,提升应用性能)。

Cache ClientIMemcachedCache 接口中的public Object get(String key,int localTTL)方法就是本地数据缓存结合远程Cache获取数据的接口。具体流程参看下图:

 

 

2.  从数据更新角度,采用异步数据更新。(即不等待数据更新结果,直接进行其他业务流程)。这类操作使用场景比较局限,首先数据不会用作判断(特别是高并发系统中的阀值),其次不需要返回结果作为后续流程处理输入(例如计数器),时时性要求比较低。(这类操作其实是采用了集群数据传播的一种策略,原先对于集群中所有节点都想即时传播到,但是这样对于性能损失很大,因此采用key对应的主Node采用即时设置数据,其他的通过后台任务数据传播来实现,由于key对应的主Node是数据第一操作和读取节点,因此这类数据传播操作时时性要求较低,适合这样处理)。具体接口参见Cache Client 使用文档。

3.  一次获取,多次使用。这点和系统设计有关,当前服务集成平台的安全流程是链状的,一次请求会经历很多安全拦截器,而在每一个安全拦截器中会根据情况获取具体的业务数据或者流程控制策略等缓存数据,每一个安全拦截器都是彼此独立的,在很早以前是每一个安全拦截器各自在需要数据的时候去远程获取,但是压力测试下来发现请求次数相当多,而且好些重复获取,因此将这些业务数据作为上下文在链式检查中传递,按需获取和设置,最大程度上复用了数据。(其实也是一种减少数据获取的方式)。

4.  规划好你的Cache区。有些同学在使用Cache的时候问我是否有什么需要注意的,我觉得在使用Cache之前,针对需要缓存的数据需要做好规划。那些数据需要放在一个Cache虚拟节点上,那些数据必须分开放。一方面是根据自己业务系统的数据耦合程度(未来系统是否需要合并或者拆分),另一方面根据数据量及读写频繁度来合理分配(毕竟网络IO还是稀缺资源)。当然有时候业务系统设计者自己也不知道未来的发展,那么最简单的方式给Key加上前缀,当前可以合并,未来也可以拆分。同时数据粒度也需要考虑,粒度设计太小,那么交互频繁度就会很高,如果粒度太大,那么网络流量就会很大,同时将来业务模块拆分就会有问题。

 

 

巧用Memcached Cache特有接口

       Memcached Cache提供了计数器一整套接口和addreplace两个接口。这些特有接口可以很好的满足一些应用的高并发性处理需求。例如对于资源访问次数控制,采用Cache的计数器接口就可以实现在集群中的数量控制,原本通过Cachegetput是无法解决并发问题的(就算是本地缓存一样),这就是一组原子操作的接口。而AddReplace可以满足无需通过get方法获取内容,就可以对于key是否存在的不同情况作出相应处理,也是一种原子性操作。这些原子操作接口对于高并发系统在集群中的设计会很有帮助。

 

Cache Client Cluster

       Memcached Cache是集中式Cache,它仅仅是支持将数据能够分片分区的存储到一台或者多台的Cache Server实例中,但是这些数据并没有作冗余,因此任何一个服务实例不可用,都会导致部分缓存数据丢失。当然很多人采取持久化等方式来保证数据的完整性,但是这种方式对于效率以及恢复的复杂性都会有影响。

       简单的来想,为什么不把数据在多保存一份或者多份呢,当其中一份不可用的情况下,就用另外一份补上。这就是最原始的Cache Client Cluster的构想。在这里具体的设计细节就不多说了,主要说一下几个要点,也让使用Cache Client Cluster的同学有大致的一个了解。

       先来看看Cache Cluster的结构图:




       这张图上需要注意四个角色:Application(使用Cache的应用),Cache ClusterCache配置的虚拟集群),Cache NodeCache的虚拟节点,在同一个Cluster中的Cache Node数据保持完全一致),Cache InstanceCache虚拟节点中实际包含的Memcached Cache服务端实例)。

       应用仅仅操作Cache Node,不了解具体数据存储或数据获取是操作哪一个Cache 服务端实例。(这点也就是Memcached Cache可扩展性的基础设计)。Cache Cluster又将多个Cache Node组成了虚拟的集群,通过数据冗余,保证了服务可用性和数据完整性。

 

       当前 Cache Client Cluster主要有两种配置模式:active standby。(这里是借鉴了硬件的名词,其实并不完全一样,因为还是考虑到了效率问题)

       Cache Client Cluster主要的功能点:

1.  容错。当被分配到读取或者操作数据的Cache虚拟节点不可用的情况下,集群其他节点支持代替错误节点服务于客户端应用。

2.  数据冗余。当操作集群中某一个Cache虚拟节点时,数据会异步传播到其他集群节点。

3.  软负载。客户端通过对操作的key作算法(当前采用简单的key hash再取余的方式)选择集群中的节点,达到集群中节点简单的负载分担。同时也由于这种模式,可以使得key都有默认的第一操作节点,此节点的操作保持时时更新,而其他节点可以通过客户端异步更新来实现效率提升。

4.  数据恢复。当集群中某一节点失效后恢复时,其数据可能已经完全丢失,此时通过配置成为Active模式可以将其他节点上冗余的数据Lazy复制到该节点(获取一个复制一个,同时只支持一个冗余节点的数据获取(不采取遍历,防止低效))。

 

Active模式拥有1,2,3,4的特性。Standby模式拥用1,2,3特性。(其实本来只考虑让Standby拥有1特性)。未来不排除还会有更多需要的特性加入。Activekey不存在的情况下会有些低效,因为会判断一个冗余节点是否存在内容,然后决定是否修复当前节点。(考虑采用短期失败标示之类的,不过效率不一定高,同时增加了复杂度)

 

 

运行期动态扩容部署

       Memcached cache客户端算法中比较出名的是Consistent Hashing算法,其目的也就是为了在节点增加或者减少以后,通过算法尽量减小数据重新分布的代价。采用虚拟节点,环状和二叉树等方式可以部分降低节点增加和减少对于数据分布的影响,但是始终还是有部分数据会失效,这点还是由于Memcached Cache是集中式Cache所决定的。

       但如果有了Cache Cluster的话,数据有了冗余,就可以通过逐步修改集群中虚拟节点配置,达到对于单个虚拟节点的配置动态扩容。

       支持动态部署前提:

配置文件动态加载。(配置文件可以在Classpath中,也可以是Http资源的方式)通过Cache Client Cache Manager可以停止Cache 服务,重新加载配置文件,即时生效。

当前动态部署的两种方式:

1.              修改集群配置中某一套虚拟节点的服务实例配置(socketPool配置),增加或者减少后端数据存储实例。然后动态加载新的配置文件(可以通过指定远端的http配置作为新的配置文件),通过集群的lazy的修复方式,逐渐的将数据从冗余节点复制到新的节点上来,最终实现数据迁移。

2.              修改集群配置中某一套虚拟节点的服务实例配置(socketPool配置),增加或者减少后端数据存储实例。然后动态加载新的配置文件(可以通过指定远端的http配置作为新的配置文件),在调用Cache Manager主动将数据由某一虚拟节点复制到指定的集群中,实现数据批量迁移,然后根据需要看是否需要修改其他几套虚拟节点配置。

 

存在的问题:

1.       当前没有做到不停止服务来动态部署。(后续考虑实现,当前将编译配置和重新启动服务器的工作节省了)

2.       不论是lazy复制还是批量数据迁移,都是会将原本有失效时间的数据变成了无失效时间的数据。(这个问题暂时还没有一种可行的高效的方式解决)

 

 

后话

       性能优化这点事还是那句老话,需要了再去做也不迟。同时如果你开发的是一个每天服务访问量都是上亿,甚至更高的系统,那么有时候斤斤计较会收获不少。(当然是不影响系统本身业务流程的基础)。

       Cache客户端自从作为开源放在Google上也收到了不少朋友的支持和反馈,同时自己业务系统以及其他部门同学的使用促使我不断的去优化和满足必要的一些功能扩展(但是对于Cache来说,还是那句话,简单就是美,高效是使用Cache的最原始的需求)。

       当前Cache Client版本已经到了2.5版本,在Google上有详细的Demo(单元测试,压力测试,集群测试)和说明使用文档。是否速度会慢于其他Memcached客户端,这不好说的很绝对,反正大家自己拉下去比较一下看看就知道了,当然为了集群和其他的一些必要的附加功能还是做了一些性能牺牲。

 

项目地址在:http://code.google.com/p/memcache-client-forjava/

在首页的右侧有demo,doc,binary,src的链接,直接可以下载使用和察看。希望对需要的同学有帮助。

posted @ 2009-04-28 23:19 岑文初 阅读(3364) | 评论 (6)编辑 收藏

        blog已经快两年了,起初仅仅是为了自己“备个案”,结果慢慢演变成为了“分享成瘾”。前几天一个朋友给我的blog留言,谈到希望在新年里能够看到的不仅仅是我对技术的分享,更希望能够看到对于技术学习、职业发展的规划。因此想到了写一点什么分享一下自己这些年的一点点“收获”,周星驰的喜剧之王里面说到他是一个演员(虽然被叫做跑龙套的),我想我,就一个写代码的。

爱这行

       从事任何行业都一样,只有真正的爱上了这份工作,才会投入热情,才会在顺境中自我警醒,在逆境中寻找突破。这个行业的竞争很激烈,你停下来走,别人就立刻会跑步超过你,没有对这一行业的一种热情,就很难在困境中保持一种执着的态度坚持到底。

踏踏实实“扎马步”

       今天无意中看了“校长”的“程序员&司机”,其中谈到了关于程序员速成的问题。其实速成班毕业的 “系统杀手”早已在遍布大江南北,只是在互联网时代,互联网的应用型软件生命周期越来越短,业务驱动主导的情况下,这种速成方式看起来反而提高了企业生产效率。但这样的人才也就只能写几个Facebook上的插件应用或者iGoogle上的Gadget,真的要出GoogleAmazonYahoo改变互联网世界的企业,还是需要踏踏实实先学“扎马步”的人。

       很多在学校的同学或者刚刚毕业的朋友都看什么热门学什么,SpringAJAXHibernate等等,又有多少人在看Spring之前把J2SENIOXMLCollection等先好好学习一下,在看AJAX之前把Http协议、DTDXML Schema好好看一下,在学习Hibernate以前先把J2EE事务规范搞清楚。Java最大的好处就是开源,能够让人们站在更高的起点来作出更多的创新,但是对于学习者来说,不了解自己站在什么上面的时候,可能摔下来会很痛。在用的时候多问一些为什么,在遇到问题的时候多找找原因,在了解以后多提出一些优化的方案,这样才会进步的更快,走的更远。

       记得我前一阵子回家的时候和妈妈聊起最近的工作,虽然妈妈不太明白,但是也知道我现在做的东西技术含量比较高,嘱咐我“千万不要什么都教给自己的同事,徒弟带出就不要师傅了”(这当然是老一辈的观念了)。我和她说:“不要担心,这种学的会的不教迟早也会,学不会的教了也学不会”。其实这里说的学的会的就是技术,而学不会的就是经验和能力。这个行业的人在日积月累过程中并不会去比较掌握的知识面有多广多深,毕竟这行业更新很快,其实能力强的人在多年的学习中就积累了很多的找问题,分析问题,总结问题,提出建议,发掘创新的能力,这些才是这行业人在发展中最宝贵的财富,也是一个人成长的标志。开始的过程中,踏踏实实地“扎马步”,了解一些最基本的知识,那么上层技术的发展对于他来说仅仅只是一个短暂的学习过程,甚至可以触类旁通。因此还是要奉劝每一个新入行的同学,踏踏实实,静下心来做技术,就算工作安排得都是一些浮躁和重复的工作,用高效的方式来结束那些重复劳动,多留一些时间给自己打基础。

逆境养兵、顺境攻城掠地

       普通人的工作经历通常都是起伏不定的,一个人的能力是否能够得到体现,不仅仅靠自己的努力,有时候也需要“天时”、“地利”。马云比较有名的一句话:“今天很残酷,明天更残酷,后天很美好,但是大多数人死在明天晚上,看不到后天的太阳!!!”,其实也在说明一件事,就是很多时候需要一种坚持的精神才能得到宝贵的机会。

今天是我进入阿里巴巴满3年,这3年让我感触很深的是:1.逆境不要气馁,厚积薄发。2.顺境不要懈怠,一股作气,把握机会展现自己最大的能力。3.在逆境和顺境的转换过程中,创造机会,不要坐等机会,要学会不在其位,也谋其职。最后一点就拿我自己的亲身经历来说,我原来就职于一家通信公司,因此对于互联网应用的开发和架构设计要比很多人弱,进入阿里巴巴以后工作了半年(主要作业务开发),正好阿里软件创立,当时被分配到了阿里软件第一个产品负责客户模块,当时的应用是通过MDA框架配置搭建的,开发人员很大程度上不需要自己做太多的编码,但是这个平台并没有搭建过如此复杂的大型应用,因此存在着不少问题,当然这些问题都是通过业务产品线的人反馈给平台部的人,当时平台部门人员很少,但是却要修复和完善诺大一个平台,因此常常搁置开发人员的反馈。当时在自己工作之余就琢磨和研究平台,同时跟踪调试平台,最后直接给出解决方案,逐渐的就融入到了平台开发中,最后被吸收到了平台部门,进入平台部门以后遇到了两位很好的老大,根据我的特质给我安排了研究和学习的工作。接下去就是不断地参与阿里软件各个基础平台的构建,核心技术的研究和探索,找到了兴趣和工作的最佳结合点。因此,当你困惑的时候首先不是去抱怨,而是审视一下自己是否还有作的不够的,是否还有可以提升的空间,多给自己制造一些机会,也许我们不用等到后天,也不会死在明天夜里,明天早晨我们就看到了太阳。

海纳百川、冰冻三尺

       很多朋友可能听老师或者前辈也说过类似的话,就是作为一个技术人员要广也要钻。就好比现在很多人都要DB Scale out,同时也要Scale up。我从自己的角度来说一下广和钻的看法。广:1.要有容人之量。(很多时候程序员最大的毛病就是喜欢在技术上比较,未尝不是好事,但是一个人的能力总归有限,多看看别人的,多听听别人的,也许能够让自己少用时间获得更多的收获,特别是自己战友的声音)2.触类旁通,多问个为什么,多跨过界去学习。在阿里巴巴,PDSADBAUI等等职位各司其职,作为开发的我们其实也应该去了解如何去画Use Case,如何假设服务器和应用环境,如何写一些略微复杂的SQL,了解一些DB的特性,如何能够简单的作出一些基础的页面,使用简单的css来美化一下门面。这些就是需要多跨过界,多虚心的去学习。钻:1.本职工作技术一定要扎实,每作一个技术点就要把技术吃透,同时延伸开来,发掘更多的技术亮点。2.多接触新鲜事物,但是有选择的去了解,有目的的去学习和实践(目的的源泉就是工作的需求)。3.学会分享,一个人自己搞懂一个技术很容易,一个人要把他熟悉的技术写下来就会发觉原来自己还有那么多没有搞清楚,一个人如果要把写下来的东西宣讲给别人听,他就会发现,原来写下来的仅仅是那么一小块,因此学会分享,从自己了解,到记录分享,到演讲传播就是一个不断深化和广化的过程。个人觉得小公司锻炼人(啥都自己干),大公司培养人(该干的要干好),因此自己常回头看看自己在广和钻上的不足,可以让自己进步的更快,学的更全面。    

       学中医积累经验,学西医寻找突破

       中医以对人体经络血脉了解为基础,通过望闻问切来寻找病理根源,行医年限越久,找问题解决问题的经验越强。西医以科学技术为手段,通过试验化的方式不断寻找突破,并且将成果积累并且传递给更多的人,但是否年限越久越有能力,或者是使用得器材越广越资深,这点全要看个人对于医术的理解,如果仅仅停留在对器械的使用和对成果的依赖,那么只会成为一个庸医。当然这里绝对没有对中西医的差别化或者评价,仅仅要说明的是,在手段丰富的情况下,容易忽视了本质,只看到了皮毛,积累的时候多一些追根溯源,站在别人的成果上才更踏实,因此在对经验积累上向中医多学一些,在寻找突破,传播技术上多学一点西医的风格。不过说到低,还是要看学习的人,静的下心,沉得住气,才会有积累,才会有突破.

不做一个纯粹的“技术人员”

       不做一个纯粹的“技术人员”,其实也就是说要培养自己多方面的能力,我仅仅把自己想到的一些点列出来说说:

1. 项目产品化的思想。现在就算在学校里面给导师作项目都讲究一个商业价值,更不要说在企业里工作。作为一个开发或者架构师最重要的就是要有产品化的概念,这也是项目是否成功的关键。软件的目的是为人服务,如何服务的好,那就要以一个产品的思路去做项目,而不是作为实验室的实验品,为客户提供好服务就会给公司带来商业价值,对自己的工作也会有很好的肯定。这是一个良性循环,反之则是恶性循环(多赢变成多输)。如何做到产品化,首先就是需要去了解需求,而不是布置需求,其次就是设计时多听取一些不同角色的意见,最后就是在客户的反馈过程中反省。

2. 多一些设计,少砌两块砖。代码写的再好,其实也只是用砖块砌墙砌的比较好罢了,这年代已经不会为了节省两块砖而给一个优秀工作者了,同时技术的日新月异,总是摆弄技巧,学习花拳绣腿已经跟不上时代了。多了解一些行业背景,多参与一些架构设计,将业务设计用良好的架构体系来实现,那才是一个称得上有能力的技术人员。

3. 学会前瞻,学会自己找事。记得我刚进平台组,最不适应的就是我的老大基本不太给我布置太详细的任务,这就好比进入大学,老师不给作业,自己反而心里没底了,其实自己找事的过程就是一个自己学习的过程,当我一天下来感觉没干什么,没学到什么,心里就开始发虚。如何能够前瞻性的去选择一些目标,如何对现有情况提出一些创新和建议,都是一种更高能力的要求。现在SIP组也是一样,在我们这个组里虽然现在每周还是布置一定工作,但是我对其他两个同学的要求也是希望能够有前瞻性,学会发现问题,预防问题,更甚者就是提出创新。当你具备了这种环境的时候,你就需要锻炼自己的能力了。

4. 做个让老大放心的人。这点也许很多人和我一样在业务上很早就让老大觉得可以安心睡觉了,但是其实另一方面,如何在商业角度看问题,如何培养新人,如何协调部门合作等等,都会让你的老大更加安心。另一方面来看,其实在这些能力的培养过程中,你不再局限于业务水平的提升,让自己在更多方面更加成熟。

六脉神剑

       今天是我进入阿里巴巴3年整。在阿里巴巴有个说法,只有在阿里巴巴工作了3年,才能算是一个真正的阿里人,因为理解阿里巴巴的文化,需要三年时间的沉淀。这里就从一个写代码的角度分享一下阿里巴巴的六脉神剑文化。

客户第一:如果你是做架构的,作平台的,作开发工具的,那么客户就是和自己一样的开发者,多学习一下开源项目的精神,多从使用者角度去考虑问题,那么你的东西才会被更多的人认可和使用,永远不要去做一个“玩具”的开发者。如果你是做产品的,那么就多听,多想,多问,永远不要急着去写代码。

拥抱变化:敏捷开发的基本原则。互联网应用尤其如此,不要害怕变化,在需求和架构之间找到平衡点(说起来比较容易^_^)。

团队合作:一个人的力量始终有限,分享,交流,合作能够让自己事半功倍,学的更多,看得更远。

诚信:说到就要做到,做了就要做好,做软件开发一样也需要有责任感,贴满狗皮膏药的代码上如果注释是你的名字未来也会给你蒙羞。踏踏实实地用心去写代码,去设计架构,不经意间得到的要远远比那么一点工资来的多。

激情:还是那句话,你如果不爱这行,乘着年轻赶快转行。

敬业:专业执着,精益求精

很感谢各位能看完这篇感受分享,以上都仅仅是个人的一点感受,能够引起共鸣那么证明我们的经历很相似,如果能够给到你一点帮助,那写这些就真的有意义了。不论你在别人眼里是一个资深架构师还是开发人员,其实如果你爱这个行业的话,你应该就是一个写代码的,但是每个人的经历都是一本“写代码的自我修养”,珍惜自己的选择,让自己在兴趣和工作中找到最佳结合点。

posted @ 2009-03-11 02:38 岑文初 阅读(4997) | 评论 (20)编辑 收藏

 

       今天看了“Database Sharding at Netlog, with MySQL and PHP”一文,和去年我们讨论扩展的思路很类似(不过这种分布式扩展,计算,存储的思路都很类似),但是这片文章的作者是在日益爆炸式增长的用户数据下实践的分享,因此这里将文中的一些思想记录下来分享一下。

       Netlog拥有4000万活跃用户,每个月有超过5000万的独立用户访问网站,每个月有5亿多的PV。数据量应该算是比较大的。作者是Jurriaan Persyn,他从一个开发者角度而非DBA或者SA角度来谈Netlog是如何通过数据切分来提高网站性能,横向扩展数据层的。原文在:http://www.jurriaanpersyn.com/archives/2009/02/12/database-sharding-at-netlog-with-mysql-and-php/

       首先,还是先谈到关于数据库在数据日益庞大的情况下一个演变过程。
   
   第一阶段:读写同在一台数据库服务器

   

第二阶段:读写分离(可以解决读写比例均衡或者读居多的情况,但是带入了数据复制同步的问题)



   第三阶段:部分数据独立部署结合读写分离。(部分数据根据其业务独立性情况,可以将所有的数据独立存储到数据库服务器,分担数据读写压力,前提是要求数据具有较高的业务独立性)

    
       第四阶段:数据分拆结合读写分离(三阶段的增强)


       第五阶段:问题出现,分拆也无法解决数据爆炸性增长,同时读写处于同等比例。


       解决问题两种方式:DB Scale up DB Scale out。前者投入以及后期扩展有限,因此需要进行数据切分。



       上图就是将photo的数据切分到了10台数据库服务器上。

       切分数据的两个关键点:

1. 如何根据存储的数据内容判断数据的存储归属,也就是什么是内容的分区主键。

2. 采用什么算法可以根据不同的主键将内容存储到不同的分区中。

分区主键的选择还是要根据自身的业务场景来决定,Netblog选择的是用户ID

采用什么方式将分区主键映射到对应的分区可以通过以下四种方式:

1. 根据数据表来切分。(前提就是数据独立性较强,和前面提到的三阶段类似)

2. 基于内容区间范围的分区。(就好比前1000个用户的信息存储在A服务器,1000-2000存储在B服务器)

3. 采用Hash算法结合虚拟节点的方式。(这类在memcached等等分布式场景中最常见,其实也是一个难点),缺点就是在于动态增加存储节点会导致数据部分或者全部失效。

4. 目录式的分区。最简单也是最直接的方式,key和分区的对应关系被保存,通过查找目录可以得到分区信息。适合扩展,就是增加查询损耗。

如何将数据分布的尽量均匀,如何平衡各个服务器之间的负载,如何在新增存储机器和删除存储机器的时候不影响原有数据,同时能够将数据均摊,都是算法的关键。在分布式系统中DHTDistribute Hash Table)被很多人研究,并且有很多的论文是关于它的。

数据的横向切分给应用带来的问题:

1. 跨区的数据查询变得很困难。(对于复杂的关联性数据查询无法在一个请求中完成)

2. 数据一致性和引用完整性较难保证。(多物理存储的情况下很难保证兼顾效率、可用性、一致性)

3. 数据分区之间的负载均衡问题。(数据本身的不均衡性,访问和读写的不均衡性都会给数据分区的负载均衡带来困难)

4. 网络配置的复杂性。(需要保证服务器之间的大数据量频繁的交互和同步)

5. 数据备份策略将会变得十分复杂。

解决这些问题当前已经有的一些开源项目:

1. MySql Cluster,解决读写分离问题已经十分成熟。

2. MySql Partitioning,可以将一个大表拆分为很多小表,提高访问速度,但是限制与这些小表必须在同一台服务器上。

3. HSCALESpock Proxy都是建立与MySql Proxy基础上的开源项目,MySql Proxy采用LUA脚本来进行数据分区。

4. HiveDBMySql分区框架的java实现。

5. 另外还有HyperTable,HBase,BigTable等等。

Netblog几个需求:

1.              需要灵活的可扩展性。对于存储增加减少需要能够动态的及时实施,因为数据量增长很快,如果策略会导致数据失效或者部署需要重新启动,则就不能满足需求。

2.              不想引入全新的数据层和与原有系统不匹配的抽象层,因为并不是所有数据都需要切分,仅仅在需要的情况下通过API的方式来透明切分数据。

3.              分区的主键需要可配置。

4.              需要封装API,对开发人员透明数据切分的工作。

      Netblog Sharding的实现




    上图就是
NetblogSharding的结构图,主要分成了三部分:ShardSharddbSharddbhostShard就是一个表,里面存放了部分用户数据。Sharddb是一个表的组合就像一个虚拟的DBSharddbhost是具体的存储分区。ShardSharddb可以根据负载的情况被移动到不同的host中去。

       对于Shard的管理,Netblog采用的是目录查询的管理方式。目录信息也存储在MySql中,同时会通过互备,Memcache,集群来确保安全性和高效性。

       Shard Table API采用了多一层的映射模式来适合各种不同属性的查询情况。数据和记录在数据库中存储除了UserID以外还有对应的ItemIDItemID的作用就是定义了具体获取数据的字段信息,例如关联照片表时,ItemID就是PhotoId,关联视频表时,ItemID就是videoID

       一个获取用户id26博客信息的范例:

1Where is user 26?

   User 26 is on shard 5.

2On shard 5; Give me all the $blogIDs ($itemIDs) of user 26.

That user's $blogIDs are: array(10,12,30);

3On shard 5; Give me all details about the items array(10,12,30) of user 26.

Those items are: array(array('title' => "foo", 'message' => "bar"), array('title' => "milk", 'message' => "cow"));

对于Shard的管理Netblog采取的措施主要有这些:

1. 服务器之间的负载均衡根据用户数,数据库文件大小,读写次数,cpu load等等作为参数来监控和维护。根据最后的结果来迁移数据和分流数据。

2. 移动数据时会监控用户是否在操作数据,防止不一致性。

3. 对于数据库的可用性,采用集群,master-mastermaster-slave复制等手段。

最后通过三种技术来解决三个问题:

1. Memcached解决shard多次查询的效率问题。

根据上面的范例可以看到,一次查询现在被分割成为了三部分:shard查询,item查询,最终结果查询。通过memcached可以缓存三部分内容,由前到后数据的稳定性以及命中率逐渐降低,同时通过结合有效期(内容存储时效)和修改更新机制(add,update,delete触发缓存更新),可以极大地解决效率问题。甚至通过缓存足够信息减少大量的数据库交互。

2. 并行计算处理。

由于数据的分拆,有时候需要得到对于多Shard数据处理的结果汇总,因此就会将一个请求分拆为多个请求,分别交由多个服务器处理,最后将结果汇总。(类似于Map-reduce

3. 采用Sphinx全文搜索引擎解决多数据分区数据汇总查询,例如察看网站用户的最新更新情况或者最热门日至。这个采用单独系统部署,通过建立全局信息索引,来查询数据情况。

以上是技术上的全部内容,作者在最后的几个观点十分值得学习,同时也不仅仅限于数据切分,任何框架设计都可以参考。

“Don't do it, if you don't need to!" (37signals.com)

"Shard early and often!" (startuplessonslearned.blogspot.com)

看起来矛盾的两句话,却是说出了对于数据切分的一些考虑。

首先在没有必要的情况下就不要考虑数据切分,切分带来的复杂性直接影响可用性,可维护性和一致性。在能够采用Scale up的情况下,可以选择Scale up降低框架复杂度。

另一方面,如果发现了业务增长情况出现必须要扩展的趋势,那么就要尽早着手去实施和规划扩展的工作,并且在切分和扩展过程中要不断地去优化和重构。

后话:

       其实任何架构设计首要就是简单直接,不过度设计,不滥竽充数。其实就是要平衡好:可用性、高效性、一致性、可扩展性这四者之间的关系。良性循环、应时应事作出取舍和折中。用的好要比学会用更重要,更关键。

posted @ 2009-03-04 00:58 岑文初 阅读(1996) | 评论 (2)编辑 收藏

 

目标:

         根据四方面的配置调整,观察SIP5.5在高并发下的性能情况。

         由于SIP接收的请求都是服务型处理请求,因此认为Apache+Jboss只会带来多余的转发损耗,所以正好这次也作一个验证,看看Apache+JBoss是否不适合于这种纯动态服务请求的情况。 
        四方面环境比较:

1. JBoss APR模式与Http1.1模式性能差异。(确切来说应该是JBoss内置Tomcat采用APR的情况)。

2. 是否采用Apache+JBossApache不同的转发模块带来的性能差异。

3. Memcached Client版本优化后对性能影响。

4. ISP有不同延时对于SIP的性能影响。

前置条件:

SIP版本5.5,并发用户600ISP默认耗时20msApache配置和JBoss WebContainer配置,一些优化配置参见附加信息。

最终结果:

       SIP采用Apache(Mod_jk)+JBoss(APR)+Cache2.4.2,具体配置参见附加信息。

测试结果表格:

       详细的测试报告可以参看:http://spreadsheets.google.com/pub?key=pcsQ9Wm01cIEjjQcistPNDg

JBoss配置差异测试比较:

 

Apache(2.0.52)配置

JBoss(4.2.1)配置

Cache Client Version

TPS

TPS区间

APR

2.4.2

1705

1600-1900

HTTP1.1

2.4.2

1615

1550-1700

Mod_jk(1.2.27)

HTTP1.1

2.4.2

2090

1800-2800

Mod_jk(1.2.27)

APR

2.4.2

3223

3200-3400

补充:

         配置成为Http1.1模式的两种情况下,测试结果TPS波动频率很高,在Mod_jk模式下波动幅度也很大。

1.         可以证实在非APR模式和高并发的情况下Web容器处理请求能力不稳定,同时也直接影响到了SIP的性能。

2.         在测试中发现不采用APR模式的情况下,Web容器会消耗大量的socket连接通道。

Apache模块差异测试比较:

 

Apache(2.0.52)配置

JBoss(4.2.1)配置

Cache Client Version

TPS

TPS区间

APR

2.4.2

1705

1600-1900

Mod_jk(1.2.27)

APR

2.4.2

3223

3200-3400

Weblogic.so

APR

2.4.2

1033

350-1400

补充:

         Weblogic.so模块是以前系统遗留的http请求转发模块。在测试过程中Weblogic模块的测试中波动频率和幅度都很大。根据测试结果可以看出:

1.       APR模式下,Apache+JBoss对于SIP这种无静态资源访问,纯API性质的服务来说依旧会有比较好的优化效果,特别是在接受请求环节。(不论是TPS还是TPS波动区间和频率都有很好的表现)

2.       Weblogic.so这个模块性能绝对不行,稳定性极差。

Cache客户端版本差异测试比较:

 

Apache(2.0.52)配置

JBoss(4.2.1)配置

Cache Client Version

TPS

TPS区间

APR

2.4.2

1705

1600-1900

APR

2.4

1615

1550-1700

Mod_jk(1.2.27)

APR

2.4.2

3223

3200-3400

Mod_jk(1.2.27)

APR

2.4

2485

2650-2800

补充:

         2.4.22.4版本在单独测试的环境下:500并发用户,每个并发用户1000getset,性能相差40%左右。

         上面测试结果可以看出:

1.    在无apache时,性能有所提升,但不明显,而在有apache时,性能大幅提升,证明在无apache的情况下,memcache客户端已经不是性能瓶颈,因此替换版本效果不大,在http请求处理性能大幅提升的情况下,memcache客户端性能优化的优势就得到了体现。

2.    在测试中也发现Apache + JBoss波动频率和区间都小于其他几个测试情况,图形十分平稳,证明处理请求不是系统瓶颈。

ISP响应时间差异测试比较:

 

Apache(2.0.52)配置

JBoss(4.2.1)配置

Cache Client Version

ISP响应时间(ms)

TPS

TPS区间

Mod_jk(1.2.27)

APR

2.4.2

20

3223

3200-3400

Mod_jk(1.2.27)

APR

2.4.2

110

Mod_jk(1.2.27)

APR

2.4.2

900

测试优化总结:

1. 不要认为内存使用无关性能。现在很对开发者认为内存申请分配是由gvm来管理,但是内存是否合理使用很可能会影响互联网应用的高并发下性能。GC带来的系统短暂停滞会在高并发下影响性能。

2. 使用java的方法需要有足够的“理由”和“度”。Java1.5以后对concurrent方面做了很不错的支持,但是这些并发处理毕竟会消耗资源,因此在能够避免频繁使用的情况下尽量优化流程。在一些简单的场景下,是否有必要使用一些比较耗时的方法,例如split,用起来很方便,但是在设计底层通信操作的时候还是分秒必争(JProfiler看看消耗的时间占用的比例以及调用的次数),用一些自己简单的方式替换。

3. 眼见未必为实,测试才得真知。在SIP5.5中考虑连接后端ISP方式由HttpURLConnection替换成为HttpClient,感觉HttpClient的开发模式更加容易认为是共享传输通道(Get,Post都单独作为包来交由HttpClient单个实例),虽然看到HttpURLConnection说明中提到会共享通道。结果一压,HttpClient根本上不去,原因是构建这些Get,Post本身也很耗时,同时HttpURLConnection底层共享优化的也很不错。HttpClient的优势还是在于去构建简单的客户端,能够处理附加cookies等额外需求。

4. 链式处理的情况下,上下文中共享信息减少数据频繁访问缓存。

5. 操作系统配置以及Web容器的配置会直接影响应用的性能,特别是一些Socket交互比较频繁,会有很大并发的应用。具体的配置可以参见最后的说明。

6. APR模式对于服务器处理能力有很大的影响,EpollUnix socket都会来带很大的性能提高(降低资源消耗)。

7. 在过去谈过异步Servlet的方式(Servlet3.0的特性之一),但是JBoss5测试下来看,稳定性并不好,并且可能会有一些并发问题。

8. 先列出性能瓶颈可能点,然后分别对已经优化的模块进行单独测试,最后整合并且通过多场景测试来验证优化结果。


附加信息:

JBoss Web Container配置:

<Connector port="8128" address="${jboss.bind.address}"   

         maxThreads="1024" maxHttpHeaderSize="8192"

         emptySessionPath="true" protocol="HTTP/1.1"

         enableLookups="false" redirectPort="8443" acceptCount="1024"

         connectionTimeout="20000" disableUploadTimeout="true" useBodyEncodingForURI="true"/>

Apache work的配置:

Keep alive off

<IfModule worker.c>

ServerLimit          80

ThreadLimit          128

StartServers         10

MaxClients           8000

MinSpareThreads      64

MaxSpareThreads      800

ThreadsPerChild      100

MaxRequestsPerChild 10000

</IfModule>

Linux配置信息:

执行:vi /etc/sysctl.conf   

添加一行:net.ipv4.ip_local_port_range = 1024 65535

       再执行:sysctl -p

         更改ulimit –n属性,可用端口数,还有ip_conntrack_max 

APR

       Tomcat优化了IO(sendfile,epoll,OpenSSL)。操作系统的一些函数(随机数的产生,系统状态的获取等),本地进程优化(共享内存,NT的管道,UnixSocket)Tomcat有配置监听器直接会检测APR模块是否存在,在bin目录下建立native目录,并且放置对应的so或则dll即可。

posted @ 2009-03-03 20:14 岑文初 阅读(1816) | 评论 (2)编辑 收藏

    在大型网站中常常会遇到大流量的数据输出问题,过于频繁的输出到DB、文件、第三方系统都会带来不稳定性和低效率。因此需要采用一定的方式来解决这个问题,其实这部分内容的简单处理框架早就用在实际项目中,不过今天正好有外部的朋友问起我,我就整理了一下作为google的开源代码放上去了,这里也简单介绍一下,有兴趣的朋友可以去看看,最好是能够给一些建议。

  

场景:

         应用频繁访问接口服务器,需要控制每个应用在可配置时间段内(例如一分钟)对于某一服务的访问次数,同时需要记录每一次访问内容到数据库中。

几个点:

1. 高并发情况下,集群服务器需要全局计数。(需要将更新和判断作为原子操作,而非两阶段操作,保证高并发事务)

2. 异步日志批量输出。防止高频率访问第三方系统(DB,本地IO),提高性能。

3. 采用黑名单简化计数器判断。

1,3通过memcache就可以实现,如果需要使用客户端可以看看google code上的:http://code.google.com/p/memcache-client-forjava/

这里主要在说一下2,在很多场景中都会有这样的需求,一些需要输出到DB或者文件的内容需要缓存起来异步批量操作,提高性能也降低对于第三方系统的压力。大致设计结构图如下:

 

 自上而下来看,ThreadA,B,C都是程序中其他模块的线程,他们需要输出记录到数据库或者DB中。当有数据到达需要输出时,仅仅只是将数据放入阻塞队列中,而有一个消费者线程池中的线程发现队列中有数据就将数据写入其中某一个线程的数据分页中(每一个线程维护一个自己的内存分页,当页满或者到达了配置的输出间隔时间以后就将页内数据交给输出线程池中的输出线程完成批量数据输出)。


        

下面是三个类图,囊括了这个小工具框架的所有类:

 

         上图是对外提供的异步输出模板,其他模块可以直接使用模板来输出数据。

上图是异步输出器包,是异步输出模板的内置逻辑实现,其他线程直接使用异步输出模板来输出记录。

         上图是消费者和输出线程的接口和默认实现类,可以替换及扩展。

整个框架基本都可以通过配置文件扩展每一个角色(异步输出类,消费者,写出者),扩展方式就是通过在classpath下增加目录META-INF/services/然后将需要扩展的接口作为文件名称,内容就是接口的实现类,这样既可扩展和替换任何一个角色的具体实现。

具体的代码和测试用例可以去http://code.google.com/p/asynwriter/ 下载。

posted @ 2009-02-12 21:09 岑文初 阅读(2466) | 评论 (5)编辑 收藏

     摘要: Author:文初 Email:wenchu.cenwc@alibaba-inc.com Blog:http://blog.csdn.net/cenwenchu79            什么是Dynamo? Dynamo是Amazon的高效Key-Value存储基础组件(类似于现在被广泛应用的Mem...  阅读全文
posted @ 2009-01-13 08:06 岑文初 阅读(2588) | 评论 (0)编辑 收藏

该文前半部分在程序员1月刊上,由于杂志篇幅有限,因此后半部分没有被刊登,这里就在blog上增加一下:

 

服务集成平台

经过前面的介绍和实践两部分,在Open API在概念和实际操作上都有了一定的理解和认识,这里就再谈谈服务集成平台的作用、角色和定位。这里大致描述一下集成平台当前的实现点,这些实现点也就是服务集成平台的价值所在。

服务集成平台(SIP)的角色和作用



3 SIP Role

ISV(独立软件开发商)最关心什么?

1.       服务资源是否丰富。这关系着是否能够创新。

2.       服务质量是否有保证。这关系着是否能够满足用户最基本的需求。

3.       开发集成是否便利。这关系着开发成本。

ISP(独立服务提供商)最关心什么?

1.       服务安全性是否可靠。如果损害到自身或者用户利益,则就失去了原来开放的初衷。

2.       是否有足够多的应用开发者使用服务。

3.       服务的非业务性需求是否可以满足。(服务监控告警,计费,统计分析等)

SIP是连接ISVISP的“桥梁”。它能解决什么双方最关心的什么问题?

1.       丰富的ISV资源以及丰富的ISP资源。这其实是一个良性循环的过程,就好比一个建材市场,买家和卖家数量远远要比在单独一家实体店中多,从淘宝的B2C模式就可以看出,市场大了以后传统的“大鳄”都要聚集人气。

2.       统一安全标准和多种控制策略,即保证了ISP的安全,又能够让ISV开发起来方便。在前面实践过程中可以很明显的看到,众多的应用id,各自的安全流程,让开发者Mash up无形中增加了很大的开发成本和维护成本。

3.       SIP目的就是让ISP专注于业务服务的开发,而将非业务性的需求,如安全,服务监控预警,日志分析统计,计费,社区等都一揽子解决。这样既解决了ISP的第三个问题,同时也为ISV关心的服务质量无形中作了促进。

在年初的时候,分析和研究国外的Open API时,感觉类似于SIP形态的产品在国外还没有,大家都是各做各的,但这阵子回过头来看,YouTubeGoogle开放平台,FlickrYahoo开放平台,这些平台都属于SIP形态的产品,而且Google要比当前我们做的SIP还要更进一步,那就是数据格式规范化(GData),而SIP当前仅仅只是做到流程规范化。

那是否任何公司都适合去做SIP这类形态的平台呢,这不仅仅是技术问题,还是一个资源的问题。阿里巴巴每一家子公司都有实力去做一个这样的开放平台,但各自独做一套的结果就是资源浪费,同时技术没有得到积累(SIP技术积累是在ISV和不同形态的ISP接入中逐渐产生的),最重要的是这些子公司其实真正需要关注的是如何将业务和数据开放给开发者,吸引更多的开发者来构建出围绕Open API的创新应用,最大化数据和服务的商业价值。

服务集成平台功能特性

服务路由

         服务集成平台就好比硬件里面的“路由器”,服务调用者只需要提供服务注册的名称,就可以调用到某一个服务提供商提供的服务,对于调用者来说无需关心此服务的地址以及提供者。

         根据现阶段的服务集成来看,主要分成四类的服务路由,同步服务路由,异步服务路由,订阅服务路由,大数据量上传服务。同步服务路由就是普通的Http无状态单次请求和响应。异步服务路由应用于服务提供商提供的服务无法在当时处理完毕,先返回一个请求响应,当服务处理结束以后再将服务处理结果返回给服务调用者(短信业务就是一种异步服务)。订阅服务和互联网上RSS之类的订阅十分相似,服务调用者只需要订阅服务即可获得服务提供商推送的服务内容。大数据量上传服务其实也是属于同步服务,但是由于消耗资源和性能压力不同,因此被单独作优化处理。

         对于服务形态不同,服务路由需要支持REST风格的服务路由和类REST风格服务的路由,但对于开发者来说,调用的方式都是用服务名称来路由。

正式环境和测试环境的隔离和切换

         对于服务开发者来说,在应用开发期间需要有外部测试环境的支持,在商用以后需要有正式环境支持,同时两个环境的切换需要尽量的简单。

         服务集成平台支持服务提供商提供测试环境和正式环境的不同服务路由,同时两套环境切换成本低。当服务提供商只有一套环境的时候可以根据策略配置的不同,对调用者访问的范围,频度,次数作限制,保证测试服务不影响正式服务。

安全

         提供对应用身份认证以及服务提供商身份认证的支持,采用多种数字签名算法实现基本的身份认证,支持IP白名单和动态算法更新后端插件提供更高级别的服务安全保证。

         细化了用户授权流程。对于用户Token细分为请求级别和会话级别,同时对于会话级别的权限操作,失效时间可根据服务提供商的配置自定义。同时平台托管维护每个应用每个用户的多身份绑定Token,降低服务提供商开发维护成本。

         服务提供商可配置服务访问量控制和频率控制(所有应用或者单个应用)。也支持配置需要订购才可以使用的服务(有限次数订购,无限次数订购)。

         支持多级服务安全策略配置,为服务配置(无授权,应用授权,用户授权,可选用户授权)等多种级别的安全策略。注:可选用户授权是指如果没有被用户授权的情况下使用接口将返回部分公开数据,而在用户授权情况下使用则返回全部的私有和公开数据。

         对服务提供商多级分类,提供不同的安全策略组合。

监控与告警

         服务使用者服务使用出错监控和告警。

服务提供商提供的服务可用性,超时状况的监控和告警。

服务集成平台服务处理状况,内部模块运行状况监控和告警。

日志采集与统计分析

         高并发下日志采集异步处理,采集服务正常访问和异常访问日志,采集用户绑定类,异步服务类,平台内部服务类等特殊日志。

         每日,每周,每月访问日志统计分析,基础报表和趋势分析图的创建。

         支持分析结果预警配置。

         历史统计数据管理和归档。

 

平台内置服务

         平台为服务提供商以及服务调用者提供了平台级别的服务,为开发商和服务提供者获取平台业务数据以及运行期配置安全策略提供方便。

         平台提供一系列平台模块监控、配置、重置服务,支持在线问题查找、定位、解决的一套机制。

非功能性需求(当前情况)

         性能:压力测试单机500并发用户1600+tps,多机处理能力线性增长。

         模块化:内部处理模块化结构,支持运行期配置、装载、卸载。

容错:服务集成平台核心数据都缓存在Memcache中,因此Memcache集群以及容错策略的扩展都为平台稳定和容错作了基础保证。

配套支持

         通过ISV,ISP,Admin三个Portal,使开发者,服务提供商以及后台维护人员能够自主维护基本信息和查看相关数据。

         为开发者提供社区,测试区的支持,并且提供开发工具包和文档,方便开发。

扩展集成

         支持不同平台的服务集成。支持Google,Flickr,Yahoo等等不同的服务平台的服务集成,当前还没有完全将安全体系集成,只能够支持安全流程透传,消息数据完整过滤。

服务集成平台的一些发展趋势

1.       数据集成和流程集成

当前很多服务都是基础的数据型服务,使用者通过数据筛选获取相应的数据,然后展现给用户,这些服务的集成相对来说功能比较单一,流程也不复杂。但随着服务提供商的发展,数据类型服务将会作为基础服务的一部分,而越来越多业务处理型服务会成为使用者的首选,此时,如何让服务和服务之间数据互通,服务可以通过一定的描述编排,就会变得越来越有价值,就如前面提到的,Google采用GData作为数据规范格式,同时对于安全流程的统一制定,为第二阶段的集成打下了基础。

2.       服务基础平台间的互通

最近Open ID也再次由于各大网站的支持而被人们广泛关注,在未来Open API体系中,伴随着Open ID的发展,服务基础平台之间的服务互通也将会变得越来越容易,但是数据的安全性也会对每个服务平台要求更高。

3.       服务集成平台的层次化

在这篇文章的介绍中仅仅介绍的是最基础的Open APIMash up,其实当前已经有更高层次的Mash up被广为使用,JavaScriptActionScriptFlash/Flex这些技术使得展现更为灵活和丰富。因此未来的服务集成平台将会层次化,从数据集成到流程集成到UI集成,会成为一套自下而上的解决方案,适合各种场景的裁剪选择。

Open API的一些思索和感触

不同角色,不同收获

平台开发者:

         这是我的本职工作和角色。当淘宝等服务提供商的服务接上来以后我就要为它的安全和稳定负责。当SIP一旦出现问题,那么服务提供商和软件开发商将都无法再正常工作,套用蜘蛛侠的一句话:“能力越大,责任越大”。作平台的尤为如此。

服务提供商:

服务提供商接触的最多的就是淘宝的同学,首先看到的就是做一个服务提供商很不容易,要把原有系统中复杂的逻辑抽象出来并不是抽象一个公用函数那么简单,同时对于模块化,边界性,容错性方面的要求要远远高于封闭系统开发本身,因为你现在要面对的是倍于原有访问量上百甚至上千的调用者,对任何一个小疏漏都可能带来灾难性的影响。

软件开发商:

         在写这个文档以前,最多也就是写几个测试的Demo来测试SIP环境中的服务,在淘宝API讨论群中会看到很多新的或者老的ISV在抱怨或者询问一些自己觉得很简单的问题(例如签名等等),同时在监控中也看到很多及其简单错误统计数都会有很高的比例。但是经历过这次对于各种各样国内国外的API的开发,让我深刻体会到了开发者的一些痛苦(当然我没有去使用各个开发社区的第三方语言开发包,这也增加部分的开发难度),我也曾因为签名问题在豆瓣API测试的时候折腾了半天,在调试Google Calendar的时候不得不跟踪开发包代码才找到了一些隐晦的设置通过测试。其实Open API在国内国外都没有完全可以称得上成熟,因此开发者其实是最容易受到影响的。同时他们面对着最难应付的客户,平台或者服务提供商出现问题,客户最先抱怨的也是服务开发商,因此作为平台开发者和ISP其实都要给与一定的支持和帮助,这样才会走向更好的良性循环。

其实上面说的那些无非都是大家最长说的换位思考,一个新兴的开发模式需要各方合作才会走向良好的发展方向。

创意就是财富

         记得前一阵子支付宝能够在上海交水电费引起了很多人关注,杭州本地论坛中都有很多人在问:“什么时候杭州能够也用支付宝交水电费就好了”。其实如果开放了支付服务和水电缴费服务,这种Mash up的应用又有什么难的呢?你都可以直接每个自然月通过Google Calendar设置好日程安排,自动缴完所有的费用,然后短信提示一下即可。未来当各行各业发现了自身资源的潜在价值以后,以服务的方式通过平台互通,那么创意就是财富。

posted @ 2009-01-11 21:22 岑文初 阅读(2244) | 评论 (1)编辑 收藏

     摘要: Author:文初 Email:wenchu.cenwc@alibaba-inc.com Blog:http://blog.csdn.net/cenwenchu79 问题凸现:        年关到了,商家忙着促销,网站忙着推广,阿里软件的服务集成平台也面临第一次多方大规模的压力考验,根据5.3版本的压力测试结果,估算了一下现有的...  阅读全文
posted @ 2009-01-11 21:13 岑文初 阅读(2726) | 评论 (3)编辑 收藏

    应该是去年的年初,我受到普元公司的邀请去参加了一次SCA、SOA的技术交流会,当时也是自己第一次去和那么多陌生的朋友交流技术心得,同时也被普元公司的这种纯技术性的交流方式所打动,也在想哪一天阿里巴巴也能够举办一次这样的小规模有针对性地技术交流会那会让我们这些技术人员收益菲浅。一年后的今天,当老大问我有个这样的会议是否要参加的时候,自己毫不犹豫地报了名,虽然看起来和自己专注的工作不是很相关,但是还是那个想法:首先不了解是无法知道是否和自己相关与否,其次就算不相关,多学多听,触类旁通的技术延展只会给自己带来更多的想象空间和创新思维。

    按照会议安排,早晨有两个讲座,下午有四个讲座,每个讲座1个小时左右。第一个出场的是腾讯的安全中心总监杨勇,整个演讲很轻松,首先是对腾讯的整体产品结构和背景作了一下阐述,然后就从安全中心这个整体来讲述安全对于腾讯的意义,如何实施以及一些流程的制定。没有过多的牵涉安全问题的细节,着重是讲述了安全中心面临的四个方面的问题,以及通过什么手段去解决。这其实和他本身所处的工作职责来说相符合,如果仅仅只是来讲某一个安全技术应用,那么就有些太过狭隘了。不过在提问的时候一个问题的回答让我还是留有一些印象的,主持人收集到一个问题:“腾迅安全中心的建设初期遇到的最迫切需要解决的问题是什么?”,他回答道:“其实腾讯安全中心从建设初期到现在一直面临各种迫切的问题,只是随着时间的不同而不同的演变,最早的协议安全到客户端安全到奥运期间的信息安全都是一个发展的阶段”(因为没有ppt以及记录,因此描述的可能不太准确)。但是这个思想任何技术行业都是一样的,时代不同关注不同,需要解决的问题也是在发展的。

   第二个议题是Discuz的剑心带来的“web应用程序中的字符集攻击”,这个演讲就相对来说比较注重专业细节方面的阐述。作为互联网应用开发者,使用Java的人第一堂课就是中文乱码,很多人只看到如何去配置或者写一点转换语句就可以解决,但是对于编码方式就不求甚解,ISO-8859-1,GB2312,UTF-8,UTF-16区别是什么,为什么会引起乱码。其实了解了编码的原理就很能够解释如何会产生乱码,同时产生乱码的时候也可以根据乱码的情况了解可能是因为什么编码转化造成的(阿里巴巴的宝宝写了一篇很详细的文章说了这个问题,进入公司以后我也是看了那片文章才系统地对编码方式做了完整的了解,以前都是碎片)。不过今天听了这个演讲,到让我知道了原来编码方式也被人用来攻击。其实基本的思想主要就是一点:由于信息转发中对于不同编码解析的方式不同或者是过滤不同,导致出现一些漏洞。通俗的比喻就是刺杀秦始皇的图穷匕见,侍卫就好比第一层把关的信息转发者认为着幅图没有威胁,但是真的按照刺客的处理方式那么就是一个最好的攻击性工具。记得我在和同事探讨REST对于Http协议的使用时说最重要的就是REST不再使用Http协议作为传输承载协议而是作为业务协议,那么解析业务的时候究竟是分析协议中指定的编码方式还是内容中的编码方式,结果会大不一样,同时作为安全人员的角度来看,这也会存在一种安全隐患。所以其实任何一种错误都可以被利用作为攻击的手段。

    下午的议题一共有四个,虽然时间比较长一直连续讲到6点多,但是就像主持人讲的,每一个人都“坚持”下来了,呵呵,当然坚持并不是因为不好听,而是做在那儿听比写代码要累很多,当然讲课的同学们也是十分辛苦的。下午第一个演讲的是team509的创始人吴石,讲的主题是“部分软件安全的思考”,内容专业化很强,对很多比较底层的安全问题(操作系统等)作了一些介绍,当然对于我这个门外汉只能听懂个大概意思,不过还是有所了解那些名词的意思到底是什么。第二个是微软的大牛蛙同学,也是安全领域专家讲述了一下微软的SDL(Security Development Lifecycle),望名生意,安全实施的流程化。第三个演讲是两位同学做的,也是我下午听得比较有感触地,先是网名余弦(钟晨鸣)北京知道创宇信息技术公司的安全研究员,讲的是CSRF蠕虫技术,从一个黑客的角度来阐述CSRF的原理以及危害性。这部分比较技术化一些,但是由于和我关注的Web安全也比较相关一些,所以听起来也不是比较迷糊。虽然听着他讲CSRF,但是其实我脑子里面已经在考虑关于Open API的一些安全问题。其实在阿里软件承载淘宝的API过程中,对于客户端的安全问题就一直都在谈,但是对于SIP来说总是鞭长莫及,因为服务集成平台只会保障ISV和ISP之间的信息交互的真实性,但是用户是否由于ISV的技术问题导致信息伪造提交,那么就不得而知,但是最后表现出来的结果就是ISP的Open API计划为ISP带来了更多的安全隐患,也就是说原来淘宝一家漏洞,以后可能会是千千万万家ISV的漏洞,其实这也是上面几个演讲提到的合作风险问题,第三方的技术能力不得而知,同时产生的风险也会很难控制。其实从这里也多多少少看出来为什么FaceBook,myspace,最早对于用户安全隐私数据的开放不仅仅是开放了数据API,同时也会有整一套上层框架支持,其实也是出于开发者能力不足引起隐私数据被恶意修改而作的防护措施。那么现在Open 用户的数据特别是以后涉及到金额的api如何保证isv不欺诈,isv不被欺骗,这可能是后续需要更加重视的问题。同时,在听了CSRF的攻击中谈到的对于资源定位猜测以及操作的时候,让我对REST的风格又打了一个冷颤,REST对于资源的规划和定位十分容易,但是这也为这类攻击提供了便利,同时对于资源操作依赖于Http协议,也会让资源的安全性打了折扣,这需要对Open API开发人员做更多的安全工作指导,或者提供安全框架来防范Open API可能会产生的安全漏洞。紧接着后面的演讲是北京知道创宇信息技术公司的创始人赵伟,应该是业界比较资深的专家了,本来的议题是“恶意网站检测”,不过他还是讲了他这些年来的一些经历以及安全领域的黑色产业链的问题。平时这方面关注的不多,不过今天这一番交流,让我对安全领域的发展以及现况有所了解,甚至有时候就觉得现在上网就算装了一大堆东西还是感觉在“裸奔”。最后一个议题是51.com的郑歆炜讲的“运维安全经验谈”,总结了运维所面对的问题以及解决方案,协调,沟通,总结,知识库,其实这些对于开发人员来说何尝不是呢。最后小黑作了一个简短的总结,同时预报了明天他会做一次附加的构建安全Web架构的讲座,期待明天半天的研讨会和附加讲座。

    好久没有踏踏实实地坐着好好听课了,这次一天半的学习对于自己来说也算是一次新知识的扫盲,同时也为自己后续的工作可能存在的问题或者可以借鉴的知识作一个铺垫。

posted @ 2008-12-17 22:32 岑文初 阅读(1843) | 评论 (1)编辑 收藏

Author:文初

Blog: http://blog.csdn.net/cenwenchu79/

问题

         小丹同学在旺旺上问我是否可以用Memcached实现简易消息中间件类似的功能。觉得这个需求很奇怪,就问了一下具体的应用场景,然后小丹就上来和我具体的谈了究竟需求是什么。其实小丹的应用场景是这样的:客户需要分析一些业务数据,但是业务数据又是很庞大的,在原有系统每天晚上都有一次日分析,将业务数据分析并且归档,但是如果要产生即时分析的效果,用原有系统无法实现,因为当天的数据内容没有被分析,同时如果即时的去分析并且累加到历史分析数据上,性能也不能满足需求,因此考虑通过消息机制来实现异步分析,至于异步处理的时间容忍度,可以通过配置来实现,同时希望异步分析是可线性扩展的,支持集群,提高效率。为什么不直接使用中间件呢?高并发的稳定性,维护的成本,性能要求,使用成本,这些直接就排出了直接去使用中间件的想法。

起始方案的讨论

         在回到小丹最初提到是否可以通过Memcached来实现类似于简易消息中间件的问题上来。首先是否将消息队列作为一个对象保存在Memcached中,这种做法明显不支持高并发的情况,因为Cache本身的get,put无法保证事务。在Memcached中只有计数器是支持高并发的操作,因此考虑是否使用计数器并且按照一定规则来生成key,通过对计数器的增减来让不同消费者获取到不同的消息,这种机制最大的问题在于:1.轮询的压力不小(小丹希望是订阅者模式,Push过去而不是Pull)。2.计数器增减不论怎么做都实现的是栈而不是队列。那么是否使用我扩展的MemcachedKeySet,这点我自己就反对了,这个功能效率很低,而且对于Memcached本身在高并发下操作是否有影响还不得而知。问题越绕越走向死胡同了。

方案的转变

         转换思路,重新分析小丹的需求,究竟哪几点是他真实需要的:1.通过消息方式解耦Web应用和业务分析处理。2.消息必须较为及时的传递到业务分析模块。3.业务分析模块需要支持集群方式线性扩展性能。实现这些需求真的需要简单的消息中间件或者集中式存储么?看看下图的结构:





         从图上可以看出这么几个问题:1.消息中间件本身处于单点,如果需要扩展或者消息本地化增加了复杂度。2.对于消息的获取是采用push还是pull,如果是push那么需要中间件支持订阅者的维护,如果是pull,则需要考虑并发以及性能问题。3.消息的即时性,这个还是依赖于消息中间件的实现机制。总的来说,如果要通过集中式缓存方式实现消息中间件的简单功能,还是有很多问题。那是否直接使用消息中间件的第三方支持呢,其实又回到了最初提出的不使用的缘由。这么设计是否太复杂呢?

回过头来看看Memcached的使用情况,突然发现其实事情可以简单来说,我记得写过一些说明来解释为什么我说Memcached是集中式缓存而不是分布式缓存,其实是客户端的分发算法让很多人觉得好像分布了数据和可无限扩展。其实这种技术结合Hadoop HDFS的部分设计思路,可以给出一个比较好的解决方案。看看下图的结构设计:



上图去掉了消息中间件的角色,增加了Asyn Processor Manager的角色,但是此角色也可以去掉,更为简化的实现需求,增加Asyn Processor Manager的功能仅仅是为了提供动态增减Asyn Processor的功能。具体说一下流程:

1.              Web应用启动时,读取本地配置获取Asyn Processor列表载入内存,同时根据Asyn Processor Manager的配置去发起请求获取Asyn Processor最新的可用列表(如果无法获取,则以本地的为准)。

2.              Web应用根据本地实现的分发算法(最简单就是采用key hash),来选择Asyn Processor,发送请求处理的消息。

3.              如果Asyn Processor Manager不存在,Web应用也可以实现定时发起query status请求来确认Asyn Processor的存活状态,并且更新,保证消息的正常发送。如果Asyn Processor Manager存在,那么确认Asyn Processor状态是否存活可以由Asyn Processor Manager来做(Push或者Pull),而Web应用则可以使用对Asyn Processor Manager的定时查询来获得最新的Asyn Processor列表。

4.              Asyn Processor Manager可以提供增加和删除Asyn Processor的接口,这样就可以支持Asyn Processor的增加和删除,但也正因为Asyn Processor Manager的单点易于注册和管理Asyn Processor,也增加了单点的风险,因此每一台Web应用需要对Asyn Processor Manager不可用作好本地化配置的后备策略。

5.              使用Http协议作为消息传输协议,这样避免SA去维护端口的麻烦,同时也能够充分利用REST的方式来完成业务逻辑(Options方法可以用于心跳,PutDelete可以用于Processor的增减(设置Http Head认证方式即可解决安全问题),Get方式获取信息(xml,json等等格式可以很容易处理))。

上面的方案可以看出,如果去掉Asyn Processor Manager,其实方案很简化,就是每一个客户端有一层类似于Memcached客户端的分发机制,同时比Memcached免去了对于连接池维护的复杂性,仅仅只需要维护状态标示即可。

最后还嘱咐小丹对于Asyn Processor的设计需要合理化,这部分需要支持消息接受和处理的并行处理,提高Asyn Processor的处理能力,同时通过分页批量处理消息的方式减少对于DB的压力(当然需要根据具体的时效性设置消息页的大小以及消息页Flush的时间)。

后话

         上面的方案可能不是最好或者最优的,这里仅仅只是分享一下自己解决这个问题的一些心得。这此的方案讨论也走了一些弯路,有时候在做任何选择以前首先需要考虑的是到底自己需求是什么,然后再去考虑选择什么技术去实现。同时尽量还是那句老话”Make it Simple”,做技术的人总是喜欢做的很复杂,功能很强大,但是最后迷失了最初的目标,忙于去完善那些80%没有用的功能,却没有去做好那20%客户最Care的功能。化繁为简,见招拆招,才能四量拨千斤。
posted @ 2008-12-12 11:49 岑文初 阅读(1774) | 评论 (0)编辑 收藏

     今天收到InfoQ的推荐邮件,看了标题就很感兴趣,花了一些时间一看,果然是很不错的一个案例分析,同时也让自己学到了不少。大致罗列一下看后的一些文章重点内容。案例地址:http://www.infoq.com/cn/articles/webber-rest-workflow

    1.通过REST服务请求完成状态迁移,同时合理利用OPTIONS来查看资源操作权限。

    2.合理利用Http Heads来返回资源URI,以及通过ErrorCode来确定操作结果,并且作后处理。

    3.通过返回内容指定后续流程资源定位以及操作来实现流程化。

    4.通过Put报头的两种版本比较标示来防止并发修改。(其实也可以优化来做查询缓存的工作)

    5.使用Atom协议来发布和管理资源(Atom是最适合REST风格服务的数据源格式定义)

    6.URI模版的使用建议,慎用,如果确实能够有把握抽象资源定位。

    7.Auth可以通过轻量级Http Head中的Authentication或者WS-*的方式来实现。(也可以通过https实现)

    总的来说,其实整个案例分析下来以后,可以发现如果要使得服务流程化,那么前提就是数据交互格式统一(XML,Atom),然后利用Http协议作为服务协议而非承载协议,利用已有的操作约定,报文头部标示和返回的错误码来完成资源状态迁移的工作,同时通过在返回内容中嵌入流程化内容,使得整个流程可以贯穿。(这里还是简单的流程串联,其实如果在流程规则协议中增加复杂的逻辑定义,则可以实现更为强大的Web workflow)。

    但对于Open API或者类似的REST流程化业务来说,安全其实还是最大的挑战,特别是在对资源的访问控制权限上。当然可以类似于WS-Security提出一套较为安全成熟的方案,但是性能和使用简易性则会大打折扣,也失去了REST本身的优势。

posted @ 2008-12-10 11:32 岑文初 阅读(2159) | 评论 (2)编辑 收藏

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



Uploaded on authorSTREAM by cenwenchu
posted @ 2008-12-10 10:23 岑文初 阅读(3955) | 评论 (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 岑文初 阅读(1940) | 评论 (0)编辑 收藏

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

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

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

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

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

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

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

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

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

 

幻灯片1

 

幻灯片2

 

幻灯片3

 

幻灯片4

 

幻灯片5

 

幻灯片6

 

幻灯片7

 

幻灯片8

 

幻灯片9

 

幻灯片10

 

幻灯片11
posted @ 2008-10-31 09:53 岑文初 阅读(1967) | 评论 (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 岑文初 阅读(4831) | 评论 (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 岑文初 阅读(1710) | 评论 (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 岑文初 阅读(3277) | 评论 (2)编辑 收藏

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

这是我最后一部分在InfoQ上连载的文章,测试部分其实很有启发
http://www.infoq.com/cn/articles/hadoop-process-develop
posted @ 2008-08-13 23:06 岑文初 阅读(757) | 评论 (0)编辑 收藏

 

最近关注Hadoop,因此也顺便关注了一下Hadoop相关的项目。HBASE就是基于Hadoop的一个开源项目,也是对GoogleBigTable的一种实现。

       BigTable是什么?GooglePaper对其作了充分的说明。字面上看就是一张大表,其实和我们想象的传统数据库的表还是有些差别的。松散数据可以说是介于Map Entrykey & value)和DB Row之间的一种数据。在我使用Memcache的时候,有时候的需求是需要存储的不仅仅是简单的一个key对应一个value,可能我需要类似于数据库表结构中多属性的存储,但是又不会有传统数据库表结构中那么多关联关系的需求,其实这类数据就是所谓的松散数据。BigTable最浅显来看就是一张很大的表,表的属性可以根据需求去动态增加,但是又没有表与表之间关联查询的需求。

       互联网应用有一个最大的特点,就是速度,功能再强大,速度慢,还是会被舍弃。因此在大访问量的网站都采取前后的缓存来提升性能和响应时间。对于Map Entry类型的数据,集中式分布式Cache都有很多选择,对于传统的关系型数据,从MySQLOracle都给了很好的支持,唯有松散数据这类数据,采用前后两种解决方案都不能最大化它的处理能力。因此BigTable才有了它用武之地。

       HBASE作为Apache的开源项目,也是出于起步阶段,因为其实它所依赖的Hadoop也不能说已经到了成熟阶段,所以都有很大的发展空间,这也为我们这些开源爱好者提供了更多空间去贡献。这里主要会谈到HBASE的框架设计方面的知识和它的一些特点,不论是否采用HBASE去解决工作中的问题,一种好的流程设计总会给开发者和架构设计者带来一些思想上的火花。

HBASE设计介绍

数据模型

       HBASE中的每一张表,就是所谓的BigTableBigTable会存储一系列的行记录,行记录有三个基本类型的定义:Row Key,Time Stamp,ColumnRow Key是行在BigTable中的唯一标识,Time Stamp是每次数据操作对应关联的时间戳,可以看作类似于SVN的版本,Column定义为:<family>:<label>,通过这两部分可以唯一的指定一个数据的存储列,family的定义和修改需要对HBASE作类似于DBDDL操作,而对于label的使用,则不需要定义直接可以使用,这也为动态定制列提供了一种手段。family另一个作用其实在于物理存储优化读写操作,同family的数据物理上保存的会比较临近,因此在业务设计的过程中可以利用这个特性。

看一下逻辑数据模型:

Row Key

Time Stamp

Column "contents:"

Column "anchor:"

Column "mime:"

"com.cnn.www"

t9

"anchor:cnnsi.com"

"CNN"

t8

"anchor:my.look.ca"

"CNN.com"

t6

"<html>..."

"text/html"

t5

"<html>..."

t3

"<html>..."

上表中有一列,列的唯一标识为com.cnn.www,每一次逻辑修改都有一个timestamp关联对应,一共有四个列定义:<contents:>,<anchor:cnnsi.com>,<anchor:my.look.ca>,<mime:>。如果用传统的概念来将BigTable作解释,那么BigTable可以看作一个DB Schema,每一个Row就是一个表,Row key就是表名,这个表根据列的不同可以划分为多个版本,同时每个版本的操作都会有时间戳关联到操作的行。

再看一下HBASE的物理数据模型:

Row Key

Time Stamp

Column "contents:"

"com.cnn.www"

t6

"<html>..."

t5

"<html>..."

t3

"<html>..."

Row Key

Time Stamp

Column "anchor:"

"com.cnn.www"

t9

"anchor:cnnsi.com"

"CNN"

t8

"anchor:my.look.ca"

"CNN.com"

Row Key

Time Stamp

Column "mime:"

"com.cnn.www"

t6

"text/html"

物理数据模型其实就是将逻辑模型中的一个Row分割成为根据Column family存储的物理模型。

对于BigTable的数据模型操作的时候,会锁定Row,并保证Row的原子操作。

框架结构及流程


1 框架结构图

       HBASE依托于HadoopHDFS作为存储基础,因此结构也很类似于HadoopMaster-Slave模式,Hbase Master Server 负责管理所有的HRegion Server,但Hbase Master Server本身并不存储HBASE中的任何数据。HBASE逻辑上的Table被定义成为一个Region存储在某一台HRegion Server上,HRegion Server Region的对应关系是一对多的关系。每一个HRegion在物理上会被分为三个部分:HmemcacheHlogHStore,分别代表了缓存,日志,持久层。通过一次更新流程来看一下这三部分的作用:


2 提交更新以及刷新Cache流程

       由流程可以看出,提交更新操作将会写入到两部分实体中,HMemcacheHlog中,HMemcache就是为了提高效率在内存中建立缓存,保证了部分最近操作过的数据能够快速的被读取和修改,Hlog是作为同步HmemcacheHstore的事务日志,在HRegion Server周期性的发起Flush Cache命令的时候,就会将Hmemcache中的数据持久化到Hstore中,同时会清空Hmemecache中的数据,这里采用的是比较简单的策略来做数据缓存和同步,复杂一些其实可以参照java的垃圾收集机制来做。

       在读取Region信息的时候,优先读取HMemcache中的内容,如果未取到再去读取Hstore中的数据。

几个细节:

1.              由于每一次Flash Cache,就会产生一个Hstore File,在Hstore中存储的文件会越来越多,对性能也会产生一定影响,因此达到设置文件数量阀值的时候就会Merge这些文件为一个大文件。

2.              Cache大小的设置以及flush的时间间隔设置需要考虑内存消耗以及对性能的影响。

3.              HRegion Server每次重新启动的时候会将Hlog中没有被FlushHstore中的数据再次载入到Hmemcache,因此Hmemcache过大对于启动的速度也有直接影响。

4.              Hstore File中存储数据采用B-tree的算法,因此也支持了前面提到对于ColumnFamily数据操作的快速定位获取。

5.              HRegion可以Merge也可以被Split,根据HRegion的大小决定。不过在做这些操作的时候HRegion都会被锁定不可使用。

6.              Hbase Master Server通过Meta-info Table来获取HRegion Server的信息以及Region的信息,Meta最顶部的一个Region是虚拟的一个叫做Root Region,通过Root Region可以找到下面各个实际的Region

7.              客户端通过Hbase Master Server获得了Region所在的Region Server,然后就直接和Region Server进行交互,而对于Region Server相互之间不通信,只和Hbase Master Server交互,受到Master Server的监控和管理。

后话

       HBase还没有怎么使用,仅仅只是看了wiki去了解了一下结构和作用,暂时还没有需要使用的场景,不过对于各种开源项目的设计有所了解,对自己的框架结构设计也会有很多帮助,因此分享一下。

posted @ 2008-08-08 11:37 岑文初 阅读(5973) | 评论 (1)编辑 收藏

这部分内容是分布式计算开源框架Hadoop入门实践的第二部分,讲述了关于实际使用配置的内容.第三部分是对于集群配置的测试结果分析的部分,下周三应该会在InfoQ刊登.

http://www.infoq.com/cn/articles/hadoop-config-tip

posted @ 2008-08-08 08:47 岑文初 阅读(1412) | 评论 (1)编辑 收藏

分布式计算开源框架Hadoop入门实践(一)
第一部分已经在InfoQ.cn上刊登了第一部分

http://www.infoq.com/cn/articles/hadoop-intro

posted @ 2008-08-04 17:52 岑文初 阅读(1872) | 评论 (1)编辑 收藏

 

SIP的第四期结束了,因为控制策略的丰富,早先的的压力测试结果已经无法反映在高并发和高压力下SIP的运行状况,因此需要重新作压力测试。跟在测试人员后面做了快一周的压力测试,压力测试的报告也正式出炉,本来也就算是告一段落,但第二天测试人员说要修改报告,由于这次作压力测试的同学是第一次作,有一个指标没有注意,因此需要修改几个测试结果。那个没有注意的指标就是load average,他和我一样开始只是注意了CPU,内存的使用状况,而没有太注意这个指标,这个指标与他们通常的限制(10左右)有差别。重新测试的结果由于这个指标被要求压低,最后的报告显然不如原来的好看。自己也没有深入过压力测试,但是觉得不搞明白对将来机器配置和扩容都会有影响,因此去问了DBASA,得到的结果相差很大,看来不得不自己去找找问题的根本所在了。

       通过下面的几个部分的了解,可以一步一步的找出Load Average在压力测试中真正的作用。

CPU时间片

       为了提高程序执行效率,大家在很多应用中都采用了多线程模式,这样可以将原来的序列化执行变为并行执行,任务的分解以及并行执行能够极大地提高程序的运行效率。但这都是代码级别的表现,而硬件是如何支持的呢?那就要靠CPU的时间片模式来说明这一切。程序的任何指令的执行往往都会要竞争CPU这个最宝贵的资源,不论你的程序分成了多少个线程去执行不同的任务,他们都必须排队等待获取这个资源来计算和处理命令。先看看单CPU的情况。下面两图描述了时间片模式和非时间片模式下的线程执行的情况:


1 非时间片线程执行情况


2 非时间片线程执行情况

       在图一中可以看到,任何线程如果都排队等待CPU资源的获取,那么所谓的多线程就没有任何实际意义。图二中的CPU Manager只是我虚拟的一个角色,由它来分配和管理CPU的使用状况,此时多线程将会在运行过程中都有机会得到CPU资源,也真正实现了在单CPU的情况下实现多线程并行处理。

       CPU的情况只是单CPU的扩展,当所有的CPU都满负荷运作的时候,就会对每一个CPU采用时间片的方式来提高效率。

       Linux的内核处理过程中,每一个进程默认会有一个固定的时间片来执行命令(默认为1/100秒),这段时间内进程被分配到CPU,然后独占使用。如果使用完,同时未到时间片的规定时间,那么就主动放弃CPU的占用,如果到时间片尚未完成工作,那么CPU的使用权也会被收回,进程将会被中断挂起等待下一个时间片。

CPU利用率和Load Average的区别

       压力测试不仅需要对业务场景的并发用户等压力参数作模拟,同时也需要在压力测试过程中随时关注机器的性能情况,来确保压力测试的有效性。当服务器长期处于一种超负荷的情况下运行,所能接收的压力并不是我们所认为的可接受的压力。就好比项目经理在给一个人估工作量的时候,每天都让这个人工作12个小时,那么所制定的项目计划就不是一个合理的计划,那个人迟早会垮掉,而影响整体的项目进度。

CPU利用率在过去常常被我们这些外行认为是判断机器是否已经到了满负荷的一个标准,看到50%-60%的使用率就认为机器就已经压到了临界了。CPU利用率,顾名思义就是对于CPU的使用状况,这是对一个时间段内CPU使用状况的统计,通过这个指标可以看出在某一个时间段内CPU被占用的情况,如果被占用时间很高,那么就需要考虑CPU是否已经处于超负荷运作,长期超负荷运作对于机器本身来说是一种损害,因此必须将CPU的利用率控制在一定的比例下,以保证机器的正常运作。

Load AverageCPULoad,它所包含的信息不是CPU的使用率状况,而是在一段时间内CPU正在处理以及等待CPU处理的进程数之和的统计信息,也就是CPU使用队列的长度的统计信息。为什么要统计这个信息,这个信息的对于压力测试的影响究竟是怎么样的,那就通过一个类比来解释CPU利用率和Load Average的区别以及对于压力测试的指导意义。

我们将CPU就类比为电话亭,每一个进程都是一个需要打电话的人。现在一共有4个电话亭(就好比我们的机器有4核),有10个人需要打电话。现在使用电话的规则是管理员会按照顺序给每一个人轮流分配1分钟的使用电话时间,如果使用者在1分钟内使用完毕,那么可以立刻将电话使用权返还给管理员,如果到了1分钟电话使用者还没有使用完毕,那么需要重新排队,等待再次分配使用。


3 电话使用场景

       上图中对于使用电话的用户又作了一次分类,1min的代表这些使用者占用电话时间小于等于1min2min表示使用者占用电话时间小于等于2min,以此类推。根据电话使用规则,1min的用户只需要得到一次分配即可完成通话,而其他两类用户需要排队两次到三次。

       电话的利用率 = sum (active use cpu time)/period

每一个分配到电话的使用者使用电话时间的总和去除以统计的时间段。这里需要注意的是是使用电话的时间总和(sum(active use cpu time)),这与占用时间的总和(sum(occupy cpu time))是有区别的。(例如一个用户得到了一分钟的使用权,在10秒钟内打了电话,然后去查询号码本花了20秒钟,再用剩下的30秒打了另一个电话,那么占用了电话1分钟,实际只是使用了40秒)

电话的Average Load体现的是在某一统计时间段内,所有使用电话的人加上等待电话分配的人一个平均统计。

电话利用率的统计能够反映的是电话被使用的情况,当电话长期处于被使用而没有的到足够的时间休息间歇,那么对于电话硬件来说是一种超负荷的运作,需要调整使用频度。而电话Average Load却从另一个角度来展现对于电话使用状态的描述,Average Load越高说明对于电话资源的竞争越激烈,电话资源比较短缺。对于资源的申请和维护其实也是需要很大的成本,所以在这种高Average Load的情况下电话资源的长期“热竞争”也是对于硬件的一种损害。

低利用率的情况下是否会有高Load Average的情况产生呢?理解占有时间和使用时间就可以知道,当分配时间片以后,是否使用完全取决于使用者,因此完全可能出现低利用率高Load Average的情况。由此来看,仅仅从CPU的使用率来判断CPU是否处于一种超负荷的工作状态还是不够的,必须结合Load Average来全局的看CPU的使用情况和申请情况。

所以回过头来再看测试部对于Load Average的要求,在我们机器为8CPU的情况下,控制在10 Load左右,也就是每一个CPU正在处理一个请求,同时还有2个在等待处理。看了看网上很多人的介绍一般来说Load简单的计算就是2* CPU个数减去1-2左右(这个只是网上看来的,未必是一个标准)。

补充几点:

1.对于CPU利用率和CPU Load Average的结果来判断性能问题。首先低CPU利用率不表明CPU不是瓶颈,竞争CPU的队列长期保持较长也是CPU超负荷的一种表现。对于应用来说可能会去花时间在I/O,Socket等方面,那么可以考虑是否后这些硬件的速度影响了整体的效率。

这里最好的样板范例就是我在测试中发现的一个现象:SIP当前在处理过程中,为了提高处理效率,将控制策略以及计数信息都放置在Memcached Cache里面,当我将Memcached Cache配置扩容一倍以后,CPU的利用率以及Load都有所下降,其实也就是在处理任务的过程中,等待Socket的返回对于CPU的竞争也产生了影响。

2.未来多CPU编程的重要性。现在服务器的CPU都是多CPU了,我们的服务器处理能力已经不再按照摩尔定律来发展。就我上面提到的电话亭场景来看,对于三种不同时间需求的用户来说,采用不同的分配顺序,我们可看到的Load Average就会有不同。假设我们统计Load的时间段为2分钟,如果将电话分配的顺序按照:1min的用户,2min的用户,3min的用户来分配,那么我们的Load Average将会最低,采用其他顺序将会有不同的结果。所以未来的多CPU编程可以更好的提高CPU的利用率,让程序跑的更快。

以上所提到的内容未必都是很准确或者正确,如果有任何的偏差也请大家指出,可以纠正一些不清楚的概念。

posted @ 2008-06-30 17:35 岑文初 阅读(37438) | 评论 (17)编辑 收藏

     摘要:          我对于Memcached的接触,还是在去年看了CSDN的一系列国外大型网站架构设计而开始的。最初的时候只是简单的封装了Memcached Java版的客户端,主要是对于配置的简化以及Memcached多点备份作了一些工作,然后就作为ASF的组件一部分提供给其他Team使用。其实看过Memcached Jav...  阅读全文
posted @ 2008-06-04 23:10 岑文初 阅读(8961) | 评论 (2)编辑 收藏

 在《Java 载入Jar内资源问题的探究》这个文档贴出来以后,有朋友给了我反馈,最终知道了问题就出现在JarOutputstream输出的时候,虽然支持直接写入目录中的文件来同时产生目录和文件,但是这样在jar中目录就不是一个有效的entry,因此在资源定位的时候就无法得到,因此必须也把目录作为entry写入,这样才会正常定位资源。这个问题作了测试以后反馈到我们的工具开发人员那边,做了修改以后一切都恢复正常,细节决定成败,那么一点细微的差异,会让各种框架都无法正常运作。

代码修改如下:

JarOutputStream jos;

       try

       {

           jos = new JarOutputStream(new BufferedOutputStream(new FileOutputStream(file)));

           String f = "spring/sip-analyzer-dataSource.xml";

           String dir = "spring/";
           JarEntry je1 = new JarEntry(dir);
           jos.putNextEntry(je1);

   
     
    
           JarEntry je =
new JarEntry(f);

           jos.putNextEntry(je);

           BufferedInputStream bis = new BufferedInputStream(new FileInputStream("D:/work/sip3/analyzer/src/conf.test/spring/sip-analyzer-dataSource.xml"));

           int i = 0;

           while ((i=bis.read())!=-1)

           {

              jos.write(i);

           }

           bis.close();

           jos.closeEntry();

           jos.close();

} catch  ...

posted @ 2008-06-03 14:24 岑文初 阅读(2330) | 评论 (3)编辑 收藏

 

       工作忙,有些许时间没有更新Blog了,这次在开发监控模块的时候遇到了这个问题,整个问题定位过程真是走了不少路,所以觉得有必要记录下来分享一下。在我看来很多时候结果也许就很简单一个原因,但是开发人员却要探究很久,也许在找到了其他可实现业务逻辑方法的情况下,就会放弃寻找原因,这期间我也是一样。

问题初现:

       在服务集成平台中需要新增一块写入数据库的逻辑,因此考虑最简便就是弄个SpringBeanFactory来搞定这一切,谁知道,问题就这么出现了。很简单,通过SpringClassPathXmlApplicationContext来构建BeanFactory,下面的语句大家应该很熟悉:

ctx = new ClassPathXmlApplicationContext("/spring/sip-*.xml");

       通过通配符来载入ClassPath下的所有的符合规则的spring配置文件。然后在Eclipse中作完单元测试和集成测试,一切正常。然后用我们内部的打包部署,而这些配置文件都被打在Jar中作为lib库依赖。结果启动以后,在分析完日志需要写入到数据库的时候出现异常:

Could not resolve bean definition resource pattern [/spring/sip-*.xml]; nested exception is java.io.FileNotFoundException: class path resource [spring/] cannot be resolved to URL because it does not exist

就提示来说,就是没有找到spring这个目录,也就是在ClassPath下面就没有找到资源。

第一次试图解决问题:

以前调整过Jboss关于ClassLoader的配置,即自上而下搜索还是自下而上搜索,以及是否采用Web容器的ClassLoader,开始怀疑是否是这种修改造成的问题。修改了没有问题,然后就设置断点跟踪SpringClassPathXmlApplicationContext的构造过程,发现Spring在分析此类通配类型的过程中,首先将前面的文件目录和后面的具体通配文件分开,先定位文件目录资源,也就是在定位文件目录资源的过程中,找不到spring目录,而出现了那个异常。看了代码中也有对Jar的处理,但是在处理之前就出现了问题。

自己做了尝试,将spring目录和其内容解压到WebClasses目录下运行正常,或者解压到war下面也是正常的,这些地方其实都是ClassPath可以找到的,但是lib目录下的jar也应该是可以找到的。在仔细跟踪了代码中最后载入这些资源的ClassLoader内的数据,所有的Jar都是包含在内的。

由于工作太多,因此将原有的打包模式作了修改,每次打包将这部分配置解压到war下面,这样就找到了可解决方案了,因此细致的缘由也就没有再去追究。(如果不是后面再次遇到,这个问题就会在此了结)

问题再现:

       监控模块中需要新增一块写入数据库的逻辑,在单元测试和集成测试通过的情况下出现了问题,由于此次是普通的J2SE的应用,所有的配置和依赖都打入在了Jar中,所以问题和前次一样。

       这次决定花一些时间好好找到问题所在,首先觉的Spring的资源载入应该不会不支持从Jar中载入,这是最基本的功能,因此再次打开了Spring的源码。

问题二次定位:

先看看ClassPathXmlApplicationContext的类图结构:




    关键方法就是getResource方法,ClassPathXmlApplicationContext的资源定位就是采用了DefaultResourceLoadergetResource方法。内部也没有做太多的工作,其实就是如下的代码:

try

{

                      // Try to parse the location as a URL...

                      URL url = new URL(location);

                      returnnew UrlResource(url);

           }

           catch (MalformedURLException ex)

{

                 // No URL -> resolve as resource path.

                 return getResourceByPath(location);

      }

上面的代码都是标准的j2se的代码.作为URL通过字符串来构造,通常需要能够首先获得URL的资源全路径,而在当前情况下发现到获取资源的时候location还是保持了spring/的状态,而没有被替换成为所在jar的资源全路径,那么就先作以下测试:

    新建简单的项目,然后在项目中加入包含spring配置的jar,然后作单元测试,测试代码如下:

URL url = Thread.currentThread().getClass().getResource("/spring/");

    未获取到URL,出现异常。

URL url = Thread.currentThread().getClass().getResource("/spring/sip-analyzer-dataSource.xml");

       正常获取到了URL

由此看来应该是在获取Jar中的目录资源路径的时候出现问题导致后续载入出现问题,尝试直接传入具体的文件名:

ctx = new ClassPathXmlApplicationContext("/spring/sip-analyzer-dataSource.xml");

发现还是出现问题,在new URL的时候传入的是没有翻译过的文件名,考虑在传入的过程中就直接替换成为资源路径,因此写了一个简单的方法:

publicstatic String[] getRealClassPath(String[] locationfile)

      {

           String[] result = locationfile;

                 for(int i = 0 ; i < locationfile.length; i++)

                 {

                      try

                      {

                            URL url = Thread.currentThread().getClass().getResource(locationfile[i]);

                            String file = url.getFile();

                            if (file.indexOf(".jar!") > 0)

                                  result[i] = new StringBuffer("jar:").append(file.substring(0,file.indexOf(".jar!")+".jar!".length()))

                                             .append(locationfile[i]).toString();

                      }

                      catch(Exception ex)

                      {}

                 }

          

           returnresult;

}

在将构造工厂类修改为:

ctx = new ClassPathXmlApplicationContext(BaseUtil.getRealClassPath(new String[]{"/spring/sip-analyzer-dataSource.xml"}));

运行测试,正常启动,这也就是又变成最原始的文件罗列的模式。问题虽然找到了解决方案,但是始终觉得很别扭,同时对于无法在Jar中载入配置资源的情况我一直都还是觉得应该不是Spring的问题。

峰回路转:

晚上到家还是有点不死心,就直接建了个项目作单元测试,然后将一个自己建立的Jar加入到Classpath下面,作单元测试,结果大吃一惊。

URL url = Thread.currentThread().getClass().getResource("/test/");

URL url = Thread.currentThread().getClass().getResource("/test/test.txt");

都正常获取到了资源,这完全推翻了我早先认为在Jar中无法获得目录作为资源的问题。然后把公司里面的项目重新打包然后加入到ClassPath下,验证spring的目录,出错,目录无法获取,此时我确定看来应该不是应用的问题,而是环境问题。检查了两个Jar,看似没有什么区别,将公司项目的Jar中的spring目录拷贝到测试的jar中,然后作测试,可以找到目录。那么问题完全定位到了Jar本身。通过RAR的压缩工具看了一下两个Jar的信息,除了显示所谓的压缩平台不同(一个是DOS,一个是Unix)其他没有任何区别。然后自己用RAR打了一个Jar以及在linux下打了一个Jar做了测试,两个Jar内的目录都是正常可以被获取。

无意中我换了一下需要获取的目录名称,也就是说在公司项目中有多个目录在jar中,这次换成为ibatis目录,正常获取,看来不是Jar的格式。回想了一下,公司的打包工具是自己人写的,其中提供了一个特性,如果一个项目内部的一些配置信息是需要让调用它的第三方在编译期配置,那么可以通过在第三方项目构建的过程中,动态的生成配置文件然后植入到被依赖的jar中。而spring这个目录中由于那些数据库的配置都是需要动态配置的,因此spring的那个目录是后期被写入的,而ibatis是早先就固化在项目中的。

由于我们的JarMETA-INF中都有INDEX.LIST文件,过去遇到过在JAR中自己手工放入一些文件由于没有修改INDEX.LIST而导致虽然文件已经存在但是不会被发现,于是打开公司项目中的Jar,果然INDEX.LIST中只有ibatis,而没有spring,看来是我的同事在写入的时候没有将INDEX.LIST更新。立刻将INDEX.LIST作了更新,测试spring目录,结果依然出错。看来这还不是问题的根本。

立刻问了我们开发打包工具的同事,向他们要写入Jar的代码,对方的回答是就是采用简单的JarOutputStream来写入,没有什么特殊的。那我就开始怀疑是否是因为采用这种方式写入到Jar中的目录在被资源定位的时候会出现问题。于是写了下面的代码:

JarOutputStream jos;

           try

           {

                 jos = new JarOutputStream(new BufferedOutputStream(new FileOutputStream(file)));

                 String f = "spring/sip-analyzer-dataSource.xml";

                 File source = new File(f);

                 JarEntry je = new JarEntry(f);

                 jos.putNextEntry(je);

                 BufferedInputStream bis = new BufferedInputStream(new FileInputStream("D:/work/sip3/analyzer/src/conf.test/spring/sip-analyzer-dataSource.xml"));

                 int i = 0;

                 while ((i=bis.read())!=-1)

                 {

                      jos.write(i);

                 }

                 bis.close();

                 jos.closeEntry();

                 jos.close();

} catch  ...

结果创建出来的Jar中的spring目录无法被资源定位,同样在这个Jar中直接拖入一个目录test,然后刷新测试,test目录可以被定位。

后续

       就到了这个阶段来看如果以上面这种方式写入,对于目录资源定位的却存在问题。这个问题还没有最终的肯定的结论,在我现在所有的试验来看,不论是否有INDEX.LIST,或者INDEX.LIST,如果用程序写入到Jar中,目录作为资源定位都会出现问题(起码是上面那种普通写入方式)。

       这种情况可能是由于这种写法还有一些其他需要配置的,例如写入到META-INF/INDEX.LIST中,或者就是J2SE现在API存在的一个问题。不过不管是什么问题,起码值得引起重视,特别是现在类似于Spring框架载入Jar目录下的配置。

posted @ 2008-05-28 16:42 岑文初 阅读(3842) | 评论 (2)编辑 收藏

 

       在完成ASF集成REST以后,接到的任务就是要完成一个日志分析应用。需求没有很明确,只是要有这么一个东西能够满足分析收集后的日志,将分析后的原始数据入库,作为后期分析和统计使用。

       在动手做之前,我还是给这个应用作了最基本的需求定义:灵活配置(输入源,输出目标,分析器的实现等),高效(并行任务分解)。就这两点能够做到,那么将来需求如何变化都可以适应。TigerConcurrent包是满足后面那项最好的实现,因此打算好好的实践一把,也就这部分Tiger的特性还没有充分使用过,里面的线程池,异步服务调用,并发控制都能够极好的完成并行任务分解的工作。也就是在这个过程中,看到了IBM开发者论坛上的一片文章,讲关于《应用fork-join框架》,谈到了在J2SE 7 Concurrent包中将会增加fork-join风格的并行分解库,其实这个是更细粒度的任务分解,同时能够在当前多CPU的情况下提高执行效率,充分利用CPU的一种实现。无关的话不多说了,就写一下整个设计和实现的过程以及中间的一些细节知识。

 

背景:

       由于服务路由应用访问量十分大,即时的将访问记录入库对于路由应用本身以及数据库来说无疑都会产生很大的压力和影响。因此考虑首先将访问信息通过log4j记录在本地(当然自己需要定制一下Log4jAppenderFilter),然后通过服务器的定时任务脚本来将日志集中到日志分析应用所在的机器上(这里通过配置可以决定日志是根据什么时间间隔来产生新文件)。日志分析应用就比较单纯的读取日志,分析日志,输出分析结果(包括写入数据库或者是将即时统计信息存入到集中式缓存Memcached中)。网络结构图如下:

1 网络结构图

 

Concurrent概述:

       Tiger出来也有些年头了,但是每一个新的特性是否都在实际的工作中使用过,起码我自己是没有作到的,包括对于Concurrent包也只是看过,写了几个Test case玩一下,但具体使用到实际开发中还是比较少的。在这个工作之前,如果考虑要使用对象池或者线程池,那么一定会去采用apachecommon pool,不过在现在jdk日益“强大”的基础下,能够通过jdk自己搞定的,就尽量不再引入第三方包了。看Java Doc很容易就理解了Concurrent,这里我只是大致的说一下几个自己在应用中使用的接口:

 

BlockingQueue<E>:看看名字就知道了,阻塞式队列,可以设置大小。适合于生产者和消费者模式,生产者在队列满时阻塞,消费者在队列空时阻塞。在日志分析应用开发中被用于分析任务(生产者)和输出任务(消费者)之间的分析结果存储通道。

 

Callable<V>:任何需要执行的任务都可以定义成Callable,类似于线程的Runnable接口,可以被Service Executor指派给内部的线程异步执行,并且返回对象或者抛出异常。在日志分析应用开发中,非定时性的任务都定义成为此类型。

 

ConcurrentMap<K,V>:这个以前常常使用,因为效率要远远高于Collections.synchronizedCollectionsynchronized。后面还会提到实践中的几个实用的技巧来防止在高并发的情况下出现问题。在日志分析应用中,此类型的Map作为保存日志文件分析状态的缓存(日志文件分为两种状态:分析中,分析结束。如果不存在于Map中就认为尚未分析,那么将其纳入Map然后启动分析处理线程工作,如果存在于Map中标示为分析中,那么将不会再分析此文件,如果分析结束并且被输出,将会标示此文件分析结束,异步清理线程将会定时根据策略删除或移动文件)。

 

ExecutorService:内置线程池,异步执行指派任务,并可以根据返回的Future来跟踪执行情况。在日志分析应用开发中,被用于非定时性任务执行。

 

ScheduledExecutorService:内置线程池,定时异步执行指派任务,并可以根据返回的Future来跟踪执行情况。在日志分析应用开发中,被用于定时性任务执行。

 

以上就是被使用到的接口,具体实现策略配置就不在此赘述了。

 

整体结构设计:

       整体设计还是基于开始设定的两个原则:灵活配置,高效性(任务分解,并行流水线执行)。说到任务分解又会想起读书时候的离散数学中关键路径等等。任务分解还是要根据具体情况来分析和设计,不然并行不但不会提高效率,反而还降低了处理效率。

就日志分析来看,主要的处理过程可以分成这么几个任务:

1.             检查日志来源目录,锁定需要分析的文件。(执行需要时间很短,可通过定时间隔执行)。

2.             分析已经被锁定的日志文件,产生分析结果。(执行需要时间根据日志文件大小来决定,因此需要线程异步执行,结果根据设定拆分成细粒度包,降低输出线程等待时间)。

3.             检查分析结果队列。(执行需要时间很短,当前是配置了SingleThreadExecutor来执行检查阻塞队列的工作,同时获取到分析结果包以后立刻创建线程来执行输出任务)

4.             输出分析结果,如果输出成功,将分析过的日志文件在日志文件状态缓存中的状态更新为已分析。(执行时间根据输出情况来定,当前实现的是批量输出到数据库中,根据配置来批量提交入库,后续还会考虑实时统计到集中式Cache作为监控使用)。

5.             清理分析日志文件。(执行时间较短,设定了定时线程池执行清理任务,根据策略配置来执行清理和移动文件任务,并且清除在日志文件状态缓存中的信息)

 

根据上面的分解可以看到,其实在单线程工作的过程中,容易造成阻塞而影响性能的主要是读取,分析和写出这三个过程的协调,一个一个读取分析和写出,性能一定低于读取和分析并行工作,而分析完毕才写出,性能一定低于分析部分,写出部分。

同时由于细分各个任务,因此任务与任务之间的耦合度降低,可以运行期获取具体的任务实现配置,达到灵活配置的目的。

下面就具体的看看整个流程,以及其中的一些细节的说明,这里根据下图中的序号来逐一描述:

1.              配置了Schedule Executor来检查日志所属目录中的日志文件,Executor的线程池大小以及检查时间间隔都根据配置来设定。

Tip:定时任务可以设置delay时间,那么可以根据你的任务数量以及时间间隔来设定每一个任务的delay时间,均匀的将这些任务分布,提高效率。

 

2.              Read Schedule被执行时,将会去检查Analysis Log File State Concurrent Cache(也就是上面提到的ConcurrentMap)中是否存在此文件,如果不存在证明尚未分析,需要将其置入Cache,如果已经存在就去查询其他文件。Tip:这里用了一点小技巧,通常我们对于此类操作应该做两部分工作,get然后再put,但是这样可能就会在高并发的情况下出现问题,因为这两个操作不是一个原子操作。ConcurrentMap提供了putIfAbsent操作,这个操作意思就是说如果需要putkey没有存在于Map中,那么将会把key,value存入,并且返回null,如果已经存在了key那么就返回keymap已经对应的值。通过if (resources.putIfAbsent(filename, Constants.FILE_STATUS_ANALYSISING) == null)就可以把两个操作合并成为一个操作。

3.              日志读取的工作线程完成锁定文件以后,就将后续的工作交给Log Analysis Service Executor来创建分析任务异步执行分析操作,日志读取工作线程任务就此完成。

4.              Log Analysis Schedule是运行期装载具体的接口实现类(采用的就是类似于JAXP等框架使用的META-INF/services来读取工厂类,载入接口实现)。Analysis Schedule执行的主要任务就是分析文件,并且根据配置将分析结果拆分并串行的置入到Block Queue中,提供给输出线程使用。

5.              Receiver主要工作就是守候着Block Queue,当有数据结果产生就创建Write Schedule来异步执行输出。

6.              Log Writer Service Executor根据配置来决定内置线程池大小,同时在Receiver获取到数据包时产生Write Schedule来异步执行输出工作。

7.              Write ScheduleAnalysis Schedule一样可以运行期装载接口实现类,这样提供了灵活的输出策略配置。

Tips:在数据库输出的时候需要配置批量提交记录最大数,分批提交提高性能,也防止过大结果集批量提交问题。

8.              写出完成以后需要更新锁定文件的状态,标示成为已经分析成功。这里还遗留一点问题,在一个日志文件分包的过程中每一个包都回记录隶属于哪一个分析文件,文件的最后一个数据包将会被标示。在输出成功以后会去检查哪些包是文件最后数据包,更新此文件为已分析成功,如果出现异常,那么将会把这些文件状态清除,接受下一次的重新分析。这里一个文件部分包提交暂时没有做到事务一致,如果出现部分成功可能会重复分析和记录。

9.              最后就是Clean Schedule被定时执行,根据策略来删除或者移动已经被分析过的文件。

 

Tips:

ScheduledExecutorService内部可以配置线程池,当执行定时任务比较耗时,线程池中的线程都被占用的情况下,定时任务将不会准确的按时执行,因此设计过程中需要注意的是,定时任务一般是简短的工作任务,如果比较耗时,那么应该结合ScheduledExecutorServiceExecutorService,定时任务完成必要工作以后将耗时工作转交给ExecutorService创建的即时执行异步线程去处理,保证Schedule Executor正常工作。

2 流程结构设计

 

类图:

 

 

3 类图1

 

       上面的类图中主要描述的就是日志分析应用的三个主类:类似于控制台的LogAnalyzer,具体内部资源管理类,配置类。(T表示采用泛型)

 

4 类图2

       类图2主要就是描述了在整个应用中所有的被分解可并行的任务定义。ClearSchedule是用来在控制台输入stop停止日志分析的时候,做后续资源回收工作的任务。CleanSchedule是用来清除被分析后的日志文件任务。ConsumerSchedule是阻塞队列消费者任务。

       其他还有一些辅助工具类以及工厂类和定义类就不画了。

 

后话:

       做这个设计和开发的过程中又好好的实践了一些编程细节方面的内容,作为架构设计来说,需要多一些全局观和业务观,作为一个良好的开发者来说需要多实践,多了解一些细节,在不断学习和掌握各种大方向技术框架的同时,适当的了解一些细节也是一种很好的补充,同时也可以衍生思考。

       REST风格的服务结合云计算的思想,会被使用的更为广泛,而云计算其实就是一个问题分解和组合处理的过程,可以说是一种宏观的问题解决策略。高效解决问题,提供服务,通过组合体现业务最大价值,就是互联服务的最重要目的。

更多文章请访问:http://blog.csdn.net/cenwenchu79/

posted @ 2008-04-23 08:41 岑文初 阅读(2440) | 评论 (6)编辑 收藏

  
       假期结束,开始收心回来继续工作。晚上有一个项目要发布,公司的同事突然打手机给我,说ASF的文件解析又出了上次的问题,希望尽快解决。
 
问题描述:
 
上一次问题:
多台机器运行同一个分支的应用,但是有些机器正常,有一台机器始终在启动的时候报文件解析错误,从提示看来,主要是因为解析配置文件的时候校验dtd失效,这台机器无法连接外网。最后降低了我们内部的核心解析包,问题解决(或者让这台机器连接到外网)。(当时由于自己手头工作比较多,也没有在意,既然解决了就随之过去了)
 
此次问题:
       问题的提示和上次的类似,不过这次的机器时连接外网的。
 
问题查找:
       解析出错的文件是ASF(SCA的服务框架)的组件配置文件(composite文件),格式为xml的格式,解析方式是通过StAX标准来实现的。
       按照上一次的解决方法,我将内部的tuscany0.998降级到tuscany0.997,解析正常。看了一下我对于这两个版本升级作的修改,主要是支持了SCA框架中的Spring配置文件能够使用import的标签,内签多个标准的spring文件。
       跟踪代码内部发现,果然是在解析某几个spring的配置文件时出现了问题,比较了一下ASF的Spring(正常解析)和标准的Spring配置文件,差别主要是在关于Xml的校验申明的区别。ASF的Spring配置文件是由ASF Spring插件来自己解析的(采用Schema申明(固定的Target namingspace),因此早先所有的ASF的Spring我都要求大家采用Schema的校验申明),而对于原来不是ASF的spring都是采用dtd的校验方式申明(互相拷贝导致都是这样)。下面就是两种申明:
 
Schema:
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:sca="http://www.springframework.org/schema/sca"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/sca http://www.springframework.org/schema/sca/spring-sca.xsd">
 
Dtd:
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
 
       早先由于在0.997版本中没有支持import,因此也就不会去解析那些不是ASF的Spring文件,而现在因为需求支持了import所以需要解析那些原来不属于ASF的Spring的配置文件。因此降低版本不是解决问题的办法。
       进一步跟进问题,发现是在解析Dtd的申明时候出现问题,抛出异常说连接超时。通过IE访问了一下dtd的地址,的却也是有问题,无法连接。看来是Spring的dtd的服务器出现了问题,导致了我们解析文件时候校验无法正常,最终无法正常启动。
 
问题解决:
       这里先说一下最后解决的几个方案,后面会有一些详细的解释和说明。
 
1. 升级ASF的Spring插件包,去除对于Xml的格式校验。
XMLInputFactory xmlFactory = XMLInputFactory.newInstance();          
//add by wenchu.cenwc cancel support dtd check
xmlFactory.setProperty(XMLInputFactory.SUPPORT_DTD, "false");
 
2. 将Dtd的校验申明修改成为Schema的校验申明。
3. 建立公司的Xml校验服务器,控制管理dtd或者schema,将所有的xml Schema或者dtd申明指向该服务器。
 
问题延伸开来的思考:
就问题的解决方案来看这个问题的一些值得注意和思考的地方。
 
方案1:
当前对于XML解析来说,各种框架都已经统一的实现了StAX的标准,同时在jdk6得rt.jar中都已经将StAX API作为基础框架API纳入其内。而通常情况下,如果不配置是否校验Xml,那么都将默认会主动校验Xml,此时就会出现上面我所遇到的问题,如果当你依赖的Xml DTD 或者 Schema服务器出现问题,就会导致你本地应用可能受到影响。在Dtd的申明说明中,<!DOCTYPE rootElement PUBLIC "PublicIdentifier" "URIreference">红色部分说可以在网络出现问题或者网络速度很慢的时候被部分xml解析器替代URIreference使用,不过我这边没有成功过。我通过自己屏蔽网络连接来模拟环境的情况,都是无法通过的。(Schema比较奇怪,就算无法连接网络还是可以正常的,这个后续需要继续研究看看,或者有朋友对这个问题有了解请告知一下)
 
方案2:
       其实早在2002年就已经有将dtd替换成为schema的趋势了,两者的区别和优劣网上的文章介绍了很多了,这儿不再罗列,其实最更本一点就是schema就是用xml来校验xml,而dtd却采用了另一套规则来校验xml,其本身也是xml,扩展性,可读性,学习曲线等等都次与schema。除了遗留系统,我个人看不出还有什么必要去使用老的dtd来校验xml的格式。Spring的dtd服务的出现问题,也说明了其实对于dtd这种方式的校验,spring也已经不会保证几个9的稳定性。
       Xml常常会被作为数据承载中介,使双方能够在跨平台跨语言的情况下松耦合的交互信息,也是现在的SOA的实施基础。那么双方势必需要有协议和数据格式规范来约束,schema作为dtd的新一代替代者已经广为使用。另一方面,xsd也早已独立于wsdl作为数据描述和可重用的数据描述说明被采用到各种互联网应用。
       看看国外的Facebook,亚马逊,ebay等公司的REST风格的API,就可以清楚地了解到xsd十分适合作为轻量级的数据交互协议。在后续ASF中融入REST配置的实现中,也需要采用XSD这种Schema描述来实现数据交互解析。
       因此替换掉Dtd的配置是迟早要做的一件事情,所以迟作不如早作,更避免拷贝引起的问题放大效果(不过这个问题由于要考虑QA和业务组的项目经理的顾虑,因此我只能做到的是建议)。
 
方案3:
       看看Maven这些年这么火,其实在我们自己公司内部的antx同样都是在做一件事情,就是对于第三方的依赖包的版本控制。对于开源项目依赖的管理其实很重要,作的好项目能够很好的利用已有的成果,管理的不好就会被一些不太稳定的开源项目搞得头破血流。
       记得在上次三亚的聚会上谈到了对于Tuscany的依赖,其实对于这个项目来说,如果要作为成熟的产品来说,那么势必要获取一个版本然后就作为稳定的依赖,而不是一味的升级更新,由于我们产品的特殊性以及早期的Tuscany的不成熟,因此我们仅仅只是使用了Tuscany的最核心解析文件框架部分,其他的插件都采取自己设计或者在原有设计上优化和更新的做法。当然这不是说对于所有的第三方依赖都是采取这样的策略,其实如果不是基础框架设计,仅仅只是应用级别的使用,只需要拿来主义就完全可以了。
       回过头来看,大家现在对于类库的管理已经都很重视了,但是对于配置性的或者数据格式类的文件还没有引起足够的重视,不过看到很多朋友已经在本地作了这样的工作,不过就我来看,如果能够对dtd,schema作版本控制和服务器搭建,在长远来看还是有一定的好处的,只是说根据各自的需求来做这样的工作。
 
 
后续
       问题出现的当天,我其实晚上就一直比较担心,因为如果问题不解决,那么将会影响到很多项目组(框架被使用的越广泛,自己所要承担的责任越重大)。其实也是由于自己上次对于这个问题的不上心,导致了问题的再次出现。刨根问底是件好事,做我们这行的还是需要多问一些为什么,这样就会少不少危急时刻的怎么办了。
       很多时候为什么程序员自己喜欢什么都自己做,因为掌握在自己手中的事情总是能够解决,但是现在项目中对于第三方的依赖越来越多,在选择和控制上必须慎之又慎,有时候依赖也是双刃剑。
 
posted @ 2008-04-07 08:22 岑文初 阅读(1498) | 评论 (0)编辑 收藏

       两个半小时之后,我和BlueDavy大叔回到了杭州,一个忙碌而充实的周末就这么过去了。回到家已经1点多了,洗了个澡,精神又来了,想把这个充实的周末写下来,做个纪念。

缘起

       几周前,受到了普元的邀请,去三亚参加一个SOA的技术交流会,原本以为是因为在他们的论坛同时更新了一些文章所以被邀请一起去参加产品部活动,开始不是很想去,毕竟厂商出钱多半就是让你去听听他们的产品,一个周末飞来飞去的也累,但淘宝的BlueDavy大叔要去,顺便也叫我一起去热闹热闹,那么就决定去凑这个热闹。周五下班以后背了个包就直奔机场了,结果到了机场才发现办护照的时候身份证放在家里了,幸好机场还有个临时身份证明办理的便民业务,总算顺利的上了飞机。

人物

       第一天到场,第一个环节就是自我介绍,江湖中人就悉数登场了。Robbin,白衣,俞黎敏,小刚,杨戈,InfoQ中文站的主编霍泰稳和其他几个编辑,灰狐的主创:银狐999,程勇,普元的老大Chris以及普元的很多技术架构师牛人。陈勇的一句话就把我们这批人概括了一下,其实大家都是江湖中人,平时混各个堂口的,今天在普元召集的这么一次活动,有机会能够汇聚一起,谈天说地。

活动

       第一天上午有两个Topic关于SOA的市场价值,下午也有两个Topic关于SOA的企业架构,主讲的都是我们这些外部请来的人。上午下午有很多时间是自由讨论,每个人都有机会发表自己的意见,应该是算圆桌会议,所以你要发言随时随地都可以直接说,不需要顾忌什么,只要觉得要说,该说,就能说。这一天碰撞出不少火花。晚上边吃烧烤边聊,后面去沙滩上继续讨论技术圈的构建(不过这个到了最后也没有一个很好的解决方案)。

       第二天主要是普元分享了他们对于SOA技术的理解和具体的实现,从实际角度去展示了一个SOA的实实在在的成果。

收获

       其实这部分才是我要写得重点。一部分一部分来说吧。

首先是识人,这次在会上看到了很多都是自己曾经蛮佩服的国内开源技术论坛或者社区的创办者,其实从他们身上除了看到技术人员本身所特有的对于技术的追求更多的是对于新事物的理性的学习和分析。其实我一直觉得有一点对于一个架构师或者一个优秀的开发人员来说很重要的特质就是开放的去学习和接受新事物,有一个包容的心态去看待每一个陌生的新生事物,那么才能不断地进步。而排斥和固步自封,只能最后束缚了自己的脚步,慢慢的落后与他人。SOA这个概念提出来很多年了,炒作到实质性的转变也就在这一两年,具体的实践还需要后面几年的实施和验证。所以到场的每一位没有一个敢说自己完全了解SOA是什么,绝大部分都是抱着希望了解SOA实际上究竟是什么的想法而来。在讨论的过程中大家就很直白的说出了SOA是否还是一个虚概念,过去的EAI,BPM以及现在的EBI的区别。没有绝对的否定,只有探讨,学习和交流。我是第一认识他们,而他们之间已经相互很熟悉,但是和他们交流并不存在任何障碍,没有说牛人就是一个圈子,基本很难接触的那种感觉。我想也正是一种Open的心态才能让人进步得更快。

再则谈谈接触普元的朋友。和普元的朋友交流的机会不算很多,不过在第一天的休息的时候和几个主要的技术人员作了交流。因为其实我做SCA也还算比较早,所以对于SCA还是比较熟悉的,同时普元又是SCA中国区的推广者,所以和他们的架构师交流SCA很容易,不像在单位里面基本就是写好使用文档给开发者看,如果要谈具体的原理,基本没有人看过SCA规范,我是在SCA0.96版本上作了稳定版本的,现在解析使用Tuscany,再次组装是自己做的,一来早期的Tuscany对于两个级别的ServiceReference没有做级别控制,二来包括WS模块以及其他模块有很多的Bug,所以作了再次的开发和封装以适应业务开发的需求,其实在我们的研讨会上也说了,开源的缺点就是不稳定,不一定适应商业开发,如果商业开发的话,一般是需要稳定在某一个版本上。同时由于SCA本身的模块化特点,所以部分升级和开发十分方便。这些思想和概念以及遇到的问题和普元的开发团队遇到的一些问题都十分相似,特别是在WS上面,我一度也应为搞不定某些特殊的类型想去使用SDO,不过最后还是直接修复SCAWS组件搞定了,也就没有使用SDO。不过第二天的普元的服务管理和监控让我还是眼前一亮,这个对我来说可能也是后续在服务框架中需要考虑和搭建的,因为如果要采取Service API Mashup Platform,那么这部分内容势必是必须的。同时,普元的很多高手都是SCA组织的成员,这也是我蛮羡慕的,有机会还是要和他们多多学习和交流,虽然后续我的工作重点不是在SCA上了,不过ASF的工作以及SCA的持续学习也是自己“私活”之一。总的感觉来说,普元的技术人员虽然话不多,但是内功深厚,有很多可以去学习和切磋的。

最后就是关于SOA的一些感触。第二天中午最后一点时间是给大家讲关于这一天半的讨论和交流以后对于SOA的收获,由于时间关系,到我们的时候大家尽量简短发言,我也就说了自己的两点想法:1.先学在干在学在干。2.开发人员是我们的客户,不要让我们的客户因为使用我们的产品而痛苦不堪,应该让我们的客户在使用了产品以后提高了生产效率。第一点什么意思呢?在我做服务框架以前,先去研究过OSGI,然后又接触了SOA,最后接触到了SCA。花了1个多礼拜把SCA规范啃了几遍以后,找到了使用SCA实现服务框架的优势以后,就开始搞服务框架了,看了Tuscany的源码与设计思想,做了修改和封装,第一阶段的SCA服务框架就这么出来了。曾一度写了关于SCA如何让SOA落地的一些感想文章,但是后来自己都开始有些迷惑,究竟SOA是什么,仅仅就是用SCA去实现WS来互联互通么?这时候去看了很多关于SCASOA的资料和文章,突然发现又陷入到了一个开发者的角度去审视SOA的陷阱。很多人说SOA只是一种概念,没错,其实就是概念,那么这个概念有什么价值?其实这就是一个思考的过程。参加了BEA2007大会,虽然没有实质性的一些Topic让我有所收获,但是在主题演讲的时候让我突然开了窍。其实任何技术都是有延续性的EAISOA有什么区别,可能很快有人站出来给你一大堆的技术变革,告诉你区别大了去了,但其实SOA的真正价值不是在于说和过去的技术有什么区别,而是它的一种开放,协同的思想,这种思想其实更具各个行业来说都是不同的。这次去普元的交流会上,很明显的可以看出普元是定位在企业级应用的开放协同角度去考虑SOA的价值以及解决方案和实现手段。而我从大会回来,从新考虑了在阿里软件SAAS平台模式下我所关注的SOA的价值以及解决方案,就好比现在我一直在考虑的Service Mashup Platform,这是基于互联网应用的一种角度的思考。同样不同行业的人可能都会有不同的一种思考,但都是基于SOA的一种开放,协同的思想来更具具体的使用场景来考虑。所以如果你抛开业务场景去空想SOA,那么结果就会让自己陷入技术陷阱,用技术的不同来生硬的解释SOA,这种就好比过去讲的笑话,盲人摸象。第二点是我自己在推广我的ASF的一点经验,在普元演示产品的时候看到了他们的studio作的很好,但是对于单元测试以及部分组件的集成测试却做得还不是很够,同时studio是否能够让程序员有便利感(我们很多在场的写惯了程序的人都觉得程序员不写几行代码,就会没有创造感),这些可能会影响客户(开发人员)。其实在我推广ASF的时候有很多的架构师都反对,顶着压力死推,最后救了我的是那些开发人员,一份匿名使用反馈加上性能测试,功能测试,最终还是坚持了下来,并被逐步接受。那么对我来说我能够继续做好的就是加强文档的丰富性,测试的简单性,部属的方便性,以及开发配置的简便性(由于很多是配置型的文件,因此采用schema来做提示和交验我就觉得够了,至于studio是否需要,如果能够做得很完美当然最好了)。

最后其实对于这次活动来说其实我觉得就是一次技术人员的SOA,不同的公司,不同的职业,不同的职位,协同,开放的在一起发表自己的意见来互相学习和交流,本来也就是一种SOA。这儿感谢一下普元,活动本身来说虽然没有对SOA做出很明确的定义,但是其实让每一个人能够去思考,其实问题只有一个答案却可以有千千万万,每个人的角度不同看到的就有不同的内涵。就好比InfoQ主编说的每一个海滩上的沙子都有自己一个唯一标识,我们每一个江湖人士都有自己一个堂口,一个观点一个看法。

开放的心态去学习和交流,我要走路还有很长一段。

花絮:

       白衣不小心摔了一跤,眼镜掉到海里了,我们说是观音大士让他把眼镜作为纪念留在了海南。

       毕玄同学在上岸之前换鞋子的时候,由于不买一个小MM的贝壳,被小MM一直称作大叔,因此我们笑说毕玄就是到了大叔的年纪了。

欢迎访问我的blog:http://blog.csdn.net/cenwenchu79/
msn:cenwenchu_1979@hotmail.com

posted @ 2008-03-03 03:36 岑文初 阅读(1599) | 评论 (6)编辑 收藏

       SOA的基础技术实现方式中WebService占据了很重要的地位,通常我们提到WebService第一想法就是SOAP消息在各种传输协议上交互。近几年REST的思想伴随着SOA逐渐被大家接受,同时各大网站不断开放API提供给开发者,也激起了REST风格WebService的热潮。

       在收到新需求Email之前,我对REST的理解仅仅是通过半懂不懂的看了FieldingREST博士论文,说实话当时也就是希望了解这么一个新概念,对于其内部的思想只是很肤浅的了解了一下。

       ASF的最新需求就是可能需要实现REST风格的WebService集成,因此不得不好好的去看看REST的真正思想含义以及当前各大网站的设计方式。后面所要表述的也是我这个初学者的一些看法和观点,抛砖引玉,希望在我将REST融入到ASF之前能够获得更多的反馈和意见。

SOAP

       什么是SOAP,我想不用多说,google一把满眼都是。其实SOAP最早是针对RPC的一种解决方案,简单对象访问协议,很轻量,同时作为应用协议可以基于多种传输协议来传递消息(Http,SMTP等)。但是随着SOAP作为WebService的广泛应用,不断地增加附加的内容,使得现在开发人员觉得SOAP很重,使用门槛很高。在SOAP后续的发展过程中,WS-*一系列协议的制定,增加了SOAP的成熟度,也给SOAP增加了负担。

REST

       REST其实并不是什么协议也不是什么标准,而是将Http协议的设计初衷作了诠释,在Http协议被广泛利用的今天,越来越多的是将其作为传输协议,而非原先设计者所考虑的应用协议。SOAP类型的WebService就是最好的例子,SOAP消息完全就是将Http协议作为消息承载,以至于对于Http协议中的各种参数(例如编码,错误码等)都置之不顾。其实,最轻量级的应用协议就是Http协议。Http协议所抽象的get,post,put,delete就好比数据库中最基本的增删改查,而互联网上的各种资源就好比数据库中的记录(可能这么比喻不是很好),对于各种资源的操作最后总是能抽象成为这四种基本操作,在定义了定位资源的规则以后,对于资源的操作通过标准的Http协议就可以实现,开发者也会受益于这种轻量级的协议。

       自己理解的将REST的思想归结以下有如下几个关键点:

1.面向资源的接口设计

所有的接口设计都是针对资源来设计的,也就很类似于我们的面向对象和面向过程的设计区别,只不过现在将网络上的操作实体都作为资源来看待,同时URI的设计也是体现了对于资源的定位设计。后面会提到有一些网站的API设计说是REST设计,其实是RPC-REST的混合体,并非是REST的思想。

       2.抽象操作为基础的CRUD

       这点很简单,Http中的get,put,post,delete分别对应了read,update,create,delete四种操作,如果仅仅是作为对于资源的操作,抽象成为这四种已经足够了,但是对于现在的一些复杂的业务服务接口设计,可能这样的抽象未必能够满足。其实这也在后面的几个网站的API设计中暴露了这样的问题,如果要完全按照REST的思想来设计,那么适用的环境将会有限制,而非放之四海皆准的。      

       3Http是应用协议而非传输协议

       这点在后面各大网站的API分析中有很明显的体现,其实有些网站已经走到了SOAP的老路上,说是REST的理念设计,其实是作了一套私有的SOAP协议,因此称之为REST风格的自定义SOAP协议。

4.无状态,自包含

这点其实不仅仅是对于REST来说的,作为接口设计都需要能够做到这点,也是作为可扩展和高效性的最基本的保证,就算是使用SOAPWebService也是一样。

REST vs SOAP

成熟度:

SOAP虽然发展到现在已经脱离了初衷,但是对于异构环境服务发布和调用,以及厂商的支持都已经达到了较为成熟的情况。不同平台,开发语言之间通过SOAP来交互的web service都能够较好的互通(在部分复杂和特殊的参数和返回对象解析上,协议没有作很细致的规定,导致还是需要作部分修正)

REST国外很多大网站都发布了自己的开发API,很多都提供了SOAPREST两种Web Service,根据调查部分网站的REST风格的使用情况要高于SOAP。但是由于REST只是一种基于Http协议实现资源操作的思想,因此各个网站的REST实现都自有一套,在后面会讲诉各个大网站的REST API的风格。也正是因为这种各自实现的情况,在性能和可用性上会大大高于SOAP发布的web service,但统一通用方面远远不及SOAP。由于这些大网站的SP往往专注于此网站的API开发,因此通用性要求不高。

ASF上考虑发布REST风格的Web Service,可以参考几大网站的设计(兄弟公司的方案就是参考了类似于flickr的设计模式),但是由于没有类似于SOAP的权威性协议作为规范,REST实现的各种协议仅仅只能算是私有协议,当然需要遵循REST的思想,但是这样细节方面有太多没有约束的地方。REST日后的发展所走向规范也会直接影响到这部分的设计是否能够有很好的生命力。

总的来说SOAP在成熟度上优于REST

效率和易用性:

       SOAP协议对于消息体和消息头都有定义,同时消息头的可扩展性为各种互联网的标准提供了扩展的基础,WS-*系列就是较为成功的规范。但是也由于SOAP由于各种需求不断扩充其本身协议的内容,导致在SOAP处理方面的性能有所下降。同时在易用性方面以及学习成本上也有所增加。

       REST被人们的重视,其实很大一方面也是因为其高效以及简洁易用的特性。这种高效一方面源于其面向资源接口设计以及操作抽象简化了开发者的不良设计,同时也最大限度的利用了Http最初的应用协议设计理念。同时,在我看来REST还有一个很吸引开发者的就是能够很好的融合当前Web2.0的很多前端技术来提高开发效率。例如很多大型网站开放的REST风格的API都会有多种返回形式,除了传统的xml作为数据承载,还有(JSON,RSS,ATOM)等形式,这对很多网站前端开发人员来说就能够很好的mashup各种资源信息。

       因此在效率和易用性上来说,REST更胜一筹。

安全性:

       这点其实可以放入到成熟度中,不过在当前的互联网应用和平台开发设计过程中,安全已经被提到了很高的高度,特别是作为外部接口给第三方调用,安全性可能会高过业务逻辑本身。

       SOAP在安全方面是通过使用XML-SecurityXML-Signature两个规范组成了WS-Security来实现安全控制的,当前已经得到了各个厂商的支持,.net php java 都已经对其有了很好的支持(虽然在一些细节上还是有不兼容的问题,但是互通基本上是可以的)。

       REST没有任何规范对于安全方面作说明,同时现在开放REST风格API的网站主要分成两种,一种是自定义了安全信息封装在消息中(其实这和SOAP没有什么区别),另外一种就是靠硬件SSL来保障,但是这只能够保证点到点的安全,如果是需要多点传输的话SSL就无能为力了。安全这块其实也是一个很大的问题,今年在BEA峰会上看到有演示采用SAML2实现的网站间SSO,其实是直接采用了XML-SecurityXML-Signature,效率看起来也不是很高。未来REST规范化和通用化过程中的安全是否也会采用这两种规范,是未知的,但是加入的越多,REST失去它高效性的优势越多。

应用设计与改造:

       我们的系统要么就是已经有了那些需要被发布出去的服务,要么就是刚刚设计好的服务,但是开发人员的传统设计思想让REST的形式被接受还需要一点时间。同时在资源型数据服务接口设计上来说按照REST的思想来设计相对来说要容易一些,而对于一些复杂的服务接口来说,可能强要去按照REST的风格来设计会有些牵强。这一点其实可以看看各大网站的接口就可以知道,很多网站还要传入function的名称作为参数,这就明显已经违背了REST本身的设计思路。

       SOAP本身就是面向RPC来设计的,开发人员十分容易接受,所以不存在什么适应的过程。

总的来说,其实还是一个老观念,适合的才是最好的

       技术没有好坏,只有是不是合适,一种好的技术和思想被误用了,那么就会得到反效果。RESTSOAP各自都有自己的优点,同时如果在一些场景下如果去改造REST,其实就会走向SOAP(例如安全)。

       REST对于资源型服务接口来说很合适,同时特别适合对于效率要求很高,但是对于安全要求不高的场景。而SOAP的成熟性可以给需要提供给多开发语言的,对于安全性要求较高的接口设计带来便利。所以我觉得纯粹说什么设计模式将会占据主导地位没有什么意义,关键还是看应用场景。

       同时很重要一点就是不要扭曲了REST现在很多网站都跟风去开发REST风格的接口,其实都是在学其形,不知其心,最后弄得不伦不类,性能上不去,安全又保证不了,徒有一个看似象摸象样的皮囊。

REST设计的几种形式

参看了几个大型网站的REST风格的API设计,这里做一下大致的说明:

FaceBook:

请求消息:

       URI设计上采取了类似于REST的风格。例如对于friends的获取,就定义为friends.get,前面部分作为资源定义,后面是具体的操作,其他的API也是类似,资源+操作,因此就算使用httpget方法都可能作了update的操作,其实已经违背了REST的思想,但是说到,其实那么复杂的业务接口设计下,要通过RUCD来抽象所有的接口基本是不实际的。在URI定义好以后,还有详细的参数定义,包括类型以及是否必选。

响应消息:

       有多种方式,XML,JSONXMLXSD作为参考。有点类似于没有HeadSOAP,只不过这里将原来可以定义在WSDL中的XSD抽取出来了。

Flickr:

       请求消息:

       http://api.flickr.com/services/rest/?method=flickr.test.echo&name=value

       这里就可以很明显看出它所定制的REST请求其实和RPC没有什么太大的区别。

       消息返回:

正确处理返回

<?xml version="1.0" encoding="utf-8" ?>

<rsp stat="ok">

         [xml-payload-here]

</rsp>

错误处理返回

<?xml version="1.0" encoding="utf-8" ?>

<rsp stat="fail">

         <err code="[error-code]" msg="[error-message]" />

</rsp>

       根据返回可以看出已经违背了REST的思想,还是把Http协议作为传输承载协议,并没有真正意义上使用Http协议作为资源访问和操作协议。

       总的来说,只是形式上去模仿REST,自己搞了一套私有协议。

Ebay

       请求消息:

       采用xml作为承载,类似于SOAP,不过去除SOAP消息的封装和包头,同时在请求中附加了认证和警告级别等附加信息。

       消息返回:

       类似于SOAP消息,不过删除了SOAP的封装和包头,同时在返回结构中增加了消息处理结果以及版本等附加信息。

       这个很类似于当前Axis2框架的做法,将SOAP精简,同时根据自身需求丰富了安全,事务等协议内容。

Yahoo Maps

       请求消息:

       http://local.yahooapis.com/MapsService/V1/geocode?appid=YahooDemo&street=701+First+Ave&city=Sunnyvale&state=CA

       采用REST推荐的方式,URI+Parameters

       返回消息:

<?xml version="1.0" encoding="UTF-8"?>

<ResultSet xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xmlns="urn:yahoo:maps"

xsi:schemaLocation="urn:yahoo:maps http://local.yahooapis.com/MapsService/V1/GeocodeResponse.xsd">

 <Result precision="address">

    <Latitude>37.416384</Latitude>

    <Longitude>-122.024853</Longitude>

    <Address>701 FIRST AVE</Address>

    <City>SUNNYVALE</City>

    <State>CA</State>

    <Zip>94089-1019</Zip>

    <Country>US</Country>

 </Result>

</ResultSet>

SOAP的精简xml返回,其他信息,例如出错码等信息由Http协议头来承载。

YouTube

请求消息:

http://www.youtube.com/api2_rest?method=youtube.users.get_profile&dev_id=YOUR_DEV_ID&user=YOUTUBE_USER_NAME

可以看到对于资源操作的URI定义也是参数的一部分。

返回消息:

<?xml version="1.0" encoding="utf-8"?>

<ut_response status="ok">

    <user_profile>

        <first_name>YouTube</first_name>

        <last_name>User</last_name>

        <about_me>YouTube rocks!!</about_me>

        <age>30</age>

        <video_upload_count>7</video_upload_count>

    </user_profile>

</ut_response>

       自定义的类SOAP消息。

Amazon

       请求消息:

       https://Amazon FPS web service end point/?AWSAccessKeyId=Your AWSAccessKeyId

      &Timestamp=[Current timestamp] &Signature=[Signature calculated from hash of Action and Timestamp]

      &SignatureVersion=[Signature calculated from hash of Action and Timestamp]

      &Version=[Version of the WSDL specified in YYYY-MM-DD format] &Action=[Name of the API]

      &parameter1=[Value of the API parameter1] &parameter2=[Value of the API parameter2]

      &...[API parameters and their values]

       返回消息:

       类似于SOAP的自有协议,消息体中包含了消息状态等附加信息。

总结:

       看了上面那么多网站的设计,总结一下主要有这么几种设计方式。

请求消息设计:

1. 基本符合REST标准方式:资源URI定义(资源操作)+参数。这类设计如果滥用get去处理其他类型的操作,那么和2无异。

2. REST风格非REST思想:资源URI定义+参数(包含操作方法名)。其实就是RPCREST跟风。

3. 类似于SOAP消息,自定义协议,以xml作为承载。(可扩展,例如鉴权,访问控制等),不过那就好比自己定义了一套SOAPSOAP extends。大型的有实力的网站有的采取此种做法。

响应消息设计:

1.       REST标准方式,将Resource State传输返回给客户端,Http消息作为应用协议而非传输协议

2.       XML作为消息承载体,Http作为消息传输协议,处理状态自包含。

3.       自定义消息格式,类似于SOAP,提供可扩展部分。

作为遵循REST的理念来看我的选择是响应1和请求1的设计。

RESTASF的集成

ASF要集成REST就现在来看有两种比较合适的方法。

一.就是采用Axis2REST实现,这种方式的好处就是开发周期短,容易集成,但是请求和响应的格式无法改变,资源URI设计受限,Axis2REST其实就是将SOAP消息精简,请求的时候删除了SOAP的头,响应的时候仅仅返回资源信息,如果提供xsd就可以被各种客户端所解析。并非真正的REST

二.就是采用Restlet开源框架,将Restlet开源框架集成到ASF中,由于Restlet本身就是可内嵌的应用框架,因此集成不成问题,同时Restlet框架只是API结构框架,因此实现和定义完全分开,集成Restlet以后可以自己实现其中的解析引擎也可以采用默认提供的引擎,同时对于内嵌Jetty等多种开源项目的支持,将更多优势融入到了Rest中。看了一下国内也有很多朋友已经关注Restlet开源项目,看了它的架构设计,个人觉得还是比较灵活和紧凑的。

题外话

       在写这篇文章以前写了一篇调研报告群发给各个架构师们参考,期待反馈。下午正好和我们的首席架构师聊了一会儿。其实我和他的感觉是一样的,REST是否真的在我们现有的服务框架中需要集成,理解了REST的思想再去看应用场景,那么可以发现如果要完全遵循REST的设计理念来设计接口的话,那么强要去改变现有已经存在的或者还未开发的接口就会落入为了技术而技术,为了潮流而跟风的近地。当然并不否认REST的好,其实我们兄弟公司的一些业务场景有部分的接口十分合适这类设计,面向资源,高效,简洁,易用都能够体现出它的价值。我们将会和我们的兄弟公司合作,也会参考他们的设计理念,在参考当前各个网站的实现情况下,部分的采用这类形式的发布,提供给第三方的ISV,无疑是我现在把REST融入到ASF中最好的理由。

       有了需求去做才不会陷入为了技术而技术,毕竟技术是由商业价值驱动的,同样社会上充斥着各种技术的鼓吹,如果稍不留神就会陷入跟风的潮流中。

欢迎访问我csdn的blog:http://blog.csdn.net/cenwenchu79/,也欢迎和我做交流,msn:cenwenchu_1979@hotmail.com,关于SOA,REST,SAAS。

posted @ 2008-02-22 08:45 岑文初 阅读(4280) | 评论 (1)编辑 收藏

 

JVM优化配置

       这里首先要说明的是这里提到的JVMSunHotSpot JVM 5和以上的版本。性能优化在应用方面可以有很多手段,包括Cache,多线程,各种算法等等。通常情况下是不建议在没有任何统计和分析的情况下去手动配置JVM的参数来调整性能,因为在JVM 5以上已经作了根据机器和OS的情况自动配置合适参数的算法,基本能够满足大部分的情况,当然这种自动适配只是一种通用的方式,如果说真的要达到最优,那么还是需要根据实际的使用情况来手动的配置各种参数设置,提高性能。

       JVM能够对性能产生影响的最大部分就是对于内存的管理。从jdk 1.5以后内存管理和分配有了很多的改善和提高。

内存分配以及管理的几个基本概念和参数说明:

Java Hotspot Mode

server client两种模式,如果不配置,JVM会根据应用服务器硬件配置自动选择模式,server模式启动比较慢,但是运行期速度得到了优化,client启动比较快,但是运行期响应没有server模式的优化,适合于个人PC的服务开发和测试。

Garbage Collector Policy

       Jdk 1.5的时候已经提供了三种GC,除了原来提供的串行GCSerialGC)以外,还提供了两种新的GCParallelGCConcMarkSweepGCParallelGC采用了多线程并行管理和回收垃圾对象,提高了回收效率,提高了服务器的吞吐量,适合于多处理器的服务器。ConcMarkSweepGC采用的是并发方式来管理和回收垃圾对象,降低垃圾回收产生的响应暂停时间。这里说一下并发和并行的区别,并发指的是多个进程并行执行垃圾回收,那么可以很好的利用多处理器,而并行指的是应用程序不需要暂停可以和垃圾回收线程并发工作。串行GC适合小型应用和单处理器系统(无需多线程交互,效率比较高),后两者适合大型系统。

       使用方式就是在参数配置中增加-XX:+UseParallelGC等方式来设置。

       对于这部分的配置在网上有很多的实例可以参考,不过最终采用哪一种GC还是要根据具体的情况来分析和选择。

Heap

       OOM的各种经历已经让每一个架构师开发人员看到了了解Heap的重要性。OOM已经是Heap的临界点,不得不引起注意,然而Heap对于性能的潜在影响并未被引起重视,不过和GC配置一样,在没有对使用情况作仔细分析和研究的情况下,贸然的去修改Heap配置,可能适得其反,这里就来看一下Heap的一些概念和对于性能的影响。

       我们的应用所能够得到的最大的Heap受三部分因素的制约:数据处理模型(32位或者64位操作系统),系统地虚拟内存总数和系统的物理内存总数。首先Heap的大小不能超过不同操作系统的进程寻址范围,当前大部分系统最高限度是4GWindows通常是2GLinux通常是3G。系统的虚拟内存也是分配的依据,首先是不能超过,然后由于操作系统支持硬盘来做部分的虚拟内存,如果设置过大,那么对于应用响应来说势必有影响。再则就是要考虑同一台服务器上运行多个Java虚拟机所消耗的资源总合也不能超过可用资源。就和前面OOM分析中的一样,其实由于OS的数据处理模型的限制,机器本身的硬件内存资源和虚拟内存资源并不一定会匹配,那么在有限的资源下如何调整好资源分配,对于应用来说尤为重要。

关于Heap的几个参数设置:

       说了Heap的有限资源问题以后,就来看看如何通过配置去改变JVM对于Heap的分配。下面所说的主要是对于Java Heap的分配,那么在申请了Java Heap以后,剩下的可用资源就会被使用到Native Heap

       Xms: java heap初始化时的大小。默认情况是机器物理内存的1/64。这个主要是根据应用启动时消耗的资源决定,分配少了申请起来会降低启动速度,分配多了也浪费。

       Xmx:java heap的最大值,默认是机器物理内存的1/4,最大也就到1G。这个值决定了最多可用的Java Heap Memory,分配过少就会在应用需要大量内存作缓存或者零时对象时出现OOM的问题,如果分配过大,那么就会产生上文提到的第二类OOM。所以如何配置还是根据运行过程中的分析和计算来确定,如果不能确定还是采用默认的配置。

       Xmn:java heap新生代的空间大小。在GC模型中,根据对象的生命周期的长短,产生了内存分代的设计:青年代(内部也分成三部分,类似于整体划分的作用,可以通过配置来设置比例),老年代,持久代。每一代的管理和回收策略都不相同,最为活跃的就是青年代,同时这部分的内存分配和管理效率也是最高。通常情况下,对于内存的申请优先在新生代中申请,当内存不够时会整理新生代,当整理以后还是不能满足申请的内存,就会向老年代移动一些生命周期较长的对象。这种整理和移动会消耗资源,同时降低系统运行响应能力,因此如果青年代设置的过小,就会频繁的整理和移动,对性能造成影响。那是否把年青代设置的越大越好,其实不然,年青代采用的是复制搜集算法,这种算法必须停止所有应用程序线程,服务器线程切换时间就会成为应用响应的瓶颈(当然永远不用收集那么就不存在这个问题)。老年代采用的是串行标记收集的方式,并发收集可以减少对于应用的影响。

       Xss:线程堆栈最大值。允许更多的虚拟内存空间地址被Java Heap使用。

以下是sun公司的性能优化白皮书中提到的几个例子:

1.对于吞吐量的调优。机器配置:4G的内存,32个线程并发能力。

java -Xmx3800m -Xms3800m -Xmn2g -Xss128k -XX:+UseParallelGC -XX:ParallelGCThreads=20

       -Xmx3800m -Xms3800m 配置了最大Java Heap来充分利用系统内存。

       -Xmn2g 创建足够大的青年代(可以并行被回收)充分利用系统内存,防止将短期对象复制到老年代。

    -Xss128 减少默认最大的线程栈大小,提供更多的处理虚拟内存地址空间被进程使用。

    -XX:+UseParallelGC 采用并行垃圾收集器对年青代的内存进行收集,提高效率。

    -XX:ParallelGCThreads=20 减少垃圾收集线程,默认是和服务器可支持的线程最大并发数相同,往往不需要配置到最大值。

 

2.尝试采用对老年代并行收集

java -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:+UseParallelGC -XX:ParallelGCThreads=20 -XX:+UseParallelOldGC

-Xmx3550m -Xms3550m 内存分配被减小,因为ParallelOldGC会增加对于Native Heap的需求,因此需要减小Java Heap来满足需求。

-XX:+UseParallelOldGC 采用对于老年代并发收集的策略,可以提高收集效率。

 

3.提高吞吐量,减少应用停顿时间

java -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:ParallelGCThreads=20 -XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:SurvivorRatio=8 -XX:TargetSurvivorRatio=90 -XX:MaxTenuringThreshold=31

 

-XX:+UseConcMarkSweepGC -XX:+UseParNewGC 选择了并发标记交换收集器,它可以并发执行收集操作,降低应用停止时间,同时它也是并行处理模式,可以有效地利用多处理器的系统的多进程处理。

-XX:SurvivorRatio=8 -XX:MaxTenuringThreshold=31 表示在青年代中Eden和Survivor比例,设置增加了Survivor的大小,越大的survivor空间可以允许短期对象尽量在年青代消亡。

 

-XX:TargetSurvivorRatio=90 允许90%的空间被占用,超过默认的50%,提高对于survivor的使用率。

 

类似的例子网上很多,这儿就不在列下来了,最终是否采取自己配置来替换默认配置还是要根据虚拟机的使用情况来分析和配置。

posted @ 2008-01-23 16:16 岑文初 阅读(2954) | 评论 (2)编辑 收藏

       OOM这个缩写就是Java程序开发过程中让人最头痛的问题:Out of Memory。在很多开发人员的开发过程中,或多或少的都会遇到这类问题,这类问题定位比较困难,往往需要根据经验来判断可能出现问题的代码。原因主要是两个:对象没有被释放(多种情况引起,往往是比较隐蔽的引用导致被Hold而无法被回收)。另一种就是真的Memory不够用了,需要增加JVMHeap来满足应用程序的需求。最近有同事发的关于解决OOM的问题,让我了解了原来OOM除了在JVM Heap不够时会发生,在Native Heap不够的时候也会发生,同时JVM HeapNative Heap存在着相互影响和平衡的关系,因此就仔细的去看了关于OOMJVM配置优化的内容。

OOM

       在其他语言类似于C,Delphi等等由于内存都是由自己分配和管理,因此内存泄露的问题比较常见,同时也是很头痛的一件事情。而Java的对象生命周期管理都是JVM来做的,简化了开发人员的非业务逻辑的处理,但是这种自动管理回收机制也是基于一些规则的,而违背了这些规则的时候,就会造成所谓的“Memory Leak”。

OOM(Java Heap)

       错误提示:java.lang.OutOfMemoryError

这类OOM是由于JVM分配的给应用的Heap Memory已经被耗尽,可能是因为应用在高负荷的情况下的却需要很大的内存,因此可以通过修改JVM参数来增加Java Heap Memory(不过也不能无限制增加,后面那种OOM有可能就是因为这个原因而产生)。另一种情况是因为应用程序使用对象或者资源没有释放,导致内存消耗持续增加,最后出现OOM,这类问题引起的原因往往是应用已不需要的对象还被其他有效对象所引用,那么就无法释放,可能是业务代码逻辑造成的(异常处理不够例如IO等资源),也可能是对于第三方开源项目中资源释放了解不够导致使用以后资源没有释放(例如JDBCResultSet等)。

       几个容易出现问题的场景:

       1.应用的缓存或者Collection:如果应用要缓存Java对象或者是在一个Collection中保存对象,那么就要确定是否会有大量的对象存入,要做保护,以防止在大数据量下大量内存被消耗,同时要保证Cache的大小不会无限制增加。

       2.生命周期较长的对象:尽量简短对象的生命周期,现在采用对象的创建释放代价已经很低,同时作了很好的优化,要比创建一个对象长期反复使用要好。如果能够设置超时的情景下,尽量设置超时。

       3.类似于JDBCConnection Pool,在使用Pool中的对象以后需要释放并返回,不然就会造成Pool的不断增大,在其他Pool中使用也是一样。同样ResultSetIO这类资源的释放都需要注意。

       解决的方法就是查找错误或者是增加Java Heap Memory。对于此类问题检测工具相当多,这里就不做介绍了。      

OOM(Native Heap)

错误提示:requested XXXX bytes for ChunkPool::allocate. Out of swap space

       Native Heap MemoryJVM内部使用的Memory,这部分的Memory可以通过JDK提供的JNI的方式去访问,这部分Memory效率很高,但是管理需要自己去做,如果没有把握最好不要使用,以防出现内存泄露问题。JVM 使用Native Heap Memory用来优化代码载入(JTI代码生成),临时对象空间申请,以及JVM内部的一些操作。这次同事在压力测试中遇到的问题就是这类OOM,也就是这类Memory耗尽。同样这类OOM产生的问题也是分成正常使用耗尽和无释放资源耗尽两类。无释放资源耗尽很多时候不是程序员自身的原因,可能是引用的第三方包的缺陷,例如很多人遇到的Oracle 9 JDBC驱动在低版本中有内存泄露的问题。要确定这类问题,就需要去观察Native Heap Memory的增长和使用情况,在服务器应用起来以后,运行一段时间后JVM对于Native Heap Memory的使用会达到一个稳定的阶段,此时可以看看什么操作对于Native Heap Memory操作频繁,而且使得Native Heap Memory增长,对于Native Heap Memory的情况我还没有找到办法去检测,现在能够看到的就是为JVM启动时候增加-verbose:jni参数来观察对于Native Heap Memory的操作。另一种情况就是正常消耗Native Heap Memory,对于Native Heap Memory的使用主要取决于JVM代码生成,线程创建,用于优化的临时代码和对象产生。当正常耗尽Native Heap Memory时,那么就需要增加Native Heap Memory,此时就会和我们前面提到增加java Heap Memory的情况出现矛盾。

应用内存组合

       对于应用来说,可分配的内存受到OS的限制,不同的OS对进程所能访问虚拟内存地址区间直接影响对于应用内存的分配,32位的操作系统通常最大支持4G的内存寻址,而Linux一般为3GWindows2G。然而这些大小的内存并不会全部给JVMJava Heap使用,它主要会分成三部分:Java HeapNative Heap,载入资源和类库等所占用的内存。那么由此可见,Native Heap Java Heap大小配置是相互制约的,哪一部分分配多了都可能会影响到另外一部分的正常工作,因此如果通过命令行去配置,那么需要确切的了解应用使用情况,否则采用默认配置自动监测会更好的优化应用使用情况。

       同样要注意的就是进程的虚拟内存和机器的实际内存还是有区别的,对于机器来说实际内存以及硬盘提供的虚拟内存都是提供给机器上所有进程使用的,因此在设置JVM参数时,它的虚拟内存绝对不应该超过实际内存的大小。

待续……


JVM
优化配置


更多内容可访问:http://blog.csdn.net/cenwenchu79/
posted @ 2008-01-22 16:54 岑文初 阅读(2820) | 评论 (2)编辑 收藏