Linux_网络项目_WEB服务器 处理服务器写入失败后sigpipe信号导致服务器崩溃退出问题,引入线程池缓解大量请求,服务器组件化重构,在线计算机业务测试

news2024/10/5 21:24:02

文章目录

  • 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
在这里插入图片描述
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1514878.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

界面开发框架DevExpress XAF v24.1新版预告 - 跨平台应用UI(二)

DevExpress XAF是一款强大的现代应用程序框架&#xff0c;允许同时开发ASP.NET和WinForms。XAF采用模块化设计&#xff0c;开发人员可以选择内建模块&#xff0c;也可以自行创建&#xff0c;从而以更快的速度和比开发人员当前更强有力的方式创建应用程序。 本文中的内容概述了…

Toy 语言到 LLVM IR 实现源码注释

对从程序源代码到AST的转换部分做了注释 源码&#xff1a; toy.cpp #include "llvm/IR/DerivedTypes.h" #include "llvm/IR/IRBuilder.h" #include "llvm/IR/LLVMContext.h" #include "llvm/IR/Module.h" #include "llvm/IR/Ver…

Java Day9 Stream流

Stream流 1、认识2、Stream流使用步骤3、如何获取Stream流4.Stream流的中间方法5、 Stream流终结方法 1、认识 2、Stream流使用步骤 3、如何获取Stream流 //list获取stream流List<String> listnew ArrayList<>();Collections.addAll(list,"崔十一","…

【目标检测经典算法】R-CNN、Fast R-CNN和Faster R-CNN详解系列二:Fast R-CNN图文详解

RCNN算法详解&#xff1a;【目标检测经典算法】R-CNN、Fast R-CNN和Faster R-CNN详解系列一&#xff1a;R-CNN图文详解 学习视频&#xff1a;Faster RCNN理论合集 Fast RCNN 概念辨析 1. RoI 在Fast R-CNN中&#xff0c;RoI&#xff08;Region of Interest&#xff0c;感兴…

Python导入类说一说

要在Python中导入一个类&#xff0c;需要使用import关键字。 详细去看下面的代码 1、多例类 class Restaurant:餐馆类def __init__(self,restaurant_name,cuisine_type):#类的属性self.restaurant_name restaurant_nameself.cuisine_type cuisine_type# self.stregth_leve…

Python网络基础爬虫-python基本语法

文章目录 逻辑语句if,else,elifforwhile异常处理 函数与类defpassclass 逻辑语句 熟悉C/C语言的人们可能很希望Python提供switch语句&#xff0c;但Python中并没有这个关键词&#xff0c;也没有这个语句结构。但是可以通过if-elif-elif-…这样的结构代替&#xff0c;或者使用字…

解决JVM进程被系统杀掉问题

背景 服务A在测试环境&#xff0c;隔几个小时接口就无法访问。登录机器查看&#xff0c;发现进程已经没了。大致猜想是进程使用的内存或CPU资源使用太多&#xff0c;导致被系统kill。 问题定位 使用dmesg命令查看进程被kill的详情。 > dmesg --time-format iso2024-03-0…

【Python如何与电脑玩石头剪刀布游戏】

1、石头剪刀布Python代码如下&#xff1a; import random while True:a random.randint(0, 2)b int(input("请输入一个数字&#xff08;0石头, 1剪刀, 2布&#xff09;: "))c [石头, 剪刀, 布]if b ! 0 and b ! 1 and b ! 2:print("傻子&#xff0c;你出错了…

五子棋小游戏(sut实验报告)

实验目的 实现人与人或人与电脑进行五子棋对弈 实验内容 启动游戏&#xff0c;显示游戏参数设置界面&#xff0c;用户输入参数后进入游戏界面&#xff0c;显示棋盘及双方博弈过程&#xff0c;游戏过程中可选择退出游戏。判定一方获胜后结束本局游戏&#xff0c;可选择继续下…

S4 Hana SD -信贷管理 - 02

2.3 给信贷控制范围分配公司代码 TCODE: SPRO 配置路径:IMG > 企业结构 > 分配 > 财务会计 > 给信贷控制区分配公司代码 配置路径截图: 公司:被分配的公司代码。 公司名称&城市:已在公司代码数据中维护。 CCAR:分配的信贷控制范围。 覆盖CC范围:如…

InstantID Zero-shot Identity-Preserving Generation in Seconds

InstantID: Zero-shot Identity-Preserving Generation in Seconds TL; DR&#xff1a;InstantID IP-Adapter (Face) ControlNet&#xff0c;实现了具有较高保真度的人脸 ID 生成。 方法 InstantID 想做到的事情是&#xff1a;给定一张参考人脸 ID 图片&#xff0c;生成该…

专升本 C语言笔记-07 逗号运算符

1.逗号表达式的用法 就是用逗号隔开的多个表达式。逗号表达式&#xff0c;从左向右依次执行。 2.逗号表达式的特性 2.1.当没有括号时&#xff0c;第一个表达式为整个表达式的值。 代码 int x 3,y 5,a 0; a x,y; printf("a %d",a); 说明:因为逗号优先级最低,会…

利用Python进行网络爬虫:Beautiful Soup和Requests的应用【第131篇—Beautiful Soup】

利用Python进行网络爬虫&#xff1a;Beautiful Soup和Requests的应用 在网络数据变得日益丰富和重要的今天&#xff0c;网络爬虫成为了获取和分析数据的重要工具之一。Python作为一种强大而灵活的编程语言&#xff0c;在网络爬虫领域也拥有广泛的应用。本文将介绍如何使用Pyth…

【智能硬件、大模型、LLM 智能音箱】MBO:基于树莓派、ChatGPT 的桌面机器人

MAKER:David Packman/译:趣无尽(转载请注明出处) 这是国外 Maker David Packman 制作的基于树莓派机器人 MBO,该机器人的外观设计灵感来自动漫 Adventure Time 中的机器人 MBO。它具有强大的交互功能,可实现脱机唤醒词检测、调用 ChatGPT 3.5 进行聊天、机器视觉对图像进…

解决Git:Author identity unknown Please tell me who you are.

报错信息&#xff1a; 意思&#xff1a; 作者身份未知 ***请告诉我你是谁。 解决办法&#xff1a; git config --global user.name "你的名字"git config --global user.email "你的邮箱"

Android 15 首个开发者预览版到来

作者 / 工程副总裁 Dave Burke Android 15 的首个开发者预览版现已发布&#xff0c;以便各位开发者能与我们通力协作&#xff0c;打造更优秀的 Android 平台。 在 Android 15 中&#xff0c;我们继续致力于打造一个既能提升工作效率&#xff0c;又能提供全新功能的平台。这些新…

蓝桥杯-模拟-4402. 刷题统计

题目 思路 代码 a,b,nmap(int,input().split()) sa*5b*2 resn//s*7 # 存在周期 d[a,a,a,a,a,b,b] n%s i0 while n>0: # 对剩余数量进行枚举&#xff0c;如果等于0&#xff0c;相当于还会再进去加一天n-d[i]i1res1 print(res)

es 聚合操作(一)

前言 Elasticsearch除搜索以外&#xff0c;提供了针对ES 数据进行统计分析的功能。聚合(aggregations)可以让我们极其方便的实现对数据的统计、分析、运算。例如&#xff1a; 衣服品牌的受欢迎程度这些衣服的平均价格、最高价格、最低价格这些衣服的每天、每月销量如何 使用…

cpp qt 一个奇怪的bug

今天在用cpp qt的时候发现了一个奇怪的东西 这是我的源代码 #include "mywidget.h" #include <QPushButton>myWidget::myWidget(QWidget *parent): QWidget(parent) {QPushButton * btn1 new QPushButton;btn1->show();btn1->setParent(this);btn1-&g…

在集群模式下,Redis 的 key 是如何寻址的?分布式寻址都有哪些算法?了解一致性 hash 算法吗?

目录 一、分布式寻址算法 1. hash 算法 2. 一致性 hash 算法 3. Redis cluster 的 hash slot 算法 二、Redis cluster 的高可用与主备切换原理 1. 判断节点宕机 2. 从节点过滤 3. 从节点选举 4. 与哨兵比较 一、分布式寻址算法 hash 算法(大量缓存重建) 一致性 hash…