您的位置:首页 > 数码常识数码常识
高性能服务器架构(c++高性能服务器开发)
2025-05-11人已围观
高性能服务器架构(c++高性能服务器开发)
1|0日志文件系统对文件系统进行修改时,需要进行很多操作。这些操作可能中途被打断,也就是说,这些操作不是“不可中断”(atomic)的。如果操作被打断,就可能造成文件系统出现不一致的状态。
c++高性能服务器开发1|0日志文件系统
例如:删除文件时,先要从目录树中移除文件的标示,然后收回文件占用的空间。如果在这两步之间操作被打断,文件占用的空间就无法收回。文件系统认为它是被占用的,但实际上目录树中已经找不到使用它的文件了。
在非日志文件系统中,要检查并修复类似的错误必须对整个文件系统的数据结构进行检查。这个操作可能会花费很长的时间。
为了避免这样的错误,日志文件系统分配了一个称为日志的区域来提前记录要对文件系统做的更改。在崩溃后,只要读取日志重新执行未完成的操作,文件系统就可以恢复一致。这种恢复是原子的,因为只存在几种情况:
不需要重新执行:这个事务被标记为已经完成成功重新执行:根据日志,这个事务被重新执行无法重新执行:这个事务会被撤销,就如同这个事务没有发生过一样日志本身不完整:事务还没有被完全写入日志,他会被简单忽略2|0日志系统的设计2|1为什么需要日志
对于一些高频操作(如心跳包、定时器、界面绘制下的某些高频重复的行为),可能在少量次数下无法触发我们想要的行为,而通过断点的暂停方式,我们不得不重复操作几十次、上百次甚至更多,这样排查问题效率是非常低下的。对于这类操作,我们可以通过打印日志,将当时的程序行为上下文现场记录下来,然后从日志系统中找出某次不正常行为的上下文信息。
2|2日志系统的技术上的实现
同步写日志
所谓同步写日志,指的是在输出日志的地方,将日志即时写入到文件中去。采用这种方式,势必造成CPU等待,进而导致主线程“卡”在写文件处,进而造成界面卡帧。
但是,很多时候我们不用担心这种问题,主要有两个原因:其一,是用户感觉不到这种同步写文件造成的延迟;其二,是客户端除了UI线程外,还有其他与界面无关的工作线程,在这些线程中写文件一般不会对用户的体验产生什么影响。
多线程同步写日志出现的问题一——不同线程的日志事件时间序列错乱
产生这种问题的主要原因是由于多个线程同时写日志到同一个文件时,产生日志的时间和实际写入磁盘的时间不是一个原子操作。我们可以这样来理解,一个线程T1在t1时刻产生了日志,另一个线程T2在t2时刻也产生了一个日志(t2 > t1)。但是由于一些原因导致线程T1发生阻塞,并没有把日志即刻写入到文件中,而线程T2并没有发生阻塞,即刻就把日志信息写入到了文件中。这种情况的存在就会导致不同线程的日志事件时间序列错乱。
多线程同步写日志出现的问题二——不同线程的日志输出错乱拼接
假设线程A的日志信息为AAAAAA,线程B的日志信息为BBBBBB,线程C的日志信息为CCCCCC。那么会不会产生一种情况使得日志文件中的输出结果为AABBCCAABBCCAABBCC。
实际上,在类Unix系统上(包括Linux),同一个进程内针对同一个FIEL*的操作是线程安全的,也就是说在Linux系统上不会产生上述的情况发生。
在Windows系统上,由于对FILE*的操作并不是线程安全的,可能会发生上述情况。
这种同步日志的实现方式,一般用于低频写日志的软件系统中,所以可以认为这种多线程同时写日志到同一个文件中是可行的。
异步写日志
所谓异步写日志,就是通过一些线程同步技术将日志先暂存下来,然后再通过一个或多个专门的日志写入线程将这些缓存的日志写入到磁盘中。
实现的时候可以使用一个队列来存储其他线程产生的日志,日志线程从该队列中取出日志,然后将日志内容写入文件。
关于C/C++ Linux后端开发网络底层原理知识 点击 「链接」 获取,内容知识点包括Linux,Nginx,ZeroMQ,MySQL,Redis,线程池,MongoDB,ZK,Linux内核,CDN,P2P,epoll,Docker,TCP/IP,协程,DPDK等等。免费学习地址:C/C++Linux服务器开发/后台架构师【零声教育】-学习视频教程-腾讯课堂
3|0日志系统的实现
设计一个日志系统需要考虑哪些问题?
既然是日志系统肯定需要记录日志(LogEvent),那么我们就需要一个类来表达日志的概念,这个类至少应该包含两个属性,一个是时间戳,另一个是消息本身。
其次是日志输出(LogAppender),可以输出到不同的地方,控制台、文件。
然后是将日志信息进行格式化输出(LogFormatter),LogAppender 可以引用 LogFormatter 这样就可以将 LogEvent 事件中的日志消息经过 LogFormatter 进行格式化,然后再由 LogAppender 输出。
如果想要获取日志,必须得先获取一个什么东西,这个东西可以成为 Logger,此外,还可以使用 LoggerManager 对这些不同的 Logger 进行管理。
LogLevel 类定义了日志的级别。
LogEventWarp 对 LogEvent 进行了封装,当 LogEventWarp 析构的时候,能够触发 LogEvent 将日志信息写入到指定位置。
3|1类图3|2日志写入流程图3|3LogEvent
LogEvent 是日志事件,所有的日志信息都是由 LogEvent 来管理,同时 LogEvent 也提供了格式化写入的功能。
下面的这两个函数提供了一种获取变长参数的方法,具体的原理可以查看这篇博客,大致的意思就是通过传入的第一个参数 fmt 来确定每一个变长参数在内存中的位置,进而获取参数的取值。
(gdb) p fmt$3=0x414228 "test macro fmt error %s"
从上面调试的结果可以看出该函数获取变长参数的具体做法是在给定字符串的最后加上变长参数的格式化输出。
vasprintf 函数将变长参数的内容输出到 buf 中,若成功则返回输出内容的长度,若失败则返回 -1.
上面的这种格式化输出主要用在了下面的这个宏定义里面
3|4LogAppender
通过该类派生出的不同子类可以将日志信息输出到不同的位置。一个 Logger 可以有多个 Appender,LogAppender 有单独的日志级别,因此可以通过设置不同级别的 Appender 从而将日志输出到不同的位置。此外每一个 LogAppender 也都会有自己单独的日志格式,从而方便进行管理。
还可以通过 scoket 套接字,将日志输出到服务器上。
3|5LogFormatter
日志格式器(LogFormatter)可以将传入的日志格式进行解析,并可以和 LogEvent 指针结合将特定格式的日志信息输出到 stringstream 中,等待 LogEventWrap 析构的时候将日志信息写入到指定的 Appender 中。
3|6Logger
日志可以用 Logger 类来进行表示,每一个 Logger 类含有多个 LoggerAppender,可以通过指针把 Logger 类传递给 LogEvent 从而使日志事件能够获取 Logger 类的一些信息。
std::enable_shared_from_this 能让一个对象(假设其名为 t ,且已被一个 std::shared_ptr 对象 pt 管理)安全地生成其他额外的 std::shared_ptr 实例(假设名为 pt1, pt2, ... ) ,它们与 pt 共享对象 t 的所有权。
3|7LoggerManager
管理所有的日志器,并且支持通过日志器的名字获取日志器。
上面就是小居数码小编今天给大家介绍的关于(c++高性能服务器开发)的全部内容,希望可以帮助到你,想了解更多关于数码知识的问题,欢迎关注我们,并收藏,转发,分享。
94%的朋友还想知道的:
如何在IDM中设置代理服务器(idm怎么设置代理)
网页显示找不到dns(浏览器找不到服务器或dns错误怎么办)
电子邮件服务的主要协议有哪些(邮件服务器使用的基本协议)
家庭影音服务器搭建(自建家庭音乐服务器)
153050
1|0日志文件系统对文件系统进行修改时,需要进行很多操作。这些操作可能中途被打断,也就是说,这些操作不是“不可中断”(atomic)的。如果操作被打断,就可能造成文件系统出现不一致的状态。
c++高性能服务器开发1|0日志文件系统
例如:删除文件时,先要从目录树中移除文件的标示,然后收回文件占用的空间。如果在这两步之间操作被打断,文件占用的空间就无法收回。文件系统认为它是被占用的,但实际上目录树中已经找不到使用它的文件了。
在非日志文件系统中,要检查并修复类似的错误必须对整个文件系统的数据结构进行检查。这个操作可能会花费很长的时间。
为了避免这样的错误,日志文件系统分配了一个称为日志的区域来提前记录要对文件系统做的更改。在崩溃后,只要读取日志重新执行未完成的操作,文件系统就可以恢复一致。这种恢复是原子的,因为只存在几种情况:
不需要重新执行:这个事务被标记为已经完成成功重新执行:根据日志,这个事务被重新执行无法重新执行:这个事务会被撤销,就如同这个事务没有发生过一样日志本身不完整:事务还没有被完全写入日志,他会被简单忽略2|0日志系统的设计2|1为什么需要日志
对于一些高频操作(如心跳包、定时器、界面绘制下的某些高频重复的行为),可能在少量次数下无法触发我们想要的行为,而通过断点的暂停方式,我们不得不重复操作几十次、上百次甚至更多,这样排查问题效率是非常低下的。对于这类操作,我们可以通过打印日志,将当时的程序行为上下文现场记录下来,然后从日志系统中找出某次不正常行为的上下文信息。
2|2日志系统的技术上的实现
同步写日志
所谓同步写日志,指的是在输出日志的地方,将日志即时写入到文件中去。采用这种方式,势必造成CPU等待,进而导致主线程“卡”在写文件处,进而造成界面卡帧。
但是,很多时候我们不用担心这种问题,主要有两个原因:其一,是用户感觉不到这种同步写文件造成的延迟;其二,是客户端除了UI线程外,还有其他与界面无关的工作线程,在这些线程中写文件一般不会对用户的体验产生什么影响。
多线程同步写日志出现的问题一——不同线程的日志事件时间序列错乱
产生这种问题的主要原因是由于多个线程同时写日志到同一个文件时,产生日志的时间和实际写入磁盘的时间不是一个原子操作。我们可以这样来理解,一个线程T1在t1时刻产生了日志,另一个线程T2在t2时刻也产生了一个日志(t2 > t1)。但是由于一些原因导致线程T1发生阻塞,并没有把日志即刻写入到文件中,而线程T2并没有发生阻塞,即刻就把日志信息写入到了文件中。这种情况的存在就会导致不同线程的日志事件时间序列错乱。
多线程同步写日志出现的问题二——不同线程的日志输出错乱拼接
假设线程A的日志信息为AAAAAA,线程B的日志信息为BBBBBB,线程C的日志信息为CCCCCC。那么会不会产生一种情况使得日志文件中的输出结果为AABBCCAABBCCAABBCC。
实际上,在类Unix系统上(包括Linux),同一个进程内针对同一个FIEL*的操作是线程安全的,也就是说在Linux系统上不会产生上述的情况发生。
在Windows系统上,由于对FILE*的操作并不是线程安全的,可能会发生上述情况。
这种同步日志的实现方式,一般用于低频写日志的软件系统中,所以可以认为这种多线程同时写日志到同一个文件中是可行的。
异步写日志
所谓异步写日志,就是通过一些线程同步技术将日志先暂存下来,然后再通过一个或多个专门的日志写入线程将这些缓存的日志写入到磁盘中。
实现的时候可以使用一个队列来存储其他线程产生的日志,日志线程从该队列中取出日志,然后将日志内容写入文件。
关于C/C++ Linux后端开发网络底层原理知识 点击 「链接」 获取,内容知识点包括Linux,Nginx,ZeroMQ,MySQL,Redis,线程池,MongoDB,ZK,Linux内核,CDN,P2P,epoll,Docker,TCP/IP,协程,DPDK等等。免费学习地址:C/C++Linux服务器开发/后台架构师【零声教育】-学习视频教程-腾讯课堂
3|0日志系统的实现
设计一个日志系统需要考虑哪些问题?
既然是日志系统肯定需要记录日志(LogEvent),那么我们就需要一个类来表达日志的概念,这个类至少应该包含两个属性,一个是时间戳,另一个是消息本身。
其次是日志输出(LogAppender),可以输出到不同的地方,控制台、文件。
然后是将日志信息进行格式化输出(LogFormatter),LogAppender 可以引用 LogFormatter 这样就可以将 LogEvent 事件中的日志消息经过 LogFormatter 进行格式化,然后再由 LogAppender 输出。
如果想要获取日志,必须得先获取一个什么东西,这个东西可以成为 Logger,此外,还可以使用 LoggerManager 对这些不同的 Logger 进行管理。
LogLevel 类定义了日志的级别。
LogEventWarp 对 LogEvent 进行了封装,当 LogEventWarp 析构的时候,能够触发 LogEvent 将日志信息写入到指定位置。
3|1类图3|2日志写入流程图3|3LogEvent
LogEvent 是日志事件,所有的日志信息都是由 LogEvent 来管理,同时 LogEvent 也提供了格式化写入的功能。
下面的这两个函数提供了一种获取变长参数的方法,具体的原理可以查看这篇博客,大致的意思就是通过传入的第一个参数 fmt 来确定每一个变长参数在内存中的位置,进而获取参数的取值。
(gdb) p fmt$3=0x414228 "test macro fmt error %s"
从上面调试的结果可以看出该函数获取变长参数的具体做法是在给定字符串的最后加上变长参数的格式化输出。
vasprintf 函数将变长参数的内容输出到 buf 中,若成功则返回输出内容的长度,若失败则返回 -1.
上面的这种格式化输出主要用在了下面的这个宏定义里面
3|4LogAppender
通过该类派生出的不同子类可以将日志信息输出到不同的位置。一个 Logger 可以有多个 Appender,LogAppender 有单独的日志级别,因此可以通过设置不同级别的 Appender 从而将日志输出到不同的位置。此外每一个 LogAppender 也都会有自己单独的日志格式,从而方便进行管理。
还可以通过 scoket 套接字,将日志输出到服务器上。
3|5LogFormatter
日志格式器(LogFormatter)可以将传入的日志格式进行解析,并可以和 LogEvent 指针结合将特定格式的日志信息输出到 stringstream 中,等待 LogEventWrap 析构的时候将日志信息写入到指定的 Appender 中。
3|6Logger
日志可以用 Logger 类来进行表示,每一个 Logger 类含有多个 LoggerAppender,可以通过指针把 Logger 类传递给 LogEvent 从而使日志事件能够获取 Logger 类的一些信息。
std::enable_shared_from_this 能让一个对象(假设其名为 t ,且已被一个 std::shared_ptr 对象 pt 管理)安全地生成其他额外的 std::shared_ptr 实例(假设名为 pt1, pt2, ... ) ,它们与 pt 共享对象 t 的所有权。
3|7LoggerManager
管理所有的日志器,并且支持通过日志器的名字获取日志器。
上面就是小居数码小编今天给大家介绍的关于(c++高性能服务器开发)的全部内容,希望可以帮助到你,想了解更多关于数码知识的问题,欢迎关注我们,并收藏,转发,分享。
94%的朋友还想知道的:
如何在IDM中设置代理服务器(idm怎么设置代理)
网页显示找不到dns(浏览器找不到服务器或dns错误怎么办)
电子邮件服务的主要协议有哪些(邮件服务器使用的基本协议)
家庭影音服务器搭建(自建家庭音乐服务器)
153050
很赞哦! ()
上一篇:610m鼠标值得买吗(无线RGB高阶电竞鼠标太帅了)
下一篇:返回列表