就如Google总监说的,我还没见过没有bug的程序。

即使如此,你还是可以做得更好:采用预估、预演、持续改善的方式,可以让你对你程序更有信心。

比如首先从产品经理那里得知系统将来的在线人数、最大事务并发量以及使用环境等,并将之换算为系统参数。接着尝试编写测试去模拟它,以验证程序是否能胜任。最后如果程序发生错误,想办法改善架构或程序。

预估

一个好的产品经理总是可以告诉你需要的信息,比如系统的规模、可能的在线人数、事务并发量等,你也可以从客户那里获得这些信息。你需要做的就是应用一些数学,将之转换为你测试程序的依据。

比如我有一个公路测速的项目,从用户那获得这样的信息:

  1. 即将有100个测速点,每个测速点都会有一个测速器。
  2. 测速器最快0.7s完成拍摄图片和测速工作,并即时上传图片。
  3. 要完成的功能就是开发一个Web服务(图片服务器,环境不支持ftp)来接受所有点的图片上传
  4. 每张图片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() {

public void run() {

ready.countDown();

try {

start.await();

boolean result = upload();

if(result) {

success.incrementAndGet();

}

} catch (Exception e1) {

} finally {

done.countDown();

}

}

});

}

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自带的监控工具),然后得到这样的图标。

你是如何让老板相信你的程序?_在线人数_02

我发现了问题:我的程序在持续的创建线程。

但是我代码的意图是创建一个设定线程数的共享池,这样即使我循环无法次,它也只会创建和我设定的值一样多的线程。我一开始就考虑了要避免浪费。

直到这里,我又从头仔细阅读了代码,我终于知道自己犯了很愚蠢的错误--就是那句代码,它实现的是每次调用都创建一个线程池,所以线程数线性增加的。我将那句代码被移出方法意外,问题就解决了。

除了线程数,通过工具你还可以看到程序使用的内存、CPU,创建的实例等等。你可以根据这些图表来决定如何改善你的架构和程序。这个在下节在介绍。