文章目录

  • 轻量级Web服务器搭建涉及的知识点总结 2022春
    • 整体知识框架
    • Eventfd 事件通知创建文件描述符
    • HTTP请求之get/post & 长连接和短连接
    • Reactor(反应池) 架构
    • 异步日志系统的实现
    • 阻塞IO和非阻塞IO
    • 串行化和闩锁 latch
    • 介绍thread.join()方法
    • 移动构造函数
    • Java之wait() notify()/notifyall()
    • 条件变量和互斥量一起使用
    • 内联函数
    • Muduo 架构的几个封装函数
    • RALL(资源获取即初始化)机制详解
    • 并发控制的悲观主义策略和乐观主义策略(乐观锁和悲观锁)
    • Muduo 多线程异步日志

轻量级Web服务器搭建涉及的知识点总结 2022春

参考资料

整体知识框架

项目描述:为了掌握Linux环境下多进行、多线程编程、IO复用技术,TCP/IP、HTTP协议等,实现了一个轻量级的高并发Web服务器;
负责任务:搭建Linux环境下的Web服务器
技术点**:**
1、 使用Reactor+非阻塞IO+线程池的并发模型,与IO复用中Epoll技术,提高并发度;
2、 使用单例设计模式实现异步日志系统,记录服务器状态;
3、 使用Mysql实现了用户账号的注册和登录;
4、 使用状态机解析HTTP请求,可以解析GET和POST请求;
5、 使用智能指针等RAII机制,避免内存泄漏;
6、 使用eventfd实现了线程的异步唤醒;

项目成果:实现了高并发响应的Web服务器,能够解析GET和POST请求,经过Webbend压力测试,能响应数万的并发请求,并且实现了日志系统,记录服务器的运行状态;


Eventfd 事件通知创建文件描述符

参考资料
是一种用来实现事件通知的系统调用。
实现机制:eventfd包含一个由内核维护的64位无符号整型计数器,创建eventfd时会返回一个文件描述符,进程可以通过对这个文件描述符进行read/write来读取/改变计数器的值,从而实现进程间通信。


HTTP请求之get/post & 长连接和短连接

Get/post参考资料
长短连接参考资料
首先,get和post都是HTTP的请求方法;
Get:请求指定的页面信息,并返回实体主体;
Post:向指定资源提交数据进行处理请求,数据包含在请求体中,POST请求可能会导致新的资源的建立或已有资源的修改;

请求方法如何使用?
在此之前,首先了解一下HTTP的请求报文格式:

HTTP的请求报文由三部分组成
请求行:由请求方法(Method)、URL 字段和 HTTP 的协议版本组成,注意其中的空格、回车符和换行符均不可省略,所以我们的请求方法实际上就是位于请求行中的了。
请求头部:位于请求行之后,个数可以为0~若干个,每个请求头部都包含一个头部字段名和一个值,它们之间用冒号 “:” 分隔,在最后用回车符和换行符表示结束。
请求数据:如果请求方法为 GET,那么请求数据为空。它主要是在 POST 中进行使用,适用于需要填表单(FORM)的场景。


Reactor(反应池) 架构

参考
资料

首先reactor是一种设计模式,这个项目采用的是多Reactor多线程模型

主要部分解释
将reactor分为两部分
1、 mainReactor负责监听server socket,用来处理新连接的建立,将建立的socketChannel指定注册给subReactor
2、 subReactor维护自己的selector,基于mainReactor注册的sokcetChannel多路分离IO读写事件,读写网络数据,对业务处理的功能,另其扔给worker线程池来完成;

Reactor的处理方式
1、 同步的的等待多个事件源到达(采用select()实现)
2、 将事件多路分解以及分配相应的 事件进行处理,这个分派采用server集中处理(dispatch)
3、 分解的事件以及对应的时间服务器从分派服务器中分离出去;

采用reactor的原因
常见的网络服务中,如果每一个客户端都维持一个与登陆服务器的连接。那么服务器将维护多个和客户端的连接以出来和客户端的contnect 、read、write ,特别是对于长链接的服务,有多少个c端,就需要在s端维护同等的IO连接。这对服务器来说是一个很大的开销;
为了避免资源消耗,我们可以采用线程池的方式处理读写服务。
但是线程池也会存在以下弊端
1、 同步阻塞IO,读写阻塞,线程等待时间过长
2、 在制定线程策略的时候,只能根据CPU的数目来限定可用线程资源,不能根据连接并发数目来制定,也就是连接有限制。否则很难保证对客户端请求的高效和公平。
3、 多线程之间的上下文切换,造成线程使用效率不高,且不易扩展;
4、 状态数据以及其他需要保持一致的数据,需要采用并发同步控制;

Reactor的优势
1、 非阻塞的IO读写
2、 基于IO事件进行分发任务,同时支持对多个fd的监听
3、 基于事件驱动->selector(支持对多个socketChannel的监听)
4、 统一的事件分派中心->dispatch
5、 事件处理服务 read&write

Reactor的分类
1、 单reactor单线程模型

2、单reactor多线程模型

3、多reactor多线程模型


异步日志系统的实现

日志指南
参考资料

Nagle 算法资料
线程池
资料1
资料2

线程池:一种管理线程的概念,线程池的好处是可以更方便的管理线程,减少内存消耗
在一个应用程序中,我们需要多次创建销毁线程,而创建销毁线程的过程势必会消耗内存。
为了避免重复的创建线程,线程池的出现可以让线程进行复用;可以理解为,当有任务需要执行时,就会向线程池拿一个线程,当任务执行完毕后,不是直接关闭线程,而是将这个线程归还给线程池供其他任务使用;

线程锁(互斥锁)
多线程中会存在数据不一致的情况,可以考虑在同一时间内只有一个线程访问数据来保证数据的一致;互斥量就是一把锁
多个线程只有一把锁一把钥匙,谁上的锁就只有谁可以开锁;当一个线程要访问一个共享变量时,先用锁把变量锁住,然后再操作,操作完后再释放掉锁,操作完成;
当有另一个线程要访问这个变量是,会发现这个变量被锁住了,无法访问;它就会一直等待,直到锁没了,它再给这个变量上锁,然后进行操作;
这样的过程使得即使有多个线程同时访问这个变量,也可以保证各个线程对这个变量的操作是顺序进行的。

条件变量
条件变量是利用线程间共享的全局变量进行同步的一种机制,主要包括两个动作:一个线程等待"条件变量的条件成立"而挂起;另一个线程使"条件成立"(给出条件成立信号)。为了防止竞争,条件变量的使用总是和一个互斥锁结合在一起
条件变量是利用线程间共享的全局变量进行同步的一种机制,主要包括两个动作:
1)一个线程等待"条件变量的条件成立"而挂起;
2)另一个线程使"条件成立"(给出条件成立信号)。

为了防止竞争,条件变量的使用总是和一个互斥锁结合在一起
定义互斥锁(全局变量)的函数,定义互斥锁对象的形式为:
Pthread_mutex_t lock;定义互斥锁
pthread_mutex_lock —互斥锁上锁,持有锁
pthread_mutex_unlock ----互斥锁解锁,释放锁
pthread_cond_wait() / pthread_cond_timedwait -----等待条件变量,挂起线程,区别是后者,会有timeout时间,如果到了timeout,线程自动解除阻塞,这个时间和 time()系统调用相同意义的。

pthread_cond_wait,先会解除当前线程的互斥锁,然后挂线线程,等待条件变量满足条件。一旦条件变量满足条件,则会给线程上锁,继续执行pthread_cond_wait)
pthread_cond_broadcast() -------唤醒所有等待线程列表中最先入队的线程

socket套接字
应用程序中使用的IP地址和端口号以结构体的形式给出
Sockaddr_in
其中的参数:
Sin_family表示地址族
Sin_port 表示端口号
Sin_addr 表示32位地址


阻塞IO和非阻塞IO

参考资料
阻塞IO:
Socet的阻塞模式意味着必须要完成IO操作才会返回
非阻塞IO
非阻塞模式下无论操作是否完成都会立刻返回,需要通过其他方式来判断具体操作是否成功;


串行化和闩锁 latch

数据库系统本身是一个多用户并发处理系统,在同一个时间点上,可能会有多个用户同时操作数据库。多个用户同时在相同的物理位置上写数据时,不能发生互相覆盖的情况,这叫做串行化
串行化会降低系统的并发性,但这对于保护数据结构不被破坏来说则是必需的。在Oracle数据库中,通过闩锁(latch)、锁定(lock)、互斥(mutex)来实行串行化,保护数据结构一致性的

Latch的定义和作用
Oracle数据库使用闩锁(latch)来管理SGA内存的分配和释放,Latch是用于保护SGA中共享数据结构的一种串行化锁定机制。Latch的实现是与操作系统相关的,尤其和一个进程是否需要等待一个latch、需要等待多长时间有关。
Latch是一种能够极快地被获取和释放的锁,它通常用于保护描述buffer cache中block的数据结构。


介绍thread.join()方法

在很多情况下,主线程生成并起动了子线程,如果子线程里要进行大量的耗时的运算,主线程往往将于子线程之前结束,但是如果主线程处理完其他的事务后,需要用到子线程的处理结果,也就是 主线程需要等待子线程执行完成之后再结束,这个时候就要用到join()方法了。
即:join()方法的作用是:等待该线程终止,这里需要理解的是该线程指的是主线程等待子线程的终止;也就是在子线程调用了join()方法后面的代码,只有等到子线程结束可才能执行;


移动构造函数

C++11在运行期有所增强,通过增加核心的右值引用机制来改善临时对象导致的效率低下的问题。C++临时对象引入了多余的构造、析构及其内部资源的申请释放函数调用,导致程序运行时性能受损,这一点被广为诟病。C++标准委员会在C++11中引入了右值引用这个核心语言机制,来提升运行期性能.
std::move,可以避免不必要的拷贝操作
std::move是为性能而生。
std::move是将对象的状态或者所有权从一个对象转移到另一个对象,只是转移,没有内存的搬迁或者内存拷贝


Java之wait() notify()/notifyall()

参考资料


条件变量和互斥量一起使用

参考资料

条件变量和互斥量一起使用可以允许线程以无竞争的方式等待特定条件的发生
为什么必须一起使用呢?
1)假如当某个资源满足了一定的条件时它要发送信号告诉需要它的线程,假如现在资源为空,条件不满足,那么线程就要等待,那么在线程条件检查时刻t1,到线程放到表上的时刻t2,在t1到t2这段时间内,可能资源来了,然而CPU调度到另外的工作进程上了,这样条件就不能改变,这样线程就错过了条件变化,虽然等线程放到队列上后,资源变化可以通知线程,但是会有时间延迟。
2)传递给pthread_cond_wait(pthread_cont_t *restrict cond, pthread_mutex_t *restrict mutex)中的mutex必须是已经锁住的,如果这个互斥锁不是锁住的,那么假如满足条件,线程一对资源访问的同时线程二也可能访问,就造成了资源的不一致。所以这个过程还是挺复杂的,当需要等待时,把一个资源的互斥量和等待条件一同传给这个wait函数,这个wait函数把线程放到这个条件的等待队列上,然后对互斥量解锁(注意这两步是个原子操作,具体原因如1)。解锁一来可以让条件发生(如果死守着资源,那么资源也就不会变了),二来可以让其他线程同时访问资源,同样可以检查条件。”


内联函数

使用函数能够避免将相同代码重写多次的麻烦,还能减少可执行程序的体积,但也会带来程序运行时间上的开销;
增加了 inline 关键字的函数称为“内联函数”。
内联函数和普通函数的区别在于:当编译器处理调用内联函数的语句时,不会将该语句编译成函数调用的指令,而是直接将整个函数体的代码插人调用语句处,就像整个函数体在调用处被重写了一遍一样
有了内联函数,就能像调用一个函数那样方便地重复使用一段代码,而不需要付出执行函数调用的额外开销。很显然,使用内联函数会使最终可执行程序的体积增加。以时间换取空间,或增加空间消耗来节省时间,这是计算机学科中常用的方法。


Muduo 架构的几个封装函数

参考资料
在使用mutex时,有时会忘记给mutex解锁,为了防止这种情况发生,常常使用RALL机制(资源的地点时析构函数,释放点时析构函数),MuetexLockGuard就是为此设计产生的。


RALL(资源获取即初始化)机制详解

参考资料
核心是把资源和对象的生命周期绑定,对象创建获取资源,对象销毁释放资源;对象的析构是自动完成的。


并发控制的悲观主义策略和乐观主义策略(乐观锁和悲观锁)

悲观并发控制
一个锁定系统,可以阻止用户以影响其他用户的方式修改数据。如果用户执行的操作导致应用了某个锁,只有这个锁的所有者释放该锁,其他用户才能执行与该锁冲突的操作。这种方法之所以称为悲观并发控制,是因为它主要用于数据争用激烈的环境中,以及发生并发冲突时用锁保护数据的成本低于回滚事务的成本的环境中。

乐观并发控制
在乐观并发控制中,用户读取数据时不锁定数据。当一个用户更新数据时,系统将进行检查,查看该用户读取数据后其他用户是否又更改了该数据。如果其他用户更新了数据,将产生一个错误。一般情况下,收到错误信息的用户将回滚事务并重新开始。这种方法之所以称为乐观并发控制,是由于它主要在以下环境中使用:数据争用不大且偶尔回滚事务的成本低于读取数据时锁定数据的成本。

悲观锁和乐观锁
悲观锁
顾名思义,悲观锁是基于一种悲观的态度类来防止一切数据冲突,它是以一种预防的姿态在修改数据之前把数据锁住,然后再对数据进行读写,在它释放锁之前任何人都不能对其数据进行操作,直到前面一个人把锁释放后下一个人数据加锁才可对数据进行加锁,然后才可以对数据进行操作,一般数据库本身锁的机制都是基于悲观锁的机制实现的;
特点:可以完全保证数据的独占性和正确性,因为每次请求都会先对数据进行加锁,然后进行数据操作,最后再解锁,而加锁释放锁的过程会造成消耗,所以性能不高;
乐观锁
乐观锁是对于数据冲突保持一种乐观态度,操作数据时不会对操作的数据进行加锁(这使得多个任务可以并行的对数据进行操作),只有到数据提交的时候才通过一种机制来验证数据是否存在冲突(一般实现方式是通过加版本号然后进行版本号的对比方式实现);
特点:乐观锁是一种并发类型的锁,其本身不对数据进行加锁通而是通过业务实现锁的功能,不对数据进行加锁就意味着允许多个请求同时访问数据,同时也省掉了对数据加锁和解锁的过程,这种方式因为节省了悲观锁加锁的操作,所以可以一定程度的的提高操作的性能,不过在并发非常高的情况下,会导致大量的请求冲突,冲突导致大部分操作无功而返而浪费资源,所以在高并发的场景下,乐观锁的性能却反而不如悲观锁。


Muduo 多线程异步日志

参考
资料

muduo异步日志库的实现(双缓冲技术

双缓冲技术
参考
资料

百度百科
双缓冲即在内存中创建一个与屏幕绘图区域一致的对象(在后端自定义一块缓冲区域),先将图形绘制到内存中的这个对象上,再一次性将这个对象上的图形拷贝到屏幕上,这样能大大加快绘图的速度。

为什么需要双缓冲技术
场景撕裂问题:例如在游戏场景中,各个场景不是同时出现的,而是出现一半;这就是经常出现的问题;

可以从剧场的场景切换来理解:
剧院有设计了舞台A(缓冲区一)和舞台B(缓冲区二),当舞台A上正在表演时,灯光是照在舞台A的,此时舞台B是黑暗的,并且已经准备好了下一个场景;当舞台A结束表演时,灯光从A断开,并立刻找到舞台B的区域,此时舞台A黑暗了,并开始准备第三个场景,依次类推。
我们继续这个整个游戏的过程,使用黑暗的舞台作为我们可以设置下一个场景的工作区域。 每个场景过渡,我们只是在两个阶段之间切换灯光。 我们的观众在场景之间不间断地表现出来。 他们从来没有看到舞台。
这正是双缓冲的工作原理,这个过程是您所见过的每一个游戏的渲染系统的基础。 而不是单个帧缓冲区,我们有两个。 其中一个代表当前的框架, GPU可以随时扫描它,只要它想要。同时,我们的渲染代码正在写入另一个帧缓冲区。 这是我们黑暗的舞台B.当我们的渲染代码完成绘制场景时,它通过交换缓冲区来切换灯光。 这告诉视频硬件现在开始从第二个缓冲区读取,而不是第一个缓冲区。 只要在刷新结束时进行切换,我们就不会有任何撕裂,整个场景将立即出现。
这个技术缺点是会增加内存的使用

WebServer-master相关推荐

  1. Saltstack Master 配置文件详解

    #主配置 /etc/salt/master interface 默认值:0.0.0.0(所有的网络地址接口) 绑定到本地的某个网络地址接口 interface: 192.168.30.131 publ ...

  2. slatstack Master的配置

    Salt系统非常简单并且易于配置,Salt系统的两个组件都有各自的配置文件.如"salt-master"是通过主配置文件来配置的,"salt-minion"是通 ...

  3. k8s1.11.0安装、一个master、一个node、查看node名称是ip、node是扩容进来的、带cadvisor监控服务...

    一个master.一个node.查看node节点是ip# 安装顺序:先在test1 上安装完必要组件后,就开始在 test2 上单独安装node组件,实现node功能,再返回来配置test1加入集群, ...

  4. 从零开始自制实现WebServer(十九)---- 正式系统的学习一下Git 捣鼓捣鼓github以及一些其他的小组件

    文章目录 全流程实现博客链接 前引 (十九)---- 正式系统的学习一下Git 捣鼓捣鼓github以及一些其他的小组件 1.悔!为什么不在一开始做项目的时候就用Git 错过学习实践Git的最好机会 ...

  5. [warn] the “user“ directive makes sense only if the master process runs with super-user privileges

    原文链接 linux中启动nginx报错: [warn] the "user" directive makes sense only if the master process r ...

  6. git 创建分支并合并到master 上

    1 创建一个dev的分支 git branch dev 2 切换到dev 分支上 git checkout dev (创建并切换是git checkout -b 分支名) -------------- ...

  7. 查询Master下的系统表和系统视图获取数据库的信息和简单的渗透测试

    在SQL中可以通过查询Master下的系统表(sys)和系统视图(information_schema)获取数据库的信息.SQL2000和SQL2005的结构略有不同. 系统表结构参考系统表详细说明. ...

  8. git ssh创建分支_Git(2):在gitlab中创建开发用户,以及master分支的安全管理

    一.创建用户 1.创建管理gitlab的开发人员的用户 2.配置用户信息 3.将用户添加到java-daem组中 4.用户登录成功后,在用户界面为用户添加ssh认证 5.在linux主机中将maste ...

  9. Blender程序性纹理学习教程大师班 Creative Shrimp – Procedural Texturing Blender Master Class

    标题:创意虾-程序纹理Blender大师班 信息: 什么是程序纹理? 程序纹理将简单的数学转换为无限的真实感着色器,具有无限的多样性和分辨率. 超越看起来像一团像素特写的图像纹理,运用程序纹理的力量, ...

  10. c4d跟踪特效合成视频教程 Master Motion Tracking with Cinema 4D

    c4d跟踪特效合成视频教程 Master Motion Tracking with Cinema 4D MP4 |视频:h264,1280×720 |音频:aac,48000 Hz 语言:英语+机译中 ...

最新文章

  1. zabbix--监控MySQL主从状态
  2. ubuntu 使用FFTW快速计算离散傅里叶变换
  3. 【Numpy 学习记录】np.stack 和 np.concatenate
  4. mcq 队列_MCQ | 基础知识 免费和开源软件| 套装4
  5. 火狐浏览器59.0.1英文版如何改为中文版界面
  6. 绿色清爽win7电脑主题 +炫酷紫色win7主题+美女写真电脑主题
  7. 【经验分享】如何将拍摄的照片制作成扫描件 PS+WORD教程
  8. 极客大学python训练营目录_极客大学算法训练营笔记
  9. 下一代CRM是怎么样的?
  10. 吉林大学软件学院黄庆道《最优化算法》对偶单纯形使用大M法条件
  11. android好用的文件管理器,安卓哪种文件管理器好用 三款文件管理器横向评测
  12. 计算机设备编号中字母代号对照,设备位号编号规则及含义
  13. 【Linux】CPU信息速查(品牌型号 | 物理CPU数 | 物理核数 | 逻辑核数)
  14. 神经功能缺损评分 css,不同神经功能缺损程度脑梗死病人血尿酸水平变化及其与近期预后关系分析...
  15. 红米7 自编译不完美 twrp 可root手机
  16. conda install安装不了任何包,一直下载一直错
  17. 搭建wnmp开发环境
  18. ORA-01779: 无法修改与非键值保存表对应的列
  19. springboot默认日志log,控制台不打印mybatis sql执行日志解决办法
  20. SVN出现红绿双向箭头原因

热门文章

  1. 作为程序员赚取额外收入的 6 个的简单副业
  2. 游戏内统一支付系统设计与实现
  3. 如何添加局域网打印机(网络打印机)?
  4. 三、单片机读取ID卡(EM4100的数据格式)
  5. 《长江七号》:周星驰的童梦奇缘
  6. 青岛新生儿手续办理事项
  7. 小红书22届校招薪资待遇怎么样?值得进吗?
  8. 06——驾校科目一考试系统——考试时间
  9. go template基本使用
  10. HX530系列串口通信高频RFID读写器|读卡器串口调试工具测试读卡操作说明