Posts Tagged ‘经验’

异步调用

June 29th, 2010

1 什么是异步

异步显然是和同步相对应的。同步调用,被调用者要执行完所有的代码才将执行流程交给调用者,在被调用者执行的过程中,调用者只能被动的等待;而异步,则是被调用先将执行流程转给调用者,然后自己再继续执行。如果调用者必须在得到被调用者的所有执行结果才能确定下一步该做什么的话,那么这种情况就该使用同步调用;相反,如果调用者不需要被调用者的执行结果,那么就可以使用异步。

2 怎样异步

在单线程或单进程等单一执行流程的情况下,实现不了能让被调用者个调用者并发执行的异步调用,这种情况下被调用者只能先将要执行的任务环境记录下来,带调用者执行完成后,再回头来执行之前要执行的任务,这样的方式其实算不上是异步,称之为推迟执行更贴切。

在多线程或多进程环境里,才能真正的实现异步,实现调用者和被调用者并发执行的异步调用。被调用者只要开启一个新的线程在新线程里执行任务,而立刻返回让调用者在当前线程继续执行,这样就可以实现异步调用了。

借助于ejb 3.1 , spring 3, seam这样的容器和框架,实现异步调用的方法非常简单,只需要再方法上加上指定的Annotation就可以了。使用spring 2的时候,可以注入taskExecutor,用taskExecutor.execute(Runnable r) 方法来实现异步。另外,通过JMS也可以实现异步。

3 异步有什么好处

说异步比同步调用性能好并不准确。假设 被调用方法里面要完成两个任务,分别耗时 t1和t2,从开始调用到调用返回的耗时,即响应时间,同步调用的时候,Rs = t1 + t2,异步调用的时候Ra = t1 + a1,其中a1代表为了异步调用开启新线程或新进程的耗时,如果要Ra < Rs,显然要求a1 < t2,可见如果异步执行的任务并不比启用异步执行耗时太多的话,异步执行也不会缩短响应时间。如果计算完成同等任务的总耗时的话,同步的时候 Ts = t1 + t2,异步的时候 Ta = t1 + a1 + t2,可见Ts < Ta,此时异步没有任何好处。在要求响应时间非常短的互联网应用中,合理的采用异步可以提高响应速度,提升程序的负载能力。快速的响应用户的请求,将耗时多而用户不急于知道结果的任务交给有限的线程来执行。

然而,对于Java EE这样的环境,一个事务的边界往往是在一个线程里的,所以使用异步之后,就失去了事务的保护,异常后的数据补偿工作增加了程序的复杂性,也让程序更容易出错。一个不好的异步实现机制,往往给程序带来更多的隐患。比如,为每一个异步执行的任务都新建一个线程,很可能在并发数到一定程度的时候,由于创建过多的线程,达到性能瓶颈,甚至于导致程序崩溃。

4 什么时候用异步

首先只有调用者不需要立刻知道结果的任务才能被异步执行;其次被异步执行的任务耗时多于使用异步执行本身的时候,采用异步才值得;再次,要求响应时间短的时候采用异步才有意义。

典型的例子:用户支付订单后要立刻告知用户支付是否成功,并给用户发送邮件,通知仓库发货,告知物流送货。这种情况下,用户只要知道支付成功就行了,发送邮件等任务用户可以过会再感知到,而且发送邮件等任务很耗时,用户肯定不愿意等待完成所有一切之后才知道支付结果。这种情况下,将发送邮件等任务异步执行就很合适。

另外一个例子,A,B两个系统每隔一个小时对一次账,如果B发现账户和A系统不一样,则自动调整。尽管A不需要知道B的调整结果,但是A完全可以等待B执行,反正也没人等也没人急,而且对账频率低负载低,这种情况下就没有必要异步执行调整动作。

  • Share/Bookmark

漫谈PHP和Java

September 27th, 2009

我为公司内部交流而写的PPT,谈论PHP和JAVA相关的话题。在这里查看:

漫谈PHP和Java

  • Share/Bookmark

IO和性能

July 3rd, 2009

最近几天看了几家互联网公司的和架构有关的PPT,有支付宝,linkedin,douban等。这些公司所使用的具体的技术有差别,但是为了能做到支持大规模访问,他们都在与缓慢的IO做斗争! 比如说豆瓣网的历次架构变迁,不是因为磁盘IO成为瓶颈,就是因为网络IO成为瓶颈。如果能把IO的问题解决掉,那么对于互联网企业来说,性能问题就解决掉一半了,我想。

与飞速的CPU和内存相比,磁盘和网络的IO慢如蜗牛,这是问题的根源。如果没有大规模廉价而又足够快的存储方案出现的话,这个问题还将继续下去。我们只能期待这一革命性的技术出现来从根本上解决这一问题吧!

那对于现在的我们该怎么办呢?大家的想法可能都是一样的,一是加快IO速度,二是避免不必要的IO。豆瓣曾经通过购买更高性能的硬盘来解决这个问题,如果想要更好的效果,需要投入更多的资金来购买整套的高性能存储方案,但是这成本太高了,很多公司后来都会放弃。于是大部分公司都会极力采用后者,就是避免不必要的IO,缓存就是用来做这样的事的。

将低速介质上的数据放到高速介质上,访问者先访问高速介质,在找不到需要数据时才访问低速介质,以此来提升访问数据的速度,这样的技术就是缓存了。设在一段时间内有m次没有命中缓存,n次命中,而访问一次缓存的时间是t1, 访问慢速介质的时间是t2,那么为了是缓存有效就得满足不等式: m * (t1 + t2) + n * t1 < (m + n) * t2, 于是有 m / n < t2 / t1 -1 。由此可见,在介质的速度比一定的情况下,命中比率要高于一定的值,缓存才有效果。提高命中比率,就是要把那些最常用的数据放到缓存里。另一方面,缓存里数据如果被修改了,就要同步到低速存储介质中,同样的,如果低速存储介质的数据被修改了,也要同步到缓存里。如果加上同步的开销,就需要更高的命中率。提高命中率和保证数据的同步,是使用缓存的两个艰巨的任务。

豆瓣网使用memcache,还使用专门的nigix服务器来提供图片的服务,其实也是cache。linkedin则使用了数十台大内存的服务器作为缓存服务器,运行着用c++写的专门的缓存程序。可见这些网站在使用缓存方面都是不遗余力呀。如果能把缓存用好,那么就把一大半的IO问题解决掉了。

IO往往会成为程序的瓶颈,因此,程序员应当时刻的注意,你写的代码是否在进行IO操作,是否可以减少IO操作,等等。

  • Share/Bookmark

DRY, Don’t Repeat Yourself!

July 1st, 2009

DRY,就是不要写重复的代码,这也是程序设计的一个黄金准则之一。

1 引申自关注点分离

其实这一条准则,也是从关注点分离引申而来的。如果完成相同或相似功能的代码片段,在一个程序的很多地方出现,那么无疑会使包含它的代码多了一个关注点。把相同或相似的代码抽象出来,放在一个地方,这样就可以使调用者减少一个关注点,同时将所有的对同类问题的关注合并到一个地方。比如说,判断一个用户是否登录,这一功能被很多地方使用,如果把这一功能包装成函数放到一个地方,需要使用的地方只要引用一下函数就可以了,这样引用者就不用关心如何判断一个用户是否登录了。

2 好处

2.1 提高效率

同样的功能,完成一次就可以了,如果每次要使用轮子的时候,都重复发明轮子,那是对人类智慧的浪费。如果发明一个万用轮子不容易,那么发明一个够用的可以反复使用的轮子就可以了。

2.2 更易读

如过你打开一个程序的源代码,发现有大量类似又稍有差别的代码,你还有多少心情读下去吗?要读下去的话,你就得像diff工具一样去比较类似代码间的细微差别,还要揣摩它的含义。这样的代码读起来太痛苦了。因此,如果没有重复的代码,就没有这份痛苦。

2.3 更易维护

如果你用类似的算法,重复写了很多的程序,结果却发现这个算法有个错误,那你不得不小心翼翼的找到所有的这样的程序,并一个一个又一个的修正。那是一个多么无聊的工作呀。如果漏掉了,或者将其中的某些一不小心修改错了,还得回头再重新修改,那台痛苦了!所以充斥着copy&paste的代码,是维护者的噩梦。

3 怎么做

程序语言和编程方法的进步,也就是人们在实践DRY的过程。要做到DRY,就是要不断的通过抽象,把重复的功能抽象并包装起来。我们先是有了函数和子程序,后来有了类和方法,等等。我知道,为了DRY,还会有新的工具和方法论被发明。

  • Share/Bookmark