拆拆拆

程序的拆拆拆

敏捷开发 & 快速迭代

去年实习的时候就听过这两个词,很时髦,很新颖。大体的理解就是,因为创业公司技术壁垒很低,大家都争分夺秒,所以新功能的开发必须要快,一个新的特性开发出来之后,进入一个快速迭代的流程,其实也就是开发-测试-发布的一个流程,通过自动化的手段,将人为干预降到最低,把从代码到交付这个流程贯通起来,快速上线。

技术壁垒这个东西,无非就是时间壁垒。曾经面试的时候跟一个创业公司的CEO聊过,他也给我灌输了这一点,当时我问他产品有没有壁垒,他就很坦然的说,半年。以一个时间跨度去衡量技术跨度,我觉得OK。同样一种服务,厉害一点的团队,可能半年开发完,那一般一点的团队,一年,一年半也能写个差不多。日光底下又没有新鲜事,无非就是写的好坏的问题嘛,但这些都可以通过优化,重构去解决。好的代码是重构出来的,我很同意这句话。

而敏捷开发,我理解为,拆拆拆。从定义上讲:

敏捷开发是一种以人为核心、迭代、循序渐进的开发方法。在敏捷开发中,软件项目的构建被切分成多个子项目,各个子项目的成果都经过测试,具备集成和可运行的特征。换言之,就是把一个大项目分为多个相互联系,但也可独立运行的小项目,并分别完成,在此过程中软件一直处于可使用状态。

项目的概念,可以是一个单纯的project,工程上的的projet,交付的产物是一个程序;但从项目经理的角度出发,一个项目则是可以给用户带来价值的一组服务。所以这里有点模糊,但我个人理解,项目就是repo,服务就是service,两者可以联系在一起,也可以不联系在一起,一个repo可以就是一个service,但也有可能是一个service的一部分。其实结合docker的stack和container的关系,就容易理解:stack中定义一组container,一个stack对外提供服务,但一个container组成的service也可以看作为一个stack。

敏捷开发即将原先的大项目拆分成一个一个的小项目,不同的小项目组合起来就是这个“大项目”,即“服务”。但也有可能,这个“大项目”也只是某个服务的一部分。小项目之间互相独立,只要定义好接口,不关心内部实现。这样就能以程序解耦为基础,在人员上解耦,大家互相独立的开发,写好需求,写清楚需求,这个IO流就跑起来了。

其实程序嘛,无非就是个IO,给个输入,程序给出输出。只不过大程序完成了很多事,小程序只做一件事而已。

也谈微服务

近些年,“微服务”这个词也挺时髦的。很幸运,一开始去的公司比较时尚,由于是做Docker服务的,所以接触Docker也还不晚。而Docker和微服务,我个人认为是紧密联系在一起的。

什么是微服务。我们知道服务是对外提供功能的一组程序,一个服务,或者说,公司的一组业务,是由很多不同的组件构成的,那微服务就是在组件这一个层次上(组件可以理解为特定的程序),把组建拆拆拆,把程序拆开,每个组件对外还是相同的功能,但是对内而言,已经七零八散了。在微服务构架里,“七零八散”并不是个贬义词,看似很小的程序在逻辑上都是有组织有纪律的。忽然又想起微信小程序了,看来总体的发展趋势还是微小化,一开始是服务器,虚拟机,后来有了虚拟空间,再后来GAE,SAE,Docker,还有亚马逊的lambda服务,大家都是朝着微小化的方向发展,我们把机器拆开,服务拆开,接口拆开,程序拆开,进程拆开,函数拆开,最后都变成一个个的IO。

就像是从一家家的小作坊,变成了工厂的流水线,每个人负责单一的功能,每个人都是对外提供一种服务,但由于这个服务比较小,所以就叫微服务。小作坊和工厂都是服务,不同的是,工厂的资源高度集中,工人稳定可靠(不会因为不上班而DOS),流水线分工明确,产品可追溯,流程可控制。一群工人组成的team,就像一群微服务组成的集群。

说到集群,就不得不说管理和调度,这也是一门博大精深的学问,可以看下google的borg系统。还有集群的监控,日志,存储,网络等等等等。又想起那天去头条面试的时候,那个老大问我对成千上万的容器怎么监控,metric有哪些,当时我竟然脑子短路,只说了alive这一个,哪怕uptime呢,最后他告诉我,还要有对请求的统计(可能是他就说了这一个,也有可能我只记住了这一个)。不过现在手头正做一个API的服务,audit啊,auth啊,cache啊,metrics啊,很涨经验。

拆拆拆

这个也是今天想到的,从程序分层,想到组件(component)分层,然后又回到了微服务。程序的分层,就是在逻辑和代码上做内部的解耦,不同的DAO,不同的Handler,不同的Type,都要一一区分,能抽象的都抽象,能解耦的都解耦,这样的好处是,在逻辑上很清晰,在测试方面也容易进行单元测试。

又想到一个“传递和全局变量”的问题。其实一直在纠结,因为程序经常会与数据库打交道,也就是DAO的处理,初始化handler的时候,到底是将new出来的DAO传递进去,还是在DAO中定义一个全局变量,当有handler需要与数据库交互的时候,直接调用这个全局变量呢。我代码看的也少,不了解别人是怎么做的,但经讨论认为,如果说DAO的session或者client能够保证并发安全,那么这个全局变量是没问题的。如果不能保证并发安全,那么就要么加锁(或者队列),要么就再new一个新的出来给handler用。这就要具体情况具体分析了,像influxdb的UDP的client,就可以每次调用的时候new一个出来。但既然其HTTP的client能够保证并发安全,那么全局变量的做法也是OK的。

拆程序不仅在逻辑上的分层很清晰,在流程上的层次也很明确。以后有新的功能开发,直接找到对应的module或者handler或者别的什么名字,在其基础上append代码就可以,如果有其他地方的改动,也只是小修小补,只要做好feature的unit test,其他的改动也不会有问题。

如果一个程序让人读起来很乱,那么就需要考虑重构,内部解耦,划分好调用层次,流程顺序等等。

实在不行,就重写吧。

扩展阅读

comments powered by Disqus