Linux服务器7 --- 多路IO复用+线程池服务端模型(高并发)分析

一、服务端特性概述

1、使用EPOLL模型在服务器中加入(网络IO监听,大量的监听能力)

EPOLL采用边缘触发模式(后话)

2、线程池模型进行并发处理业务(并发处理能力)

1)提高线程重用性(避免频繁创建销毁线程)。

2)线程管理能力较强(根据需求扩容缩减)。

3)预创建处理线程,可以更为及时有效地为客户端服务。

3、不要让处理线程与客户端绑定(影响并发),处理线程可以重用为不同的客户端服务。    
4、不要让线程池与业务绑定,使用者自行定义实现业务并传递给线程池, 线程负责执行即可。

5、服务端任务传递采用生产者消费者模式(EPOLL生产者, 线程池消费者)。 生产者消费者模型采用条件变量完成线程控制(挂起与唤醒)。

6、管理者角色,负责对线程池进行监控, 完成线程控制(扩容与缩减)。

7、业务分类,可以根据不同网络IO就绪,制定不同的业务(当然也可以根据客户端协议制定业务)。

8、考虑线程安全,全局资源访问用互斥手段。

二、服务端处理流程分析

1、首先userA客户端去connect服务端,连接请求被epoll监听到,生产者是主控线程控制epoll,通过epoll模型监听到就绪并传递任务。serverfd绑定业务A(建立连接),clientfd绑定业务B(处理请求),serverfd就绪产生了业务A,生产者就会将业务A添加到投放到线程池的业务队列中,线程池一般会先预创建一些线程,这些线程都是消费者,一生产者,多消费者,消费者从业务队列里面取出业务,拿到业务以后去执行,比如某一消费者取到的是业务A,那么就会去建立连接(accept),然后将返回的新的clientfd添加到监听集合中。如果取到的是业务B,处理完以后反馈给客户端,如果某一时刻客户端异常退出,将其对应的网络IO从监听树中删除。

2、主控线程生产者,普通线程消费者,还有一个单独的线程是管理者,负责对线程池中的普通线程进行扩容和缩减,监管普通线程,线程池

线程的扩容和缩减条件,线程执行业务,线程被占用,一段时间内不能再执行其他业务,线程状态busy,没有任务线程状态就是idle,创建多少线程,就有多少线程存活,线程数量计数的时候一般不算生产者和管理者。扩容条件一,忙线程占存活线程数量的70%,扩容条件二,当前任务数量>=闲线程数量。缩减条件为闲线程是忙线程的倍数。

三、服务端模块分析

#include<SOCKET_API.h> /* 服务端宏定义 */ #define SERVER_IP "127.0.0.1" #define SERVER_PORT 8000 #define BUFSIZE 1500 #define BACKLOG 128 #define TIMEOUT 2 //等待超时 #define IPSIZE 16 #define EPOLLSIZE 90000 #define TRUE 1 #define FALSE 0 #define THREAD_CODE 10 //扩容缩减量 int epfd; //监听树描述符 pthread_mutex_t busines_lock; //业务锁 /* 结构体定义 */ typedef struct{ void * (*BUSINES_ADDR)(void *); //业务地址 函数指针 哪个业务 void * BUSINES_ARG; //业务参数 socket }busines_t; //业务类型 //业务传给线程处理 typedef struct{ /*线程池阈值*/ int thread_shutdown; //线程池开关0/1 , 根据开关决定是否结束线程池 int thread_max; //线程最大值 int thread_min; //线程最小值 int thread_busy;//繁忙线程数 int thread_alive; //有效线程数量 int thread_exitcode; //用于线程缩减 pthread_t * customer_tids; //消费者线程tid数组 pthread_t manager_tid; //保存管理者线程tid busines_t * busines_queue; //保存业务的唤醒队列 int b_front; //业务队列头索引 int b_rear; //业务队列尾索引 int b_max; //业务队列最大数 int b_cur; //业务队列当前业务数 pthread_mutex_lock thread_lock; //互斥锁 pthread_cond_t not_full; //生产者条件变量 pthread_cond_t not_empty; //消费者条件变量 }thread_pool_t; //线程池类型 /* 服务端模型模块分析 */ void thread_pool_error(const char * strerror , int exitcode , int errno); //如果errno==0则是标准错误处理,如果errno>0则是线程函数错误处理  将错误处理分成两类 int thread_pool_net_init(void); //成功返回serverfd , 失败返回-1  //服务端网络初始化 int thread_pool_epoll_init(int Sockfd); //成功返回监听树描述符,失败返回-1  //服务端epoll初始化  且监听serverfd thread_pool_t * thread_pool_create(int Max , int Min , int QueMax); //线程池创建初始化,成功返回线程池地址 int thread_pool_destroy(thread_pool_t *); //释放销毁线程池资源  int thread_pool_epoll_listen(int epfd , thread_pool_t * p,int sockfd);//负责网络IO事件监听,辨别就绪并添加任务 producer int thread_pool_add_busines(thread_pool_t * , busines_t); //生产者向线程池中添加业务 void * thread_pool_customer_job(void * arg); //消费者默认工作,创建时传递线程池参数 从任务队列中取任务 void * thread_pool_manager_job(void * arg); //管理者默认工作,创建时传递线程池参数 需要线程池中的阈值 int if_thread_alive(pthread_t); //测试一个线程是否存活 ? 返回0表示线程已经结束,1表示线程存活 void * BUSINES_ACCEPT(void * arg); //TCP连接业务 void * BUSINES_RESPONSE(void * arg); //业务处理

全部评论

相关推荐

找不到工作死了算了:没事的,雨英,hr肯主动告知结果已经超越大部分hr了
点赞 评论 收藏
分享
牛客5655:其他公司的面试(事)吗
点赞 评论 收藏
分享
点赞 收藏 评论
分享
牛客网
牛客企业服务