华子的代码空间

逆水行舟,不进则退。 关注系统编程、网络编程、并发、分布式。

[转]服务器开发架构模式

我看过最坑爹的模式的书,莫过于《数据访问模式》。

读数据库和写DAO这样简单的事情,搞个蛋疼的模式,可怜我这种傻子乖乖地做了笔记,把重点一一进行了誊写。

受到伤害的我决定报复,于是我总结出了《服务器开发架构模式》:

 

 

1.读写分离模式

   如果是DB,可以从slave读,写到master上。两台数据库可以针对读写的不同需求而进行优化,性能加倍。缺点是,数据同步可能有延迟。

   对于进程:比如业务需求是从cache读写数据,则可以分为读写进程。

   1.1 .职责的分离,读进程和写进程各自的业务逻辑变得很简单,足够小足够清晰,性能高,不容易出BUG。缺点是可能代码量比合在一起高一些;

   1.2 针对读写的业务特点和请求量,可做不同的部署量,读多写少,或者写多读少。对于本身无状态的服务,还可以部署到多个机器上,扩展性更好。

2. 异步写与写合并模式

    对于写量较大的服务器,如果每次IO都落到磁盘上,则IOPS只有400-500的机械式磁盘根本无法满足需求。

    这时就可以考虑写合并模式:把要写的数据先写在内存中组织起来,写到一定量后再一次性写到磁盘。业务线程如果要处理一次性写入的工作,可能会带来服务抖动,某个时间段内的响应延迟会增加,这就需要异步写。利用操作系统提供的API,或者另起线程(进程)来完成写入。

     写合并可能会因为掉电丢失数据,可以在写入的同时写入流水日志,掉电重启后立即从流水日志中恢复数据。

3. 消息总线模式

    假设系统中有多个子系统,某信息写入A系统后,同时也要同步给B系统,简单的做法是A系统中调用B系统的接口。随着业务的增加,又有C系统,D系统都需要A系统的消息。开发A系统的程序员就会觉得很坑爹,有完没完啊?

    于是,系统中引入消息总线。当A系统写入信息后,朝消息总线中写入一个消息。其余的子系统挂接到消息总线上,异步收取消息。

    这样,利用消息总线,原本耦合的系统就被解耦开了。

    缺点就是,可能存在消息丢失的可能。

4. 通知loader模式

    假设业务逻辑是这样的,用户读请求的时候,先从cache取,取不到再从DB取,再更新CACHE,再返回数据给用户。

    假设CACHE的命中率很低,或者CACHE服务器挂掉一部分。这个时候就会对DB产生大量的读,从而产生雪崩。

    通知loader模式来解决这个问题:当用户请求的内容不在CACH中的时候,读进程发出一个CACHE不命中的消息给消息总线。然后向用户返回稍后再试的消息。

     另启动一个loader进程,从消息总线读取通知消息,收到消息后从DB读取数据,更新到CACHE。同时,loader这层是有最大访问量的限制的,避免DB发生雪崩。

     这个模式的缺点是:以牺牲用户的可用性为代价。

5. 两阶段提交模式

    两阶段提交的概念可以看这里:http://blog.csdn.net/junli0310/article/details/1781736

    作为穷矮搓的屌丝一枚,用不起牛叉的数据库,就自己用两阶段提交解决分布式事物的问题吧。

6. SOA模式

     这里的描述和模式1有些类似。

     在此我并不按经典的SOA的理论来说明:在后台服务中,如果在一个服务进程中处理所有的业务逻辑,则这是非常不明智的。业务的扩展会带来新的BUG,服务进程不稳定,则会影响到所有的服务。

     因此,保障每个服务尽量的小且功能单一。这样:每个服务都会非常的清晰并易于维护,精简的代码也会搞来高效率和低BUG率。然后通过额外的胶合层来整个多个服务的数据。一个完整的服务,是由一大堆功能单一的小服务集成起来的。

     在业务告诉增长的情况下,SOA能把业务整合的痛苦降到最低。

7. 多读/多写模式

     这个思想源自于Amazon的Dynamo:写的时候,写多份到不同的服务器上,读的时候,读出多份,取版本最新的一份。

     通过冗余写和冗余读来达到高可用性、分区扩展性和最终一致性,牛叉啊!

     不过,成本很高。(我也一直怀疑,当业务量增长到某个阶段,内网流量会成为系统的瓶颈)

8.agent模式

     假设系统使用fastcgi作为对外的HTTP服务框架,然后机器上会启动上千个fastcgi进程来进行服务,碰巧会有很多台fastcgi的机器,碰巧每个fastcgi进程会连接后端的某个服务器……那么,这个后端的服务器会被大量的TCP连接连上去,光处理连接就够呛了,别说处理业务了。

     OK,如何解决连接过多的问题呢?agent模式是酱紫解决的:在fastcgi的机器上开辟一块(或者两块)共享内存,每个fastcgi进程挂载共享内存,把请求的信息写入共享内存。在fastcgi的机器上启动一个agent进程,agent进程从共享内存读取请求,然后agent与后端服务器建立长连接,把请求发给后端,收到响应后把结果写入共享内存。最后,fastcgi进程从共享内存中取出结果。

9.请求队列模式

 

    假设有两台服务器提供某类服务,前端做了容灾处理,某个服务器停止后,会把请求发给另一个服务器。可是,假设每个服务器都处于满负载状态,某个服务器崩溃后,另一个服务器的请求量会猛涨一倍,它会受不了,结果也崩溃……

    为了避免这种情况,就需要服务器自身有自我保护机制,请求量过大的时候,不至于崩溃。

    具体的做法是内部设定一个请求队列,每收到一个请求,把当前的时间和请求内容塞到队列中;然后工作线程从请求队列获取请求进行处理。

    这个模型会有两种情况:

       a. 请求来得太多,工作线程处理得太慢,导致请求队列被塞满了。这个时候,丢弃塞不进队列的请求。

       b. 工作线程处理得太慢,导致任务在队列中等了很久才被处理到,而处理的时候前端请求已经超时。于是恶性循环,所有的请求都超时。这个时候,工作线程取出请求时,把当前时间和存入队列的时间进行比较,发现已经超过一定时间,直接丢弃请求,处理更新的。(工作线程说:抱歉,我只吃最新鲜的!)

 

后续还会抽时间去总结更多的模式,并坑掉更多人. :-)

 http://hi.baidu.com/ah__fu/item/3f1ade2ed15eaad00e37f996

posted on 2012-07-23 01:36  华子的代码空间  阅读(331)  评论(0编辑  收藏  举报

导航