epoll+线程池模型

server/2024/9/25 19:08:28/

🔥博客主页: 我要成为C++领域大神
🎥系列专栏:【C++核心编程】 【计算机网络】 【Linux编程】 【操作系统】
❤️感谢大家点赞👍收藏⭐评论✍️

本博客致力于知识分享,与更多的人进行学习交流

负载均衡技术

大量的用户请求可能导致任务分发不均匀,导致资源浪费,不能很好的处理和响应

通过预先设定的分发策略,最大的尝试均匀分发业务,让每台处理机都有任务负载

代理服务器

代理服务器是一个以数据中转为主要职责的中间件,代理服务器可以将用户的请求中转给处理服务器机,也可以将结果反馈给用户,避免用户直接访问服务器主机,提高安全性(安全策略都可以部署在代理服务器中),还可以进行任务的控制与分发,例如负载均衡可以在代理服务器中完成

HA高可用性结构

某个处理机宕机,可用通过HA概念将数据任务转发给正常的处理机。察觉处理机异常,快速反应

线程池设计原则

epoll具有强大的socket监听能力,可以快速察觉所有套接字,线程池具有高并发处理能力,大量的用户请求可用快速处理。

1)提高线程重用性,线程不能与用户绑定,可重复为多个用户处理业务,避免频繁创建销毁线程,减少不必要的开销。

2)预创建,提前准备好部分线程待用,用户发送请求后直接选择线程处理,提高响应速度

3)线程管理策略,设定线程池阈值,通过阈值管理调度线程,线程扩容与缩减

4)为服务器提供并发处理能力,可以更快处理请求或业务

5)提高线程池的重用性,用户实现任务,线程池负责执行任务

6)线程池使用生产者消费者实现,任务传递模式

工作流程

线程池

生产者将要处理的业务传递到任务队列中去,如果任务队列中有可获取的任务,消费者一直获取执行,生产者投递的任务不允许持续占用消费者。管理者会对消费者进行扫描,根据阈值检测是否需要扩容和缩减,对消费者进行创建或者杀死。

epoll模型:

生产者负责实现epoll模型,将事件转换成业务,投递到线程池

线程池的扩容和缩减

使用线程池最小阈值min,作为扩容增减数量

扩容:当前任务量>=闲消费者数量 或者 忙线程数量占活线程数量的70%

缩减:当前线程数量+扩容量,小于最大线程阈值

消费者与管理者配合实现缩减

epoll的水平触发模式和边缘触发模式

epoll 的水平触发(Level Triggered, LT)和边缘触发(Edge Triggered, ET)是两种不同的事件通知机制,它们定义了 epoll 如何向应用程序报告文件描述符上的事件。


水平触发(LT)

在水平触发模式下,只要满足条件的事件仍然存在,epoll 就会重复通知这个事件。比如,如果一个文件描述符上有可读数据,那么只要没有读完,epoll_wait 就会不断报告该文件描述符是可读的。这种模式的特点是:

容错性较好,不易丢失事件。

更易于编程和理解。

可以用于多线程程序中,多个线程可以共享同一个 epoll 文件描述符。

缺点:开销大,往返在socket缓冲区和用户之间

在水平模式下,我们的epoll+线程池模型有问题,当第一轮事件监听未处理完毕,epoll_wait不会阻塞,当再次有客户端发送任务时epoll_wait立即返回,并且会对任务进行误添加。

可以以边缘触发模式监听socket的读事件来避免这种问题,node.events=EPOLL | EPOLLET


边缘触发(ET)

边缘触发模式下,事件只在状态变化时被通知一次,之后即使条件仍然满足,也不会再次通知,直到状态再次发生变化。例如,只有当新数据到达使得文件描述符从非可读变为可读时,epoll_wait 才会报告可读事件。边缘触发模式的特点是:

效率更高,因为它减少了事件的重复通知。

需要更加小心地处理每次通知,确保处理所有的数据,否则可能会丢失未处理完的数据。

更适合单线程或者每个线程使用独立 epoll 文件描述符的场景。

代码实现

文件结构:

makefile:

server.h

#ifndef __server_H__
#define __server_H__#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>#define TIMEOUT 1
pthread_mutex_t lock;typedef struct
{void *(*bussiness)(void *);//任务函数指针void *arg;}bs_t;typedef struct
{int thread_shutdown;//线程池开关int thread_max;//最大线程数int thread_min;//最小线程数int thread_alive;//有效线程数int thread_busy;//忙线程数量int kill_number;//缩减码bs_t *queue;//任务队列int front;int rear;int cur;int max;pthread_cond_t Not_Full;pthread_cond_t Not_Empty;pthread_t * ctids;//存储消费者IDpthread_t mtid;//存储管理者ID
}pool_t;//线程池类型pool_t * thread_pool_create(int Max,int Min,int Qmax);//线程池初始化
int Producer_add_task(pool_t * p,bs_t bs);//生产者添加任务模块,执行一次添加一次任务
void *Customer_thread(void *arg);//消费者线程,参数为线程池地址
void *Manager_thread(void *arg);//管理者线程,参数为线程池地址
int thread_pool_destroy(pool_t *p);//销毁线程池
void * user_bussiness(void *arg);//自定义线程任务
int is_thread_alive(pthread_t pid);

thread_pool_create.c

#include "../include/server.h"pool_t * thread_pool_create(int Max,int Min,int Qmax)
{pool_t *ptr=NULL;if((ptr=(pool_t*)malloc(sizeof(pool_t)))==NULL){perror("thread_pool_create malloc pool failed");exit(0);}ptr->thread_shutdown=1;ptr->thread_max=Max;ptr->thread_min=Min;ptr->thread_alive=0;ptr->kill_number=0;ptr->thread_busy=0;if((ptr->queue=(bs_t*)malloc(sizeof(bs_t)*Qmax))==NULL){perror("thread_pool_create malloc queue failed");exit(0);}if((ptr->ctids=(pthread_t*)malloc(sizeof(pthread_t)*Max))==NULL){perror("thread_pool_create malloc ctids failed");exit(0);}ptr->front=0;ptr->rear=0;ptr->cur=0;ptr->max=Qmax;if(pthread_cond_init(&ptr->Not_Full,NULL)!=0 || pthread_cond_init(&ptr->Not_Empty,NULL)!=0|| pthread_mutex_init(&lock,NULL)!=0){printf("thread pool create failed,init Cond or Lock Failed\n");exit(0);}int err;for(int i=0;i<Min;++i){if((err=pthread_create(&ptr->ctids[i],NULL,Customer_thread,(void*)ptr))!=0){printf("thread pool create failed,customer thread create failed:%s",strerror(err));exit(0);}++ptr->thread_alive;}if((err=pthread_create(&ptr->mtid,NULL,Manager_thread,(void*)ptr))!=0){printf("thread pool create failed,manager thread create failed:%s",strerror(err));exit(0);}pthread_create(&ptid,NULL,print_thread,(void*)ptr);printf("Print Thread Create Success...\n");return ptr;
}

thread_pool_destroy.c

#include "../include/server.h"int thread_pool_destroy(pool_t *p)
{pthread_mutex_destroy(&lock);pthread_cond_destroy(&p->Not_Full);pthread_cond_destroy(&p->Not_Empty);free(p->ctids);free(p->queue);free(p);return 0;
}

Epoll_Listen.c

#include "../include/server.h"int Epoll_Listen(int serverfd,pool_t * p)
{struct epoll_event ready_array[EPOLLMAX];int ready,flag;bs_t tmp;printf("Epoll_Thread Server,Epoll_Listen Running...\n");while(p->thread_shutdown){if((ready=epoll_wait(epfd,ready_array,EPOLLMAX,-1))==-1){perror("Epoll_Listen call failed,epoll_wait call failed");exit(0);}flag=0;while(ready){if(ready_array[flag].data.fd==serverfd){tmp.business=Business_Accept;tmp.arg=(void *)&serverfd;Producer_add_task(p,tmp);}else{tmp.business=Business_Retime;tmp.arg=((void*)&ready_array[flag].data.fd);Producer_add_task(p,tmp);}++flag;--ready;}}close(serverfd);return 0;
}

Manager_thread.c

#include "../include/server.h"void * Manager_thread(void *arg)
{pthread_detach(pthread_self());pool_t *p=(pool_t *)arg;int alive,cur,busy;pthread_mutex_lock(&lock);alive=p->thread_alive;busy=p->thread_busy;cur=p->cur;pthread_mutex_unlock(&lock);int add,flag;//持续执行while(p->thread_shutdown){if((cur>=alive-busy||(double)busy/alive*100>=70)&&alive+p->thread_min<=p->thread_max){for(flag=0,add=0;flag<p->thread_max&&add<p->thread_min;flag++){if(p->ctids[flag]==0 || !is_thread_alive(p->ctids[flag])){pthread_create(&p->ctids[flag],NULL,Customer_thread,(void*)p);add++;pthread_mutex_lock(&lock);++(p->thread_alive);pthread_mutex_unlock(&lock);}}pthread_kill(ptid,SIGUSR1);}if(busy*2<=alive-busy && alive-p->thread_min >= p->thread_min){printf("%d\n",p->thread_min);pthread_mutex_lock(&lock);p->kill_number=p->thread_min;pthread_mutex_unlock(&lock);for(int i=0;i<p->thread_min;++i){pthread_cond_signal(&p->Not_Empty);}}sleep(TIMEOUT);}printf("Thread shutdown 0,manager thread[0x%x]exiting...\n",(unsigned int)pthread_self());pthread_exit(NULL);
}

Business_Retime.c

#include "../include/server.h"void * Business_Retime(void *arg)
{int toupper_flag=0;char recv_buf[1024];bzero(recv_buf,sizeof(recv_buf));char time_buf[100];bzero(time_buf,sizeof(time_buf));time_t tp;int recvlen;int sockfd=*(int*)arg;printf("111\n");while((recvlen=recv(sockfd,recv_buf,sizeof(recv_buf),MSG_DONTWAIT))==-1){if(errno==EINTR)break;perror("Business_Retime recv call failed");exit(0);}if(recvlen>0){if (strcmp(recv_buf, "localtime") == 0) {tp = time(NULL); // 获取时间种子ctime_r(&tp, time_buf);time_buf[strcspn(time_buf, "\n")] = '\0';send(sockfd, time_buf, strlen(time_buf) + 1, MSG_NOSIGNAL);bzero(time_buf, sizeof(time_buf));} else {toupper_flag = 0;while (recvlen > toupper_flag) {recv_buf[toupper_flag] = toupper(recv_buf[toupper_flag]);++toupper_flag;}send(sockfd, recv_buf, recvlen, MSG_NOSIGNAL);bzero(recv_buf, sizeof(recv_buf));}}else if(recvlen==0){close(sockfd);epoll_ctl(epfd,EPOLL_CTL_DEL,sockfd,NULL);}return NULL;
}

Epoll_Create.c

#include "../include/server.h"int Epoll_Create(int serverfd)
{int epfd;if((epfd=epoll_create(EPOLLMAX))==-1){perror("Epoll_Create call failed,epoll_create call failed");exit(0);}struct epoll_event node;node.data.fd=serverfd;node.events=EPOLLIN|EPOLLET;if((epoll_ctl(epfd,EPOLL_CTL_ADD,serverfd,&node))==-1){perror("Epoll_Create call failed,epoll_ctl call failed");exit(0);}printf("Epoll_Server Epoll Create success...\n");return epfd;}

Business_Accept.c

#include "../include/server.h"void *Business_Accept(void *arg)
{struct sockaddr_in addr;socklen_t addrlen;int sockfd=*(int *)arg;int customerfd;struct epoll_event node;char response[1024];char ip[16];bzero(response,sizeof(response));bzero(ip,sizeof(ip));addrlen=sizeof(addr);if((customerfd=accept(sockfd,(struct sockaddr*)&addr,&addrlen))==-1){perror("Business_Accept accept call failed");exit(0);}inet_ntop(AF_INET,&addr.sin_addr.s_addr,ip,16);node.data.fd=customerfd;node.events=EPOLLIN|EPOLLET;if(epoll_ctl(epfd,EPOLL_CTL_ADD,customerfd,&node)==-1){perror("Business_Accept epoll_ctl call failed");exit(0);}sprintf(response,"hi Thread [%s] welcome to epoll demo",ip);send(customerfd,response,strlen(response),MSG_NOSIGNAL);return NULL;
}

print_thread.c

#include "../include/server.h"void *print_thread(void*arg)
{pthread_detach(pthread_self());pool_t *ptr=(pool_t*)arg;PTR=ptr;struct sigaction act,oact;act.sa_handler=sig_usr;act.sa_flags=0;sigemptyset(&act.sa_mask);sigaction(SIGUSR1,&act,&oact);//设置捕捉sigprocmask(SIG_SETMASK,&act.sa_mask,NULL);//解除屏蔽while(ptr->thread_shutdown)sleep(TIMEOUT);//等待信号pthread_exit(NULL);
}

sig_usr.c

#include "../include/server.h"void sig_usr(int n)
{//显示一次阈值信息printf("[Thread_Epoll_Server Info] alive[%d] busy[%d] Idel[%d] Cur[%d] Busy/Alive[%.2f%%] Alive/max[%.2f%%]\n",
PTR->thread_alive,PTR->thread_busy,PTR->thread_alive-PTR->thread_busy,PTR->cur,(double)PTR->thread_busy/PTR->thread_alive*100,(double)PTR->thread_alive/PTR->thread_max*100);
} 

Producer_add_task.c

#include "../include/server.h"int Producer_add_task(pool_t *p,bs_t bs)
{if(p->thread_shutdown){//上锁pthread_mutex_lock(&lock);while(p->cur==p->max){pthread_cond_wait(&p->Not_Full,&lock);if(!p->thread_shutdown){pthread_mutex_unlock(&lock);printf("thread shutdown 0,exiting...\n");pthread_exit(NULL);}}//添加一个业务p->queue[p->front].business=bs.business;p->queue[p->front].arg=bs.arg;++p->cur;p->front=(p->front+1)%p->max;//解锁pthread_mutex_unlock(&lock);pthread_kill(ptid,SIGUSR1);//唤醒一个消费者pthread_cond_signal(&p->Not_Empty);}else{printf("thread shutdown 0,exiting...\n");pthread_exit(NULL);}printf("Producer Thread [0x%x] Add Task Successfully,business_addr=%p\n",(unsigned int)pthread_self(),bs.business);return 0;
}

Net_init.c

#include "../include/server.h"int Net_init(void)
{int sockfd;struct sockaddr_in sockAddr;bzero(&sockAddr,sizeof(sockAddr));sockAddr.sin_family=AF_INET;sockAddr.sin_port=htons(8080);sockAddr.sin_addr.s_addr=htonl(INADDR_ANY);if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1){perror("socket call failed");exit(0);}if(bind(sockfd,(struct sockaddr*)&sockAddr,sizeof(sockAddr))==-1){perror("bind call failed");exit(0);}listen(sockfd,BACKLOG);printf("Epoll_thread Server Net init Success...\n");return sockfd;
}

is_thread_alive.c

#include "../include/server.h"int is_thread_alive(pthread_t tid)
{pthread_kill(tid,0);if(errno==ESRCH)return 0;return 1;}

main.c

#include "../include/server.h"int main(void)
{//主线程设置对SIGUSR1信号的屏蔽,继承给所有线程sigset_t set,oset;sigemptyset(&set);sigaddset(&set,SIGUSR1);sigprocmask(SIG_SETMASK,&set,&oset);//启动接口startint sockfd=Net_init();epfd=Epoll_Create(sockfd);pool_t *ptr=thread_pool_create(300,10,1000);Epoll_Listen(sockfd,ptr);if(!ptr->thread_shutdown) thread_pool_destroy(ptr);printf("Epoll Server Closing...\n");return 0;
}

运行结果

在服务端刚启动后,有13个线程,一个生产者,十个消费者,一个管理者,一个输出线程


http://www.ppmy.cn/server/105912.html

相关文章

Java面试题———MySql篇③

目录 1.查询语句执行流程 2.、索引的数据结构是什么 3.、数据库中的锁有哪些 4.、MySQL日志类型 1.查询语句执行流程 一条查询语句到达MySQL数据库之后&#xff0c;数据库中的各个组件会按照顺序执行自己的任务 首先是连接器&#xff0c;他会负责建立连接、检查权限等操作…

Spring MVC域对象共享数据

在Spring MVC中&#xff0c;域对象&#xff08;Domain Object&#xff09;通常指的是与业务逻辑相关的模型对象&#xff0c;它们代表了应用程序中的核心数据结构。例如&#xff0c;在一个电商应用中&#xff0c;Product、User、Order等类可以被视为域对象。这些对象通常与数据库…

leetcode 2177.找到和为给定整数的三个连续整数

1.题目要求: 给你一个整数 num &#xff0c;请你返回三个连续的整数&#xff0c;它们的 和 为 num 。如果 num 无法被表示成三个连续整数的和&#xff0c;请你返回一个 空 数组。示例 1&#xff1a;输入&#xff1a;num 33 输出&#xff1a;[10,11,12] 解释&#xff1a;33 可…

Eureka中的多实例配置:如何处理微服务实例动态扩展与缩减

Eureka中的多实例配置&#xff1a;如何处理微服务实例动态扩展与缩减 1. 引言 在微服务架构中&#xff0c;服务的动态扩展与缩减是确保系统弹性和高可用性的关键因素。Eureka&#xff0c;作为一个服务注册和发现的组件&#xff0c;扮演着至关重要的角色。它由Netflix开源&…

macOS上安装nvm

一、背景 运行项目时遇到了报错&#xff0c;如下&#xff1a; Error: error:0308010C:digital envelope routines::unsupported 这个错误的原因是与 Node.js 中的一个安全特性有关&#xff0c;这个特性在某些情况下会导致兼容性问题。 解决方案&#xff1a;降级 Node.js 版本…

H5 简约四色新科技风引导页源码

源码名称&#xff1a;简约四色新科技风引导页源码 源码介绍&#xff1a;一款四色切换自适应现代科技风动态背景的引导页源码&#xff0c;源码有主站按钮&#xff0c;分站按钮2个&#xff0c;QQ联系站长按钮一个。 需求环境&#xff1a;H5 下载地址&#xff1a; https://www.…

河南萌新2024第二场

H 狼狼的备忘录 题目大意&#xff1a; 给定n本备忘录&#xff0c;里面记录了一个人的m个星座信息&#xff0c;要求按一下要求整理备忘录 A&#xff1a;同一个成员的星座信息 x 是星座信息 y 的后缀&#xff0c;那么星座信息 x 会没有星座信息 y 完整&#xff0c;从而应该只保…

CISAW安全运维认证考试重点内容介绍

CISAW安全运维认证是信息、运维方面非常重要的证书&#xff0c;从事与信息安全以及运维方向的人员都会考这个证书&#xff0c;其持有证书在工作上带来极大的帮助。 那么&#xff0c;CISAW安全运维认证考试重点内容是什么&#xff1f;就目前的问题给大家一些列讲解&#xff0c;…