高性能服务器架构思路「不仅是思路」
分布式程序的编写,一直都伴随着大量的复杂性,影响我们对代码的阅读和维护,所以我们才有各种各样的技术和概念,试图简化这种复杂性。也许我们无法找到任何一个通用的解决方案,但是我们可以通过理解各种方案的目标,来选择最适合我们的场景:
分布式数据通信 分布式的编程中,对于CPU时间片的切分本身不是难点,最困难的地方在于并行的多个代码片段,如何进行通信。因为任何一个代码段,都不可能完全单独的运作,都需要和其他代码产生一定的依赖。在动态多进程中,我们往往只能通过父进程的内存提供共享的初始数据,运行中则只能通过操作系统间的通讯方式了:Socket、信号、共享内存、管道等等。无论那种做法,这些都带来了一堆复杂的编码。这些方式大部分都类似于文件操作:一个进程写入、另外一个进程读出。所以很多人设计了一种叫“消息队列”的模型,提供“放入”消息和“取出”消息的接口,底层则是可以用Socket、共享内存、甚至是文件来实现。这种做法几乎能够处理任何状况下的数据通讯,而且有些还能保存消息。但是缺点是每个通信消息,都必须经过编码、解码、收包、发包这些过程,对处理延迟有一定的消耗。 如果我们在多线程中进行通信,那么我们可以直接对某个堆里面的变量直接进行读写,这样的性能是最高的,使用也非常方便。但是缺点是可能出现几个线程同时使用变量,产生了不可预期的结果,为了对付这个问题,我们设计了对变量的“锁”机制,而如何使用锁又成为另外一个问题,因为可能出现所谓的“死锁”问题。所以我们一般会用一些“线程安全”的容器,用来作为多线程间通讯的方案。为了协调多个线程之间的执行顺序,还可以使用很多种类型的“工具锁”。 在单线程异步并发的情况下,多个会话间的通信,也是可以通过直接对变量进行读写操作,而且不会出现“锁”的问题,因为本质上每个时刻都只有一个段代码会操作这个变量。然而,我们还是需要对这些变量进行一定规划和整理,否则各种指针或全局变量在代码中散布,也是很出现BUG的。所以我们一般会把“会话”的概念变成一个数据容器,每段代码都可以把这个会话容器作为一个“收件箱”,其他的并发任务如果需要在这个任务中通讯,就把数据放入这个“收件箱”即可。在WEB开发领域,和cookie对应的服务器端Session机制,就是这种概念的典型实现。 分布式缓存策略 在分布式程序架构中,如果我们需要整个体系有更高的稳定性,能够对进程容灾或者动态扩容提供支持,那么最难解决的问题,就是每个进程中的内存状态。因为进程一旦毁灭,内存中的状态会消失,这就很难不影响提供的服务。所以我们需要一种方法,让进程的内存状态,不太影响整体服务,甚至最好能变成“无状态”的服务。当然“状态”如果不写入磁盘,始终还是需要某些进程来承载的。在现在流行的WEB开发模式中,很多人会使用PHP+Memcached+MySQL这种模型,在这里,PHP就是无状态的,因为状态都是放在Memcached里面。这种做法对于PHP来说,是可以随时动态的毁灭或者新建,但是Memcached进程就要保证稳定才行;而且Memcached作为一个额外的进程,和它通信本身也会消耗更多的延迟时间。因此我们需要一种更灵活和通用的进程状态保存方案,我们把这种任务叫做“分布式缓存”的策略。我们希望进程在读取数据的时候,能有最高的性能,最好能和在堆内存中读写类似,又希望这些缓存数据,能被放在多个进程内,以分布式的形态提供高吞吐的服务,其中最关键的问题,就是缓存数据的同步。 ![]() PHP常用Memached做缓存 为了解决这个问题,我们需要先一步步来分解这个问题: 首先,我们的缓存应该是某种特定形式的对象,而不应该是任意类型的变量。因为我们需要对这些缓存进行标准化的管理,尽管C++语言提供了运算重载,我们可以对“=”号的写变量操作进行重新定义,但是现在基本已经没有人推荐去做这样的事。而我们手头就有最常见的一种模型,适合缓存这种概念的使用,它就是——哈希表。所有的哈希表(或者是Map接口),都是把数据的存放,分为key和value两个部分,我们可以把想要缓存的数据,作为value存放到“表”当中,同时我们也可以用key把对应的数据取出来,而“表”对象就代表了缓存。 其次我们需要让这个“表”能在多个进程中都存在。如果每个进程中的数据都毫无关联,那问题其实就非常简单,但是如果我们可能从A进程把数据写入缓存,然后在B进程把数据读取出来,那么就比较复杂了。我们的“表”要有能把数据在A、B两个进程间同步的能力。因此我们一般会用三种策略:租约清理、租约转发、修改广播
![]()
![]()
![]() 总结 (编辑:晋中站长网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |