浅谈系统拆分

xtcf
今晚回家路上,突然想到关于系统拆分的事情。举的例子比较极端,不一定有实际的意义

推演

我感觉拆分系统,和拆分代码,本质上是一样的。小到一个方法,大到几个系统,都是一个从输入到输出的序列

Level 1:

1
2
3
4
5
public void doSomething(){
System.out.println("111");
System.out.println("222");
System.out.println("333");
}

这种情况是最简单的,所有的代码都在一个方法里,没有任何拆分

Level2:

1
2
3
4
5
6
7
8
9
public void m2(){
System.out.println("222");
}

public void doSomething(){
System.out.println("111");
m2();
System.out.println("333");
}

效果和Level 1是一样的,只是中间多了一次方法的调用,实际上是内存指针偏移。但比起将所有代码写在一个方法里,这里已经做了最简单的拆分

Level3:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class Class2{

public static void m2(){
System.out.println("222");
}

}

public class Class1{

public void doSomething(){

System.out.println("111");
Class2.m2();
System.out.println("333");
}
}

还是很简单,但是比Level2又多了一层,将部分代码拆分到了另一个类里

直到目前为止,无论拆分与否,所有的代码都在同一个进程里,所以都是本地调用,过程类似下图:

这里要补充一点,就JAVA平台来说,凡是要调用的代码在同一个JVM里,且classloader可见,都可以认为是本地调用。不管它是打包在.jar文件里,还是直接存放在classpath里,也不管其源码是否来自一个地方

Level4:

现在,输出222这段逻辑,被拆分到了另外一个系统里

所以,只有通过远程调用(RPC),才能调用到这段逻辑了。但是本质并没有改变,还是从输入到输出的一串序列,只是本地调用,和远程调用的区别

事实上,这种区别对调用者来说,常常是透明的

1
2
3
4
5
6
public void doSomething(){
System.out.println("111");
Proxy proxy = getProxy();// Proxy是一个interface
proxy.print222ForMe();
System.out.println("333");
}

对这个调用者来说,它并不需要知道proxy的实现来自哪里。这是一次本地调用还是一次远程调用,对调用者来说是透明的。只有在上面的部署图里,才能看出区别

通过这个例子,我想说明的是:核心的问题是逻辑,而不是怎么拆分系统。逻辑不会因为放在一个方法里,或者放在10个不同的系统里,就发生变化。最终的结果都是一样的。同时,当需要思考“为什么要拆分系统”的时候,某种程度上和“为什么要拆分代码”是类似的

优势

实际的设计中,拆分系统一般都是必要的,我认为至少有以下3个方面的好处:

复用服务

前面举的例子过于简单了,可能无法说明问题。但是想象中间那行

1
System.out.println("222");

是一段非常重要的代码,其他很多逻辑都需要它。那么如果它是混杂在一个方法中,应用的其他部分就无法重复地调用它了(复用);而把它抽取出来之后,就可以实现复用

系统拆分也是一样,比如我画的这两张示意图,是用QQ截图功能截成JPG的。为了截这2张图,我只好把QQ给打开了。如果QQ截图被抽取为单独的模块,我就可以单独打开截图功能了;如果腾讯其他的应用也需要实现截图,就可以复用这部分功能了

当然,系统之所以被称为“系统”,而不是“应用”、“模块”、“方法”,是因为它比较大(我认为除此之外,完全是一回事,有输入、输出、逻辑的都是系统)

所以,作为系统,复用的往往不是截图这么小的功能,一般希望能够复用一个完整的服务。比如独立出“用户权限管理系统”,就可以在多个业务系统中,复用“用户校验和鉴权”的服务了。我想这就类似几年前比较流行的一个大词SAAS

性能考虑

一个大的系统,往往其中的某个部分,承担了很大的并发压力,或者逻辑运算特别复杂,因此成为系统的性能瓶颈

这时就可以考虑将这部分独立出来,从而实现单独部署,再通过负载均衡等手段,来解决性能的问题

逻辑清晰

根据业务上、流程上的不同环节,将整个系统拆分为不同的子系统,在逻辑上会更加清晰,从而更容易在架构上说清楚,或者进一步做优化