高并发服务器的设计--架构与瓶颈的设计GOOD


做架构设计,难免有时候被人问及系统的瓶颈在哪,那首先来了解下什么是瓶颈?
打个形象的比方,人的嘴巴可以吞下一整个面包,但是却咽不下去,因为食管不给力,它比较细,所以嘴巴能吞下的食物大小要受到食管的粗细限制。
城市内部每天会产生几十万件跨城快递,可是跨城的交通不给力,只允许走小型卡车,一卡车一次就能装几千件,一天下来也不一定能投送的完。
人在一定时间内能咽下多少食物,货运公司在一天运送多少货物,物理上叫做吞吐量,系统整体的吞吐量等于最小区域的吞吐量。
下面这张图能够反映:




土黄色管子的流量要受到红色部分的制约。
服务器上也是这样,好一点的设计框架结合物理高配可以处理高达几十万的并发,像土黄色的管子,可是偏偏有一些模块像图中红色的管子那样,一秒中只能同时处理几百次,这样就严重拖慢了服务器的性能,成了瓶颈。
现实开发中有时可能会要加上数据库模块,如mysql,虽然mysql号称每秒处理几十万的查询根本没问题,但那只是运算能力。
服务器连mysql 是要通过tcp网络的,有连接就需要时间,再加上数据量如果大点,自然就成了瓶颈。
相似的情况,一些特殊的业务,比如加解密服务,密钥和随机数的产生依赖加密机,中间件的性能就是我们图中的红管子。
有些开发还会涉及到跨网服务器查询,比如腾讯电商会调用QQ服务器的登录网关,跨网查询的速度肯定没有本地执行的快。
系统架构的设计是争对业务的,业务里如果存在这些红管子,就必须要有相应的解决办法。
不同人的处理方法不同,据我经验,可以将瓶颈子分成两类:
1.阻塞串行处理
2.异步并行处理

mysql,中间件的处理属于第一类,异步网关查询属于第二类。
对于第一类,一种通用的解决方法是增加处理进程,其实是横向扩容的思想,打个比方,一个进程的并发是600,10个进程就可以达到6000了,如何才能将请求均匀地分配到这10个进程是关键。
多个进程同时监听一个端口,负载均衡的方法很多,这里介绍nginx的做法,直接上代码:

  1. //接收握手后连接  
  2. void ngx_event_accept(ngx_event_t *ev)  
  3. {  
  4. ...  
  5. ngx_accept_disabled = ngx_cycle->connection_n / 8  
  6.                               - ngx_cycle->free_connection_n;  
  7.     ...  
  8. }  
  9.   
  10. //事件模型处理函数  
  11. void ngx_process_events_and_timers(ngx_cycle_t *cycle)  
  12. {  
  13. ...  
  14. if (ngx_use_accept_mutex) {  
  15.         if (ngx_accept_disabled > 0) {  
  16.             ngx_accept_disabled--;  
  17.   
  18.         } else {  
  19.             if (ngx_trylock_accept_mutex(cycle) == NGX_ERROR) {  
  20.                 return;  
  21.             }  
  22.   
  23.             if (ngx_accept_mutex_held) {  
  24.                 flags |= NGX_POST_EVENTS;  
  25.   
  26.             } else {  
  27.                 if (timer == NGX_TIMER_INFINITE  
  28.                     || timer > ngx_accept_mutex_delay)  
  29.                 {  
  30.                     timer = ngx_accept_mutex_delay;  
  31.                 }  
  32.             }  
  33.         }  
  34.     ...  
  35. }  
  36.   
  37.   
  38. //取得端口监听的锁  
  39. ngx_int_t ngx_trylock_accept_mutex(ngx_cycle_t *cycle)  
  40. {  
  41. ...  
  42. if (ngx_enable_accept_events(cycle) == NGX_ERROR) {  
  43.             ngx_shmtx_unlock(&ngx_accept_mutex);  
  44.             return NGX_ERROR;  
  45.         }  
  46.     ...  
  47. }  
  48.   
  49. //启动端口监听  
  50. static ngx_int_t ngx_enable_accept_events(ngx_cycle_t *cycle)  
  51. {  
  52.     ngx_uint_t         i;  
  53.     ngx_listening_t   *ls;  
  54.     ngx_connection_t  *c;  
  55.   
  56.     ls = cycle->listening.elts;  
  57.     for (i = 0; i < cycle->listening.nelts; i++) {  
  58.   
  59.         c = ls[i].connection;  
  60.   
  61.         if (c->read->active) {  
  62.             continue;  
  63.         }  
  64.   
  65.         if (ngx_event_flags & NGX_USE_RTSIG_EVENT) {  
  66.   
  67.             if (ngx_add_conn(c) == NGX_ERROR) {  
  68.                 return NGX_ERROR;  
  69.             }  
  70.   
  71.         } else {  
  72.             if (ngx_add_event(c->read, NGX_READ_EVENT, 0) == NGX_ERROR) {  
  73.                 return NGX_ERROR;  
  74.             }  
  75.         }  
  76.     }  
  77.   
  78.     return NGX_OK;  
  79. }  

从上面的代码可以看出,nginx用一个全局变量ngx_accept_disabled 来控制单个进程的负载,当负载达到一定值的时候,不再接受新的负载。

对于第二类情况,解决的方法就像名字一样,异步并行解决。
拿跨网查询为例:
创建一个查询的请求,将请求放进事件模型中,等待服务端的返回,异步处理。
熟悉nginx的就知道nginx的upstream反向代理,这个解决方案跟反向代理很像,只不过在与上游服务器交互的前后分别还有其他的业务处理,而且可能还会有多次交互。
相应的流水图是这样的:

当客户端请求量大时,事件模型的容量会成为瓶颈,这样仍然需要横向扩容的方式来解决,增加处理进程。

这两种情况的处理方法大致如此,有时候特殊问题特殊对待,比哪数据库的瓶颈可以借助缓存解决,有些高配服务器的内存128G,甚至几台高配服务器只为一个业务,这样的情况下,不吃点内存难免对不起老板的money


转自:http://blog.csdn.net/xiaofei_hah0000/article/details/8981712

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值