• 2006-02-18

    有状态的程序

    Tag:向上走

    版权声明:转载时请以超链接形式标明文章原始出处和作者信息及本声明
    http://dreamhead.blogbus.com/logs/1949612.html

    我现在工作的一个程序是读入Video(或连续帧图像),然后逐帧进行处理。当前帧处理的结果需要依赖于之前的处理结果,所以,这个程序的状态逐步累加的。记得刚开始开发这个程序的时候,由于程序不稳定,经常出现崩溃的现象,为了找到原因所在,我们只有选择从头开始运行,虽然可以从中间开始运行,但由于状态累加的不同,bug很可能无法重现。那是一段相当逍遥的日子,因为当时的程序是Matlab版本,处理一帧图像就要好几秒的时间,而且崩溃通常发生在数百之后,出现一个错误,通常要花几个小时来重新运行,所以,我们会在相当长一段时间内无所事事,就这样,大家有了很多时间聊天,增进了彼此的感情,而且领导绝对没有理由说我们不工作。为此,领导特意给我增加了一台机器,美其名曰提高工作效率。

    后来,我良心发现,认为这种工作方式着实不怎么样,于是,我决定开发程序的持久化功能。简而言之,就是把程序运行过程中的数据存起来。这样的话,一旦程序崩溃,我们就可以利用保存起来的数据,重建原有的状态。我知道,熟悉J2EE开发的人对持久化这个词有着特别的感情,实际上,本质上是完全一样的,存储而已。

    在《管窥OS——进程透明化》中,我曾经讨论过持久化的问题,持久化的目的就是为了恢复。实际上,对于那些问题的思考主要源自我在这个程序所写的持久化部分。持久化的关键在于数据的持久化,但并不是所有的数据都需要持久化,在应用这个层次上,我们需要保留的数据是那些需要积累的状态,因为其它的数据或者可以通过它们恢复出来,或者只是一些临时数据。在我们的应用中,用来积累状态的主要是一些全局变量。再有就是保存的时机,每当一帧处理完毕的时候,都会把相应的信息持久化,这也符合我讨论过的原子操作之后,只不过,这个原子操作的规模大了一些。在我们的应用中,有一个有趣的细节,因为涉及到图像操作,持久化的过程中要存储一些图像,如果存储为JPG图像,恢复过来就会有些问题,这是因为JGP属于有损压缩,也就是存进去的矩阵和读出来的矩阵会有所差异,虽然在视觉上几乎看不出来,不得已,存放格式选择了BMP。

    我不喜欢编写有状态的程序,因为它会带来很多的问题。记得从前编写服务端应用的时候,我们的应用希望能够做集群,从而带来更好的水平扩展性。分析的结果,发现大部分请求几乎可以在不做任何改动的情况下进行扩展,因为它不会对服务端状态有任何改变,只是一来一回。所有的问题都来自我们转发异步请求,因为我们必须在服务端记录请求的信息,这样的话,对于其它服务器处理之后的应答,我们才能进行正确的匹配,进行后续处理。就是因为保存了这些信息,扩展的复杂度一下就上来了,因为一旦做了集群,一种可能的结果是,一台机器发起的请求,应答回到了另外的机器上,这种情况下,我们仍需要进行正确的匹配。这种情况与单机处理截然不同,所以,需要进行额外的设计,而这一切的罪魁祸首就是程序中的状态。

    在宏观层面上,应用的性质决定了它是否有状态,这是程序员无法决定的,但是我们可以通过设计,将状态降至一个可以接受的程度。在微观层面上,我们可以通过自己的代码,将状态限制在一个很小的范围之内,让我们编写的大部分函数都属于无状态的。我已经见识过太多本来可以无状态的有状态函数,结果是牵一发而动全身,由于状态带来的副作用。之所以这样的代码经常存在,仅仅因为这种代码编写起来很方便,直接访问全局变量,省去了声明参数之苦。程序员是应该懒惰,但不是这么个懒法,因为这只是一时之快,后面则是无尽的痛苦在等待。本来应用的折磨已经够受了,程序员们何苦为难自己呢!

    当然,有状态的程序并不那么可怕,毕竟,身经百战的我们有着兵来将挡的本领。





    引用地址:

    评论

  • 有限状态机,在很多领域内非常有用
  • 有点不明白:

    如何知道程序什么时候Crush呢? 如果不知道,那只能是实时的记录LOG日志,不太可能实时序列化?
    dreamhead回复chenxiujie说:
    你不知道程序什么时候会崩溃,所以,记录一些东西才是有必要的。

    我在这篇blog里谈到的不仅仅是日志,而是程序的状态,通过它,可以重建系统当时的运行情景。
    2006-02-21 14:02:54
  • 无奈,spring论坛要3天才可以发帖子,急问个问题先:



    <bean id="securityInterceptor" class="com.SecurityInterceptor"/>

    <bean id="secAdvisor" class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor">

    <property name="advice">

    <ref local="securityInterceptor"/>

    </property>

    <property name="mappedNames">

    <list>

    <value>*</value>

    </list>

    </property>

    </bean>

    <bean id="serviceWithSecurityProxy" class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">

    <property name="interceptorNames">

    <list>

    <value>secAdvisor</value>

    </list>

    </property>

    <property name="beanNames">

    <list>

    <value>remoteService</value>

    </list>

    </property>

    </bean>



    <bean id="remoteService" class="org.springframework.remoting.rmi.RmiProxyFactoryBean">

    <property name="serviceUrl" value="rmi://${rmi.host}:${rmi.port}/BKService"/>

    <property name="serviceInterface" value="com.service.IService"/>

    </bean>



    这个 remoteService 怎么没有自动织入安全拦截呢?