`
sunnylocus
  • 浏览: 870070 次
  • 性别: Icon_minigender_1
  • 来自: 美国图森
社区版块
存档分类
最新评论

人在江湖:如何用代码保护自己

    博客分类:
  • Java
阅读更多

  现在上一点规模的系统,特别是金融行业的系统,业务规则复杂,一般是将系统分割成较小的子模块,每个人开发一个或几个模块,模块开发完成后做成一个jar包,供其它的模块调用,待所有模块开发完成后再集成在一起。对于充值系统而言则更为复杂,除了要将系统分解成子模块外,还要与众多外围系统交互,如收单服务商、充值中心、银行等。程序员就是其中一个或几个模块的开发者。

 

本文的讨论的要点是:在系统出现问题时,如何有理有据的保护好自己。

 

     对于软件开发者来说,我们在公司里一般处于弱势群体,每当系统出现问题造成事故的时候,运营人员一般都会将矛头指向研发人员。他们这么做一般是将责任撇清,以防引火上身。当出现事故给公司造成了实际损失,那么公司老板肯定会介入进来,总有人要背这个黑锅的,这个责任会从上级一级一级的压下来,直到推到某个模块具体编码的程序员身上,这时候我们开发者唯一进行反击的武器就是日志,如果日志没有清楚的记录什么时间调用了什么代码,什么时间调用的代码出现异常,异常的原因是什么等详细信息,那么负责编码的程序员也将是一头雾水莫口难辩,屎盆子全都扣到你头上,最后的结果就是背了黑锅走人。

 

     这种情况在我上一家公司已经发生了很多次,在入职的那段时间里Team leader经常提醒我们,编码一定要小心小心再小心,千万不要出错,出事故了我要挨老板的骂,你们有人就得被辞退。在部门svn服务器的文件夹里有专门存放事故的总结报告,一个事故,一个word文档。差不多有11个文档,直接造成经济损失的A级事故也有3个左右,这几个同仁被辞退的时候心情肯定是极其郁闷。前段时间得到上一家公司同事的消息,公司有个技术大牛写的程序和另外一家公司的系统进行业务交互,结果充值报文被人给修改了,金额被修改成了200万,结果这个大牛写的代码不严谨,没有考虑周全,收到响应报文时没有做金额校验就做了转账操作,听说这一下公司就损失了200万,老板娘在公司发飙,把笔记本重重的摔在他办公桌上。最后也是被辞退,无论技术多么牛的人还是小心些好,常言道:小心驶得万年船。

 

 

现在我用做过的一个充值系统的交互图来说明我的观点,如何调用黑洞代码(所谓黑洞代码就是说你将要调用的这个法,对你而言就是一个黑盒子,你不知道这个方法做了哪些操作,你不知道它会出现什么错误)

 

如图:

 

这是一个银联卡充值系统,这个系统的功能是:只要你将可以能够在网上进行支付的银行卡(哪个银行的都可以)和自己的手机号绑定,就可以随时用手机拨打自动语音充值电话给自己的手机号或着他人的手机号充值,这个系统的参与者有IVR语音服务商、收单服务商、电信充值中心。整个系统可划分为IVR语音模块、收单模块、电信指令交互模块、报文加解密模块、支付模块。

 

模块开发明细表:

 

模块名称 开发者 调用模块编号 序号
IVR语音交互模块 张三 2 1
收单模块 李四 5、3 2
电信指令交互模块 王五 4 3
报文加解密模块 那六   4
支付模块 小七   5

 

系统上线时非常的不稳定,充值失败率很高,平均每五笔就有一笔充值失败。问题定位在2和3这两个模块上,两位开发者各就执一词,争的面红耳赤。我们看看李四的代码,李四的代码去调用了王五的代码。

 

李四的代码截图:

 

 

 

用红色框圈起来的代码是王五的代码,王五的代码打成jar包供李四去调用,李四将王五的业务实现用Spring注入进来,然后直接去调用doCharge方法,并将结果返回,看起来没有问题。只不过只是表面看起来没有问题。

 

打问号的代码你敢大胆的这样用吗?

 

风险一:

 

doCharge方法对李四来说就是一个黑洞代码,不知道这个方法作了哪些操作,会不会有错误发生,因为doCharge方法未声明该方法可能要抛出的异常,李四以为这个doCharge方法是安全的,所以没有加try{}catch()代码捕获异常。什么事都怕万一,万一这个doCharge出现了异常怎么办?

 

风险二:

如果doCharge方法因为某种原因产生了死锁,那么你的调用结程死在里面了,永远不返回调用结果,这种情况怎么办?

 

 

风险一的应对措施:

  对于要调用的关键方法,无论它有没有声明要抛出的异常,我们都要对它保持怀疑的态度,加try{}catch捕获,并将捕获到异常,记录日志后,包装下继续抛给上层调用者。让上层调用者知道出错了,异常抛出了你就尽到了通知的义务,系统出问题与你没有关系,不然出了问题问你:你为什么不捕获异常?为什么不抛异常?虽然有很多种理由可以向质问者解释,但还是多一事不如少一事,别偷懒加个try{}catch()捕获可能出现的异常。

 

风险二的应对措施:

 

打个比喻:你是猎人,你要在山洞里抓一只狼崽出来,但是你不确定这个山洞里有什么危险,聪明的猎人会放猎狗进去抓狼崽,如果猎狗进去一段时间没有出来,说明里面有危险,猎人再想其它办法。如果猎人自己进去是有风险的,谁知道这个洞里面是狼还是虎。这个比喻想说的是,如果你要调用一个你认为不太安全的方法,不要用主线程调用(猎人),创建一个调用线程(一只猎狗)去调用,这样做的好处是能够监控调用是否成功,还可以设置调用的超时时间。

 

用这个比喻我们创建一个猎狗工具类,调用黑洞方法时,自动生成调用线程,如果调用时间超时,抛出TimeoutException

采用猎狗模式修改后的代码:

 

 

事后终于找到了bug了,问题出在王五的电信指令交互模块上,发送给电信的报文长度必须符合协议,否则电信那端收到非法包后会将Socket连接断开,协议规定充值金额必须是4位数字,不足4位的,左补0,比如说客户要充值10,补全的就是0010

充值100,就是0100.如果客户充值少于10元,要在左侧补3个0,这个bug出现在王五在处理个位充值时,少补了一个0,结果是客户充值2位数金额的话费就成功,一充值个位数的话费Socket连接就断开,李四的调用线程一直堵塞在这里。

 

如果一开始李四采用猎狗模式的话,出现问题一看日志便知道问题出在哪里,有理有据的指出问题所在,也不用背这个黑锅了。

 

上面的代码适合Jdk1.5以上使用,如果想在jdk1.4使用,请自己改造下。

 

 

PS:应网友要求,贴出猎狗的测试代码,请大家对比

 

普通调用:

 

 

猎狗调用:

 

 

 

 

 

 

 

 

 

67
10
分享到:
评论
31 楼 cj2008am 2011-10-14  
顶一个。。。。。。。。。。
30 楼 a424425044 2011-10-14  
留个记号\
29 楼 zhx_zhx12 2011-10-14  
很好,进程那块学习了
28 楼 sunnylocus 2011-10-14  
sxgkwei 写道
我一直有个疑问。关程序员毛事?人又不是机器,谁能保证不出一点点疏漏。如果一个错误都没有,那要测试人员搞毛?那责任就推到测试人员了,可关测试人员毛事?人又不是机器,谁能保证不出一点点疏漏。那责任归谁呢?
公司文化有问题。出来事情就知道往下推,怪具体的员工。

你的观点一针见血!
27 楼 sxgkwei 2011-10-14  
我一直有个疑问。关程序员毛事?人又不是机器,谁能保证不出一点点疏漏。如果一个错误都没有,那要测试人员搞毛?那责任就推到测试人员了,可关测试人员毛事?人又不是机器,谁能保证不出一点点疏漏。那责任归谁呢?
公司文化有问题。出来事情就知道往下推,怪具体的员工。
26 楼 AliceHR520 2011-10-14  
长见识了。
25 楼 jadethao 2011-10-14  
哈哈哈  。。。。 不错学了!
24 楼 sunnylocus 2011-10-14  
woodhaojava 写道
非常好的查错思路。佩服。

过奖,相互学习。
23 楼 woodhaojava 2011-10-14  
非常好的查错思路。佩服。
22 楼 nuysoft 2011-10-14  
文章是很好的,但是厌恶其中的代码,和能产生以上代码的公司。
我想还是有老板不喜欢一出问题就撇清责任的员工。我身边这样的同事,或调岗或冷藏。
责任感一切的基础。
21 楼 沙舟狼客 2011-10-14  
经验值得学习
20 楼 a276664990 2011-10-13  
给力的楼主。。。文章拿走了。。。非常感谢
19 楼 zhylandroid 2011-10-13  
谢谢,学习了。
18 楼 sunnylocus 2011-10-13  
chenqiuzhen 写道
非常感谢啊,学习了

相互学习
17 楼 chenqiuzhen 2011-10-13  
非常感谢啊,学习了
16 楼 shuangpan.zhang 2011-10-13  
文章 总结了项目中的一些经验,值得借鉴借鉴!以后开发中 应该多注意此类情况引起的错误。
15 楼 haoxiao0216 2011-10-13  
拜读,感谢分享!
14 楼 sunnylocus 2011-10-13  
Mybeautiful 写道
博主列出的两个风险,其实都是 设计者的问题,因为两个风险其实都是接口问题。随意,最起码是 给 李四,王五定接口的设计者的问题。

王五作为被调用者,他必须把可能的异常全部标示;而李四作为调用者必须考虑王五抛出的所有异常。 (是设计者的问题,如果他设计之初也不知道有什么异常,但王五发现可能有异常时,应该会向他反馈,然后他负责更新接口。)

那个 Timeout的问题,就更是王五的问题了。不用多说了。如果是我,我不会搞个猎狗,而是在调用的语句后,加一行log,程序会死掉,就让它死掉好了,跟告诉被人狗死了有区别吗? 如果我总要搞猎狗,那我养狗的成本太高了。


最后必须承认,博主的自保意识,值得我们考虑;毕竟谁都不敢保证,你会遇到好的设计者,勇于承担责任的同事,还有明事理的老板(娘).

哈哈,很幽默!
我之所以这么做,是迫不得已而为之,我被人阴过,所以才有了这篇总结
13 楼 Mybeautiful 2011-10-13  
博主列出的两个风险,其实都是 设计者的问题,因为两个风险其实都是接口问题。随意,最起码是 给 李四,王五定接口的设计者的问题。

王五作为被调用者,他必须把可能的异常全部标示;而李四作为调用者必须考虑王五抛出的所有异常。 (是设计者的问题,如果他设计之初也不知道有什么异常,但王五发现可能有异常时,应该会向他反馈,然后他负责更新接口。)

那个 Timeout的问题,就更是王五的问题了。不用多说了。如果是我,我不会搞个猎狗,而是在调用的语句后,加一行log,程序会死掉,就让它死掉好了,跟告诉被人狗死了有区别吗? 如果我总要搞猎狗,那我养狗的成本太高了。


最后必须承认,博主的自保意识,值得我们考虑;毕竟谁都不敢保证,你会遇到好的设计者,勇于承担责任的同事,还有明事理的老板(娘).
12 楼 桥下一粒砂 2011-10-13  
tedeyang 写道
惊叹的代码!更惊叹的是思路

对程序员是自利,对系统却是无谓的性能消耗。
有多少个并发调用就要启双倍的线程,虽然是缓存的,但是线程要消耗1M heap和325k的native 。

可见一个要创造一个高度互信的社会是多么重要。国内的很多问题都来源于由上到下的信用缺失


深以为然……

相关推荐

Global site tag (gtag.js) - Google Analytics