这次做的项目,涉及到6个子系统的集成,其中有2个是遗留系统,4个是新开发的系统。我负责其中一个新系统
前期还是比较顺利,提前完成了开发计划,并进行了比较充分的代码检视、单元测试和内部测试。但是在进入接口联调和系统集成以后,就变得非常痛苦,这2周平均每天工作时间超过16个小时。但是整个系统还是没有稳定下来,还是有非常多的问题,而且每天的效率非常低,大部分的时间都是花在等待中
为什么这次在开发这么顺利的情况下,在项目后期却会陷入这么糟糕的局面?怎么样才可能比较顺利地完成一个较大的系统?我从项目启动的第一个环节开始,对每个过程进行反思
1、做好需求
现状:我认为这次的需求做得不到位。一方面猜想了比较多的需求,与客户却没有充分地沟通过,这就造成工作量的浪费。更糟糕的是为了满足太多的需求,也影响了系统的架构,造成系统架构的臃肿,这个在后面会详细说明。另一方面,或许有一些客户真正关注的需求,我们却没有实现,这个可能会给后期系统的推广造成困难。
原因分析:会出现上述的2方面问题,我觉得本质的原因就是和客户的沟通太少。需求基本都是凭空猜想,或者根据部门内业务专家的经验推断。虽然业务专家确实有比较高的业务水平,但是毕竟不能完全代表真正客户的想法。所以与客户的沟通太少,也没有积极地邀请客户参与到系统的每一轮阶段性展示活动中。这使得我们系统的需求做得比较糟糕。
总结:应该采用敏捷开发的方式,在尽量小的迭代周期内拿出系统的小版本给客户演示,按照先核心需求,再逐步丰满的方式,不断地调整需求。与客户充分沟通,既保证满足核心需求,又避免工作量的浪费
2、宁可没有架构师,也不要找一个扯淡的
现状:或许大公司都会有这样那样的政治因素,这次项目的所谓“架构师”就是一个大忽悠。
这个系统,估算是200左右的并发规模,可以认为相当于不存在并发问题;然后数据量的规模也不大。如果简单处理的话,可以是一个逻辑架构和物理架构都非常清晰明了的系统。我个人认为,就一个应用服务器+一个数据库服务器,就绰绰有余了。即使以后数据库压力大了,再上数据库集群不就行了。毕竟这是一个业务系统,不是一个互联网应用,根本就不存在并发和海量数据的问题。这样简单处理,我认为是合适的
但是这个“架构师”,整上的东西就很多了:
要先上2个数据库,一个存放业务数据,一个存放资源数据。这样2个数据库就要同步来同步去。这周另外1个小组,就为了这个同步的问题熬了一周的通宵
子系统之间要走所谓“服务框架”,其实就是对CXF的一个封装,没任何意义,纯粹引入复杂性。其实每个子系统开放一个Servlet都可以了。这个服务框架带来的问题,伴随6个子系
统整个开发流程,开发时间至少增加1/4,没带来任何价值
然后来个日志框架,其实花半小时自定义一个Appender的扩展就可以解决问题
业务可能有变化的地方,整个OSGi。不好意思,开发已经延期2周了,现在大家都还没做出来。我说真有新功能,再发布一个升级的PATCH不行?何况这个系统就没热部署的需求,业务也很稳定,强行上个OSGi,这不是有病?
本来还要上分布式,幸好大领导终于意识到风险太大,制止了。。
关键是这位“架构师”,提出了上述种种方案之后,人没了?你找他谈实现,他就给你说原则。如是再三之后,我得出一个准确判断,这些方案,他自己就听说过名词。是干什么的?知道一点。怎么实现?不好意思
总结:架构要保持简单,一个数据库能解决的问题,就不要弄多个数据源同步。不准备开放给外系统调用的接口,就不要上web service。什么扯淡的分布式,OSGi,我就不评论了。当然我不是说这些技术不好,其实我对这些技术也非常感兴趣,但是毕竟是做系统,不是在玩过家家。用在合适的地方的技术,解决实际问题带来价值的技术,才是好技术。过度架构,就是200个兄弟的累死累活,就是系统的延期,就是项目跑不起来
3、其实做好上面的2项:分析清楚需求,不瞎做。保持适当架构,不折腾。一个系统到后期就不会像这次这么痛苦了,我认为上述2项是最重要的。后面的都是比较细节的问题
4、越早对齐数据库越好
服务端有2个数据库(Oracle),终端有1个数据库(SQLite),所以存在数据库字段不统一的问题。前期我们自己跑服务桩,啥问题没有,到了集成的时候,各种数据库字段对不齐,有的是类型不匹配,有的是长度限制不匹配,有的是非空约束不匹配,等等。大家都知道,到应用稳定以后,再去改数据库模型是相当痛苦的,因为从数据库字段,到模型对象,到DAO,都要改,不仅开发有工作量,测试也有工作量,越到后期,这个代价越大,所以应该尽早地对齐数据库
这个责任在我,是我系统集成经验不足造成的。这个教训我记住了 5、仅仅在文档层面对齐接口没用,对于涉及到多个子系统集成的项目,可以“接口驱动开发”
前期所有6个子系统,都是自己玩自己的。单元测试都不可谓不充分。也都模拟了实际场景,终端为了模拟测试业务场景,我还亲自写了一个完整的服务端桩。但是即使这样,到了集成的阶段,系统还是各种跑不起来。
其实前期领导就很重视对齐接口,每个子系统的负责人,花了一个星期来写接口文档,各子系统互相评审,并签字确认。到头来,没有用。因为实际运行的不是文档,是代码。文档虽然已经描述清楚了交互协议,每个字段也都对过了。但是接口太多,有的接口也相当复杂,在没有实际测试的情况下,谁也不能保证100%是按照文档来操作的,何况文档也不能保证
是100%正确的,毕竟在开发过程中,变更是绝对会发生的,但是未必文档就能及时刷新并知会对方系统。
所以这种涉及到多个子系统集成的项目,我强烈认为应该以接口来驱动开发。每个子系统,首先实现接口。当然在开发早期,每个子系统内部的业务逻辑肯定没实现,不可能有真实业务数据,但是这种时候,可以先传递符合格式的假数据,照样可以让接口跑起来。这样到系统集成阶段,就节省了非常多的工作量。我敢保证效果绝对比这次好。
6、早点集成,不能拖到最后一起集成
这条其实和上一条强相关。这次项目基本做到了每天SVN上的代码是可运行的小迭代。但是都是每个子系统自己玩自己的。应该缩短集成的周期
当然由于每个子系统的开发进度不同,很难做到像单个子系统内部一样每日集成(我怀疑是根本不可能)。但是至少也应该可以做到半个星期或者一周集成一次。把集成的工作放到前面,可以更早的暴露问题,更早的调整。效果一定也比这次强
以上是本次项目过程中个人的一点总结。此外在代码层面,也有一些心得。包括Android应用的分发模式,Android应用的简单ORM框架,Android日志规范,接口设计,Android服务管理等。待系统集成完之后,再总结一下
开发者大会最新议题发布,八折抢票! 2013年1月微软MVP申请开始啦! ―第一次亲密接触‖—有奖征文活动
使用JAVA编写报文接收端的注意事项
分类: SOCKET 2009-04-24 17:18 1182人阅读 评论(1) 收藏 举报
使用JAVA编写报文接收端的注意事项
在JAVA中编写报文服务端,是非常简单的事情,因为JAVA已经为我们封装了TCP/IP这些东西,我们不需要去关注这些;它对我们提供了方便操作的ServerSocket类,如果不考虑其它因素,那么要启动一个服务端,只需要简简单的几句代码: //以下启服务端,其中6500表示用于侦听客户端连接的端口 ServerSocket serverSocket=new ServerSocket(6500); while(true){
Socket socket=serverSocket.accept(); if(socket!=null){
System.out.println(\有客户端连接过来\); //下面对客户端做相应的动作,这里省略 } }
从上面可以看出,我们根据没有去管理那些协议去怎么样实现,走的七层网络协议还是四层网络协议,这些对我们来说都可以暂时不去管。但是,这种东西只能说你看到了希望(没有像现在的中小学奥数班一样,在你一开始的时候就让你对学习失去兴趣)、有了信心,后面要做什么事情,就是在现在的基础之上扩充,使苦适合于真实的环境中,就像大家经常说的一句话一样:社会,是现实的。我们还需要考虑以下方式的事情:
1、正确性与精确性。这句话是从软件工程上学来了,因为不管是做什么软件,写什么代码,如果连正确都谈不上,那这个东西存在的意义也不大。这里就是要能够正确处理客户过来的请求,不要将客户查询的请求搞成了更新或者是删除,那后果可就严重了,所以这是第一个要注意的。
2、性能与效率。当你写的代码能够做正确的工作的时候,不管是一分钟完成还是十分钟完成,再甚者N个小时,只要是正确的,客户都可以接受,当然你得给客户N个多可以接受的理由,如:查询的时候要关联很多表,数据量太大,计算的时间比较久,就如我现在这边的全行日终跑报表系统,客户几个小时都可以接受。但是我们不能够满足于这样,我们是程序员,在可能的情况下让自己写的东西跑得更快,而不仅限于跑得完就是了,就像马拉松,人人都可以跑,就算不得冠军也不要搞个最后一名吗!其中有一块特别需要优化的就是要采用连接池替代单一的“请求-连接”模式,成熟的、开源的、稳定的连接池网上很多,比较常用的有APACHE的DBCP、W3C的C3P0、Hibernate自带的连接池等等,性能都还不错,这样可以节省系统资源与加快运行速度。
3、能够处理的最大并发数。这时就要采用线程池的方式,不要来一个请求就新启动一个线程,这样请求越多,要求生成的线程数也越多,达不到调节系统资源的目的;采用线程池可以自动调节,如果请求少的情况,就少生成连接数,如果多的情况下就多生成,再如果超过了最大的连接数,那让就他们等到有空了再说;这同做生意一样,桌子板凳都没有了,就只有等到别人吃完了。JDK自1.5开始就自带有连接池,需要我们关心的东西,只需要调用的时候设置向个参数,如最小连接数、最大连接数、线程的生存时间及单位等,它会帮忙管理 与调用线程,以保证系统性能的最大化,启动调用和单独采用Thread启动线程没有什么区别。最简单下面两句代码就OK:
ThreadPoolExecutor threadPool = new ThreadPoolExecutor(5, 50, 3,TimeUnit.SECONDS,
new
ArrayBlockingQueue
ThreadPoolExecutor.DiscardOldestPolicy());
threadPool.execute(new MsgListener(clientSocket));
4、代码不一定要自己写。没错,能够不自己写的就最好不要写,因为代码是写的越多错的机会越多,并且还要花时间去调式,有够用现成的工具,如上面的线程池、连接池等等,都可以说是比较优秀的,因为他们都经过时间的检验,写这些代码的都是比较高的人,我们完全可以信赖。如果要对数据进行操作,我的方式是先用POWERDESIGNER做CDM,把关系都理好了,再生成PDM,然后写到库,然后再通过HIBERNATE的MIDDLEGEN工具,生成Hibernate映射文件,再通过Hibernate的HBM2JAVA,再生成基础的代码,这样你可以省去不少的时间,并且可以保证性,然后你再写关注自己业务方面的东西,开发起来又快又稳。 5、保证客户端过来的请求都能够被处理。请算你采用连接池,但有些时候遇到连接确定太忙,数据库服务忙不过来,这个时候去数据库操作,如你要将客户端过来的报文写到库中保存,但是HIBERNATE给你报过\,那你的这表交易就不处理了吗?不可能,这是金融系统,由人行的发过来的,你以为它还等你说,如果失败次数超过了规定了,那就等着挨批吧。所以这个时候就算没有成功,也要说我成功了,后面再把失败的给做进去。我这里采用将失败的即发生异常的交易的报文全部放到XML中,然后再通过线程定时去检测并处理,这样就可能保证只要是接到了的请求,在线程池没有严重超标的情况下,所有的请求都可以被处理;为什么说没有严重超标呢,因为如果线程池超时过久,就会被线程池给KILL掉,所以请求就莫明其妙的没有了,但也不能够将线程的生存周期设置太久,否则这样也不利用其它线程的处理,会导致更大的问题。
6、参数配置化。尽量将需要用到的变更,与客户环境有关的,都做成参数化,这样就不需要我们再去修改程序了。
7、多做测试。在完成某个功能,或者是某个模块的时候,一定要测试,否则等到后面来一运行,什么问题都一下子出来了,找问题都不知道从什么时候找起。边做边测试,这是只有好处没有坏处的。如这里你一定要有客户端,去测试你的服务端,测试并发的性能与处理的结果的正确性与快速性等。