RPC分布式网络通信框架(四)—— 异步日志模块设计

news/2024/5/18 22:38:59/文章来源:https://blog.csdn.net/weixin_42690036/article/details/131691002

文章目录

  • 异步日志模块
    • Logger类实现
    • 线程安全LockQueue类实现


异步日志模块

问题:由于RPC服务器端采用了epoll+多线程 ,并发处理来自客户端的请求,所以有可能造成多线程同时写日志信息。

将日志信息写入一个queue中,然后新建日志线程。但是需要注意的是,由于rpcprovider类中是epoll+多线程,所以进而会创建多个日志线程。这样必须要保证线程安全
在这里插入图片描述
于是需要自定义线程安全的queue。思路:如果queue为空,该线程就不抢锁了。
日志生成的命名格式为:年-月-日-log.txt;内容为时-分-秒+log信息

Logger类实现


enum LogLevel
{INFO,    // 普通信息ENTER, // 错误信息
}
class Logger
{
public:// 获取日志的单例static Logger& GetInstance();// 设置日志级别 void SetLogLevel(LogLevel level);// 写日志void Log(std::string msg);
private:int m_loglevel; // 记录日志级别LockQueue<std::string>  m_lckQue; // 日志缓冲队列Logger();Logger(const Logger&) = delete;Logger(Logger&&) = delete;
}

同样,单例模式记得去除拷贝和移动构造函数。

获取日志的懒汉单例模式:

Logger& Logger::GetInstance()
{static Logger logger;return logger;
}

因为要用宏调用,所以将无参构造函数作为写日志的线程,代码如下:

Logger::Logger()
{// 启动专门的写日志线程std::thread writeLogTask([&](){for (;;){// 获取当前的日期,然后取日志信息,写入相应的日志文件当中 a+time_t now = time(nullptr);tm *nowtm = localtime(&now);char file_name[128];sprintf(file_name, "%d-%d-%d-log.txt", nowtm->tm_year+1900, nowtm->tm_mon+1, nowtm->tm_mday);FILE *pf = fopen(file_name, "a+");if (pf == nullptr){std::cout << "logger file : " << file_name << " open error!" << std::endl;exit(EXIT_FAILURE);}std::string msg = m_lckQue.Pop();char time_buf[128] = {0};sprintf(time_buf, "%d:%d:%d =>[%s] ", nowtm->tm_hour, nowtm->tm_min, nowtm->tm_sec,(m_loglevel == INFO ? "info" : "error"));msg.insert(0, time_buf);msg.append("\n");fputs(msg.c_str(), pf);fclose(pf);}});// 设置分离线程,守护线程writeLogTask.detach();
}

主要功能是将m_lckQue的内容全部写入文件中。
注意:
线程在等待期间不会一直进行写入操作,而是会暂停等待,直到有新的日志信息被添加到队列中。这是由于队列操作使用了一些同步机制(条件变量或互斥锁),来确保写入线程在队列为空时等待新的日志信息的到来。

而因为日志为单例模式,多次调用宏定义时,只会有一个Logger对象实例存在。而写日志线程是在第一次调用宏定义时创建并启动的,之后的调用并不会创建新的写日志线程。

定义日志宏,让用户不用去实例化Logger类就能用可变参的形式写日志。

#define LOG_INFO(logmsgformat, ...) \do \{  \Logger &logger = Logger::GetInstance(); \logger.SetLogLevel(INFO); \char c[1024] = {0}; \snprintf(c, 1024, logmsgformat, ##__VA_ARGS__); \logger.Log(c); \} while(0) \

#define LOG_ERR(logmsgformat, ...) \do \{  \Logger &logger = Logger::GetInstance(); \logger.SetLogLevel(ERROR); \char c[1024] = {0}; \snprintf(c, 1024, logmsgformat, ##__VA_ARGS__); \logger.Log(c); \} while(0) \

其中

void Logger::Log(std::string msg)
{m_lckQue.Push(msg);
}

宏定义体中的具体操作如下:

  1. 通过调用 Logger::GetInstance() 来获取 Logger 类的单例实例
  2. 设置日志级别为 INFO/ERROR,通过调用 logger.SetLogLevel(INFO/ERROR)。
  3. 创建一个大小为 1024 字节的字符数组 c,并将其初始化为全零。
  4. 使用 snprintf 函数将格式化的日志消息和可变数量的参数写入字符数组 c 中,最多写入 1024 字节。
  5. 调用 logger.Log(c) 将字符数组中消息塞入日志系统中的lockqueue中。
  6. 整个宏定义通过 do-while 语句实现了一个代码块,目的是确保宏定义可以像普通语句一样使用,而不会受限于语法结构。while(0) 是为了确保该宏只能作为单个语句使用,并且在使用时不会引入额外的控制流。

调用方法

LOG_INFO("This is an info message: %s", message);

线程安全LockQueue类实现

  1. 首先需要封装一个日志队列的自定义的Push和Pop的api接口,通过mutex和conditional_variable来保证线程安全。
  2. 他的原理类似一个生产者消费者模型,队列Push函数处理的是rpc服务器端的多个worker线程向队列里写数据,写之前加上一把互斥锁,然后push数据,结束以后notify阻塞等待写日志线程向磁盘写数据。
  3. Pop接口 首先会检测队列是否为空,为空代表没有数据 就会进入阻塞wait状态 然后释放锁 ,有数据来了返回数据。

带锁的LockQueue类是一个模板类,他的定义不能写到lockqueue.cc中。

// 多个worker线程都会写日志queue (宏定义)
void Push(const T &data)
{std::lock_guard<std::mutex> lock(m_mutex);m_queue.push(data);m_condvariable.notify_one();
}

rpc框架调用宏定义,将日志信息Push进入lockqueue。

Logger类写线程负责Pop:

// 一个线程读日志queue,写日志文件
T Pop()
{std::unique_lock<std::mutex> lock(m_mutex);while (m_queue.empty()){// 日志队列为空,线程进入wait状态m_condvariable.wait(lock);}T data = m_queue.front();m_queue.pop();return data;
}

条件变量的意义:
条件变量通常与互斥锁配合使用,用于实现线程之间的等待和唤醒机制。当多个线程需要等待某个条件满足时,它们会调用条件变量的wait()方法进入等待状态,同时释放互斥锁。当条件满足时,另一个线程会调用条件变量的notify_one()或notify_all()方法来唤醒等待的线程。

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

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

实操:用Flutter构建一个简单的微信天气预报小程序

​ 微信小程序是一种快速、高效的开发方式&#xff0c;Flutter则是一款强大的跨平台开发框架。结合二者&#xff0c;可以轻松地开发出功能丰富、用户体验良好的微信小程序。 这里将介绍如何使用Flutter开发一个简单的天气预报小程序&#xff0c;并提供相应的代码示例。 1. 准备…

学习记录——Transformer、ViT、Swin-Transformer、SegFormer、TopFormer、Seaformer

Transformer 2017 Computation and Language Google Self-Attention、Multi-Head Attention 位置编码 原理参考链接 ransformer网络结构&#xff1a; ViT 2020 ICLR 将transformer引入到cv领域 将输入图片224x224x3按照16x16x3大小的Patch进行划分&#xff0c;接着通过…

阿里云RockMQ与SpringBoot的整合

前言&#xff1a; 开源版本Rocket和商业版本的RocketMQ有些不同&#xff0c;研究的是商业版本的RocketMQ&#xff0c;阿里云的官方文档&#xff0c;感觉有点乱。看不咋明白&#xff0c;网上虽然有教程&#xff0c;大都还是有点缺少&#xff0c;有时候会突然跳了步骤&#xff0c…

JMeter 中 3 种参数值的传递

目录 前言&#xff1a; (一) 从 CSV 文件读取要批量输入的变量 (二) 利用 Cookie 进行值的传递 (三) 利用正则匹配提取上一个接口的返回数据作为下个请求的输入 前言&#xff1a; 在JMeter中&#xff0c;参数值的传递是非常重要的&#xff0c;因为它允许你在测试过程中动态…

layui树形菜单的实现

前言 继续上一篇博客的内容&#xff0c;在原来代码的基础上实现树形菜单功能 一. 树形菜单是什么&#xff1f; 在layui中&#xff0c;树形菜单是通过 Tree 组件实现的。Tree 组件提供了一种树形结构展示数据的方式&#xff0c;常用于显示层级结构的菜单、目录等。开发者可以…

Java-通过IP获取真实地址

文章目录 前言功能实现测试 前言 最近写了一个日志系统&#xff0c;需要通过访问的 IP 地址来获取真实的地址&#xff0c;并且存到数据库中&#xff0c;我也是在网上看了一些文章&#xff0c;遂即整理了一下供大家参考。 功能实现 这个是获取正确 IP 地址的方法&#xff0c;可…

探索基于300W-LP的3D人脸关键点检测

目录 前言一、&#xff13;D 关键点可视化二、使用步骤1.300W-LP转为YOLO数据格式2.修改数据入口3.开始训练 总结 前言 300WLP数据集提供来丰富的人脸线索&#xff0c;包括&#xff12;D或&#xff13;D的关键点信息&#xff0c;Head Angle和&#xff13;DMM的参数等&#xff…

Spring5学习笔记--详细一文通

Spring5学习笔记--详细一文通 1 Spring 框架概述1.1 Spring 5 简述1.2 Spring5入门案例1.2.1 Spring5下载1.1.2 打开 idea 工具&#xff0c;创建普通 Java 工程1.2.3 导入 Spring5 相关 jar 包1.2.4 创建普通类&#xff0c;在这个类创建普通方法1.2.5 创建 Spring 配置文件&…

分布式定时任务xxl-Job

目录 前言 项目介绍 1.源码目录介绍 2 “调度数据库”配置 3 架构设计 3.1 设计思想 5.3.3 架构图 实战 1.服务端部署 2.执行端配置 3.任务开发 3.1 基于方法注解任务 3.2 基于api任务 3.3 分片广播任务 4.任务执行 4.1 单任务执行 4.2 子任务执行 4.3 分片广…

一、rocketmq整体架构及nameServer源码分析

RocketMQ源码深入剖析 1 RocketMQ介绍 RocketMQ 是阿里巴巴集团基于高可用分布式集群技术&#xff0c;自主研发的云正式商用的专业消息中间件&#xff0c;既可为分布式应用系统提供异步解耦和削峰填谷的能力&#xff0c;同时也具备互联网应用所需的海量消息堆积、高吞吐、可靠…

一次零基础靶机渗透细节全程记录

一、打靶总流程 1.确定目标&#xff1a; 在本靶场中&#xff0c;确定目标就是使用nmap进行ip扫描&#xff0c;确定ip即为目标&#xff0c;只是针对此靶场而言。其他实战中确定目标的方式包括nmap进行扫描&#xff0c;但不局限于这个nmap。 2.信息收集&#xff1a; 比如平常挖…

经典的网络安全技术

以我的理解&#xff0c;“黑客”大体上应该分为“正”、“邪”两类&#xff0c;正派黑客依靠自己掌握的知识帮助系统管理员找出系统中的漏洞并加以完善&#xff0c;而邪派黑客则是通过各种黑客技能对系统进行攻击、入侵或者做其他一些有害于网络的事情&#xff0c;因为邪派黑客…

RPC分布式网络通信框架(二)—— moduo网络解析

文章目录 一、框架通信原理二、框架初始化框架初始化 三、调用端&#xff08;客户端&#xff09;调用端框架调用端主程序 四、提供端&#xff08;服务器&#xff09;提供端主程序提供端框架NotifyService方法Run方法muduo库的优点网络代码RpcProvider::OnConnection业务代码Rpc…

win11利用start11实现全屏菜单,磁贴配置

Win11磁贴配置 最近电脑还是升级到 win11 了。我之前采用的美化方案是桌面上的图标全部移到 win10 开始菜单里的全屏菜单上&#xff0c;用磁贴贴一排。每次要访问文件的时候都去开始菜单里找&#xff0c;而不是放在桌面上&#xff0c;这样桌面也可以空出来欣赏壁纸。参考配置链…

springboot+MySQL大学生体质测试管理系统

功能需求分析的任务是通过详细调查大学生体质测试的测试信息管理系统要处理的所有对象&#xff0c;通过充分了解大学生体质测试管理系统的工作流程&#xff0c;明确使用者的各种需求&#xff0c;充分思考之后可能扩充和改变的情况&#xff0c;然后在这个基础上来设计数据库。

论文笔记--TinyBERT: Distilling BERT for Natural Language Understanding

论文笔记--TinyBERT: Distilling BERT for Natural Language Understanding 1. 文章简介2. 文章概括3 文章重点技术3.1 Transformer Distillation3.2 两阶段蒸馏 4. 数值实验5. 文章亮点5. 原文传送门6. References 1. 文章简介 标题&#xff1a;TinyBERT: Distilling BERT fo…

赛效:如何用在线压缩GIF图片

1&#xff1a;在电脑网页上打开并登录快改图&#xff0c;点击左侧菜单栏里的“GIF压缩”。 2&#xff1a;点击页面中间的上传按钮&#xff0c;将电脑本地的GIF文件上传上去。 3&#xff1a;GIF文件上传成功后&#xff0c;设置下方压缩设置&#xff0c;点击右下角“开始压缩”。…

数据结构(王卓版)——线性表

数据的存储结构之线性表 1、线性表的定义和特点

java后端开发环境搭建 mac

在mac pro上搭建一套java 后端开发环境&#xff0c;主要安装的内容有&#xff1a;jdk、maven、git、tomcat、mysql、navicat、IntelliJ、redis。 本人mac pro的系统为mac OS Monterey 12.6.7&#xff0c;主机的硬件架构为x86_64。 左上角关于本机查看系统版本&#xff1b;终端…

前端框架Layui实现动态树效果(书籍管理系统左侧下拉列表)

目录 一、前言 1.什么是树形菜单 2.树形菜单的使用场景 二、案例实现 1.需求分析 2.前期准备工作 ①导入依赖 ②工具类 BaseDao&#xff08;通用增删改查&#xff09; BuildTree(完成平级数据到父子级的转换) ResponseUtil&#xff08;将数据转换成json格式进行回显&…