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

做架构设计,难免有时候被人问及系统的瓶颈在哪,那首先来了解下什么是瓶颈?

打个形象 的比方,人的嘴巴可以吞下一整个面包,但是却咽不下去,因为食管不给力,它比较细,所以嘴巴能吞 下的食物大小要受到食管的粗细限制。

城市内部每天会产生几十万件跨城快递,可是跨城的交 通不给力,只允许走小型卡车,一卡车一次就能装几千件,一天下来也不一定能投送的完。

人 在一定时间内能咽下多少食物,货运公司在一天运送多少货物,物理上叫做吞吐量,系统整体的吞吐量 等于最小区域的吞吐量。

下面这张图能够反映:

土黄色管子的流量要受 到红色部分的制约。

服务器上也是这样,好一点的设计框架结合物理高配可以处理高达几十万 的并发,像土黄色的管子,可是偏偏有一些模块像图中红色的管子那样,一秒中只能同时处理几百次, 这样就严重拖慢了服务器的性能,成了瓶颈。

现实开发中有时可能会要加上数据库模块,如 mysql,虽然mysql号称每秒处理几十万的查询根本没问题,但那只是运算能力。

服务器连mysql 是要通过tcp网络的,有连接就需要时间,再加上数据量如果大点,自然就成了瓶颈。

相似的情 况,一些特殊的业务,比如加解密服务,密钥和随机数的产生依赖加密机,中间件的性能就是我们图中 的红管子。

有些开发还会涉及到跨网服务器查询,比如腾讯电商会调用QQ服务器的登录网关, 跨网查询的速度肯定没有本地执行的快。

系统架构的设计是争对业务的,业务里如果存在这些 红管子,就必须要有相应的解决办法。

不同人的处理方法不同,据我经验,可以将瓶颈子分成 两类:

1.阻塞串行处理

2.异步并行处理

mysql,中间件的处理属于第一类,异步 网关查询属于第二类。

对于第一类,一种通用的解决方法是增加处理进程,其实是横向扩容的 思想,打个比方,一个进程的并发是600,10个进程就可以达到6000了,如何才能将请求均匀地分配到 这10个进程是关键。

多个进程同时监听一个端口,负载均衡的方法很多,这里介绍nginx的做法 ,直接上代码:

//接收握手后连接
void ngx_event_accept(ngx_event_t *ev)
{
...
ngx_accept_disabled = ngx_cycle->connection_n / 8
                              - ngx_cycle->free_connection_n;
    ...
}  

//事件模型处理函数
void ngx_process_events_and_timers(ngx_cycle_t *cycle)
{
...
if (ngx_use_accept_mutex) {
        if (ngx_accept_disabled > 0) {
            ngx_accept_disabled--;  

        } else {
            if (ngx_trylock_accept_mutex(cycle) == NGX_ERROR) {
                return;
            }  

            if (ngx_accept_mutex_held) {
                flags |= NGX_POST_EVENTS;  

            } else {
                if (timer == NGX_TIMER_INFINITE
                    || timer > ngx_accept_mutex_delay)
                {
                    timer = ngx_accept_mutex_delay;
                }
            }
        }
    ...
}  

//取得端口监听的锁
ngx_int_t ngx_trylock_accept_mutex(ngx_cycle_t *cycle)
{
...
if (ngx_enable_accept_events(cycle) == NGX_ERROR) {
            ngx_shmtx_unlock(&ngx_accept_mutex);
            return NGX_ERROR;
        }
    ...
}  

//启动端口监听
static ngx_int_t ngx_enable_accept_events(ngx_cycle_t *cycle)
{
    ngx_uint_t         i;
    ngx_listening_t   *ls;
    ngx_connection_t  *c;  

    ls = cycle->listening.elts;
    for (i = 0; i < cycle->listening.nelts; i++) {  

        c = ls[i].connection;  

        if (c->read->active) {
            continue;
        }  

        if (ngx_event_flags & NGX_USE_RTSIG_EVENT) {  

            if (ngx_add_conn(c) == NGX_ERROR) {
                return NGX_ERROR;
            }  

        } else {
            if (ngx_add_event(c->read, NGX_READ_EVENT, 0) == NGX_ERROR) {
                return NGX_ERROR;
            }
        }
    }  

    return NGX_OK;
}

以上是小编为您精心准备的的内容,在的博客、问答、公众号、人物、课程等栏目也有的相关内容,欢迎继续使用右上角搜索按钮进行搜索服务器
, 进程
, mysql 中间件
, 瓶颈
, 处理
, cycle
吞吐量
高并发服务器架构、大并发服务器架构、c高并发服务器架构、10万并发 服务器架构、java高并发服务器设计,以便于您获取更多的相关知识。

时间: 2025-01-01 16:08:25

高并发服务器的设计之架构与瓶颈的设计的相关文章

高并发服务器的设计之内存池的设计

不同的业务,设计也不尽相同,但至少都一些共同的追求,比如性能. 做服务器开发很多年 了,有时候被人问到,服务器性能是什么呢?各种服务器间拼得是什么呢? 简单的回答就是QPS ,并发数,但有时候想想也许也不对. QPS与并发数是针对同样的业务而言的,业务不同,相同 的服务器能承受的压力也会不同. 性能,也许可以打个俗点的比方: 服务器就是一艘船 ,性能就是船的容量,开的速度,行得是否稳当. 该用的用,该省的省.能用内存就别用IO, CPU则能少用就少用,相同的QPS,CPU和内存用的少点的性能就要

高并发服务器的设计之连接池的设计

高并发服务器需要有一些池的设计,如内存池,连接池,数据库连接池. 池(pool)的设计 主要考虑到一些资源的频繁申请和释放,尤其是在高并发的服务器中,几万甚至几十万并发每秒,设计 人员不得不去考虑这些. 比如数据库连接池(sql pool),是通过TCP来通信的,属于IO类,有 一定的延时,在高并发系统中频繁的创建会严重影响系统性能. 内存( mem )的分配是要涉及锁 ( mutex )的,有锁就会有延时,因此可以在开始申请一大块内存,后面进行分配与释放,来节省锁开 销. 服务器的连接处理不仅

2高并发服务器:多线程服务器

1多线程并发服务器 在使用线程模型开发服务器时需要考虑以下问题: A调整进程最大文件描述符上限 B线程如有共享数据,考虑线程同步 C服务于客户端线程退出时,退出处理.(退出值,分离态) D系统负载,随着连接客户端增加,导致其它线程不能及时得到CPU 2.案例说明 server.c,代码如下: /* server.c */ #include <stdio.h> #include <string.h> #include <netinet/in.h> #include <

1高并发服务器:多进程服务器

 1多进程并发服务器 使用多进程并发服务器时要考虑以下几点: A.父最大文件描述个数(父进程中需要close关闭accept返回的新文件描述符) B.系统内创建进程个数(和内存大小相关) C.进程创建过多是否降低整体服务性能(进程调度) 2.案例说明 server.c,代码如下: #include <stdio.h> #include <string.h> #include <netinet/in.h> #include <arpa/inet.h> #i

高并发服务器的设计:fastcgi的设计

服务器开发中不何或缺的要使用fastcgi,其使用方法如下: while( FCGI_Accept() >= 0 ){ printf( "Content-type: text/plain \r\n" "Content-Length: %d\r\n" "Connection: close\r\n\r\n%s\r\n", strlen( buffer ), buffer ); } facgcgi 头文件有如下宏: #undef fprintf

4高并发服务器:UDP局域网服务器(组播)

 1 UDP局域网服务器 A读出每一个客户端发送过来的数据包,然后fork出子进程,由子进程去处理客户端请求. B客户端与服务器段交换多个数据报,服务器为每一个客户端连接创建新的socket,在其上bind一个临时端口,然后用该socket处理对应客户端上的所有应答,这个办法要求在客户查看服务器第一个应答中的源端口号.然后后面利用此端口号和服务器进行交互. 2函数声明 int bind(int sockfd, const struct sockaddr*addr,socklen_t addr

3高并发服务器:多路IO之epoll

 1 epoll epoll是Linux下多路复用IO接口select/poll的增强版本,它能显著提高程序在大量并.发连接中只有少量活跃的情况下的系统CPU利用率,因为它会复用文件描述符集合来传递结果而不用迫使开发者每次等待事件之前都必须重新准备要被侦听的文件描述符集合,另一点原因就是获取事件的时候,它无须遍历整个被侦听的描述符集,只要遍历那些被内核IO事件异步唤醒而加入Ready队列的描述符集合就行了. 目前epell是linux大规模并发网络程序中的热门首选模型. epoll除了提供s

1高并发服务器:多路IO之select

 1 select A:select能监听的文件描述符个数受限于FD_SETSIZE,一般为1024,单纯改变进程打开 的文件描述符个数并不能改变select监听文件个数 B:解决1024以下客户端时使用select是很合适的,但如果链接客户端过多,select采用的是轮询模型,会大大降低服务器响应效率,不应在select上投入更多精力 2 依赖的头文件 #include <sys/select.h> /* According to earlier standards */ #includ

2高并发服务器:多路IO之poll

 1 poll A 依赖的头文件 #include <poll.h> B 函数声明 int poll(struct pollfd *fds, nfds_t nfds,int timeout);   struct pollfd { int fd; /* 文件描述符*/ short events; /* 监控的事件*/ short revents; /* 监控事件中满足条件返回的事件*/ }; POLLIN普通或带外优先数据可读,即POLLRDNORM |POLLRDBAND POLLRDNO