高并发服务器的设计

编程入门 行业动态 更新时间:2024-10-09 15:14:45

高并发<a href=https://www.elefans.com/category/jswz/34/1771423.html style=服务器的设计"/>

高并发服务器的设计

不同的业务,设计也不尽相同,但至少都一些共同的追求,比如性能。 做服务器开发很多年了,有时候被人问到,服务器性能是什么呢?各种服务器间拼得是什么呢? 简单的回答就是QPS,并发数,但有时候想想也许也不对。 QPS与并发数是针对同样的业务而言的,业务不同,相同的服务器能承受的压力也会不同。 性能,也许可以打个俗点的比方: 服务器就是一艘船,性能就是船的容量,开的速度,行得是否稳当。 该用的用,该省的省。能用内存就别用IO,CPU则能少用就少用,相同的QPS,CPU和内存用的少点的性能就要比用的多点好,同样,QPS跑得多点的就比 跑得小点的性能要好,哪怕多用了点CPU和内存。 什么是性能的保障呢? 高效的事件模型,简单明了的业务架构,统一稳定的资源管理,外加纯熟的人员。 咱就从资源说起吧。 资源多半与IO有关,如果你看过我前面的文章,一定不会对连接池陌生,没错,连接是系统的一种IO资源,下面看看另一种IO资源:内存。
如果你看过apache, nginx之类服务器的代码,或者想入手,那么多半应该从内存管理开始。 与服务器性能息息相关,内存池的设计也追求快速与稳定,生命周期一般有下面三种: global: 全局的内存,存放整个进程的全局信息。 conn: 每个连接的信息,从连接产生到关闭。 busi:业务相关的信息,伴随每个业务的产生到结束
下面定义一个简单的内存池:
[cpp] view plain copy print ?
  1. typedef struct yumei_mem_buf_s yumei_mem_buf_t;  
  2. typedef struct yumei_mem_pool_s yumei_mem_pool_t;  
  3.   
  4. struct yumei_mem_buf_s  
  5. {  
  6.     int                          size;  
  7.     char                        *pos;  
  8.     char                        *data;  
  9.     yumei_mem_pool_t            *pool;  
  10. };  
  11.   
  12. struct yumei_mem_pool_s  
  13. {  
  14.     int                          size;  
  15.     char                        *data;  
  16.     char                        *last;  
  17.     yumei_mem_pool_t            *next;  
  18.     yumei_mem_pool_t            *current;  
  19. };  
  20.   
  21. yumei_mem_pool_t* yumei_mem_pool_create( int block_size, int block_num );  
  22. int yumei_mem_pool_free( yumei_mem_pool_t  *pool );  
  23. yumei_mem_buf_t* yumei_mem_malloc( yumei_mem_pool_t   *pool, int size );  
  24. int yumei_mem_buf_free( yumei_mem_buf_t *buf );  
typedef struct yumei_mem_buf_s yumei_mem_buf_t;
typedef struct yumei_mem_pool_s yumei_mem_pool_t;struct yumei_mem_buf_s
{int                          size;char                        *pos;char                        *data;yumei_mem_pool_t            *pool;
};struct yumei_mem_pool_s
{int                          size;char                        *data;char                        *last;yumei_mem_pool_t            *next;yumei_mem_pool_t            *current;
};yumei_mem_pool_t* yumei_mem_pool_create( int block_size, int block_num );
int yumei_mem_pool_free( yumei_mem_pool_t  *pool );
yumei_mem_buf_t* yumei_mem_malloc( yumei_mem_pool_t   *pool, int size );
int yumei_mem_buf_free( yumei_mem_buf_t *buf );

在每个连接开始的时候,创建连接唯一的内存池,存放IO数据,当要创建新业务时,创建业务内存池,业务处理完毕时释放内存池:

[cpp] view plain copy print ?
  1. typedef struct yumei_busi_s yumei_busi_t;  
  2.   
  3. struct yumei_busi_s  
  4. {  
  5.     yumei_mem_pool_t      *pool;  
  6.     ...  
  7.     ...  
  8.   
  9. }  
  10.   
  11. #define yumei_BUSI_MEM_BLOCL_SIZE 512   
  12. #define yumei_BUSI_MEM_BLOCK_NUM  32   
  13.   
  14. yumei_busi_t* yumei_busi_create()  
  15. {  
  16.     yumei_busi_t* busi;  
  17.     yumei_pool_t* pool;  
  18.     yumei_mem_buf_t* buf;  
  19.     int size;  
  20.   
  21.     pool = yumei_mem_pool_create( yumei_BUSI_MEM_BLOCL_SIZE, yumei_BUSI_MEM_BLOCK_NUM );  
  22.     if( !pool ){  
  23.         return 0;  
  24.     }  
  25.   
  26.     size = sizeof( yumei_busi_t );  
  27.     buf = yumei_mem_buf_malloc( pool, size );  
  28.   
  29.     if( !buf ){  
  30.         yumei_mem_pool_free( pool );  
  31.         return 0;  
  32.     }  
  33.   
  34.     busi = buf->data;  
  35.   
  36.     return busi;  
  37.   
  38. }  
  39.   
  40. #define YUMEI_BUSI_ERROR -1   
  41. #define YUMEI_BUSI_OK     0   
  42.   
  43. int yumei_busi_free( yumei_busi_t* busi )  
  44. {  
  45.     if( !busi ){  
  46.         return YUMEI_BUSI_ERROR;  
  47.     }  
  48.   
  49.     yumei_mem_pool_free( busi->pool );  
  50.   
  51.     return YUMEI_BUSI_OK;  
  52. }  
typedef struct yumei_busi_s yumei_busi_t;struct yumei_busi_s
{yumei_mem_pool_t      *pool;......}#define yumei_BUSI_MEM_BLOCL_SIZE 512
#define yumei_BUSI_MEM_BLOCK_NUM  32yumei_busi_t* yumei_busi_create()
{yumei_busi_t* busi;yumei_pool_t* pool;yumei_mem_buf_t* buf;int size;pool = yumei_mem_pool_create( yumei_BUSI_MEM_BLOCL_SIZE, yumei_BUSI_MEM_BLOCK_NUM );if( !pool ){return 0;}size = sizeof( yumei_busi_t );buf = yumei_mem_buf_malloc( pool, size );if( !buf ){yumei_mem_pool_free( pool );return 0;}busi = buf->data;return busi;}#define YUMEI_BUSI_ERROR -1
#define YUMEI_BUSI_OK     0int yumei_busi_free( yumei_busi_t* busi )
{if( !busi ){return YUMEI_BUSI_ERROR;}yumei_mem_pool_free( busi->pool );return YUMEI_BUSI_OK;
}

有些时候业务比较简单,一个连接仅对应一个业务或多个业务不是并行执行,这样的情况下,就不再需要业务内存池了,可以直接用连接内存池:

[cpp] view plain copy print ?
  1. yumei_busi_t* yumei_busi_create( yumei_conn_t* conn )  
  2. {  
  3.     yumei_busi_t* busi;  
  4.     yumei_pool_t* pool;  
  5.     yumei_mem_buf_t* buf;  
  6.     int size;  
  7.   
  8.     pool = conn->pool;  
  9.     if( !pool ){  
  10.         retur 0;  
  11.     }  
  12.   
  13.     size = sizeof( yumei_busi_t );  
  14.     buf = yumei_mem_buf_malloc( pool, size );  
  15.   
  16.     if( !buf ){  
  17.         yumei_mem_pool_free( pool );  
  18.         return 0;  
  19.     }  
  20.   
  21.     busi = buf->data;  
  22.   
  23.     return busi;  
  24.   
  25. }  
  26.   
  27. #define YUMEI_CONN_ERROR -1   
  28. #define YUMEI_CONN_OK     0   
  29.   
  30. int yumei_conn_close( yumei_conn_t* conn )  
  31. {  
  32.     if( !conn ){  
  33.         return YUMEI_CONN_ERROR;  
  34.     }  
  35.   
  36.     yumei_mem_pool_free( conn->pool );  
  37.   
  38.     return YUMEI_CONN_OK;  
  39. }  
yumei_busi_t* yumei_busi_create( yumei_conn_t* conn )
{yumei_busi_t* busi;yumei_pool_t* pool;yumei_mem_buf_t* buf;int size;pool = conn->pool;if( !pool ){retur 0;}size = sizeof( yumei_busi_t );buf = yumei_mem_buf_malloc( pool, size );if( !buf ){yumei_mem_pool_free( pool );return 0;}busi = buf->data;return busi;}#define YUMEI_CONN_ERROR -1
#define YUMEI_CONN_OK     0int yumei_conn_close( yumei_conn_t* conn )
{if( !conn ){return YUMEI_CONN_ERROR;}yumei_mem_pool_free( conn->pool );return YUMEI_CONN_OK;
}

知道内存池怎么用了,再来看看内部设计吧,pool 的四个元素里 size 对应 block_size, data和last 分别对应块的起始地址和可分配地址,next和current分别对应下块内存池和当前可用内存池。 在一些通用的服务器上还会看到另一个元素:large。 这个是争对一些大内存的分配,当不清楚业务到底需要多大内存的时候,large往往是必须的,这样内存池结构就变成这样:
[cpp] view plain copy print ?
  1. typedef struct yumei_mem_large_s yumei_mem_large_t;  
  2.   
  3. struct yumei_mem_large_s  
  4. {  
  5.     char                      *data;  
  6.     int                        size;  
  7.     yumei_mem_large_t         *next;  
  8. }  
  9.   
  10. struct yumei_mem_pool_s  
  11. {  
  12.     int                          size;  
  13.     char                        *data;  
  14.     char                        *last;  
  15.     yumei_mem_pool_t            *next;  
  16.     yumei_mem_pool_t            *current;  
  17.     yumei_mem_large_t           *large;  
  18. };  
typedef struct yumei_mem_large_s yumei_mem_large_t;struct yumei_mem_large_s
{char                      *data;int                        size;yumei_mem_large_t         *next;
}struct yumei_mem_pool_s
{int                          size;char                        *data;char                        *last;yumei_mem_pool_t            *next;yumei_mem_pool_t            *current;yumei_mem_large_t           *large;
};

对于一些特殊的业务,比如业务使用的内存大小都固定,且相近的时候,内存池就缩化成了固定大小的内存管理,其实是很简单了,这样的内存池可以绑定在连接上,且用完不用释放,留待下条连接复用,进一步节省开销。

更多推荐

高并发服务器的设计

本文发布于:2024-02-24 21:13:15,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1696669.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:服务器

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!