iis服务器助手广告
返回顶部
首页 > 资讯 > 服务器 >WebServer项目的亮点和难点
  • 375
分享到

WebServer项目的亮点和难点

服务器面试 2023-09-11 10:09:28 375人浏览 八月长安
摘要

文章目录 一、亮点1.采用了Reactor设计模式为什么选择Reactor?WebServer选择的Reactor方案WebServer对Reactor的具体实现 2.EPOLLONES

文章目录


面试被问到了这个问题,答得稀烂…但是我觉得这个问题真的问的很好,还是要好好想一想总结一下。

亮点:
并发模型为Reactor
使用Epoll水平触发+EPOLLONESHOT,非阻塞io
为充分利用多核CPU的性能,以多线程的形式实现服务器,并实现线程池避免线程频繁创建销毁造成的系统开销
实现基于小根堆的定时器,用于断开超时连接
实现可以自动增长的缓冲区,作为Http连接的输入和输出缓冲区

一、亮点

1.采用了Reactor设计模式

为什么选择Reactor?

我们的目的是实现一个高并发WEBServer。
传统的方式(让服务器为每个客户端的连接都创建一个进程或线程)会存在两个主要问题:
1.线程或进程处理完连接上的业务逻辑后,就需要进行销毁,这样不停的创建和销毁,会造成服务器性能的开销和资源的浪费。
2.如果有几万个客户端请求,要创建几万个线程或进程去处理也是不现实的。

引入I/O多路复用技术:
I/O 多路复用技术会用一个系统调用函数来监听我们所有关心的连接,也就说可以在一个监控线程里面监控很多的连接。内核提供给了我们select、poll、epoll等多路复用的系统调用。

具体的处理三种方法不太一样,这里主要说一下epoll的方式。
我们把需要检测的事件注册到工作在内核里的epoll对象上,epoll以红黑树的方式组织需要检测的事件,epoll内有回调函数,当有新事件到来时就会调用回调函数,把就绪事件添加到就绪事件链表上,内核会返回就绪事件,我们的主进程就会在用户态中处理这些事件对应的业务。
Reactor 模式就是基于I/O 多路复用技术,Reactor 对I/O多路复用技术作了一层封装,让使用者不用考虑底层网络 api 的细节,只需要关注应用代码的编写。

WebServer选择的Reactor方案

wiki上的定义:

The reactor design pattern is an event handling pattern for handling service requests delivered concurrently to a service handler by one or more inputs. The service handler then demultiplexes the incoming requests and dispatches them synchronously to the associated request handlers.

从上述文字中我们可以看出以下关键点 :

  • 是一种设计模式(design pattern)
  • 事件驱动(event handling)
  • 可以处理一个或多个(one or more inputs)同时到达(delivered concurrently)的输入源
  • 通过Service Handler同步的将输入事件(Event)采用多路复用分发给相应的Request Handler(多个)处理

本项目采用的是单 Reactor 多线程方案的思想。
为什么说是思想呢?项目确实只有一个Reactor,以及线程池。但是实际实现和「单 Reactor 多线程」方案不完全一样。
「单 Reactor 多线程」方案的示意图如下:
在这里插入图片描述
先详细说一下这个方案:
Reactor 对象通过 select (IO 多路复用接口) 监听事件,收到事件后通过 dispatch 根据收到事件的类型进行分发,具体分发给 Acceptor 对象或者Handler 对象;
如果是连接建立的事件,则交由 Acceptor 对象进行处理,Acceptor 对象会通过 accept 方法 获取连接,并创建一个 Handler 对象来处理后续的响应事件;
如果不是连接建立事件, 则交由当前连接对应的 Handler 对象来进行响应;
Handler 对象只负责数据的接收和发送,Handler 对象通过 read 读取到数据后,会将数据发给子线程里的 Processor 对象进行业务处理;
子线程里的 Processor 对象就进行业务处理,处理完后,将结果发给主线程中的 Handler 对象,接着由 Handler 通过 send 方法将响应结果发送给 client;

WebServer对Reactor的具体实现

先直接说区别吧,「单 Reactor 多线程」方案里Handler对象负责数据的接收和发送,也就是(read,write),子线程的Processor对象负责业务处理。但是项目里边对数据的接收和发送以及业务处理全都是子线程完成的。相当于Handler和Processor的职能全由子线程完成了。
具体的看项目:
首先,一个主进程负责监听,然后根据事件类型分别调用不同的函数进行处理。

int eventCnt = epoller_->Wait(timeMS);        for(int i = 0; i < eventCnt; i++) {                        int fd = epoller_->GetEventFd(i);            uint32_t events = epoller_->GetEvents(i);            if(fd == listenFd_) {                DealListen_();//处理监听的操作,接受客户端连接                    }            else if(events & (EPOLLRDHUP | EPOLLHUP | EPOLLERR)) {                assert(users_.count(fd) > 0);                CloseConn_(&users_[fd]);            }            else if(events & EPOLLIN) {                assert(users_.count(fd) > 0);                DealRead_(&users_[fd]); //处理读操作            }            else if(events & EPOLLOUT) {                assert(users_.count(fd) > 0);                DealWrite_(&users_[fd]); //处理写操作            } else {                LOG_ERROR("Unexpected event");            }

若是新连接就直接调用函数进行处理

void WebServer::DealListen_() {    struct sockaddr_in addr;   //保持连接的客户端信息    socklen_t len = sizeof(addr);    do {        int fd = accept(listenFd_, (struct sockaddr *)&addr, &len);        if(fd <= 0) {             return;        }        else if(HttpConn::userCount >= MAX_FD) {            SendError_(fd, "Server busy!");            LOG_WARN("Clients is full!");            return;        }        AddClient_(fd, addr);    } while(listenEvent_ & EPOLLET);}

若是读写事件,就交由线程池处理。

void WebServer::DealRead_(HttpConn* client) {    assert(client);    ExtentTime_(client);    threadpool_->AddTask(std::bind(&WebServer::OnRead_, this, client));}void WebServer::DealWrite_(HttpConn* client) {    assert(client);    ExtentTime_(client);    threadpool_->AddTask(std::bind(&WebServer::OnWrite_, this, client));}

2.EPOLLONESHOT

epoll有两种触发的方式即LT(水平触发)和ET(边缘触发)两种,在前者,只要存在着事件就会不断的触发,直到处理完成,而后者只触发一次相同事件或者说只在从非触发到触发两个状态转换的时才触发。

这会出现下面一种情况,如果是多线程在处理,一个Socket事件到来,数据开始解析,这时候这个SOCKET又来了同样一个这样的事件,而你的数据解析尚未完成,那么程序会自动调度另外一个线程或者进程来处理新的事件,这造成一个很严重的问题,不同的线程或者进程在处理同一个SOCKET的事件,这会使程序的健壮性大降低而编程的复杂度大大增加!!即使在ET模式下也有可能出现这种情况!!

这里用EPOLLONESHOT这种方法进行解决,可以在epoll上注册这个事件,注册这个事件后,如果在处理写成当前的SOCKET后不再重新注册相关事件,那么这个事件就不再响应了或者说触发了。要想重新注册事件则需要调用epoll_ctl重置文件描述符上的事件,这样前面的socket就不会出现竞态这样就可以通过手动的方式来保证同一SOCKET只能被一个线程处理,不会跨越多个线程。

3.基于小根堆实现了定时器

实现了高并发的服务器通常会面临的一个问题就是有大量的连接建立,但是实际活跃的连接并不多,这样会造成服务器端的资源浪费,因此我们选择为每个连接添加一个定时器,如果该连接超过了定时时间仍然没有时间到达,服务器就主动关闭这个连接。

4.实现了可以自动增长的缓冲区

主要使用了散布读和集中写

#include struct iovec{     void *iov_base;      size_t iov_len; };

成员iov_base指向一个缓冲区,这个缓冲区是存放readv所接收的数据或是writev将要发送的数据。
成员iov_len确定了接收的最大长度以及实际写入的长度。

ssize_t readv(int fd, const struct iovec *iov, int iovcnt);ssize_t writev(int fd, const struct iovec *iov, int iovcnt);

参数:

fd是要在其上进行读或是写的文件描述符;
iov是读或写所用的I/O向量;
iovcnt是要使用的向量元素个数。

返回值:

readv所读取的字节数或writev所写入的字节数;
如果有错误发生,就会返回-1,错误代码存在errno中。

在项目的实现中根据readv()函数的返回值来进行相应操作,比如返回错误、返回下一次内存读入的位置或者用临时数组实现缓冲区的增长。writev()函数也同理。

5.线程池

采用生产者消费者模型。
用互斥量和条件变量实现了线程池。unique_lock与lock_guard的区别
而且notify_one()不会导致惊群。

二、难点

也许不是项目的难点而是我自己认为难的地方。

  1. Reactor思想的运用
  2. 为什么选择了socket非阻塞与epollET模式
    这里我之前也写过一篇博文WebServer为什么需要将socket设置为非阻塞?

三、有待改进的地方

「单 Reactor」的模式存在一个问题,因为一个 Reactor 对象承担所有事件的监听和响应,而且只在主线程中运行,在面对瞬间高并发的场景时,容易成为性能的瓶颈的地方。这个也是WebServer这个项目存在的瓶颈之一。

参考链接:
【项目】高性能web服务器
C++11实现的高性能静态web服务器
高性能网络模式:Reactor 和 Proactor
Reactor详解
epoll的LT和ET使用EPOLLONESHOT

来源地址:https://blog.csdn.net/ccw_922/article/details/124635799

--结束END--

本文标题: WebServer项目的亮点和难点

本文链接: https://www.lsjlt.com/news/402957.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • WebServer项目的亮点和难点
    文章目录 一、亮点1.采用了Reactor设计模式为什么选择Reactor?WebServer选择的Reactor方案WebServer对Reactor的具体实现 2.EPOLLONES...
    99+
    2023-09-11
    服务器 面试
  • web worker在项目中的使用学习为项目增加亮点
    目录引言为什么JavaScript是单线程?什么是Web Worker?小试牛刀在单页面应用中使用注意事项小结引言 平时小伙伴们不是说日常的项目开发中,都是单纯的搬砖,没啥亮点嘛,那...
    99+
    2024-04-02
  • HTML和CSS重点难点的示例分析
    小编给大家分享一下HTML和CSS重点难点的示例分析,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!1.怎么让一个不定宽高的 p,...
    99+
    2024-04-02
  • Go语言项目开发的技术难点与解决方案
    Go语言项目开发的技术难点与解决方案随着互联网和移动互联网的快速发展,Go语言作为一种高效、简洁的编程语言,在项目开发中得到了广泛的应用。然而,虽然Go语言具有很多优点,但在项目开发中仍然会遇到一些技术难点。本文将介绍一些常见的Go语言项目...
    99+
    2023-11-02
    错误处理 包管理 技术难点:并发 解决方案:goroutine
  • Go语言项目开发的技术难点与解决方法
    Go语言项目开发的技术难点与解决方法随着互联网的普及和信息化的发展,软件项目的开发也越来越受到重视。在众多的编程语言中,Go语言因其强大的性能、高效的并发能力和简单易学的语法成为了众多开发者的首选。然而,Go语言项目开发中仍然存在一些技术难...
    99+
    2023-11-02
    并发 (concurrency) 错误处理 (Error handling) 内存管理 (Memory manageme
  • HTML和CSS的重难点知识点有哪些
    这篇文章主要介绍“HTML和CSS的重难点知识点有哪些”,在日常操作中,相信很多人在HTML和CSS的重难点知识点有哪些问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”HTML...
    99+
    2024-04-02
  • JavaScript循环的难点和易错
    JavaScript循环是开发者们最常用的控制流程语句之一。它可以帮助我们快速而高效地处理数组,对象,以及各种集合并将它们进行遍历、操作。然而,尽管它看似简单易懂,但在实际应用中它往往会带来一些难点和易错的问题。本文将重点讲解JavaScr...
    99+
    2023-05-16
  • 用 Node.js 原生插件点亮你的项目:性能优化、稳定性提升
    Node.js 是一个异步事件驱动的 JavaScript 运行时环境,它使用 V8 JavaScript 引擎来执行 JavaScript 代码。Node.js 原生插件是指使用 C/C++ 语言编写的 Node.js 扩展模块,它可...
    99+
    2024-02-25
    Node.js 原生插件 性能优化 稳定性提升
  • 黑马点评项目总结
    黑马点评 一、短信登陆功能1.基于session实现2.基于session实现登陆的问题3.基于redis实现短信登陆4.补充ThreadLocal相关知识a.ThreadLocal的数据结构...
    99+
    2023-09-08
    java 服务器 数据库
  • redis和mysql哪个比较难一点
    了解redis和mysql哪个比较难一点?这个问题可能是我们日常学习或工作经常见到的。希望通过这个问题能让你收获颇深。下面是小编给大家带来的参考内容,让我们一起来看看吧!一、redis和mysql的区别总结...
    99+
    2024-04-02
  • HTML和CSS的重难点问题有哪些
    这篇“HTML和CSS的重难点问题有哪些”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“HT...
    99+
    2024-04-02
  • 菜鸟项目练习:黑马点评项目总结
    目录 1. 项目介绍 2.各个功能模块  2.1  登录模块   2.1.1 实现短信登录   2.1.2 编写拦截器  2.2 查询商户模块    2.2.1 主页面查询商户类型    2.2.3 按距离查询商户  2.3 优惠券秒杀模...
    99+
    2023-09-05
    java redis
  • 云服务器项目有哪些类型和特点
    云服务器是一种基于互联网的服务器托管服务,它提供了一种虚拟的计算资源池,让用户可以通过互联网访问自己的服务器,并从中分配计算资源、存储空间和网络带宽。 云服务器主要有以下类型和特点: 私有云服务器:私有云服务器是用户自己管理和控制的一组...
    99+
    2023-10-26
    类型 服务器 项目
  • iOS练手项目知识点汇总
    基础理解篇 Objective-C是一种面向对象的编程语言,它支持元编程。元编程是指编写程序来生成或操纵其他程序的技术。 Objective-C中,元编程可以使用Objective-C的动态特性来实现...
    99+
    2023-09-12
    ios cocoa macos
  • 一篇文章弄懂Java和Kotlin的泛型难点
    目录一、泛型类型 二、为什么需要泛型 三、类型擦除 四、类型擦除的后遗症 五、Kotlin 泛型 六、上界约束 七、类型通配符 & 星号投影 八、协变 & 不变 九、...
    99+
    2024-04-02
  • 系统整理scrapy框架的特点与技术亮点
    Scrapy框架是一个基于Python的Web爬虫框架,专门用来从互联网上获取信息。它具有高效、灵活且可扩展的特点,可以用于爬取各种类型的数据,如网页、图像、音频等。本文将介绍Scrapy框架的主要特点和技术亮点,并提供相应的代...
    99+
    2024-01-19
    Scrapy 特点 技术亮点
  • 重定向和打包——Java中NumPy的两大难点?
    NumPy是Python中非常流行的一个库,它提供了许多高级的数值计算和数据处理功能。然而,随着Java在数据科学领域的应用越来越广泛,越来越多的人开始尝试在Java中使用NumPy。但是,与Python不同,Java中使用NumPy存在...
    99+
    2023-06-04
    打包 numy 重定向
  • vue项目中如何获取节点
    这篇文章给大家分享的是有关vue项目中如何获取节点的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。vue项目中如何获取节点?Vue的优点Vue具体轻量级框架、简单易学、双向数据绑定...
    99+
    2024-04-02
  • 云服务器项目有哪些类型和特点呢
    云服务器是一种虚拟化软件,允许客户将计算资源和存储资源分配到多个服务器上,并且使用虚拟化软件来管理这些资源。 云服务器具有以下的特点: 高可用性:通过多个服务器组成的集群来提供高可用性和可靠性,确保服务不会中断或者失败。 快速扩展:通过...
    99+
    2023-10-27
    类型 服务器 项目
  • Python 和 JavaScript:Windows 同步的技术难点和挑战是什么?
    在计算机科学领域中,Python 和 JavaScript 是两种广泛使用的编程语言。Python 是一种高级编程语言,常用于数据分析、机器学习和人工智能等领域;而 JavaScript 则是一种用于开发 Web 应用的脚本语言。Wind...
    99+
    2023-09-09
    javascript windows 同步
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作