我们有许多客户,他们有大型系统并且有许多的系统耦合,这指的是系统组件之间存在依赖关系或是某些组件直接与另外一些组件相连接。由于整个系统的强度和可靠性取决于最弱的组件,所以,这种做法会产生许多不稳定性。同时,要找到导致问题产生的组件也是很困难的一件事,因为耦合和依赖会使所有东西瞬间崩溃。

在网络系统中,最明显的例子就是PHP进程耦合到数据库中。这意味着,如果数据库比较慢或甚至处于死机状态的话,将对PHP系统造成极其巨大的影响。尤其是,如果存在二级耦合的话,如果数据库运行较慢,用户会不断累积,创建更多的PHP(或Apache)进程,会导致网络服务器内存消耗殆尽。

更有甚者,如果系统中有读写分离的情况,如果读从机数据库运行缓慢,PHP进程数将增多,连接到主机上的进程数也会增多。这就是一个例证,某台服务器(从机)上的问题会影响到另一台服务器(主机)。如果连接数很多的话,整个系统会在顷刻之间变得非常糟糕。

例如,最近有一个客户系统,它有许多数据库,是共享读-写分离的,所以,每个PHP进程可以连接到10个或更多的数据库。最重要的是,除了其它东西之外,这个客户还有外部系统调用日志系统。当日志系统相对于自己的数据库变得运行缓慢的时候,整个系统的并发数会立刻从500变到5000或更多。PHP系统会把进程数消耗掉,终端用户无法得到服务,系统基本上没有办法使用了。

乍看好像一切安好。但是我们现在有50台服务器,这些服务的CPU或I/O都不够高,而我们却有上千个进程连接,根本无法显示问题出在哪里。不幸的是,我们没有想到,我们的客户也没有想到,问题的根源竟然是外部优先级较低的日志系统,因为,通常情况下,此类问题会出现在PHP或MySQL中。最后,客户查看了日志并发现了系统运行缓慢的原因,所以,我们针对该问题给出了我们的建议。关掉了日志,整个系统在几秒钟之内就恢复正常了。

请您注意,日志系统确实是有用的,只是运行较慢而已。但是,若果您每秒运行上千次的话,在紧耦合系统中,10和100ms会出现很大的差异,这与我们在会话存储中所见到的情况类似,那里有高频率的延迟-敏感性请求存在。

在许多系统中,这种情况会导致并发数增多,造成PHP使用大量的RAM,使系统开始交换(swap)。此时,不仅系统会开始出现故障,而且看起来问题好像出在PHP、RAM或甚至是用户加载量,因为网络服务器正在交换。但是,其实问题是出在日志系统,因为这个系统产生了另外一个负面影响,就是把其它服务器的RAM消耗掉了。一旦出现这种情况,您很难发现原因,因为所有的服务器都不堪负荷都在交换,一切都处于不稳定状态。交换会造成更多的并发数,产生死循环,消耗掉数据库和其它系统资源并产生更多的并发数。

但是,您必须知道,这类耦合有两种类型:串行耦合和并行耦合。如果是串行耦合的话,整个系统运行速度取决于运行最慢的组件,通常会出现单点故障。例如,PHP会调用缓存、然后是MySQL、Solr、Redis,最后提供一个回复。这些动作对于任何给定的组件速度都是很敏感的,但是还是会产生一些其它负面影响。

谈到并行耦合,更多的情况是像上面所说的是带有数据库shard,进程中的外部调用若占有数据库连接的话,就会形成并行耦合。这种系统通常可以通过超时的时候所增加的延迟来承受某个组件故障,例如,通常会在iptables和缓存中发现这种情况,但是这种做法也有许多负面影响。

但是,通常情况下这个问题很难解决。每个人都在谈论去耦合和一些花俏的技术如队列、回调、异步代码;但是实际上,这些都是很难使用的,并且不能够很好地用到常用工具如PHP、Java和MySQL中。尽管队列是一种很好的方法,可以部分地去耦合,如上文所提到的日志系统。但是这会产生另一个影响,使依赖从日志系统转移到队列系统,但是从理论上来说,队列还是比较快、比较可靠的,所以,队列在某些情况下还是比较实用的。

其它人经常谈论说使用SOA和REST来去耦合。虽然这些命令也可以去耦合,但是它们只是在逻辑层面上去耦合,而不是在系统层面上去耦合。这些都是很好的工具,也是经常被推荐使用的架构,确实可以解决实际问题,但是,它们会因为组件数量多、分布广而产生很多同步并行依赖问题,所以,难以监控,出现故障时也难以排除。

总的来说,这个问题难以解决并且每个系统都是不同的。像队列这些技术是有用的,可以更好地编译框架,如所需要的数据库连接数量,而不是使用默认的连接数量。大型系统开发员和系统架构师一直不停地思考如何在运营层面和逻辑层面解耦自己的系统。