paulwong

#

从程序员到技术总监,分享10年开发经验

在中国有很多人都认为IT行为是吃青春饭的,如果过了30岁就很难有机会再发展下去!其实现实并不是这样子的,在下从事.NET及JAVA方面的开发的也有10年的时间了,在这里在下想凭借自己的亲身经历,与大家一起探讨一下。
明确入行的目的

很多人干IT这一行都冲着“收入高”这一点的,因为只要学会一点HTML, DIV+CSS,要做一个页面开发人员并不是一件难事,而且做一个页面开发人员更容易找到工作,收入比普通的工作还要高一些,所以成为了很多高校毕业生的选择。如果您只是抱着这样一个心态来入行的话,那阁下可真的要小心了。因为干IT这一行竞争本来就比较激烈,特别是页面设计这方面,能够开发的人很多,所以为了节省成本,大部分公司都会在需要的时候才招聘这类人员;在没有订单的时候,一些小公司还可能找各类的借口或者以降薪的手段去开除这类员工。而在招聘信息上常常会看到“招聘页面设计师,条件:30岁以下……欢迎应届毕业生前来应聘”这样一条,因为这一类工员对技术上的要求并不高,找应届生可以节约成本。所以在下觉得“IT行业是吃青春饭的”这句话只是对着以上这类人所说的,如果阁下缺乏“进取之心”,而只抱着“收入高,容易找工作”这样的态度而入行,那“IT行业是吃青春饭”将会应验了。

选择合适的工具

JAVA、C#、PHP、C++、VB……10多种热门的开发语言,哪一种最有发展潜力呢?其实开发语言只不过是一个工具,“与其分散进攻,不如全力一击”,无论是哪一种开发语言,只要您全力地去学习,到有了一定的熟悉程度的时候,要学习另一种的语言也是轻而易举的事情。开发语言主要分为三大类:

1. 网络开发

现在网络已经成为世界通讯的一座桥梁,好像Javascript、PHP、Ruby这几类开发语言大部分是用作网络开发方面。

2. 企业软件开发

JAVA、C#、VB这几类开发语言都实现了面向对象开发的目标,更多时候用于企业系统的开发。

3. 系统软件

C语言、C++、Objective-C这些软件更多是用在系统软件开发,嵌入式开发的方面。

当然,这分类不是绝对,像JAVA、C#、VB很多时候也用于动态网站的开发。在很开发项目都会使用集成开发的方式,同一个项目里面使用多种开发语言,各展所长,同步开发。但所以在刚入门的时候,建议您先为自己选择一种合适的开发工具,“专注地投入学习,全力一击”。

明确发展方向

当您对某种开发语言已经有了一定的了解,开始觉得自己如同“行尸走肉”,成为一个开发工具的时候,那您就应该要明确一下自己的发展方向了。

平常在公司,您可以看到做UI层的开发人员大多数都有20多岁,他们充满干劲,而且没有家庭负担,在两年前ASP.NET MVC 、Silverlight等刚出现的时候,他们可以在晚上回家的时候买几本书或者直接上网看看,研究三五个星期以后,对需要用到的技术就已经有一定的了解了。而年过30的人多数是已经成家了,他们每天9:00点上班唯一的希望就是快些到6:00点,能回家吃饭。吃完饭只想陪孩子玩一下,看看孩子的功课,对新增的技术缺乏了学习的欲望。所以很多接近30岁的程序员都有着一种逼迫感(包括30岁时候的我自己),再过几年应该怎么办?这时候,您就更应该明确一下目标,努力向自己的发展方向前进了。归纳一下,可从下面几项里选择适合自己的一条道路:

1. 从技术向业务过渡

在国外,很多发达国家都很重视人才,一个高级的程序员与一个Project Manager收入相差一般不超过15%。但中国是世界上人口最多的国家,国内人才众多,所以人才滥用的情况经常可以看到。一个小公司的开发部里面经常会见到新面孔,但PM却不会常换。因为做老板的对技术是一窍不通,依他们看来只到拉住PM的心,那技术方面方面就能搞得定,至于技术部要换人,他们根本不需要费力气去管。所以从一个技术员过渡到一个PM是向前发展的一个选择,但开发人员也需要知道,要成为一个PM不单单是使用技术,而更重要的是对管理方面的认识。一个PM主要的工作是组织团队,控制成本,管理业务,控制项目进度,与客户进行沟通,协调工作,定期进行工作报告等。所以要成为一个成功的PM更要重视组织能力,PM必须能提高团队的积极性,发挥团队所长,在有限的开发资源前提下为公司得到最大程度上的利润。成为一个PM后,通常不需要直接接触技术开发,而着重管理的是业务发展,但PM对技术也需要有一定的了解(在下曾经为PM对技术了解的必要性写过一篇文章,得到很多支持但也惹来不少的争议)。在这里我还是要强调自己的观点:要成为一个成功的PM最重视的是管理能力,但对技术也应该有足够的了解,因为这是与团队成员沟通的桥梁,只有这样才能与整个团队的成员有着紧密的结合,让团队成员感觉到他们自己存在的意义,从而调动团队的积极性,而不是漠视技术人员的存在。技术并非成为一个成功PM的充分条件但却是必要条件!

2. 从程序员向技术管理发展

其实一个Team Leader的职责与Project Manager相像,但Team Leader更着重于技术开发方面,通常一个大型项目都会有一两个开发团队由Team Leader带领,负责开发核心部分,而其它部分分派给不同开发小组或者分派给外包公司。在网上常看到几句话,贴切地形容了PM与TL的区别:“技术人员乐于被领导;但他们不喜欢被管理,不喜欢像牛一样被驱赶或指挥。管理者强迫人们服从他们的命令,而领导者则会带领他们一起工作。管理是客观的,没有个人感情因素,它假定被管理者没有思想和感受,被告知要做什么和该如何做。领导是引领、引导,它激励人们达成目标。领导力是带有强烈个人感情色彩的,它不是你能命令的,也不是你能测量评估和测试的。”

无论是PM与TL,对业务与技术都要有深入的了解,只是PM更侧重于业务的管理,盈利的多少,风险的大小等等,而TL则侧重于项目的成本,开发的难度,软件的架构等技术方面的问题。在某些人眼中,技术与管理就像鱼与熊掌,不可兼得,但依在下看来,两者却是秤不离砣,密不可分。只要及时提升自己对技术与管理的认识,不断地向深一层发展,要从程序员提升到技术管理人员只是时间的问题。打个比方,一个普通的.NET程序员,开始可能限制于ASP.NET的页面开发,但一旦他有了发展之心,他自然会对ASP.NET MVC、Silverlight、WinForm、WPF这些UI的开发手法感到兴趣,学习不需要多少时间,他可能就会认识这些UI开发只不过是一些工具,其实在开发原理上没什么区别。接着他就会向深一层的通讯模式进行了解,认识TCP/IP、Web Service、WCF、Remoting这些常用到的通讯方式,这时候他可能已经感觉到自己对开发技术有了进一步的了解。进而向工作流、设计模式、面向对象设计、领域驱动设计、面向服务开发等高层次进发,最后成为技术的领导者。上面只是一个比喻,但要注意的是,在学习的时期必须注意的是与同事之间沟通,很多的开发人员喜欢独来独往,开发的项目总想一个人搞定,不受外界的干扰。但要明白,就算你有天大的本事,一项大型的项目也不可能由你一个人全扛着。所以团队的合作性与同事间的沟通是必要的,这也是成功一个TL的必要条件。

3. 单方面向技术发展

能成功进行技术开发的尖端人才,这是在下最向往的工作,却也没本事登上这个位置。很多从事开发的人都会认为,业务总会带着“金钱的味道”,老板从来不管开发是否合符开发原则,是否经过必要测试,他们只会在客户面前无尽地吹嘘,项目到期能成功交货,只要不出什么大问题那这个项目就算成功了。其实我们也要明白:开发项目最终目标是为了赚钱,在开发过程中对项目成本的限制和效率的控制这也是必须,所以这才需要管理人员对项目进行管理。但开发人员也很想避开这“金钱的尘嚣”,全心投入到技术的世界当中。所以对技术有着浓厚兴趣的人,往往会深入地研究某一项技术,成为技术上的精英。但在这里说一句令人心淡的话:中国已经属于是世界上第二大经济体同盟国,但国民生产总值主要来源于第三方加工产业方面。中国可以说是人才济济,但却在高新产业上却比发达国家落后。这几年的确看到我们国家在高新科技上有着质的飞跃,但跟欧美发达国家还有着一段距离。所以想在中国成为尖端技术的人才,无可否定比在国外要难。依在下看来,要想成为尖端的开发者,必须对C、C++、汇编语言、嵌入式开发、Windows API、Linux API这些底层技术有着深入的了解。要知道解JAVA、.NET……等这些之所以称为高级开发语言,并不是指它们比C、C++、汇编语言更高级,而是指它们封装了C、C++等等的功能,更适合用于企业软件的开发,使开发变得简单。但如果要开发一些底层的软件,大型的系统的时候,就必须用到C、C++、汇编等开发语言,这是成功尖端人才的一个条件。

确定未来的目标

人是从历练中成长的,古人云:三十而立,形容的不是一个人的社会地位,经济来源,而是形容一个人对未来的目标,对人生的意向。要成为一个成功人,就应该早日为自己定下长期的发展目标,作为一个开发者也当如此。随着人的性格,取向各有不同,大家为自己所选择的路也有不同:

1. 自立门户,勇敢创业

快30岁了,很多人会认为要想真正赚得了钱,就应该自立门户,为自己创业建立一个基础。像北京、上海、广州这些一级城市,要买房子,一手楼基本要在2万~4万元/平方米左右,而在一家普通的IT公司当上一个项目经理,基本收入一般都在1.5万~3万之间(除非在大型的跨国企业内工作,那另当别论),要买一间100平方米左右的房子,就算不吃不喝也几乎要10年的年薪,所以选择自主创业,是很多IT开发人员的一个未来目标,想要达到这个目标,就应该更多地把业务作为重点。不可否认的一件事,在中国社会里很多时候讲的是“关系”,即使这30年的改革开放使中国的经济蓬勃地发展起来,但几千年来留下的歪风还是不能完全的磨灭。所以想要创业的人事建议你要多跟客户打好关系,与合作伙伴保持互利互动的模式,这将有利于日后事业的发展。

2. 急流勇退,退居二线

这也是不少人的选择。很多人在有了家庭以后,感觉到压力太大,人的一生并非只有事业,他们想把更多时间用于对亲人的照顾,对孩子的关心上。所以很多人会选择一份像系统分析、系统维护、高校教师、专业学院讲师这一类的工作。收入稳定,而且往往没有一线开发人员那么大的压力。

3. 不懈努力,更进一步

无论你是一个Project Manager或者是Team Leader,如果你想继续晋升一级,那还是会两极分化的。从一个PM到一间公司的管理层,那所面对的事件会有很多变化。一个公司的总经理,要管理的不再是一到两个项目的成本,而是整个部门的运作,整间公司的业务流程,所以要肩负的任务会更重。在下曾经有一位上司彭博士,他是企业的最高领导人,年薪超过三百万,而且在报纸杂志上也曾经亮过相。平常只会在某些会议上轻轻地亮下相,说两句讲词,平常的公司运作与业务管理都不需要他直接执行。这并不是说一个作为管理层很清闲,因为他们要面对的是更多的社会关系,与公司合作企业的联系上。这跟一个PM的工作有很大的区别,所以要从一个PM晋升到管理层,那可是要付出更多的努力与汗水。

如果要从Team Leader上升为一个技术总监,那工作的方向也有所改变。像之前所说:一个TL可能更重视的是技术层面,讲求与团队之间的互动合作性,更注重的是开发的完善。而一个技术总监就无需要直接参加某个项目的开发,而注意的是开发的效率与成果,如何合理使用有限的开发资源,控制开发的风险和可能带来的效果。

发展感受

经历了8年多时间,在下从一个程序员到一个项目经理,之间经过很多的曲折,但因为每一个人的际遇都有所不同,所走的路也有不同,正所谓条条大路通罗马,成功的路不止一条,在下也不想令各位误解,而只想为大家说一下我的发展方向。如果您是一位开发人员,“程序员->架构师->Team Leader(Project Manager)->技术总监”是一条不错路,这也是在下选择的路。在我国,想要进一步提升自己,无论你想是以技术为重点还是以业务为重点,都离不开管理二字。在一些大型的企业,一个团队往往会配备一个PM与一个架构师,尽管两个人负责的任务各有不同,但你会看到一个架构师的收入往往不如一个PM,PM往往是这个团队的核心领导者,是关键人物。因为公司能否赚钱,PM有着重要的作用。PM与TL并没有绝对的区别,而且在一些中小型企业,一个开发团队只有3~5人,一个TL往往会兼备业务处理、成本控件、架构设计、开发管理等多项任务。所以在下会把Team Leader与Project Manager定于同一层次,一个公司的老板往往不会知道团队的架构师、程序员是何人,而只会向PM询问项目的进度,所以只有晋升到这个层次,才有机会进一步提升管理能力,让自己有上升的空间。至于要成为一个技术总监,那要求就不再单单是对单个项目的管理,而应该更则重于新兴技术的引用,开发资源的合理利用,对开发项目敏捷性的处理等等,对此在下也在试探当中,未敢多言。

与编程牵手 和代码共眠 从程序员到技术总监

从业IT十年,从程序员成为技术总监,现在回头看一看,这条路也伴随国内的IT一起风雨兼程10年,对IT技术由其是IT的纯软件开发这一块,向即将要从事软件技术研发的朋友谈一谈我的看法:

一.认清当前IT形势,选择合适的技术方向和技术起点

估计大家都多多少少知道,这个IT行业知识的更新很快,竞争很急烈.

如果你对自己以后发展的方向在从业前有一个清析的计划或认识,相信你会比别人走得更好,走得更远,赚的钱也更多...呵呵

IT软件从业的方向,一般都会有这些机会:产品售前(市场,业务),产品开发(编码,设计,测试),产品售后(支持,实施),产品管理(项目管理等)

A.产品售前(市场,业务)

要从事这一块的工作,主要是在软件开发的前期(无产品),或者合同签订前期(有产品).
一般要求对相关的业务和技术都要求很高,这可不仅仅是要求人际关系,交际能力.
要想别人买你的产品,你得以专业的产品品质为后台,以专业的谈吐,专业的技术和专业的业务理解能力来取胜.

从业者要求:
要求从业者要有一定的社会经验,技术经验或业务经历,或一定的社会圈子和交际能力.

建议:
刚刚从学校毕业的朋友或不符合上面条件的朋友最好要考虑清楚了.当然这世上没有什么绝对的东西,就看你自己了.

现实情况:
据我所了解的,作这一块的都会是公司一些高层(有关系,有经验)和业务专家或特殊背景的人员等.

B.产品开发(编码,设计,测试)

这一块的工作,当然是IT从业大军的主力了,但也得要考虑清楚.
如果你要作设计师,或测试,最好先作一段时间的编码,
一个好的设计师是不可能不精通相关技术平台的!
国外好的测试人员也几乎是从开发人员中选出来的,基至是软件开发高手.

a.代码编写
在这一个职业选择范围内最好是从代码编写开始.当然你也可以先作测试,看看人家是怎么写代码的是如何来作这个软件的,
借用人家的测试经验也可以,以后有机会再来编一段时间的代码也行.
有时自己去写一个软件也可以,所以作编码和测试都是一个双向交互的.而不是编码在前测试在后的.

作代码的编写最好自己先看看别人的软件,或由一些高手带着指导一下,现在技术的学习都不成问题,关健是要连成一条线来学习和思考就会有一定的局限了.
所以要熟悉整个的项目流程或业务流程不是靠个人编码或在培训班学一下就能解决的,个人的技术学习和培训班大部分只能解决技术的学习问题,但作软件不仅是要技术呀
三分技术七分业务说得不为过,业务的学习也是一个开发人员所要必备的,如果你在不熟悉业务细节之前建议你不要急着去写代码,那样肯定会是对以后软件的影响很大.先要熟悉一下业务.

所以软件开发人员掌握一门技术平台和语言是必备条件但同时也必须要有一定的业务知识,这样才是一个合格的软件开发人员.当然精通软件编码,懂设计,熟悉业务,熟悉软件项目开发流程的软件开发人员是优秀的,那是高级研发人员的必备条件.

如果你才入门或转行或刚毕业,建议从基础的代码编写开始,跟着高手或找一些成熟的项目多学习,

b.软件设计
当然这个职业要求行业的经验,技术经验都要有一定的基础,薪水一般也会高很多,所以也是一些开发人员热烈追逐的目标.但一个好的设计师不是一二年所能练就的,精通编码,熟练设计模式和公司所采用的技术平台,熟练一些设计理论并实际多运用,熟练公司业务,其实这个层面的压力也最大,一个好的软件在设计上的比重几乎要占到七成.

建议刚毕业的朋友或软件初学者不要在这一块来凑热闹,即使你作成了设计师,但在我眼中看来你也不是一个合格的设计师...当然你有这个能力来作设计师就要恭喜你了.



c.软件测试

熟练软件测试的各种理论或实际运用,也要熟悉编码技术及相关的技术平台,熟练掌握业务.
软件测试中一般都会有:
单元测试,要求你熟练开发技术进行跟踪调试,也就是白盒测试了
集成测试,对整个项目流程的测试,要求掌握业务知识,对设计的软件能作功能上的测试或压力测试等 ,属黑盒测试
确认测试,对业务要很熟悉,测试软件是否完全满足了客户的业务需求.

总体建议:
1.熟练一种技术平台,熟悉一种业务
刚入门的朋友很容易犯的一个毛病是,熟练:VB,VC,.NET,JAVA,C++,C,Dephi,PB,几乎市场上要用的他全部会,唉,如果我看到他的简历上有这么一句话,这个人肯定不会在我考虑的范围了.
现在全球用得最广最多的技术平台体系也就三大体系:

sun的J2EE技术体系(JAVA):在高安全性,高性能上更胜一步,中高端市场上用得多

微软件的技术体系(C++,.NET,c#,VB):在中,低端市场占绝对优势,也是全球个人电脑操作平台用户最多的.

CORBA技术体系统(一种分布式技术体系和标准),
全称:Common Object Request Broker Architecture:公共对象请求代理结构,可以用不同的编程语言写成,运行在不同的操作系统上,存在于不同的机器上。
一般介于底层和上层管理软件之间,


其他的还会包括底层开发:C,汇编,属纯底层的开发,当然要求技术的起点和业务背景更强,最好是学的专业:电子电气,嵌入式行业,机械制造,数据采集等...


看中你想要从事的技术体系,选好一门语言工具,好好上路吧...:)
永远要记住:你什么都想学,你什么都学不精


2.从基础入手,不要好高鹜远,眼高手低,要与实际结合


B.产品售后(支持,实施)
这一块对于开发技术的要求来讲不是那么明显,主要工作会在软件开发后的工作,跟客户打交道多,但更多要求体现在对业务的把握和客户的交际上.
有些软件产品业务比较成熟,如果参与这一阶段的工作,可以快速学习很多的业务知识,积累客户交往的经验

建议:刚入门或刚毕业的朋友,可以在这个工作上多选择,等待时机成熟,立马杀入软件的开发或设计阶段,当然,这一块的工作作得好也不容易,如果适合你作,
工作环境或工资都不错你就大可不必多想了...



C.产品管理(项目管理等)

这一块的工作主要体现在管理上,当然适合有一定经验或管理能力的人员来担当,

最后的技术从业方向总结:

技术型:先选择好一种技术平台,熟练一种开发语言和数据库...专业专注的搞几年再说

技术+管理型:如果你有一定的技术经验了,并且人际交往,管理能力不错,你就可以向这个方向发展

技术+业务型:精通一种技术平台,精通一种业务,好好搞,这种人才最受欢迎...

管理型: 如果你有一定的社会经验,从业经验,如果人际交往,管理能力还可以,老板也喜欢,就搞这个

业务型(市场):如果你对业务很感兴趣,跟客户的交往等也不错,你可以选择了,有适合的专业技术就更能锦上添花了

技术+市场+管理:老大的位置....:) 

posted @ 2012-12-09 17:26 paulwong 阅读(562) | 评论 (1)编辑 收藏

谈谈离职和跳槽

这篇文章是我在部门会议上一次发言的总结。之所以会有这次会议,是因为我的一名员工向我提出了辞职,在思索了几天后,我整理了一下自己的思路,于是便有了这次的会议和现在的这篇文章。
收入是由什么决定的?

这位员工辞职的原因主要有两个:

公司的薪水无法达到他的预期,未来一年在公司的收入前景也不是很明确。
想要去做更底层的开发,方向是使用C/C++开发3D图形图像。而我们公司主要是.NET开发。

既然其中的一个原因是薪水无法符合预期,那么首先要搞清楚的就是收入是由什么决定的。

1.积累

首先要说的一点就是:积累。积累就是你在这家公司所创造的价值的积累。

你今天所领的薪水,并不是由你现在所创造的价值所决定的,而是包含了以前一段时期内其他同事所创造的价值。举个例子来说,公司目前排名前三的大客户:客户A、客户B、客户C。

客户A是2008年接下来的,现在每年为公司贡献600万。
客户B是2009年接下来的,现在每年为公司贡献500万。
客户C是2010年接下来的,现在每年为公司共享350万。

我的年薪是你的两倍还多。可我也承认,我现在所能创造的价值,和我的能力绝对不可能是你的两倍。可问题是:2008年、2009年、2010年这些年份我都在公司,上面的每一个大客户,都有我的贡献。而你2012年才新进公司,你并没有之前的积累。所以,新员工入职后,工资相较老员工会低一些是正常的。很多新员工总是认为自己的收入低了,吃亏了,实际上,很多情况下,新员工在加入公司的头一年,公司仅能维持平衡,即新员工创造的价值全当工资发给他了。直到第二年,有了上一年的积累之后,公司才有所盈余。而加入半年就离职的员工,对公司来说基本上是亏本的。这也就解释了为什么人员流动特别快的公司活不长,因为人力成本太高。

关于积累,我可以再举几个例子说明一下:

洪小莲,李嘉诚的秘书,几十年来一直追随李嘉诚,她从几千元的工薪族,做到身家上亿的工薪族,享受的是公司成长的回报。这种回报并非是她个人的学识和能力有了大幅的提高而得到的等价交换,很大程度上仅仅是因为她忠诚地待在这趟车上。

杨元庆,联想现在的CEO,研究生毕业后就一直追随柳传志,尽管一开始从事的是他并不很乐意的销售工作,但最终还是坚持了下来。上一次注意到他,是看到一则新闻,标题是“杨元庆自掏2000万奖励一线员工”。

上面只是正面的例子,也有反面的例子:

吴士宏,曾写了一本书叫做《逆风飞扬》,可谓是红极一时。1986年进入IBM,1998年离开IBM,进入微软,担任微软中国公司总经理,1999年进入TCL,2002年离开TCL。之后就离开了公众的视线。我特意去百度搜索“吴士宏现在在哪里”,没有任何的消息。我想如果她很成功的话,一定还属于“公众人物”,不至于连度娘都不知去向。

跳槽的话显然就要放弃先前的积累。比方说,当你跳槽到另一家公司以后,你曾经做过的系统、曾经服务过的客户仍然在为先前的公司创造着利润,可是跟你已经一毛钱关系都没有了(极少数公司有股票,另当别论)。所以跳槽之前要慎重考虑,跳得不好,有可能越跳越低。

既然新员工相对于老员工来说,收入低一些是正常的,那么老员工工资高也是合情合理的。但是有一些公司,我将其归为“无良公司”,它们会在老员工的收入高到一定程度的时候,将老员工砍掉,然后再招募低廉的新人来承担之前老员工的工作,以赚取更高的利润。我觉得这些都是小聪明,最后的结果就是,聪明能干一些的人,在看出公司的这些伎俩之后果断离职;能力一般的员工,也会把你这里当成培训基地,翅膀硬了就飞了,受损的最后还是公司,实在是得不偿失。还不如厚待老员工,也让新进的员工对未来有一个更好的预期。也有一些人向我抱怨说:“老员工待得久了,干劲都被磨光了,每天都是混日子,还不如新员工,不开他开谁?”。然后我反问他:“激励员工难道不正是你工作的一部分吗?”。这种情况的出现,更多时候,是管理者的责任,而非员工。
2.老板

这个“老板”是宽泛的老板,不一定是公司最大的老板。有的时候,公司比较大,你的职位又比较低,大老板连有没有你这个人都不知道,此时的老板就是你的顶头上司。很多时候,你的收入与他也有着莫大的关系。

对于我来说,我的原则是:在我的能力范围内,我会为我的员工争取更好的待遇。表面上看,这样做很蠢,花6000块就能雇到一个人,为什么要花8000块?我不是这样认为的,我期望能和我的员工形成这样一种互动:我尽我的能力为你争取好的待遇,你也尽你的努力做好工作。

我不能要求员工“你先把工作做好,我自然会给你好的待遇”。总是要有人先迈出一步,总是要有一方先信任另一方,所以在你什么还没有做的时候,我就先信任你,并且给你尽可能好的待遇,那么我该做的事情都做了,我问心无愧,剩下的,就看你的表现了。

可能有人会想,都这样了怎么还会有人提出辞职?实际上,提出辞职的是一个毕业刚一年的小伙子,1989年生,毕业1年多,我给他的待遇是试用期9000,转正后9500。在给他这个待遇之前,我是进行过一些调研的,我打电话给我的一个表妹,她是西安电子科技大学的研究生(陕西省排名第三的学校,211院校),她和她的同学在今年毕业找工作的时候,多得是6000到8000的工资。所以从这方面来说,我并没有亏待你,而你要求12K的工资,我并不是不愿意给这么多,你的表现也说明了你是个很有潜力的人才。只是受经济环境的影响,今年公司的效益不及往年,要在一定程度上节省开支。其次,你让其他的老员工情何以堪?所以,综合起来,你的要求超出了我的能力范围之外,我无法开口向公司申请提高你的薪水。

3.门槛

除了积累和顶头上司两个决定因素以外,第三个决定因素就是你从事工作的门槛。为什么餐厅服务员的收入很低?为什么坐在前台收发快递的文员收入很低?因为这些工作的门槛很低,门槛低就意味着你不做有的是人能做,你不做有大批的“后备队伍”在等着做。由于庞大的后备队伍的竞争,你就无法提高自己的要价。而提升自己所从事工作的门槛,实际上就缩减了竞争者的规模。

程序开发也是一样。如果你想收入高,你就做一些别人做不了,又有市场的。

.NET在程序开发中就属于门槛比较低的一类。个中原因我想大家都懂的,就不在这里赘述了。做.NET不需要你科班出身,或许一点兴趣再加上一点时间,或许一个类似北大青鸟的培训,都可以让你开始从事.NET开发了。你可以不懂指针、不懂数据结构、不懂算法、不懂汇编、不懂很多东西,但照样可以做出一个.NET程序来。而这些人往往又是对薪资的要求没那么高的,这样无形中就拉低了.NET程序员的“身价”。.NET的易学易会,很大程度上是由于它的封装性比较好。底层的东西都屏蔽掉了,你只要知道学习一下命名空间,然后寻找相关的API去调用就好了。记得我们公司曾经开发过一个基于C语言的手持设备程序,没有任何的类库支持,连排序、链表这样.NET中的基本功能,都要自己来实现,更别提内存管理和程序逻辑了,和.NET比起来,门槛就相对高一些了。

所以,如果想收入高一些,那么就去做更高难度的技术工作,这里随便想了几个例子:

百度、谷歌的搜索引擎算法。
微软、谷歌、苹果的操作系统。
网络游戏,例如《征途》的游戏引擎。
大型企业的ERP,比方说SAP。
软硬结合,比如单片机,电气自动化。
以及我这位即将离职的同事说的,3D图形图像。

所以,从这个角度来看,这位同事的辞职是明智的,他很年轻,有的是机会重新选择自己的道路,所以我也祝愿他能有更好的发展。而这些好赚的钱,就留给我们来做了:-)。

4.平台

接下来要说的一个决定因素是平台。很多程序员觉得30岁就瓶颈了,30岁写程序就到头了,实际上,这只是你的平台比较小罢了。就拿我自己的公司来说,平台就不大,只要是踏踏实实工作过5年的程序员,基本上就能够胜任公司90%的技术工作了,剩下的10%,请教一下其他同事,进行一下技术交流,也完全能够解决。这样就存在一个问题:随着你年龄的增长,你的生活压力越来越大,要求越来越高,可是公司只要5年经验的程序员就够用了。假设市场上5年经验的程序员的平均要求是10K,凭什么要给你15K?你的优势在哪里?如果你没有突破,就会有“30岁写程序就到头了”的感觉。

而如果平台大一些情况就会不一样,比方说,你去了IBM,可能5年的经验不过刚刚入门而已。IBM有一个工程院,其中有5位院士(IBM Fellow)获得过诺贝尔奖,很多人钻研技术都超过20年或者更久。如果你对技术感兴趣,并执着去钻研的话,你可以不断地去挑战和攀登。

当然,你可能没那么好的运气和实力进入IBM,那么其他一些中型的平台也是不错的,比方说阿里巴巴、金蝶、百度、腾讯等等。在这里,至少你有足够的理由和需要再去进行深入学习。因为在这些地方,5年的经验是远远不够的,还需要进一步地学习和努力。

如果你和我一样,不巧没有那么大的平台,此时的选择大概有这么几种:

1. 你可以凭借你在公司的积累(第一节讲过的),过比较安逸的日子。如果比较幸运,押对了宝,公司发展得比较好,收入一样会变得非常可观;如果比较不幸,公司经营的状况不好,那就要承担比较大的风险了。说得难听一点,公司倒闭了你去哪里?你过去的积累已经一文不值,而你的年龄已经35,水平却相当于只有5年经验。你的竞争力在哪里?

2. 你可以凭自己的努力将现在所在的平台做大,换言之,把自己的小公司做大。这当然是比较积极的做法,也是我一直努力的方向。现在你看到的大公司,不也是从小公司一步一步做起的吗?不过这里还有两个问题:1、有的时候,你的力量在公司中的占比没那么大,你再怎么努力推进的速度也还是有限;2、你缺乏慧眼,选中的公司本身就缺乏长大的资质。我们往往只看到成功了的公司,却忽视了更多在竞争中倒下的公司。

3. 主动选择更大的平台,也就是跳槽了。但是跳槽也是有风险的,尤其是过了30岁的程序员。你在这家公司的收入高,是因为有之前的积累,换一家就没有积累了,等于从新人开始,而大多数的公司,5年经验的程序员就够用了。如果跳得不好,收入还可能越跳越低,如果还有老婆、孩子、房贷,那将面临更大的压力。所以当你想要从一个低的平台向更高的平台跳跃的时候,平时就要做足功夫,认真积累自己的实力。对于我来说,我缺乏大型项目的管理经验,但是没关系,我努力学习考一个PMP没什么问题吧?我缺乏大型软件的架构经验,但是没关系,我把.NET的基础知识和各种设计模式掰开了揉碎了没什么问题吧?我缺乏大型团队的管理经验,但是没关系,每次遇到管理方面的问题我都认真思考仔细总结没什么问题吧?有些人总是抱怨没有机会,运气不好,我想机会总是有的,但只属于有准备的人。

5.行业

我想说的最后一点就是行业。有时候你觉得已经万事俱备了,可是你所处的这个行业本身就属于极低利润率的,你再怎么努力也很难有很高的收入。很多情况下,可能公司也想提高你的待遇,但是由于缺乏利润的支撑,公司也是有心无力。所以,在选择公司,尤其是小公司的时候,要重点考察一下公司所处的行业如何?是不是前景比较好、利润比较高的行业?如果是大公司的话,这方面的问题就会少一些,因为如果方向有问题,它就无法做成大公司。

行业是有周期性的,可能在一段时期内这个行业好,下一段时期这个行业就不行了。最典型的一个例子就是软盘,我现在的老板在成立这家公司之前是做销售的,他有一个客户,做索尼软盘的,这种软盘我想很多80后都见过。当时生意做得很大,可是当光盘出来以后,软盘的市场是会急剧萎缩的,可是这家公司的领导层居然没有看到,或者是看到了但不愿意转变,像鸵鸟一样在危机来临时把头埋在土里,继续做它的软盘。几年以后,这家公司就倒掉了。

选择行业也不是选择暴利行业就一定好,比方说房地产。资本都是逐利的,当一个行业属于暴利,同时所有人都知道它是暴利的时候,危机就来了。这个危机就是会有大量的社会资源、人力物力投入到这个行业中企图分一杯羹。而全局上又没有一个统一的把控,这个行业究竟需要多少公司才是合适的?最后的结果就是过剩。就好像股票在崩盘时,也许跌到3000点是比较合理也比较正常的位置,但是由于人们的恐慌,它就跌到1600点了。

感谢阅读,希望这篇文章能给你带来收获。

不是所有一年工作经验的毕业生都有这样的待遇,我主要是看能力,而不是年龄、学历等。特别说明一下,以免误导。 @import url(http://www.blogjava.net/CuteSoft_Client/CuteEditor/Load.ashx?type=style&file=SyntaxHighlighter.css);@import url(/css/cuteeditor.css);

posted @ 2012-12-06 22:36 paulwong 阅读(596) | 评论 (0)编辑 收藏

Activiti 5.11 发布,业务流程管理

1. 圣诞大礼包
这是我第一次为Activiti的发布撰文,因为这次的发布我觉得很有必要和大家分享,这其中包含了我期待的新特性和已知问题的修复;而且还有一个重量级的模块Activiti Modeler。

在5.8版本之前一直以每两个月一版的的频率发布,但是在5.8~5.10之间波动比较大,5.9和5.10都是5个月才发布一版,不过从5.11开始又开始恢复每两个月一版的频率了,我认为短周期的发布有利于占领市场,对于开发者来说能尽快修复比较严重的问题。

5.11版本在代码管理方面做了很大调整,首先把源码由原来的svn转移到了Github有利于引擎的发展,可以让更多的开源爱好者参与进来,大家一起捉虫,想Activiti Team提交自己的想法,Github可以很好的满足这些;通过Github的“Fork”我们可以很容易的做到这些。

2. 新特性及优化
新模块Activiti Modeler:添加了Web版本的流程定义设计器—Activiti Modeler到Activiti Explorer中,而且两者可以无缝结合;可以把Modeler设计的流程定义部署到Exploer中,也可以以已部署的流程定义为基础进行修改后重新部署。具体的使用可以参考这里。
汉化Activiti Explorer:本人有幸参与并完成了汉化工作,汉化介绍。
添加Native Query:允许通过本地SQL方法查询Activiti的对象;在这之前查询一直是一个比较头疼的事情,因为开发人员只能使用一些Query对象显示调用设置属性的方法过滤,如果想做到自定义条件查询没有这类接口提供;现在可以使用下面的方式灵活组装查询条件。参考手册的Query API部分。
历史变量查询功能增强:在5.11之前的版本中变量和表单属性(表单的字段,动态表单和外置表单)保存在同一张表中ACT_HI_DETAIL,以DETAIL的TYPE区分(FormProperty和VariableUpdate);在5.11中把两者做了分离,添加了一张表ACT_HI_VARINST专门用来存储变量(也就是在ACT_HI_DETAIL中类型为VariableUpdate类型的记录),这样表单的字段和变量就区分开来了(表单字段类型是字符型,变量有具体的类型);并且专门添加了一个变量实例接口HistoricVariableInstance,以及对应的变量实例查询对象HistoricVariableInstanceQuery。
运行时变量删除:允许通过API方式删除流程实例或者任务的相关变量,调用方式:runtimeService.removeVariable('foo')
改进挂起与激活状态:在5.9版本时我曾在JIRA提交了一个改进意见ACT-1228,不过最后被关闭了……在5.11中终于支持了这一特性,当挂起一个流程实例的时候同时也把相关的任务挂起;原来针对流程定义的挂起与激活功能也进行了优化,可以选择当挂起或者激活某个流程定义时是否挂起相关的流程实例,并且允许定时执行挂起与激活任务(利用Job,定时器)。如此,对于一些流程实例或者流程定义暂时不需要处理时就可以利用挂起功能,在待办任务列表仅显示处于激活状态的即可。
优化对DB2和MSSQL的支持:在SQL脚本方面做了优化。
允许在Java Delegate中调用引擎Service接口:当为某个活动添加了一个Java Service任务设置了Java Delegate时如果需要获取引擎的7个Service,只能通过自己写工具类的方式获取,或者用Spring代理监听或者Java Delegate类用注入的方式实现。现在可以通过DelegateExecution的对象获取,详细请参考这里。
结构调整:抽取独立的模块(jar包)activiti-explorer、activiti-rest,对应的activiti-webapp-explorer2依赖activiti-explorer,activiti-webapp-rest2依赖activiti-rest;如此我们可以很容的把这两个模块集成到现有系统中。创建新的模块:activiti-bpmn-converter、activiti-bpmn-model、activiti-json-converter作为基础的组件,提供bpmn文件的解析以及转换功能(稍后会写篇文章专门讲解如何使用这些基础组件)。
除了以上的改进和新特性之外还修复了大量的Bug,完整的Changlog请参考JIRA。

3. 下载新版本
5.11版本下载地址:https://github.com/downloads/Activiti/Activiti/activiti-5.11.zip

压缩包中的wars目录包含两个文件,activiti-rest模块可以单独部署了,修改数据库配置就可以作为自己的REST服务使用。
Github上的5.11分支:https://github.com/Activiti/Activiti/tree/activiti-5.11

4. 升级建议
5.11版本是一个改进版本,添加了很多有用的新特性,很值得升级;当然在升级产品环境之前做好充分的测试并备份数据。

5. 版本5.12前瞻
在5.11发布之前我曾在JIRA提交了一个关于Activiti Explorer中流程图的一个Bug-ACT-1469,是的,又被关闭了;不过后来在twitter上和Activiti的Team Leader 交流了一下,我分享了我的做法并得到了他的肯定,而且想让我参与到开发中用Javascript和Css方式实现流程图的跟踪功能。
另外国内开发者遇到的流程图中文乱码问题也考虑在5.12版本中给出解决方案,这一点我也和Tijs进行了讨论,我的想法是在引擎配置中添加一个字体名称的设置项,对于英文字符集之外的国家就可以通过这个配置使用本地语言的字体以解决乱码问题。 @import url(http://www.blogjava.net/CuteSoft_Client/CuteEditor/Load.ashx?type=style&file=SyntaxHighlighter.css);@import url(/css/cuteeditor.css);

posted @ 2012-12-06 21:50 paulwong 阅读(1331) | 评论 (0)编辑 收藏

Apache Flume 1.3.0 发布,分布式日志服务器

Flume 是一个分布式、可靠和高可用的服务,用于收集、聚合以及移动大量日志数据,使用一个简单灵活的架构,就流数据模型。这是一个可靠、容错的服务。


posted @ 2012-12-05 22:18 paulwong 阅读(338) | 评论 (0)编辑 收藏

CONTROL-M资源

CONTROL-M简单介绍
http://blog.sina.com.cn/s/blog_53d02f2f01012ha7.html

Packt - BMC Control-M 7 Oct 2012 PDF
http://www.itpub.net/thread-1738257-1-1.html

Automate job scheduling to run more jobs faster
http://www.bmc.com/products/control-m-workload-automation?intcmp=redirect_products_control-m_taxonomy&cmp=redirect_offering_control-m

File sorting in batch

posted @ 2012-12-04 01:40 paulwong 阅读(487) | 评论 (0)编辑 收藏

HBase性能优化方法总结


本文主要是从HBase应用程序设计与开发的角度,总结几种常用的性能优化方法。有关HBase系统配置级别的优化,这里涉及的不多,这部分可以参考:淘宝Ken Wu同学的博客。

1. 表的设计
1.1 Pre-Creating Regions
默认情况下,在创建HBase表的时候会自动创建一个region分区,当导入数据的时候,所有的HBase客户端都向这一个region写数据,直到这个region足够大了才进行切分。一种可以加快批量写入速度的方法是通过预先创建一些空的regions,这样当数据写入HBase时,会按照region分区情况,在集群内做数据的负载均衡。

有关预分区,详情参见:Table Creation: Pre-Creating Regions,下面是一个例子:
publicstaticbooleancreateTable(HBaseAdmin admin, HTableDescriptor table,byte[][] splits)
throwsIOException {
  try{
    admin.createTable(table, splits);
    returntrue;
  }catch(TableExistsException e) {
    logger.info("table "+ table.getNameAsString() +" already exists");
    // the table already exists
    returnfalse;
  }
}
 
publicstaticbyte[][] getHexSplits(String startKey, String endKey,intnumRegions) {
  byte[][] splits =newbyte[numRegions-1][];
  BigInteger lowestKey =newBigInteger(startKey,16);
  BigInteger highestKey =newBigInteger(endKey,16);
  BigInteger range = highestKey.subtract(lowestKey);
  BigInteger regionIncrement = range.divide(BigInteger.valueOf(numRegions));
  lowestKey = lowestKey.add(regionIncrement);
  for(inti=0; i < numRegions-1;i++) {
    BigInteger key = lowestKey.add(regionIncrement.multiply(BigInteger.valueOf(i)));
    byte[] b = String.format("%016x", key).getBytes();
    splits[i] = b;
  }
  returnsplits;
}

1.2 Row Key
HBase中row key用来检索表中的记录,支持以下三种方式:

通过单个row key访问:即按照某个row key键值进行get操作;
通过row key的range进行scan:即通过设置startRowKey和endRowKey,在这个范围内进行扫描;
全表扫描:即直接扫描整张表中所有行记录。
在HBase中,row key可以是任意字符串,最大长度64KB,实际应用中一般为10~100bytes,存为byte[]字节数组,一般设计成定长的。

row key是按照字典序存储,因此,设计row key时,要充分利用这个排序特点,将经常一起读取的数据存储到一块,将最近可能会被访问的数据放在一块。

举个例子:如果最近写入HBase表中的数据是最可能被访问的,可以考虑将时间戳作为row key的一部分,由于是字典序排序,所以可以使用Long.MAX_VALUE – timestamp作为row key,这样能保证新写入的数据在读取时可以被快速命中。

1.3 Column Family
不要在一张表里定义太多的column family。目前Hbase并不能很好的处理超过2~3个column family的表。因为某个column family在flush的时候,它邻近的column family也会因关联效应被触发flush,最终导致系统产生更多的I/O。感兴趣的同学可以对自己的HBase集群进行实际测试,从得到的测试结果数据验证一下。

1.4 In Memory
创建表的时候,可以通过HColumnDescriptor.setInMemory(true)将表放到RegionServer的缓存中,保证在读取的时候被cache命中。

1.5 Max Version
创建表的时候,可以通过HColumnDescriptor.setMaxVersions(int maxVersions)设置表中数据的最大版本,如果只需要保存最新版本的数据,那么可以设置setMaxVersions(1)。

1.6 Time To Live
创建表的时候,可以通过HColumnDescriptor.setTimeToLive(int timeToLive)设置表中数据的存储生命期,过期数据将自动被删除,例如如果只需要存储最近两天的数据,那么可以设置setTimeToLive(2 * 24 * 60 * 60)。

1.7 Compact & Split
在HBase中,数据在更新时首先写入WAL 日志(HLog)和内存(MemStore)中,MemStore中的数据是排序的,当MemStore累计到一定阈值时,就会创建一个新的MemStore,并且将老的MemStore添加到flush队列,由单独的线程flush到磁盘上,成为一个StoreFile。于此同时, 系统会在zookeeper中记录一个redo point,表示这个时刻之前的变更已经持久化了(minor compact)。

StoreFile是只读的,一旦创建后就不可以再修改。因此Hbase的更新其实是不断追加的操作。当一个Store中的StoreFile达到一定的阈值后,就会进行一次合并(major compact),将对同一个key的修改合并到一起,形成一个大的StoreFile,当StoreFile的大小达到一定阈值后,又会对 StoreFile进行分割(split),等分为两个StoreFile。

由于对表的更新是不断追加的,处理读请求时,需要访问Store中全部的StoreFile和MemStore,将它们按照row key进行合并,由于StoreFile和MemStore都是经过排序的,并且StoreFile带有内存中索引,通常合并过程还是比较快的。

实际应用中,可以考虑必要时手动进行major compact,将同一个row key的修改进行合并形成一个大的StoreFile。同时,可以将StoreFile设置大些,减少split的发生。

2. 写表操作
2.1 多HTable并发写
创建多个HTable客户端用于写操作,提高写数据的吞吐量,一个例子:


staticfinalConfiguration conf = HBaseConfiguration.create();
staticfinalString table_log_name = “user_log”;
wTableLog =newHTable[tableN];
for(inti =0; i < tableN; i++) {
wTableLog[i] =newHTable(conf, table_log_name);
wTableLog[i].setWriteBufferSize(5*1024*1024);//5MB
wTableLog[i].setAutoFlush(false);
}
2.2 HTable参数设置
2.2.1 Auto Flush
通过调用HTable.setAutoFlush(false)方法可以将HTable写客户端的自动flush关闭,这样可以批量写入数据到HBase,而不是有一条put就执行一次更新,只有当put填满客户端写缓存时,才实际向HBase服务端发起写请求。默认情况下auto flush是开启的。

2.2.2 Write Buffer
通过调用HTable.setWriteBufferSize(writeBufferSize)方法可以设置HTable客户端的写buffer大小,如果新设置的buffer小于当前写buffer中的数据时,buffer将会被flush到服务端。其中,writeBufferSize的单位是byte字节数,可以根据实际写入数据量的多少来设置该值。

2.2.3 WAL Flag
在HBae中,客户端向集群中的RegionServer提交数据时(Put/Delete操作),首先会先写WAL(Write Ahead Log)日志(即HLog,一个RegionServer上的所有Region共享一个HLog),只有当WAL日志写成功后,再接着写MemStore,然后客户端被通知提交数据成功;如果写WAL日志失败,客户端则被通知提交失败。这样做的好处是可以做到RegionServer宕机后的数据恢复。

因此,对于相对不太重要的数据,可以在Put/Delete操作时,通过调用Put.setWriteToWAL(false)或Delete.setWriteToWAL(false)函数,放弃写WAL日志,从而提高数据写入的性能。

值得注意的是:谨慎选择关闭WAL日志,因为这样的话,一旦RegionServer宕机,Put/Delete的数据将会无法根据WAL日志进行恢复。

2.3 批量写
通过调用HTable.put(Put)方法可以将一个指定的row key记录写入HBase,同样HBase提供了另一个方法:通过调用HTable.put(List<Put>)方法可以将指定的row key列表,批量写入多行记录,这样做的好处是批量执行,只需要一次网络I/O开销,这对于对数据实时性要求高,网络传输RTT高的情景下可能带来明显的性能提升。

2.4 多线程并发写
在客户端开启多个HTable写线程,每个写线程负责一个HTable对象的flush操作,这样结合定时flush和写buffer(writeBufferSize),可以既保证在数据量小的时候,数据可以在较短时间内被flush(如1秒内),同时又保证在数据量大的时候,写buffer一满就及时进行flush。下面给个具体的例子:


for(inti =0; i < threadN; i++) {
Thread th =newThread() {
publicvoidrun() {
while(true) {
try{
sleep(1000);//1 second
}catch(InterruptedException e) {
e.printStackTrace();
}
synchronized(wTableLog[i]) {
try{
wTableLog[i].flushCommits();
}catch(IOException e) {
e.printStackTrace();
}
}
}
}
};
th.setDaemon(true);
th.start();
}
3. 读表操作
3.1 多HTable并发读
创建多个HTable客户端用于读操作,提高读数据的吞吐量,一个例子:

staticfinalConfiguration conf = HBaseConfiguration.create();
staticfinalString table_log_name = “user_log”;
rTableLog =newHTable[tableN];
for(inti =0; i < tableN; i++) {
rTableLog[i] =newHTable(conf, table_log_name);
rTableLog[i].setScannerCaching(50);
}

3.2 HTable参数设置
3.2.1 Scanner Caching
通过调用HTable.setScannerCaching(int scannerCaching)可以设置HBase scanner一次从服务端抓取的数据条数,默认情况下一次一条。通过将此值设置成一个合理的值,可以减少scan过程中next()的时间开销,代价是scanner需要通过客户端的内存来维持这些被cache的行记录。

3.2.2 Scan Attribute Selection
scan时指定需要的Column Family,可以减少网络传输数据量,否则默认scan操作会返回整行所有Column Family的数据。

3.2.3 Close ResultScanner
通过scan取完数据后,记得要关闭ResultScanner,否则RegionServer可能会出现问题(对应的Server资源无法释放)。

3.3 批量读
通过调用HTable.get(Get)方法可以根据一个指定的row key获取一行记录,同样HBase提供了另一个方法:通过调用HTable.get(List)方法可以根据一个指定的row key列表,批量获取多行记录,这样做的好处是批量执行,只需要一次网络I/O开销,这对于对数据实时性要求高而且网络传输RTT高的情景下可能带来明显的性能提升。

3.4 多线程并发读
在客户端开启多个HTable读线程,每个读线程负责通过HTable对象进行get操作。下面是一个多线程并发读取HBase,获取店铺一天内各分钟PV值的例子:

publicclassDataReaderServer {
//获取店铺一天内各分钟PV值的入口函数
publicstaticConcurrentHashMap getUnitMinutePV(longuid,longstartStamp,longendStamp){
longmin = startStamp;
intcount = (int)((endStamp - startStamp) / (60*1000));
List lst =newArrayList();
for(inti =0; i <= count; i++) {
min = startStamp + i *60*1000;
lst.add(uid +"_"+ min);
}
returnparallelBatchMinutePV(lst);
}
//多线程并发查询,获取分钟PV值
privatestaticConcurrentHashMap parallelBatchMinutePV(List lstKeys){
ConcurrentHashMap hashRet =newConcurrentHashMap();
intparallel =3;
List<List<String>> lstBatchKeys =null;
if(lstKeys.size() < parallel ){
lstBatchKeys =newArrayList<List<String>>(1);
lstBatchKeys.add(lstKeys);
}
else{
lstBatchKeys =newArrayList<List<String>>(parallel);
for(inti =0; i < parallel; i++ ){
List lst =newArrayList();
lstBatchKeys.add(lst);
}

for(inti =0; i < lstKeys.size() ; i ++ ){
lstBatchKeys.get(i%parallel).add(lstKeys.get(i));
}
}

List >> futures =newArrayList >>(5);

ThreadFactoryBuilder builder =newThreadFactoryBuilder();
builder.setNameFormat("ParallelBatchQuery");
ThreadFactory factory = builder.build();
ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(lstBatchKeys.size(), factory);

for(List keys : lstBatchKeys){
Callable< ConcurrentHashMap > callable =newBatchMinutePVCallable(keys);
FutureTask< ConcurrentHashMap > future = (FutureTask< ConcurrentHashMap >) executor.submit(callable);
futures.add(future);
}
executor.shutdown();

// Wait for all the tasks to finish
try{
booleanstillRunning = !executor.awaitTermination(
5000000, TimeUnit.MILLISECONDS);
if(stillRunning) {
try{
executor.shutdownNow();
}catch(Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}catch(InterruptedException e) {
try{
Thread.currentThread().interrupt();
}catch(Exception e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}

// Look for any exception
for(Future f : futures) {
try{
if(f.get() !=null)
{
hashRet.putAll((ConcurrentHashMap)f.get());
}
}catch(InterruptedException e) {
try{
Thread.currentThread().interrupt();
}catch(Exception e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}catch(ExecutionException e) {
e.printStackTrace();
}
}

returnhashRet;
}
//一个线程批量查询,获取分钟PV值
protectedstaticConcurrentHashMap getBatchMinutePV(List lstKeys){
ConcurrentHashMap hashRet =null;
List lstGet =newArrayList();
String[] splitValue =null;
for(String s : lstKeys) {
splitValue = s.split("_");
longuid = Long.parseLong(splitValue[0]);
longmin = Long.parseLong(splitValue[1]);
byte[] key =newbyte[16];
Bytes.putLong(key,0, uid);
Bytes.putLong(key,8, min);
Get g =newGet(key);
g.addFamily(fp);
lstGet.add(g);
}
Result[] res =null;
try{
res = tableMinutePV[rand.nextInt(tableN)].get(lstGet);
}catch(IOException e1) {
logger.error("tableMinutePV exception, e="+ e1.getStackTrace());
}

if(res !=null&& res.length >0) {
hashRet =newConcurrentHashMap(res.length);
for(Result re : res) {
if(re !=null&& !re.isEmpty()) {
try{
byte[] key = re.getRow();
byte[] value = re.getValue(fp, cp);
if(key !=null&& value !=null) {
hashRet.put(String.valueOf(Bytes.toLong(key,
Bytes.SIZEOF_LONG)), String.valueOf(Bytes
.toLong(value)));
}
}catch(Exception e2) {
logger.error(e2.getStackTrace());
}
}
}
}

returnhashRet;
}
}
//调用接口类,实现Callable接口
classBatchMinutePVCallableimplementsCallable>{
privateList keys;

publicBatchMinutePVCallable(List lstKeys ) {
this.keys = lstKeys;
}

publicConcurrentHashMap call()throwsException {
returnDataReadServer.getBatchMinutePV(keys);
}
}

3.5 缓存查询结果
对于频繁查询HBase的应用场景,可以考虑在应用程序中做缓存,当有新的查询请求时,首先在缓存中查找,如果存在则直接返回,不再查询HBase;否则对HBase发起读请求查询,然后在应用程序中将查询结果缓存起来。至于缓存的替换策略,可以考虑LRU等常用的策略。

3.6 Blockcache
HBase上Regionserver的内存分为两个部分,一部分作为Memstore,主要用来写;另外一部分作为BlockCache,主要用于读。

写请求会先写入Memstore,Regionserver会给每个region提供一个Memstore,当Memstore满64MB以后,会启动 flush刷新到磁盘。当Memstore的总大小超过限制时(heapsize * hbase.regionserver.global.memstore.upperLimit * 0.9),会强行启动flush进程,从最大的Memstore开始flush直到低于限制。

读请求先到Memstore中查数据,查不到就到BlockCache中查,再查不到就会到磁盘上读,并把读的结果放入BlockCache。由于BlockCache采用的是LRU策略,因此BlockCache达到上限(heapsize * hfile.block.cache.size * 0.85)后,会启动淘汰机制,淘汰掉最老的一批数据。

一个Regionserver上有一个BlockCache和N个Memstore,它们的大小之和不能大于等于heapsize * 0.8,否则HBase不能启动。默认BlockCache为0.2,而Memstore为0.4。对于注重读响应时间的系统,可以将 BlockCache设大些,比如设置BlockCache=0.4,Memstore=0.39,以加大缓存的命中率。

有关BlockCache机制,请参考这里:HBase的Block cache,HBase的blockcache机制,hbase中的缓存的计算与使用。

4.数据计算
4.1 服务端计算
Coprocessor运行于HBase RegionServer服务端,各个Regions保持对与其相关的coprocessor实现类的引用,coprocessor类可以通过RegionServer上classpath中的本地jar或HDFS的classloader进行加载。

目前,已提供有几种coprocessor:

Coprocessor:提供对于region管理的钩子,例如region的open/close/split/flush/compact等;
RegionObserver:提供用于从客户端监控表相关操作的钩子,例如表的get/put/scan/delete等;
Endpoint:提供可以在region上执行任意函数的命令触发器。一个使用例子是RegionServer端的列聚合,这里有代码示例。
以上只是有关coprocessor的一些基本介绍,本人没有对其实际使用的经验,对它的可用性和性能数据不得而知。感兴趣的同学可以尝试一下,欢迎讨论。

4.2 写端计算
4.2.1 计数
HBase本身可以看作是一个可以水平扩展的Key-Value存储系统,但是其本身的计算能力有限(Coprocessor可以提供一定的服务端计算),因此,使用HBase时,往往需要从写端或者读端进行计算,然后将最终的计算结果返回给调用者。举两个简单的例子:

PV计算:通过在HBase写端内存中,累加计数,维护PV值的更新,同时为了做到持久化,定期(如1秒)将PV计算结果同步到HBase中,这样查询端最多会有1秒钟的延迟,能看到秒级延迟的PV结果。
分钟PV计算:与上面提到的PV计算方法相结合,每分钟将当前的累计PV值,按照rowkey + minute作为新的rowkey写入HBase中,然后在查询端通过scan得到当天各个分钟以前的累计PV值,然后顺次将前后两分钟的累计PV值相减,就得到了当前一分钟内的PV值,从而最终也就得到当天各个分钟内的PV值。

4.2.2 去重
对于UV的计算,就是个去重计算的例子。分两种情况:

如果内存可以容纳,那么可以在Hash表中维护所有已经存在的UV标识,每当新来一个标识时,通过快速查找Hash确定是否是一个新的UV,若是则UV值加1,否则UV值不变。另外,为了做到持久化或提供给查询接口使用,可以定期(如1秒)将UV计算结果同步到HBase中。
如果内存不能容纳,可以考虑采用Bloom Filter来实现,从而尽可能的减少内存的占用情况。除了UV的计算外,判断URL是否存在也是个典型的应用场景。

4.3 读端计算
如果对于响应时间要求比较苛刻的情况(如单次http请求要在毫秒级时间内返回),个人觉得读端不宜做过多复杂的计算逻辑,尽量做到读端功能单一化:即从HBase RegionServer读到数据(scan或get方式)后,按照数据格式进行简单的拼接,直接返回给前端使用。当然,如果对于响应时间要求一般,或者业务特点需要,也可以在读端进行一些计算逻辑。

5.总结
作为一个Key-Value存储系统,HBase并不是万能的,它有自己独特的地方。因此,基于它来做应用时,我们往往需要从多方面进行优化改进(表设计、读表操作、写表操作、数据计算等),有时甚至还需要从系统级对HBase进行配置调优,更甚至可以对HBase本身进行优化。这属于不同的层次范畴。

总之,概括来讲,对系统进行优化时,首先定位到影响你的程序运行性能的瓶颈之处,然后有的放矢进行针对行的优化。如果优化后满足你的期望,那么就可以停止优化;否则继续寻找新的瓶颈之处,开始新的优化,直到满足性能要求。

以上就是从项目开发中总结的一点经验,如有不对之处,欢迎大家不吝赐教。 

posted @ 2012-11-29 21:43 paulwong 阅读(6758) | 评论 (0)编辑 收藏

oracle--分区操作

一、添加分区
以下代码给SALES表添加了一个P3分区
ALTER TABLE SALES ADD PARTITION P3 VALUES LESS THAN(TO_DATE('2003-06-01','YYYY-MM-DD'));
注意:以上添加的分区界限应该高于最后一个分区界限。
以下代码给SALES表的P3分区添加了一个P3SUB1子分区
ALTER TABLE SALES MODIFY PARTITION P3 ADD SUBPARTITION P3SUB1 VALUES('COMPLETE');

二、删除分区
以下代码删除了P3表分区:
ALTER TABLE SALES DROP PARTITION P3;
在以下代码删除了P4SUB1子分区:
ALTER TABLE SALES DROP SUBPARTITION P4SUB1;
注意:如果删除的分区是表中唯一的分区,那么此分区将不能被删除,要想删除此分区,必须删除表。

三、截断分区
截断某个分区是指删除某个分区中的数据,并不会删除分区,也不会删除其它分区中的数据。当表中即使只有一个分区时,也可 以截断该分区。通过以下代码截断分区:
ALTER TABLE SALES TRUNCATE PARTITION P2;
通过以下代码截断子分区:
ALTER TABLE SALES TRUNCATE SUBPARTITION P2SUB2;

四、合并分区
合并分区是将相邻的分区合并成一个分区,结果分区将采用较高分区的界限,值得注意的是,不能将分区合并到界限较低的分 区。以下代码实现了P1 P2分区的合并:
ALTER TABLE SALES MERGE PARTITIONS P1,P2 INTO PARTITION P2;

五、拆分分区
拆分分区将一个分区拆分两个新分区,拆分后原来分区不再存在。注意不能对HASH类型的分区进行拆分。
ALTER TABLE SALES SBLIT PARTITION P2 AT(TO_DATE('2003-02-01','YYYY-MM-DD')) INTO (PARTITION P21,PARTITION P22);

六、接合分区(coalesca)
结合分区是将散列分区中的数据接合到其它分区中,当散列分区中的数据比较大时,可以增加散列分区, 然后进行接合,值得注意的是,接合分区只能用于散列分区中。通过以下代码进行接合分区:
ALTER TABLE SALES COALESCA PARTITION;

七、重命名表分区
以下代码将P21更改为P2
ALTER TABLE SALES RENAME PARTITION P21 TO P2;

八、相关查询
跨分区查询
select sum( *) from
(select count(*) cn from t_table_SS PARTITION (P200709_1)
union all
select count(*) cn from t_table_SS PARTITION (P200709_2)
);
查询表上有多少分区
SELECT * FROM useR_TAB_PARTITIONS WHERE TABLE_NAME='tableName'
查询索引信息
select object_name,object_type,tablespace_name,sum(value)
from v$segment_statistics
where statistic_name IN ('physical reads','physical write','logical reads')and object_type='INDEX'
group by object_name,object_type,tablespace_name
order by 4 desc

--显示数据库所有分区表的信息:
select * from DBA_PART_TABLES

--显示当前用户可访问的所有分区表信息:
select * from ALL_PART_TABLES

--显示当前用户所有分区表的信息:
select * from USER_PART_TABLES

--显示表分区信息 显示数据库所有分区表的详细分区信息:
select * from DBA_TAB_PARTITIONS

--显示当前用户可访问的所有分区表的详细分区信息:
select * from ALL_TAB_PARTITIONS

--显示当前用户所有分区表的详细分区信息:
select * from USER_TAB_PARTITIONS

--显示子分区信息 显示数据库所有组合分区表的子分区信息:
select * from DBA_TAB_SUBPARTITIONS

--显示当前用户可访问的所有组合分区表的子分区信息:
select * from ALL_TAB_SUBPARTITIONS

--显示当前用户所有组合分区表的子分区信息:
select * from USER_TAB_SUBPARTITIONS

--显示分区列 显示数据库所有分区表的分区列信息:
select * from DBA_PART_KEY_COLUMNS

--显示当前用户可访问的所有分区表的分区列信息:
select * from ALL_PART_KEY_COLUMNS

--显示当前用户所有分区表的分区列信息:
select * from USER_PART_KEY_COLUMNS

--显示子分区列 显示数据库所有分区表的子分区列信息:
select * from DBA_SUBPART_KEY_COLUMNS

--显示当前用户可访问的所有分区表的子分区列信息:
select * from ALL_SUBPART_KEY_COLUMNS

--显示当前用户所有分区表的子分区列信息:
select * from USER_SUBPART_KEY_COLUMNS

--怎样查询出oracle数据库中所有的的分区表
select * from user_tables a where a.partitioned='YES'

--删除一个表的数据是
truncate table table_name;

--删除分区表一个分区的数据是
alter table table_name truncate partition p5; @import url(http://www.blogjava.net/CuteSoft_Client/CuteEditor/Load.ashx?type=style&file=SyntaxHighlighter.css);@import url(/css/cuteeditor.css);

posted @ 2012-11-29 21:36 paulwong 阅读(1118) | 评论 (0)编辑 收藏

JBOSS7.1下开发JMS(HoernetQ)的示例DEMO

http://my.oschina.net/zhaoqian/blog/90796

posted @ 2012-11-21 23:30 paulwong 阅读(859) | 评论 (0)编辑 收藏

SPRING BATCH 错误通知机制

     摘要: 运行SPRING BATCH JOB 的时候,有可能出错,如果能有相关的错误处理机制,则这些错误就能及时得到处理。 SPRING BATCH 提供了监听器,可配置在JOB执行完后,或执行JOB前,要执行的方法。 JOB的定义及BEAN的配置文件: Code highlighting produced by Actipro CodeHighlighter (...  阅读全文

posted @ 2012-11-17 21:42 paulwong 阅读(4668) | 评论 (1)编辑 收藏

三种使用 Rational Team Concert 进行每日例会的方式

敏捷协作——Daily Scrum 的重要性

我不仅发挥了自己的全部能力,还将我所仰仗的人的能力发挥到极致。
——伍德罗·威尔逊,美国第28任总统(1865—1924)

只要是具备一定规模的项目,就必然需要一个团队。靠单打独斗在自家车库里面开发出一个完整产品的时代早已不再。然而,在团队中工作与单兵作战,二者是完全不同的。任何一个人的行为都会对团队以及整个项目的生产效率和进度产生影响。

项目的成功与否,依赖于团队中的成员如何一起有效地工作,如何互动,如何管理他们的活动。全体成员的行动必须要与项目相关,反过来每个人的行为又会影响项目的环境。

高效的协作是敏捷开发的基石,面对面的会议是最有效的沟通的方式。每日例会(Daily Scrum)是最早引入并被极限编程所强调的一个实践。它是将团队召集起来,并让每个人了解当前项目进展状况的一种会议。它是一个快速的会议,每个参与者只能被给予很少的发言时间(大约两分钟)来介绍自己的项目进展概要。为了保证会议议题不会发散,每个人都应该只回答三个问题:

  • 昨天有什么收获?
  • 今天计划要做哪些工作?
  • 面临着哪些障碍?

Daily Scrum 有诸多好处:

  • 让大家尽快投入到一天的工作中来。
  • 如果某个开发人员在某一点上有问题,他可以趁此机会将问题公开,并积极寻求帮助。
  • 帮助团队带头人或管理层了解哪些领域需要更多的帮助,并重新分配人手。
  • 让团队成员知道项目其他部分的进展情况。
  • 帮助团队识别是否在某些东西上有重复劳动而耗费了精力,或者是不是某个问题有人已有现成的解决方案。
  • 通过促进代码和思路的共享,来提升开发速度。
  • 鼓励向前的动力:开到别人报告的进度都在前进,会对彼此形成激励。

总之,Daily Scrum 能帮助所有的团队成员全心投入到项目中,并且一起向着正确的方向努力。IBM® Rational® Team Concert (RTC)对于团队来说,已经被证明是一种在软件开发过程中进行协作的高效方式。RTC 实现了源代码管理与工作项管理的完美集成。它能够帮助进行敏捷计划、并生成报告,方便管理工作项,并且它还提供了一种有效的框架来支持每日例会(Daily Scrum)。下面,本文将介绍三种使用 RTC 进行 Daily Scrum 的方式。

在 RTC 里使用默认的 sprint backlog 进行 Daily Scrum

双击打开项目当前所处于的 sprint backlog(sprint 是 scrum 中的术语,指敏捷开发周期中的一个迭代计划),如图 1 所示,在窗口底部选择“Planned Item”标签,在窗口右侧选中 Schedule Risk 单选按钮,窗口将呈现将列出当前 sprint 中的所有工作任务项 story 和 task。在进行 Daily Scrum 时,团队成员可以根据这个窗口,逐一更新这些任务的状态。


图 1. 默认的 sprint backlog 窗口
图 1. 默认的 sprint backlog 窗口 

用户可以展开任务项来显示其各个子任务,了解子任务是由谁负责,进展等详细信息。如图 2 所示。


图 2. 展开的默认 sprint backlog 窗口
图 2. 展开的默认 sprint backlog 窗口 

这种召开 daily scrum 的方式非常简便。它能够展示整个项目的进展和最近的变化,但是任务项不是按照团队成员分组的,不太适应于了解各个团队成员状态。为了解决这个问题,本文下一章介绍另一种用 RTC 进行 daily scrum 的方式。

在 RTC 里定制 sprint backlog 进行 Daily Scrum

定制的 sprint backlog 又称为“开发者任务一览表”。一览表按照团队成员展示任务,每一行表示一个正被开发的任务。任务显示在第一列,其余几列显示其子任务的开发状态:ToDo(将要做),In Progress(正在做)和 Done(完成)。并且,各任务根据其当前状态,分别用不同的颜色显示,一目了然。定制 sprint backlog 的具体步骤如下:

  1. 打开项目所在的当前 sprint backlog,点击 Copy 拷贝这个计划的模式。如图 3 所示:

图 3. 拷贝当前计划的模式
图 3. 拷贝当前计划的模式 
  1. 修改某些选项的值。比如修改定制 sprint backlog 的名字为“Developer's Taskboard”,风格选择“Taskboard”,分组选择“Owner”,排序选择“Creation Date”,进度条选择“Progress”。如图 4 所示:

图 4. 修改某些选项
图 4. 修改某些选项 
  1. 修改视图的布局。从窗口底部选择“View Layout”标签,从左侧列表中选择“Effort Tracking”和“Owner”到右侧列表。如图 5 所示:

图 5. 修改视图布局
图 5. 修改视图布局 
  1. 为 sprint backlog 添加色彩。从窗口底部选择“Colorize”标签,根据自己的需要添加、修改、删除各种颜色。如图 6 所示:

图 6. 添加色彩
图 6. 添加色彩 
  1. 保存所做的修改,用户将得到自己专属的 sprint backlog。显示的效果如图 7 所示:

图 7. 用户定制的 sprint backlog 显示结果
图 7. 用户定制的 sprint backlog 显示结果 

当工作任务项不是很多的时候,这种方式非常适合进行 daily scrum。但是,如果当迭代计划中的工作任务项很多时,这种方式就不再适合了。为了解决这个问题,下一章将介绍最后一种用 RTC 进行 daily scrum 的方式。

在 RTC 里创建自定义的查询进行 Daily Scrum

一般在进行 daily scrum 时,项目管理者需要查询出最近正在被修改的任务,这包括状态是“New”和“In progress”的任务。创建这种自定义的查询具体步骤如下:

  1. 在“Work Items”下的“My Queries”上点击鼠标右键,选择“New Query…”,如图 8 所示:

图 8. 创建一个查询
图 8. 创建一个查询 

在打开的窗口中点击“start from scratch”,如图 9 所示:


图 9. 从零开始创建一个查询
图 9. 从零开始创建一个查询 
  1. 在打开的窗口底部选择“Conditions”标签,在窗口右上角点击加号,选择“Add Conditions…”添加查询条件,如图 10 所示:

图 10. 添加查询条件
图 10. 添加查询条件 

根据需要选择一些查询条件,为这个查询取一个名字,保存。如图 11 所示:


图 11. 添加如下查询条件
图 11. 添加如下查询条件 
  1. 共享刚刚创建的这个查询,供每个团队成员使用。如图 12 所示,在窗口底部选择“Details”标签,在窗口右上角点击“Share”,选择“Team or Project Area…”。

图 12. 共享查询
图 12. 共享查询 

在弹出的窗口中选择共享这个查询给哪个团队,如图 13 所示,然后点击 OK,保存。


图 13. 选择共享团队
图 13. 选择共享团队 
  1. 根据需要定制查询结果的布局,包括选择显示哪些列,按照哪些列排序等,如图 14 所示:

图 14. 定制查询结果的布局
图 14. 定制查询结果的布局 
  1. 显示查询结果。如果按照上述配置,查询结果将在“Work Items”标签下显示,如图 15 所示:

图 15. 查询结果
图 15. 查询结果 

这种召开 daily scrum 的方式能够列出在最后一天工作任务项的变化,以及哪些工作任务项还没有完成。但是它们都是以列表的方式显示出来,界面友好性和可读性不是很好。

结束语

本文介绍了 daily scrum 在团队项目开发中的重要性,以及三种用 IBM Rational Team Concert 进行 daily scrum 的方式:默认的 sprint backlog,定制 sprint backlog,和创建自定义查询。这三种方式各有其优缺点:

  1. 当团队人员比较少,一般小于 5 人,并且只是关注当前 sprint task 时,比较适合采用第一种方式进行 daily scrum。使用它可以清楚的看到 Task 与 User Story 之间的层次关系,以及 User Story 的开发进度。
  2. 当团队人员比较多,规模比较大,有自定义的 RTC Task 或者有子 Scrum Team 时,比较适合采用第二种方式进行 daily scrum。使用它与第一种方式一样,也是只关注当前 sprint task,但它还可以按照自定义的方式分组显示,更清楚的了解每个团队成员的 task 状态。
  3. 当希望关注团队中所有 Task,而不仅仅是当前 sprint task 时,前两种方式都无法满足 sprint plan 的显示需求,可以考虑使用第三种方式自定义创建查询,进行 daily scrum。与前两种方式相比,它更加灵活,建立查询的条件非常丰富,可以根据需要创建多个查询同时使用。

请用户根据自己的需要选择不同的方式进行 daily scrum,进行高效的团队项目开发。

@import url(http://www.blogjava.net/CuteSoft_Client/CuteEditor/Load.ashx?type=style&file=SyntaxHighlighter.css);@import url(/css/cuteeditor.css);

posted @ 2012-11-17 11:16 paulwong 阅读(398) | 评论 (0)编辑 收藏

仅列出标题
共116页: First 上一页 74 75 76 77 78 79 80 81 82 下一页 Last