listen queue of socket full !!! 深入排查和分析

本文针对线上服务器偶尔出现的 uWSGI listen queue full 的问题进行了深入分析。通过对现象的复现及 TCP 状态跟踪,最终定位到部分 API 处理速度慢是根本原因。并给出了增加 listen 队列长度、使用 UNIX Domain Socket 和增加负载均衡等解决方案。


 最近抽时间总结下对于该问题近期的排查过程和分析;

 

问题背景:

        web服务器使用的是nginx + uwsgi 的架构;

近期发现服务器上偶然会出现 *** uWSGI listen queue of socket 4 full !!! (100/100) *** 这样的日志;并且很难重现,有时候出现一段时间会自己回归正常,如下图:

图1 uwsgi listen queue full  

 

 

问题分析:

      由于是线上不定时重现,并且自动会好,并且表面上看应该是listen 队列满的原因。排除是代码逻辑等问题,怀疑是 资源和访问方式问题导致的;

      首先要在环境中复现这种情况,以便发现更深入的原因;

       

      看到listen 队列自然会找到对应的系统调用 int listen(int fd, int backlog);

      socket listen 的时候可以指定 backlog 详细请看下边的定义:

 

An incomplete connection queue, which contains an entry for each SYN that has arrived from a client for which the server is awaiting completion of the TCP three-way handshake. These sockets are in the SYN_RCVD state .
A completed connection queue, which contains an entry for each client with whom the TCP three-way handshake has completed. These sockets are in the ESTABLISHED state.
The backlog argument to the listen function has historically specified the maximum value for the sum of both queues.
      简单的说backlog 就是 已完成队列和等待完成队列和,所以如果程序压力大,不能及时的accept socket ,那么队列就可能会满,满了以后新来的connection 就建立不起来,这里用c 简单做了个验证,服务器端listen 后不进行accept ,backlog 设置为1 ,让客户端请求时,代码如下:

 2 服务器监听8000 但是不进行accept动作 

 

图3 并发访问8000端口 TCP状态的变化统计

 

    我们观察到新来的connection 建立不起来,丢弃了很多SYN包;

 

复现过程:

    (1)首先要想办法来复现这种问题才能去排查,根据现象,预感应该是请求压力大造成的,所以使用了ab工具来并发构造请求;

              先随便构造服务器上的一个请求接口来测试;

 

     ab -n 1000 -c 300 http://xxxxxx/xxxxxx/xxxxx/api_test1/

 

             表示请求1000次,并发量是300,查看服务器uwsgi 的log 并无发现异常,加大并发量到500后还是正常;

 

    (2)看来正常情况下虽然是并发很大,也不会出现这种情况,那么就在某一个api里边人工让它处理慢一点,也就是让accept 的速度降下来:

 

import time
print "-------------", time.time()
time.sleep(10)

 

   (3)再次使用ab 来测试。果然出现了 listen queue of socket 4 full ;此时在看TCP的状态的统计情况,发现果然有大量的SYN被丢弃掉 ,问题基本上就定位了;

仔细来看,该问题主要原因是由于部分api 处理慢,影响了整体组件其他api的可用性。在并发较高的情况下就会出现该问题;直接后果就是其他api不能正常提供服务;

 

 解决办法:

 

          这里根本原因还需要分析那个慢的api 是否正常,是否可以优化,如果确实是逻辑复杂,很难优化,加上业务上确实处理不过来,那么可以使用下边的方案来解决该问题;

 

(1)增加uwsgi listen 队列长度 :通过参数 --listen 1024 来提高监听长度;

(2)使用UNIX Domain Socket 来替代网络Socket ,它不需要经过网络协议栈,不需要打包拆包等操作,它只是将应用程序数据从一个进程cp到另一个进程,这正好适合nginx 和 uwsgi 在同一台机器的情况;通过 --socket /tmp/uwsgi.sock 来使用 Domain Socket;

          当然两个方法只能暂缓,不能根除;

(3)增加负载均衡,把压力均分到其他机器上;

 

 

 

 

 

#include "threadpool.h" #include <assert.h> #include <errno.h> #include <pthread.h> #include <stdbool.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/prctl.h> #include <unistd.h> /* 初始化任务队列 */ void init_taskqueue(taskqueue* queue) { //Todo queue->front=NULL; queue->rear=NULL; queue->len=0; //初始化队列互斥锁 pthread_mutex_init(&queue->mutex,NULL); //分配并初始化任务状态控制 queue->has_jobs=(staconv*)malloc(sizeof(staconv)); pthread_mutex_init(&queue->has_jobs->mutex,NULL); pthread_cond_init(&queue->has_jobs->cond,NULL); queue->has_jobs->status=0; } /* 向任务队列中添加任务 */ void push_taskqueue(taskqueue* queue, task* new_task) { //Todo pthread_mutex_lock(&queue->mutex); if(queue->rear==NULL){ queue->front=queue->rear=new_task; }else{ queue->rear->next=new_task; queue->rear=new_task; } new_task->next=NULL; queue->len++; printf("任务队列:%d",queue->len); staconv*sc=queue->has_jobs; pthread_mutex_lock(&sc->mutex); sc->status++; pthread_cond_signal(&sc->cond); pthread_mutex_unlock(&sc->mutex); pthread_mutex_unlock(&queue->mutex); } /* 从任务队列中获取任务 */ task* take_taskqueue(taskqueue* queue) { pthread_mutex_lock(&queue->mutex); staconv*sc=queue->has_jobs; //Todo while(queue->len == 0){ pthread_cond_wait(&sc->cond, &sc->mutex); } task* curtask = NULL; if (queue->len > 0) { curtask = queue->front; queue->front = curtask->next; if (!queue->front) { queue->rear = NULL; } queue->len--; } pthread_mutex_unlock(&queue->mutex); return curtask; } /* 销毁任务队列 */ void destroy_taskqueue(taskqueue* queue) { //Todo task*current=queue->front; while(current!=NULL){ task*next=current->next; free(current->arg); free(current); current=next; } pthread_mutex_destroy(&queue->has_jobs->mutex); pthread_cond_destroy(&queue->has_jobs->cond); free(queue->has_jobs); pthread_mutex_destroy(&queue->mutex); } /* 线程运行的逻辑函数 */ void* thread_do(void* arg) { thread*pthread=(thread*)arg; threadpool* pool=pthread->pool; /* 设置线程的名称 */ char thread_name[128] = {0}; sprintf(thread_name, "thread-pool-%d", pthread->id); prctl(PR_SET_NAME, thread_name); // 在初始化线程池时,对已经创建线程的数量进行统计,执行pool->num_threads++ */ pthread_mutex_lock(&pool->thcount_lock); pool->num_threads++; pthread_mutex_unlock(&pool->thcount_lock); // 线程启动完成,标记为 ready pthread_mutex_lock(&pthread->ready_mutex); pthread->ready = true; pthread_cond_signal(&pthread->ready_cond); // 通知主线程 pthread_mutex_unlock(&pthread->ready_mutex); while (pool->is_alive) { // 如果任务队列中还有任务,则继续运行;否则阻塞* staconv*sc=pool->queue.has_jobs; pthread_mutex_lock(&sc->mutex); while(pool->queue.len==0&&pool->is_alive){ if (pool->monitor) { thread_enter_block(pool->monitor, pthread_self()); } pthread_cond_wait(&sc->cond,&sc->mutex); } pthread_mutex_unlock(&sc->mutex); if (pool->is_alive) { if (pool->monitor) { thread_enter_active(pool->monitor, pthread_self()); } pthread_mutex_lock(&pool->thcount_lock); pool->num_working++; pthread_mutex_unlock(&pool->thcount_lock); task* curtask = take_taskqueue(&pool->queue); if (curtask) { curtask->function(curtask->arg); // 执行任务 free(curtask); // 释放任务 } pthread_mutex_lock(&pool->thcount_lock); pool->num_working--; // 如果所有任务完成,通知等待线程 // TODO if(pool->queue.len==0&&pool->num_working==0){ pthread_cond_broadcast(&pool->threads_all_idle); } pthread_mutex_unlock(&pool->thcount_lock); if (pool->monitor) { thread_enter_block(pool->monitor, pthread_self()); } } } //运行到此位置表明线程将要退出,需改变当前线程池中的线程数量 // TODO pthread_mutex_lock(&pool->thcount_lock); pool->num_threads--; pthread_mutex_unlock(&pool->thcount_lock); return NULL; } /* 创建线程 */ int create_thread(threadpool* pool, thread** pthread, int id) { *pthread = (thread*)malloc(sizeof(thread)); if (*pthread == NULL) return -1; (*pthread)->id = id; (*pthread)->pool = pool; (*pthread)->ready = false; pthread_mutex_init(&(*pthread)->ready_mutex, NULL); pthread_cond_init(&(*pthread)->ready_cond, NULL); // 启动线程 pthread_create(&(*pthread)->pthread, NULL, thread_do, *pthread); pthread_detach((*pthread)->pthread); return 0; } /* 初始化线程池 */ // 修改 initThreadPool 函数,添加监控器参数并关联 threadpool* initThreadPool(int num_threads, ThreadPoolMonitor* monitor) { threadpool* pool = (threadpool*)malloc(sizeof(threadpool)); if (!pool) { perror("malloc threadpool failed"); return NULL; } pool->num_threads = 0; pool->num_working = 0; pool->is_alive = true; pool->monitor = monitor; // 关联监控器 pthread_mutex_init(&pool->thcount_lock, NULL); pthread_cond_init(&pool->threads_all_idle, NULL); init_taskqueue(&pool->queue); pool->threads = (thread**)malloc(num_threads * sizeof(thread*)); if (!pool->threads) { free(pool); return NULL; } for (int i = 0; i < num_threads; ++i) { create_thread(pool, &pool->threads[i], i); // 等待线程就绪 thread* t = pool->threads[i]; pthread_mutex_lock(&t->ready_mutex); while (!t->ready) { pthread_cond_wait(&t->ready_cond, &t->ready_mutex); // 阻塞直到线程就绪 } pthread_mutex_unlock(&t->ready_mutex); // 安全更新监控器 if (pool->monitor) { update_thread_id(pool->monitor, t->pthread, i); // 传递实际线程ID } printf("THPOOL_DEBUG: Thread %d ready with ID %lu\n", i, t->pthread); } while (pool->num_threads != num_threads) {} return pool; } /* 向线程池中添加任务 */ void addTaskToThreadPool(threadpool* pool, task* curtask) { // 将任务加入队列 push_taskqueue(&pool->queue, curtask); printf("added task to pool %d\n", *(int*)curtask->arg); } /* 等待当前任务全部运行完毕 */ void waitThreadPool(threadpool* pool) { pthread_mutex_lock(&pool->thcount_lock); while (pool->queue.len > 0 || pool->num_working > 0) { pthread_cond_wait(&pool->threads_all_idle, &pool->thcount_lock); } pthread_mutex_unlock(&pool->thcount_lock); } /* 销毁线程池 */ void destroyThreadPool(threadpool* pool) { /* No need to destroy if it's NULL */ if (pool == NULL) return; pool->is_alive = false; // 等待线程池中的所有线程完成工作 // TODO: 添加代码 staconv*sc=pool->queue.has_jobs; pthread_mutex_lock(&sc->mutex); pthread_cond_broadcast(&sc->cond); pthread_mutex_unlock(&sc->mutex); //等待所有线程完成当前任务并退出 pthread_mutex_lock(&pool->thcount_lock); while(pool->num_threads>0) { pthread_cond_wait(&pool->threads_all_idle,&pool->thcount_lock); } pthread_mutex_unlock(&pool->thcount_lock); // 销毁任务队列 destroy_taskqueue(&pool->queue); // 销毁线程指针数组,并释放所有为线程池分配的内存 // TODO: 添加代码 for(int i=0;i<pool->num_threads;i++) { free(pool->threads[i]); } free(pool->threads); // 销毁线程池的互斥量条件变量 pthread_mutex_destroy(&pool->thcount_lock); pthread_cond_destroy(&pool->threads_all_idle); free(pool); } /* 获取当前线程池中正在运行线程的数量 */ int getNumofThreadWorking(threadpool* pool) { return pool->num_working; } #ifndef THREADPOOL_H #define THREADPOOL_H #define BUFSIZE 8096 // 缓冲区大小 #include <pthread.h> #include <stdbool.h> /* 同步状态结构体(条件变量+互斥锁) */ typedef struct staconv { pthread_mutex_t mutex; pthread_cond_t cond; int status; } staconv; /* 任务结构 */ typedef struct task { struct task* next; void (*function)(void* arg); // 函数指针 void* arg; // 函数参数 } task; /* 任务队列 */ typedef struct taskqueue { pthread_mutex_t mutex; task* front; // 队首指针 task* rear; // 队尾指针 staconv* has_jobs; // 任务状态控制 int len; // 任务数量 } taskqueue; /* 线程结构 */ typedef struct thread { int id; pthread_t pthread; struct threadpool* pool; bool ready; // 新增:线程是否准备好 pthread_mutex_t ready_mutex; // 新增:互斥锁 pthread_cond_t ready_cond; // 新增:条件变量 } thread; //线程状态监控结构 typedef struct { pthread_t tid; int active; long long active_time; long long block_time; struct timespec last_time; } ThreadStatus; //线程池监控结构 typedef struct { pthread_mutex_t mutex; int thread_count; ThreadStatus *threads; int max_active; int min_active; long long total_active_time; long long total_block_time; } ThreadPoolMonitor; /* 线程池结构 */ typedef struct threadpool { thread** threads; // 线程数组 volatile int num_threads; // 线程总数 volatile int num_working; // 工作线程数 pthread_mutex_t thcount_lock; // 线程计数器锁 pthread_cond_t threads_all_idle; // 空闲条件变量 taskqueue queue; // 任务队列 volatile bool is_alive; // 线程池存活状态 ThreadPoolMonitor* monitor; //添加监控器引用 } threadpool; typedef struct{ int fd; pthread_t tid; int thread_idx; }thread_data; //消息队列节点(用于传递文件名) typedef struct{ int fd; char filename[BUFSIZE]; char filetype[BUFSIZE]; }FilenameMsg; //消息队列节点(用于传递文件内容) typedef struct{ int fd; char content[BUFSIZE]; long len; char filetype[BUFSIZE]; }Msg; typedef struct{ long long total_read_socket_time; // socket 读取时间(μs) long long total_write_socket_time; // socket 写入时间(μs) long long total_read_file_time; // 网页文件读取时间(μs) long long total_write_log_time; // 日志写入时间(μs) long long total_processing_time; // 线程处理时间(μs) int child_count; // 已处理线程数 pthread_mutex_t mutex; // 互斥锁 }Stats; /******************** 线程池对外接口 ********************/ // 初始化线程池 // threadpool.h 中修改函数声明 threadpool* initThreadPool(int num_threads, ThreadPoolMonitor* monitor); // 添加任务到线程池 void addTaskToThreadPool(threadpool* pool, task* curtask); // 等待所有任务完成 void waitThreadPool(threadpool* pool); // 销毁线程池 void destroyThreadPool(threadpool* pool); // 获取当前工作线程数 int getNumofThreadWorking(threadpool* pool); void readMsgThread(void *arg); void readFileThread(void *arg); void sendMsgThread(void *arg); void init_taskqueue(taskqueue* queue); void push_taskqueue(taskqueue* queue, task* new_task); void destroy_taskqueue(taskqueue* queue); void addTaskToThreadPool(threadpool* pool, task* new_task); void update_thread_id(ThreadPoolMonitor *monitor, pthread_t tid, int index); void thread_enter_active(ThreadPoolMonitor *monitor, pthread_t tid); void thread_enter_block(ThreadPoolMonitor *monitor, pthread_t tid); #endif // THREADPOOL_H#include "threadpool.h" #include <arpa/inet.h> // 网络地址转换 #include <errno.h> // 错误号定义 #include <fcntl.h> // 文件控制操作 #include <netinet/in.h> // 互联网地址族 #include <signal.h> // 信号处理 #include <stdio.h> // 标准输入输出 #include <stdlib.h> // 标准库函数 #include <string.h> // 字符串操作 #include <sys/socket.h> // 套接字接口 #include <sys/types.h> // 系统数据类型 #include <unistd.h> // POSIX系统调用 #include<time.h> #include<sys/time.h> #include<sys/wait.h> #include<sys/sem.h> #include<sys/ipc.h> #include<sys/shm.h> #include<pthread.h> // 常量定义 #define VERSION 23 // 服务器版本 #define ERROR 42 // 错误日志类型 #define LOG 44 // 普通日志类型 #define FORBIDDEN 403 // 禁止访问状态码 #define NOTFOUND 404 // 文件未找到状态码 #define MAX_THREADS 100 //线程池线程数量 #define MONITOR_INTERVAL 2//性能监控间隔 // 支持的文件扩展名与MIME类型映射 struct { char *ext; // 文件扩展名 char *filetype; // 对应MIME类型 } extensions[] = { {"gif", "image/gif"}, // GIF图片 {"jpg", "image/jpg"}, // JPEG图片 {"jpeg", "image/jpeg"}, // JPEG图片 {"png", "image/png"}, // PNG图片 {"ico", "image/ico"}, // ICO图标 {"zip", "image/zip"}, // ZIP压缩包(原代码MIME类型可能有误) {"gz", "image/gz"}, // GZ压缩包 {"tar", "image/tar"}, // TAR归档 {"htm", "text/html"}, // HTML文件 {"html", "text/html"}, // HTML文件 {0, 0} // 结束标记 }; // pthread_t threads[MAX_THREADS];//子线程数组 pthread_mutex_t lock; pthread_mutex_t io_stats_lock; //保护I/O统计数据 volatile long total_read_socket_time=0; volatile long total_write_socket_time=0; volatile long total_read_file_time=0; volatile long total_write_log_time=0; volatile int total_requests=0; volatile sig_atomic_t server_running=1; Stats stats; struct timespec start,end; long duration; int count; int thread_count=0; int max_thread=MAX_THREADS; pthread_t *threads = NULL; // 声明为指针 pthread_t monitor_thread;//监控线程ID // 线程池监控结构 ThreadPoolMonitor read_msg_monitor; ThreadPoolMonitor read_file_monitor; ThreadPoolMonitor send_msg_monitor; // 线程池定义 static threadpool* read_msg_pool = NULL; // 读取请求线程池 static threadpool* read_file_pool = NULL; // 读取文件线程池 static threadpool* send_msg_pool = NULL; // 发送响应线程池 /* 日志记录函数 * 参数: * type - 日志类型(ERROR/LOG/FORBIDDEN/NOTFOUND) * s1 - 主要描述信息 * s2 - 补充信息 * socket_fd - 关联的套接字描述符 */ // 计算两个时间点之间的时间差(以毫秒为单位) long long calculate_time_diff(struct timespec start, struct timespec end) { long long diff_sec = end.tv_sec - start.tv_sec; long long diff_nsec = end.tv_nsec - start.tv_nsec; return diff_sec * 1000000 + diff_nsec / 1000; } void logger(int type, char *s1, char *s2, int socket_fd) { int fd; char logbuffer[BUFSIZE * 2]; // 日志缓冲区 char timebuffer[64];// 用于存储格式化时间字符串 // 获取当前时间(秒微秒) struct timeval tv; gettimeofday(&tv, NULL); // 将秒部分格式化为字符串(年月日 时分秒) struct tm *tm_info = localtime(&tv.tv_sec); strftime(timebuffer, sizeof(timebuffer), "%Y-%m-%d %H:%M:%S", tm_info); char full_timebuffer[256]; switch (type) { case ERROR: // 致命错误日志 sprintf(logbuffer, "ERROR: %s:%s Errno=%d exiting pid=%d", s1, s2, errno, getpid()); break; case FORBIDDEN: // 403响应 // 发送HTTP 403响应头 write(socket_fd, "HTTP/1.1 403 Forbidden\nContent-Length: 185\nConnection: " "close\nContent-Type: text/html\n\n<html><head>\n<title>403 " "Forbidden</title>\n</head><body>\n<h1>Forbidden</h1>\nThe requested URL, " "file type or operation is not allowed on this simple static file " "webserver.\n</body></html>\n", 271); sprintf(logbuffer, "FORBIDDEN: %s:%s", s1, s2); snprintf(full_timebuffer, sizeof(full_timebuffer), "[%s.%03ld]", timebuffer, tv.tv_usec / 1000); break; case NOTFOUND: // 404响应 write(socket_fd, "HTTP/1.1 404 Not Found\nContent-Length: 136\nConnection: close\nContent-Type: " "text/html\n\n<html><head>\n<title>404 Not Found</title>\n</head><body>\n<h1>Not " "Found</h1>\nThe requested URL was not found on this server.\n</body></html>\n", 224); sprintf(logbuffer, "NOT FOUND: %s:%s", s1, s2); snprintf(full_timebuffer, sizeof(full_timebuffer), "[%s.%03ld]", timebuffer, tv.tv_usec / 1000); break; case LOG: // 普通日志 sprintf(logbuffer, " INFO: %s:%s:%d", s1, s2, socket_fd); // printf("INFO:[PID:%d] %s:%s:%d\n", getpid(), s1, s2, socket_fd); snprintf(full_timebuffer, sizeof(full_timebuffer), "[%s.%03ld]", timebuffer, tv.tv_usec / 1000); break; case 45: sprintf(logbuffer,"[%s.%03ld] %s", timebuffer,tv.tv_usec/1000,s2); break; } // 写入日志文件(追加模式) if ((fd = open("webserver.log", O_CREAT | O_WRONLY | O_APPEND, 0644)) >= 0) { write(fd, logbuffer, strlen(logbuffer)); write(fd, "\n", 1); // write(fd,full_timebuffer,strlen(full_timebuffer)); close(fd); } // 遇到致命错误时退出进程 if (type == ERROR || type == NOTFOUND || type == FORBIDDEN) exit(3); } void thread_enter_active(ThreadPoolMonitor *monitor,pthread_t tid){ struct timespec now; clock_gettime(CLOCK_MONOTONIC,&now); pthread_mutex_lock(&monitor->mutex); for(int i=0;i<monitor->thread_count;i++) { if(monitor->threads[i].tid==tid) { printf(" Checking thread #%d: stored tid=%lu, current tid=%lu\n", i, (unsigned long)monitor->threads[i].tid, (unsigned long)tid); if(!monitor->threads[i].active){ monitor->threads[i].block_time+=calculate_time_diff(monitor->threads[i].last_time,now); monitor->total_block_time+=calculate_time_diff(monitor->threads[i].last_time,now); } monitor->threads[i].active=1; monitor->threads[i].last_time=now; break; } } pthread_mutex_unlock(&monitor->mutex); } void thread_enter_block(ThreadPoolMonitor *monitor, pthread_t tid) { struct timespec now; clock_gettime(CLOCK_MONOTONIC, &now); pthread_mutex_lock(&monitor->mutex); for (int i = 0; i < monitor->thread_count; i++) { if (monitor->threads[i].tid == tid) { if (monitor->threads[i].active) { // 计算活跃时间 monitor->threads[i].active_time += calculate_time_diff(monitor->threads[i].last_time, now); monitor->total_active_time += calculate_time_diff(monitor->threads[i].last_time, now); // 更新最高/最低活跃线程数 int current_active = 0; for (int j = 0; j < monitor->thread_count; j++) { if (monitor->threads[j].active) current_active++; } if (current_active > monitor->max_active) monitor->max_active = current_active; if (monitor->min_active == 0 || current_active < monitor->min_active) monitor->min_active = current_active; } monitor->threads[i].active = 0; monitor->threads[i].last_time = now; break; } } pthread_mutex_unlock(&monitor->mutex); } // 初始化线程池监控 void init_threadpool_monitor(ThreadPoolMonitor *monitor, int thread_count) { monitor->threads = (ThreadStatus *)malloc(thread_count * sizeof(ThreadStatus)); monitor->thread_count = thread_count; monitor->max_active = 0; monitor->min_active = 0; monitor->total_active_time = 0; monitor->total_block_time = 0; pthread_mutex_init(&monitor->mutex, NULL); struct timespec now; clock_gettime(CLOCK_MONOTONIC, &now); for (int i = 0; i < thread_count; i++) { monitor->threads[i].tid = 0; monitor->threads[i].active = 0; monitor->threads[i].active_time = 0; monitor->threads[i].block_time = 0; monitor->threads[i].last_time = now; } } // 更新线程ID void update_thread_id(ThreadPoolMonitor *monitor, pthread_t tid, int index) { pthread_mutex_lock(&monitor->mutex); if (index < monitor->thread_count) { printf("UPDATE: Thread #%d set to tid=%lu\n", index, (unsigned long)tid); monitor->threads[index].tid = tid; // 存储实际线程ID } pthread_mutex_unlock(&monitor->mutex); } // 性能数据收集线程 void* performance_monitor(void* arg) { while (server_running) { sleep(MONITOR_INTERVAL); // 收集线程池状态 pthread_mutex_lock(&read_msg_monitor.mutex); pthread_mutex_lock(&read_file_monitor.mutex); pthread_mutex_lock(&send_msg_monitor.mutex); // 计算当前活跃线程数 int read_msg_active = 0; int read_file_active = 0; int send_msg_active = 0; for (int i = 0; i < read_msg_monitor.thread_count; i++) { if (read_msg_monitor.threads[i].active) read_msg_active++; } for (int i = 0; i < read_file_monitor.thread_count; i++) { if (read_file_monitor.threads[i].active) read_file_active++; } for (int i = 0; i < send_msg_monitor.thread_count; i++) { if (send_msg_monitor.threads[i].active) send_msg_active++; } // 计算平均活跃时间阻塞时间 long long read_msg_avg_active = 0; long long read_msg_avg_block = 0; long long read_file_avg_active = 0; long long read_file_avg_block = 0; long long send_msg_avg_active = 0; long long send_msg_avg_block = 0; if (read_msg_monitor.thread_count > 0) { read_msg_avg_active = read_msg_monitor.total_active_time / read_msg_monitor.thread_count; read_msg_avg_block = read_msg_monitor.total_block_time / read_msg_monitor.thread_count; } if (read_file_monitor.thread_count > 0) { read_file_avg_active = read_file_monitor.total_active_time / read_file_monitor.thread_count; read_file_avg_block = read_file_monitor.total_block_time / read_file_monitor.thread_count; } if (send_msg_monitor.thread_count > 0) { send_msg_avg_active = send_msg_monitor.total_active_time / send_msg_monitor.thread_count; send_msg_avg_block = send_msg_monitor.total_block_time / send_msg_monitor.thread_count; } // 打印性能数据 printf("\n===== 服务器性能监控 [%d秒间隔] =====\n", MONITOR_INTERVAL); printf("\n读取请求线程池:\n"); printf(" 当前活跃线程: %d/%d\n", read_msg_active, read_msg_monitor.thread_count); printf(" 最高活跃线程: %d\n", read_msg_monitor.max_active); printf(" 最低活跃线程: %d\n", read_msg_monitor.min_active); printf(" 平均活跃时间: %lld μs\n", read_msg_avg_active); printf(" 平均阻塞时间: %lld μs\n", read_msg_avg_block); printf(" 消息队列长度: %d\n", read_msg_pool->queue.len); printf("\n读取文件线程池:\n"); printf(" 当前活跃线程: %d/%d\n", read_file_active, read_file_monitor.thread_count); printf(" 最高活跃线程: %d\n", read_file_monitor.max_active); printf(" 最低活跃线程: %d\n", read_file_monitor.min_active); printf(" 平均活跃时间: %lld μs\n", read_file_avg_active); printf(" 平均阻塞时间: %lld μs\n", read_file_avg_block); printf(" 消息队列长度: %d\n", read_file_pool->queue.len); printf("\n发送响应线程池:\n"); printf(" 当前活跃线程: %d/%d\n", send_msg_active, send_msg_monitor.thread_count); printf(" 最高活跃线程: %d\n", send_msg_monitor.max_active); printf(" 最低活跃线程: %d\n", send_msg_monitor.min_active); printf(" 平均活跃时间: %lld μs\n", send_msg_avg_active); printf(" 平均阻塞时间: %lld μs\n", send_msg_avg_block); printf(" 消息队列长度: %d\n", send_msg_pool->queue.len); printf("\n====================================\n"); pthread_mutex_unlock(&send_msg_monitor.mutex); pthread_mutex_unlock(&read_file_monitor.mutex); pthread_mutex_unlock(&read_msg_monitor.mutex); } return NULL; } void readMsgThread(void *arg) { thread_data *args = (thread_data *)arg; int fd = args->fd; char buffer[BUFSIZE + 1]; long ret; struct timespec start_time, end_time; long long read_socket_time = 0; long long write_log_time = 0; long i; clock_gettime(CLOCK_MONOTONIC, &start_time); ret = read(fd, buffer, BUFSIZE); clock_gettime(CLOCK_MONOTONIC, &end_time); read_socket_time += calculate_time_diff(start_time, end_time); if (ret == 0 || ret == -1) { // 读取失败处理 clock_gettime(CLOCK_MONOTONIC, &start_time); logger(FORBIDDEN, "failed to read browser request", "", fd); clock_gettime(CLOCK_MONOTONIC, &end_time); write_log_time += calculate_time_diff(start_time, end_time); pthread_mutex_lock(&stats.mutex); stats.total_read_socket_time += read_socket_time; stats.total_write_log_time += write_log_time; pthread_mutex_unlock(&stats.mutex); close(fd); free(args); return; } buffer[ret] = 0; // 确保字符串终止 // 替换换行符用于安全日志记录 for (i = 0; i < ret; i++) if (buffer[i] == '\r' || buffer[i] == '\n') buffer[i] = '*'; clock_gettime(CLOCK_MONOTONIC, &start_time); logger(LOG, "request", "", args->thread_idx); // 记录请求日志 clock_gettime(CLOCK_MONOTONIC, &end_time); write_log_time += calculate_time_diff(start_time, end_time); // 验证请求方法是否为GET if (strncmp(buffer, "GET ", 4) && strncmp(buffer, "get ", 4)) { clock_gettime(CLOCK_MONOTONIC, &start_time); logger(FORBIDDEN, "Only simple GET operation supported", buffer, fd); clock_gettime(CLOCK_MONOTONIC, &end_time); write_log_time += calculate_time_diff(start_time, end_time); pthread_mutex_lock(&stats.mutex); stats.total_read_socket_time += read_socket_time; stats.total_write_log_time += write_log_time; pthread_mutex_unlock(&stats.mutex); close(fd); free(args); return; } // 提取请求URI(定位第一个空格后的第二个空格) for (i = 4; i < BUFSIZE; i++) { if (buffer[i] == ' ') { buffer[i] = 0; break; } } // 路径遍历攻击防护(检查..) for (int j = 0; j < i - 1; j++) if (buffer[j] == '.' && buffer[j + 1] == '.') { clock_gettime(CLOCK_MONOTONIC, &start_time); logger(FORBIDDEN, "Parent directory (..) path names not supported", buffer, fd); clock_gettime(CLOCK_MONOTONIC, &end_time); write_log_time += calculate_time_diff(start_time, end_time); pthread_mutex_lock(&stats.mutex); stats.total_read_socket_time += read_socket_time; stats.total_write_log_time += write_log_time; pthread_mutex_unlock(&stats.mutex); close(fd); free(args); return; } // 处理根路径请求(重定向到index.html) if (!strncmp(&buffer[0], "GET /\0", 6) || !strncmp(&buffer[0], "get /\0", 6)) strcpy(buffer, "GET /index.html"); // 确定文件扩展名对应的MIME类型 long buflen = strlen(buffer); char *fstr = 0; for (long i = 0; extensions[i].ext != 0; i++) { long len = strlen(extensions[i].ext); if (!strncmp(&buffer[buflen - len], extensions[i].ext, len)) { fstr = extensions[i].filetype; break; } } if (!fstr) { clock_gettime(CLOCK_MONOTONIC, &start_time); logger(FORBIDDEN, "file extension type not supported", buffer, fd); clock_gettime(CLOCK_MONOTONIC, &end_time); write_log_time += calculate_time_diff(start_time, end_time); pthread_mutex_lock(&stats.mutex); stats.total_read_socket_time += read_socket_time; stats.total_write_log_time += write_log_time; pthread_mutex_unlock(&stats.mutex); close(fd); free(args); return; } // 构造 FilenameMsg 并发送到 filename queue FilenameMsg *filenameMsg = (FilenameMsg *)malloc(sizeof(FilenameMsg)); filenameMsg->fd = fd; strcpy(filenameMsg->filename, &buffer[5]); // 跳过 "GET " strcpy(filenameMsg->filetype, fstr); task *new_task = (task *)malloc(sizeof(task)); new_task->function = (void (*)(void*))readFileThread; new_task->arg = filenameMsg; addTaskToThreadPool(read_file_pool, new_task); thread_enter_block(&read_msg_monitor, pthread_self()); pthread_mutex_lock(&stats.mutex); stats.total_read_socket_time += read_socket_time; stats.total_write_log_time += write_log_time; pthread_mutex_unlock(&stats.mutex); free(args); } void readFileThread(void *arg) { FilenameMsg *filenameMsg = (FilenameMsg*)arg; int fd = filenameMsg->fd; char *filename = filenameMsg->filename; char *filetype = filenameMsg->filetype; int file_fd; long len; char buffer[BUFSIZE]; struct timespec start_time, end_time; long long read_file_time = 0; long long write_log_time = 0; clock_gettime(CLOCK_MONOTONIC, &start_time); file_fd = open(filename, O_RDONLY); // 跳过"GET "后的/ clock_gettime(CLOCK_MONOTONIC, &end_time); read_file_time += calculate_time_diff(start_time, end_time); if (file_fd == -1) { clock_gettime(CLOCK_MONOTONIC, &start_time); logger(NOTFOUND, "failed to open file", filename, fd); clock_gettime(CLOCK_MONOTONIC, &end_time); write_log_time += calculate_time_diff(start_time, end_time); pthread_mutex_lock(&stats.mutex); stats.total_read_file_time += read_file_time; stats.total_write_log_time += write_log_time; pthread_mutex_unlock(&stats.mutex); close(fd); free(filenameMsg); return; } clock_gettime(CLOCK_MONOTONIC, &start_time); logger(LOG, "SEND", filename, fd); clock_gettime(CLOCK_MONOTONIC, &end_time); write_log_time += calculate_time_diff(start_time, end_time); // 获取文件大小 len = (long)lseek(file_fd, (off_t)0, SEEK_END); // 获取文件大小 lseek(file_fd, (off_t)0, SEEK_SET); // 重置文件指针 // 分块读取文件内容 memset(buffer, 0, sizeof(buffer)); clock_gettime(CLOCK_MONOTONIC, &start_time); long ret = read(file_fd, buffer, BUFSIZE); clock_gettime(CLOCK_MONOTONIC, &end_time); read_file_time += calculate_time_diff(start_time, end_time); if (ret <= 0) { clock_gettime(CLOCK_MONOTONIC, &start_time); logger(ERROR, "failed to read file", filename, fd); clock_gettime(CLOCK_MONOTONIC, &end_time); write_log_time += calculate_time_diff(start_time, end_time); pthread_mutex_lock(&stats.mutex); stats.total_read_file_time += read_file_time; stats.total_write_log_time += write_log_time; pthread_mutex_unlock(&stats.mutex); close(file_fd); close(fd); free(filenameMsg); return; } // 构造 Msg 并发送到 msg queue Msg *msg = (Msg*)malloc(sizeof(Msg)); msg->fd = fd; msg->len = len; memcpy(msg->content, buffer, ret); strcpy(msg->filetype, filetype); task *new_task = (task *)malloc(sizeof(task)); new_task->function = (void (*)(void*))sendMsgThread; new_task->arg = msg; addTaskToThreadPool(send_msg_pool, new_task); thread_enter_block(&read_file_monitor, pthread_self()); pthread_mutex_lock(&stats.mutex); stats.total_read_file_time += read_file_time; stats.total_write_log_time += write_log_time; pthread_mutex_unlock(&stats.mutex); close(file_fd); free(filenameMsg); } void sendMsgThread(void *arg) { Msg *msg = (Msg*)arg; int fd = msg->fd; long len = msg->len; char *content = msg->content; char *filetype = msg->filetype; char buffer[BUFSIZE]; struct timespec start_time, end_time; long long write_socket_time = 0; long long write_log_time = 0; struct timespec total_start_time, total_end_time; long long total_processing_time = 0; clock_gettime(CLOCK_MONOTONIC, &total_start_time); size_t max_len = BUFSIZE - 1; // 留出字符串结束符的空间 // 构造HTTP响应头 int bytes_written = snprintf(buffer, max_len, "HTTP/1.1 200 OK\nServer: nweb/%d.0\nContent-Length: %ld\nConnection: " "close\nContent-Type: %s\n\n", VERSION, len, filetype); // 检查是否截断 if (bytes_written >= max_len) { // 日志记录或错误处理 logger(ERROR, "HTTP header too large", "Truncated", fd); } // 发送响应头 write(fd, buffer, strlen(buffer)); // 记录响应头日志 clock_gettime(CLOCK_MONOTONIC, &start_time); logger(LOG, "Header", buffer, fd); clock_gettime(CLOCK_MONOTONIC, &end_time); write_log_time += calculate_time_diff(start_time, end_time); clock_gettime(CLOCK_MONOTONIC, &start_time); write(fd, buffer, strlen(buffer)); clock_gettime(CLOCK_MONOTONIC, &end_time); write_socket_time += calculate_time_diff(start_time, end_time); // 发送文件内容 clock_gettime(CLOCK_MONOTONIC, &start_time); write(fd, content, len); clock_gettime(CLOCK_MONOTONIC, &end_time); write_socket_time += calculate_time_diff(start_time, end_time); clock_gettime(CLOCK_MONOTONIC, &total_end_time); total_processing_time += calculate_time_diff(total_start_time, total_end_time); pthread_mutex_lock(&stats.mutex); stats.total_write_socket_time += write_socket_time; stats.total_write_log_time += write_log_time; stats.total_processing_time += total_processing_time; stats.child_count++; pthread_mutex_unlock(&stats.mutex); close(fd); free(msg); } // 清理资源函数 void cleanup_resources() { server_running=0; pthread_join(monitor_thread,NULL); waitThreadPool(read_msg_pool); waitThreadPool(read_file_pool); waitThreadPool(send_msg_pool); destroyThreadPool(read_msg_pool); destroyThreadPool(read_file_pool); destroyThreadPool(send_msg_pool); free(read_msg_monitor.threads); free(read_file_monitor.threads); free(send_msg_monitor.threads); pthread_mutex_destroy(&stats.mutex); pthread_mutex_destroy(&read_msg_monitor.mutex); pthread_mutex_destroy(&read_file_monitor.mutex); pthread_mutex_destroy(&send_msg_monitor.mutex); pthread_mutex_destroy(&send_msg_monitor.mutex); } /* 主函数 * 参数: * argc - 参数个数 * argv - 参数数组([端口号] [根目录]) */ // 信号处理函数(处理Ctrl+C) void sigint_handler(int sig) { printf("\nReceived SIGINT (Ctrl+C), shutting down...\n"); server_running = 0; // 停止主循环 //等待监控线程结束 pthread_join(monitor_thread,NULL); // 等待线程池中的所有任务完成 waitThreadPool(read_msg_pool); // 确保所有任务已处理完毕 waitThreadPool(read_file_pool); waitThreadPool(send_msg_pool); pthread_mutex_lock(&stats.mutex); int total_requests = stats.child_count; // 获取总请求数 if (total_requests == 0) { printf("No requests processed.\n"); pthread_mutex_unlock(&stats.mutex); cleanup_resources(); exit(0); } // 计算各项总耗时平均值(注意单位为微秒) long long total_processing = stats.total_processing_time; long long avg_processing = total_processing / total_requests; long long avg_read_socket = (stats.total_read_socket_time) / total_requests; long long avg_write_socket = (stats.total_write_socket_time) / total_requests; long long avg_read_file = (stats.total_read_file_time) / total_requests; long long avg_write_log = (stats.total_write_log_time) / total_requests; // 输出统计结果 printf("\n===== 服务器性能统计 =====\n"); printf("总处理请求数: %d\n", total_requests); printf("总处理时间: %lld μs\n", total_processing); printf("平均请求处理时间: %lld μs/请求\n", avg_processing); printf("------------------------\n"); printf("Socket读取平均耗时: %lld μs/请求\n", avg_read_socket); printf("Socket写入平均耗时: %lld μs/请求\n", avg_write_socket); printf("文件读取平均耗时: %lld μs/请求\n", avg_read_file); printf("日志写入平均耗时: %lld μs/请求\n", avg_write_log); printf("==========================\n"); pthread_mutex_unlock(&stats.mutex); // 清理资源(线程/共享内存等) cleanup_resources(); exit(0); } int main(int argc, char **argv) { int port, listenfd, socketfd, hit; socklen_t length; static struct sockaddr_in cli_addr; // 客户端地址 static struct sockaddr_in serv_addr; // 服务器地址 pthread_mutex_init(&stats.mutex,NULL); pthread_mutex_init(&io_stats_lock,NULL); init_threadpool_monitor(&read_msg_monitor,20); init_threadpool_monitor(&read_file_monitor,50); init_threadpool_monitor(&send_msg_monitor,20); read_msg_pool=initThreadPool(20,&read_msg_monitor); read_file_pool=initThreadPool(50,&read_file_monitor); send_msg_pool=initThreadPool(20,&send_msg_monitor); if(pthread_create(&monitor_thread,NULL,performance_monitor,NULL)!=0) { logger(ERROR,"Failed to create performance monitor thread","",0); return 1; } if(!(threads=malloc(max_thread*sizeof(pthread_t)))){ perror("内存分配失败"); return 1; } if (!read_msg_pool||!read_file_pool||!send_msg_pool) { perror("Failed to initialize thread pool"); return 1; } //注册SIGINT处理函数 signal(SIGINT,sigint_handler); // 参数验证 if (argc < 3 || argc > 3 || !strcmp(argv[1], "-?")) { printf("用法:nweb 端口号 根目录\n"); exit(0); } // 禁止使用系统关键目录 if (!strncmp(argv[2], "/", 2) || !strncmp(argv[2], "/etc", 5) || !strncmp(argv[2], "/bin", 5) || !strncmp(argv[2], "/lib", 5) || !strncmp(argv[2], "/tmp", 5) || !strncmp(argv[2], "/usr", 5) || !strncmp(argv[2], "/dev", 5) || !strncmp(argv[2], "/sbin", 6)) { printf("错误:禁止使用系统目录:%s\n", argv[2]); exit(3); } // 切换工作目录 if (chdir(argv[2]) == -1) { printf("错误:无法切换到目录:%s\n", argv[2]); exit(4); } logger(LOG, "webserver启动", argv[1], getpid()); // 创建监听套接字 if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) logger(ERROR, "socket系统调用失败", "", 0); // 设置端口号 port = atoi(argv[1]); if (port < 0 || port > 60000) logger(ERROR, "非法端口号(有效范围1-60000)", argv[1], 0); // 配置服务器地址 serv_addr.sin_family = AF_INET; serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); // 监听所有接口 serv_addr.sin_port = htons(port); // 绑定端口 if (bind(listenfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) logger(ERROR, "bind系统调用失败", "", 0); // 开始监听 if (listen(listenfd, 64) < 0) logger(ERROR, "listen系统调用失败", "", 0); // 主服务循环 for (hit = 1;; hit++) { length = sizeof(cli_addr); // 接受新连接(使用 socketfd 而非 connfd) if ((socketfd = accept(listenfd, (struct sockaddr *)&cli_addr, &length)) < 0) logger(ERROR, "accept系统调用失败", "", 0); // 创建任务参数(使用 thread_data 而非 TaskArgs) thread_data *args = (thread_data *)malloc(sizeof(thread_data)); if (!args) { close(socketfd); // 修正为 socketfd continue; } args->fd = socketfd; // 修正为 socketfd // 获取线程索引(使用互斥锁保护) pthread_mutex_lock(&stats.mutex); args->thread_idx = stats.child_count++; pthread_mutex_unlock(&stats.mutex); // 创建任务并提交到线程池 task *new_task = (task *)malloc(sizeof(task)); if (!new_task) { free(args); close(socketfd); // 修正为 socketfd continue; } new_task->function = (void (*)(void*))readMsgThread; // 显式类型转换 new_task->arg = args; addTaskToThreadPool(read_msg_pool, new_task); printf("Added task for FD %d to thread pool\n", socketfd); } return 0; }我的线程能够正常运行,然后服务器性能监控运行之后,线程就运行不了了,请问为什么,我怀疑是锁的原因
最新发布
06-08
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值