文章目录
- 1. 处理服务器写入管道出错
- 2. 引入线程池缓解大量请求导致服务器崩溃
- 设计线程任务类
- 单例线程池组件设计
- 3.代码位置
- 4. 在线计算机业务运行截图
1. 处理服务器写入管道出错
经过测试,服务器在读取报文时如果出错可以选择直接关闭这个TCP里链接来节省资源。这个问题好解决不在赘述
此外如果服务器使用CGI时,向管道写入数据失败时,服务器进程会收到sigpipe信号直接崩溃。所以这里选择直接忽视这个信号。
class HttpSever
{
private:
int port;
TcpSever *tcp_sever;
bool states; // 标记服务器运行状态
void InitSever()
{
// 信号SIGPIPE需要进行忽略,防止服务器写入管道时失败服务器崩溃
signal(SIGPIPE, SIG_IGN);
tcp_sever = TcpSever::GetInstance(port);
}
public:
HttpSever(int _port = PORT)
{
port = _port;
tcp_sever = nullptr;
states = true;
InitSever();
}
~HttpSever() {}
void Loop()
{
LOG(INFO, "---http sever loop begin---");
int listen_socket = tcp_sever->GetLinstenSocket();
while (states != false)
{
struct sockaddr_in client; // 客户端信息
socklen_t len = sizeof(client);
int sock = accept(listen_socket, (struct sockaddr *)&client, &len);
if (sock < 0)
{
// 套接字监听失败
continue;
}
LOG(INFO, "get a new link");
int *_sock = new int(sock);
pthread_t tid = 0;
pthread_create(&tid, nullptr, Entrance::HanderReq, _sock);
pthread_detach(tid); // 线程分离
}
}
};
2. 引入线程池缓解大量请求导致服务器崩溃
设计线程任务类
#pragma once
#include <iostream>
#include "../Protocol.hpp"
// 设计线程任务队列
class Task
{
private:
int sock;
CallBack callback; // 设置回调
public:
Task() {}
Task(int _sock)
{
sock = _sock;
}
void ProcessOn()
{
callback(sock);
}
};
服务器在收到链接时不创建线程,而是构建线程任务,然后将线程任务放入到线程任务队列上,最后线程池从任务队列上拿去任务处理即可,线程拿到任务后,通过任务里的CallBack回调函数执行不同的任务
线程任务CallBack设计紧贴前面重构前的代码,将Entrance类更改成CallBack即可,这里设计仿函数,线程回调函数通过()直接访问解析请求函数
// 线程工作入口
class CallBack
{
public:
void operator()(int sock)//仿函数
{
HanderReq(sock);
}
// 处理HTTP请求
static void HanderReq(int _sock)
{
LOG(INFO, "http request hander begin");
EndPoint *endpoint = new EndPoint(_sock);
endpoint->ReadRequest();
if (endpoint->Stop() != true)
{
LOG(INFO, "recv success! build request begin");
endpoint->BuildResponse();
endpoint->SendResponse();
}
else
{
LOG(WARNING, "recv error! please try again");
}
delete endpoint;
LOG(INFO, "http request hander end");
}
};
单例线程池组件设计
ThreadPool
#pragma once
#include <iostream>
#include "task.hpp"
#include <queue>
#include <pthread.h>
#include "../log/log.hpp"
#define THREAD_NUM 6
// 单例模式
class ThreadPool
{
private:
// 生产者消费者模型
std::queue<Task> task_queue; // 临界资源
int thread_count; // 线程数
bool stop; // 线程池是否停止
pthread_mutex_t mutex;
pthread_cond_t cond;
static ThreadPool *instance;
bool init_threadPool() // 初始化线程池
{
for (int i = 0; i < thread_count; i++)
{
pthread_t thread_id;
if (0 != pthread_create(&thread_id, NULL, thread_routine, this))
{
// 创建线程失败
LOG(FATAL, "init threadpool error!");
return false;
}
}
LOG(INFO, "http sever init threadpool success");
return true;
}
static void *thread_routine(void *args) // 线程执行函数 static 删除类的this指针,对象通过线程的参数传递
{
ThreadPool *pool = (ThreadPool *)args;
while (!pool->stop)
{
Task task;
pthread_mutex_lock(&pool->mutex);
while (pool->task_queue.empty()) // while 不能换成if
{
pool->thread_wait(); // 线程唤醒后一定占有互斥锁
}
task = pool->task_queue.front();
pool->task_queue.pop();
pthread_mutex_unlock(&pool->mutex);
task.ProcessOn();
}
return NULL;
}
void thread_wait() // 任务队列无任务,线程休眠
{
pthread_cond_wait(&cond, &mutex);
}
void thread_wakeup() // 任务队列有任务,线程唤醒
{
pthread_cond_signal(&cond); // 唤醒一个线程即可
}
ThreadPool(int thread_num = THREAD_NUM)
{
thread_count = thread_num;
stop = false;
pthread_mutex_init(&mutex, nullptr);
pthread_cond_init(&cond, nullptr);
init_threadPool();
}
ThreadPool(const ThreadPool &) = delete;
public:
static ThreadPool *GetInstance()
{
static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; // 静态锁直接初始化,不需要释放
if (instance == nullptr)
{
pthread_mutex_lock(&lock);
if (instance == nullptr)
{
instance = new ThreadPool();
}
pthread_mutex_unlock(&lock);
}
return instance;
}
~ThreadPool()
{
pthread_mutex_destroy(&mutex);
pthread_cond_destroy(&cond);
}
void push_task(const Task &task)
{
pthread_mutex_lock(&mutex);
task_queue.push(task);
pthread_mutex_unlock(&mutex);
thread_wakeup();
}
bool is_stop()
{
return stop;
}
};
ThreadPool *ThreadPool::instance = nullptr;
HttpSever函数更换为线程池同时组件化
#pragma once
#include "./TcpSever/TcpSever.hpp"
#include "Protocol.hpp"
#include "./log/log.hpp"
#include <signal.h>
#include <pthread.h>
#include "./ThreadPool/task.hpp"
#include "./ThreadPool/threadpool.hpp"
#define PORT 8080
class HttpSever
{
private:
int port;
TcpSever *tcp_sever;
bool states; // 标记服务器运行状态
ThreadPool threadpool;
void InitSever()
{
// 信号SIGPIPE需要进行忽略,防止服务器写入管道时失败服务器崩溃
signal(SIGPIPE, SIG_IGN);
tcp_sever = TcpSever::GetInstance(port);
}
public:
HttpSever(int _port = PORT)
{
port = _port;
tcp_sever = nullptr;
states = true;
InitSever();
}
~HttpSever() {}
void Loop()
{
LOG(INFO, "---http sever loop begin---");
int listen_socket = tcp_sever->GetLinstenSocket();
while (states != false)
{
struct sockaddr_in client; // 客户端信息
socklen_t len = sizeof(client);
int sock = accept(listen_socket, (struct sockaddr *)&client, &len);
if (sock < 0)
{
// 套接字监听失败
continue;
}
LOG(INFO, "get a new link");
// 构建任务
Task task(sock);
threadpool.push_task(task);
}
}
};
测试运行结果如下:线程池创建6个线程,初始化线程池正确。同时用户报文发送错误,日志级别WARING级别
3.代码位置
Github
Gitee
4. 在线计算机业务运行截图
除0错误等其他错误会导致子进程错误退出,服务器捕捉到后跳转到错误页面即404.html