关于架构

2014-01-27

凌晨2点半。半夜睡不着,起来写文章。

我一直说自己真正感兴趣的是架构。以我对程序架构的理解。架构跟代码结构是两码事。我不喜欢研究设计模式,因为在我的理解中它研究的仅仅是代码结构。而我说的架构更强调的是程序运行起来的结构,RESTful的论文中好像有提到这一点。并没有多少人真正明白什么是架构,往往跟设计模式这类东西混淆了。对我而言,研究后者的人比较务虚,就好比玩C++可以玩出各种奇技淫巧,但不具有太多实际价值。我喜欢比较务实的。

我对架构的理解,强调的是系统运行起来之后的结构。既然说到运行起来,说想到各种概念了。随便说说吧--状态。如果研究过函数式语言,就会知识它们对状态这个概念有多么的强调。我们说一个东西是有状态的,或者无状态的。比如说一段代码,或者说一个函数。给它一个输入,它给出一个确定的输出,不依赖于外部数据,那么它就是无状态的。举例来说,fib函数,这就是一个无状态的。什么是有状态的呢?依赖于除了函数输入之外的外部数据,就是有状态的。比如说一个游戏中的AOI(感兴趣区域)算法,输入一个坐标点,输出这个坐标点的AOI区域的对象,这其实是一个有状态的。因为它依赖于函数输入之外的额外数据--地图中各个对象的坐标。

函数式语言那一套东西想做的事情就是希望函数都是无状态的,正所谓纯函数语言。哦,有点不太准确。准确点说应该是无副作用,无副作用有个很好有优点,就是可以开多线程没有任何加锁方面的问题。所以函数式语言中说很强调状态。但是我觉得无副作用是做不到的....比如说IO就是不可避开的副作用。

我要架构是多线程的(可扩展的),但是又不能用锁那种反人类的东西,而函数式语言那套避免状态的做法又行不通,怎么办呢?只有一种办法,那就是Go语言采用的方式:Don't communicate by share memory, share memory by communicate instead. 再直白一点就是goroutine + channel。

这无疑是经典的。但是不完整。我所说的关于架构的基本原则,这条其中原则之一。下面是我自己总结的归属问题,是对这条原则的展开。

  1. 数据是有归属的,归属于某个执行单元。就是说,一个数据只能由一条线程处理,其它线程想要数据,只能通过与这条线程communicate。
  2. 模块/函数/代码是无归属的。但是当它们有状态,就是有归属了。

这个怎么理解呢?因为模块/函数/代码(这里统一称为代码好了,反正我指的是静态的可被执行的东西)它们可以被任何一个执行单元(线程或者goroutine之类的概念)执行,这种情况是无归属的。但是如果代码是有数据依赖的,由于数据是有归属的,就导致这段代码也是有归属的了。

设计模式那种东西过于偏种于代码静态的结构,而我喜欢强调的架构则侧重于动态的结构。前者说的是一个模块该怎么写,而后者说包含这个模块在运行时应该被哪个执行单元执行,怎么要把计算分摊,怎么样避免加锁。


下面继续看几个概念。我觉得自己总是概念不清,或者大多人跟我一样是概念不清。模块/服务/实体。

模块,是静态的概念。并没有指明归属。它就是完成一段功能的代码,给定输入,给出输出,无论与外界有或者没有数据依赖。

服务,按我个人理解,应该是做成一个以通信协议方式提供功能的模块。强调的是通信协议的方式进行交互。比如通过发http获取数据。而这个例子,应该也能叫做服务:通过一个channel传入输入给某个goroutine,再通过channel读出处理好的输出。服务相比模块概念上应该更加动态一些,必须是有执行单元提供才会有服务。但是还是没有很明确地讲归属问题--服务提供者是一个线程采用进程内的共享方式通信?或者是一个独立进程?又或者提供者是像goroutine这种协程形式的执行单元,通过channel通信。

实体,这个我就更弄不清了。一种理解是静态的,比如说Map结构体里面有一个Aoi结构体的对象,那么说Map结构体拥有Aoi的实体。另一种理解是动态的,某各线程做heartbeat时,处理Map里的东西,通过Aoi结构体,那么说这个线程拥有Aoi实体。

好吧,夜深了,我自己也不知道自己在说什么。

架构编程感悟