就如Google总监说的,我还没见过没有bug的程序。
即使如此,你还是可以做得更好:采用预估、预演、持续改善的方式,可以让你对你程序更有信心。
比如首先从产品经理那里得知系统将来的在线人数、最大事务并发量以及使用环境等,并将之换算为系统参数。接着尝试编写测试去模拟它,以验证程序是否能胜任。最后如果程序发生错误,想办法改善架构或程序。
预估
一个好的产品经理总是可以告诉你需要的信息,比如系统的规模、可能的在线人数、事务并发量等,你也可以从客户那里获得这些信息。你需要做的就是应用一些数学,将之转换为你测试程序的依据。
比如我有一个公路测速的项目,从用户那获得这样的信息:
- 即将有100个测速点,每个测速点都会有一个测速器。
- 测速器最快0.7s完成拍摄图片和测速工作,并即时上传图片。
- 要完成的功能就是开发一个Web服务(图片服务器,环境不支持ftp)来接受所有点的图片上传。
- 每张图片300k;
这个web服务没有任何业务,需要考虑的就是能否支持100个点同时上传图片?关键考虑的有带宽、CPU以及硬盘。所以我们可以这样预估:带宽 = 100 * 300k * 2(大约值,单张图片上传的时间,单位为秒) *sqrt(2)/(1024*0.7)=100M;硬盘容量=测速点的每日车流量*300k*100*天数,而CPU很少情况下需要在这里考虑。
这一步很重要,而接下来的事情就简单多了。
预演
编写模拟程序,这个程序应该是多并发的。
使用java的并发工具包可以轻松实现,比如使用Executors.newFixedThreadPool(并发线程数)创建一个限定线程数的共享池,然后使用CountDownLatch类实现并发 。
ExecutorService executor = Executors.newFixedThreadPool(c); 。。。 private void concurrency() throws Exception{ log.debug("....................... start test ..................."); long sTime = System.currentTimeMillis(); final CountDownLatch ready = new CountDownLatch(c); final CountDownLatch start = new CountDownLatch(1); final CountDownLatch done = new CountDownLatch(c); final AtomicInteger success = new AtomicInteger(0); for (int i = 0; i < c; i++) { executor.execute(new Runnable() {
} ready.await(); start.countDown(); done.await(); long eTime = System.currentTimeMillis(); log.info("....................... time for spend is " + (eTime - sTime)/1000 + " ..................."); if(success.get()!= c) { log.warn("....................... count for files uploaded successly is " + success.get() + " .but it expect to be " + c); } else { log.info("....................... all the file have upload successly! ..................."); } sCount += success.get(); fCount += c - success.get(); Thread.sleep(1000); } |
代码中的concurrency()函数支持并发调用uoload()方法c次,并记录上传失败或者成功的次数。
持续改善
如果需要改善你的程序,首先你得知道它发生了什么问题,着意味着你想要一些工具。
比如,上述程序中的代码ExecutorService executor = Executors.newFixedThreadPool(c);之前被放在concurrency()方法的开头,我使用while(true)循环中调用了该方法,期望它能一直正常运行1天一夜,但是它才10分钟就发生错误了,结果截图如下
我一直调试了这个程序几个小时,一直没有发现问题,后来打开jconsole工具(这是一个java自带的监控工具),然后得到这样的图标。
我发现了问题:我的程序在持续的创建线程。
但是我代码的意图是创建一个设定线程数的共享池,这样即使我循环无法次,它也只会创建和我设定的值一样多的线程。我一开始就考虑了要避免浪费。
直到这里,我又从头仔细阅读了代码,我终于知道自己犯了很愚蠢的错误--就是那句代码,它实现的是每次调用都创建一个线程池,所以线程数线性增加的。我将那句代码被移出方法意外,问题就解决了。
除了线程数,通过工具你还可以看到程序使用的内存、CPU,创建的实例等等。你可以根据这些图表来决定如何改善你的架构和程序。这个在下节在介绍。