Rpc异步日志模块

news/2024/11/19 4:33:04/

Rpc异步日志模块作用

在一个大型分布式系统中,任何部署的分布式节点都可能发生崩溃,试想如果用普通的办法,即先排查哪个节点down掉了,找到down掉的节点后采取调试工具gdb调试该节点,进而排查宕机的原因。这中排查方法对于人力物力都是无法接受的。
那么由此记录日志就变得至关重要,分布式RPC框架必定存在一个异步日志模块,用于记录所有分布式站点的调试信息。通过日志分析就能很容易排查出哪个节点出了问题

Rpc异步日志模块实现思路

一个日志模块必须是异步的,不能影响主程序的运行,例如在RPC框架中显然不能阻塞了RPCProvider(服务提供者)和RPCConsumer(服务调用者)的运行
RPCProvider是一个能接受高并发rpc请求的高性能服务器(epoll+多线程),那么存在多个线程同时写日志的情况,这里的“写日志”并不是真正意义上的写磁盘文件的操作,因为磁盘IO会严重拖累该线程原本执行的其他任务。所以这里的写日志 只是多个线程将日志写入一个异步缓冲队列(这个操作是在内存进行的非常快),并且这个队列必须是线程安全的。
此外,应该另起一个线程来读取队列里面的日志数据进行真正的磁盘写文件操作,这样写日志线程是单独工作的,它只是依赖于异步缓冲队列里面的数据,不会影响RPC服务线程和其他IO线程。
在这里插入图片描述

Rpc异步日志模块实现

下面提供一个简单版本的实现
异步缓冲队列类:

#pragma once#include <queue>
#include <thread>
#include <mutex>
#include <condition_variable>//异步写日志的日志队列
template<typename T>
class LockQueue
{public://多个work线程都会写日志queuevoid Push(const T& data){std::lock_guard<std::mutex> lock(m_mutex);m_queue.push(data);m_condvariable.notify_one();}//一个线程读日志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;}
private:std::queue<T> m_queue;std::mutex m_mutex;std::condition_variable m_condvariable;};

Logger.h类:

#pragma once#include "lockqueue.h"
#include <utility>//日志级别
enum LogLevel{INFO = 1, //普通信息ERROR  //错误信息
};//Mprpc框架提供的日志系统
class Logger
{
public://获取日志的单例static Logger& GetInstance();//写日志void Log(std::pair<LogLevel,std::string> msg);private:LockQueue<std::pair<LogLevel,std::string>> m_lckQue; //日志缓冲队列Logger();Logger(const Logger&) = delete;Logger(Logger&& ) = delete;Logger& operator=(const Logger&) = delete;
};//定义宏  LOG_XXX("xxx %d %s", 20, "sdasd");
#define LOG_INFO(logmsgformat, ...) \do \{ \Logger &logger = Logger::GetInstance(); \char c[1024] = {0};           \snprintf(c, 1024, logmsgformat, ##__VA_ARGS__); \std::pair<LogLevel,std::string> pr = std::make_pair(INFO, std::string(c)); \logger.Log(pr); \} while (0);#define LOG_ERR(logmsgformat, ...) \
do \
{ \Logger &logger = Logger::GetInstance(); \char c[1024] = {0};           \snprintf(c, 1024, logmsgformat, ##__VA_ARGS__); \std::pair<LogLevel,std::string> pr = std::make_pair(ERROR, std::string(c)); \logger.Log(pr); \
} while (0);

Logger.cc

#include "logger.h"
#include <time.h>
#include <iostream>//获取日志的单例
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::pair<LogLevel,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,(msg.first == INFO ? "info" : "error"));msg.second.insert(0, time_buf);msg.second.append("\n");fputs(msg.second.c_str(), pf);fclose(pf); }});//设置分离线程, 守护线程writeLogTask.detach();}//写日志,把日志信息写入到lockqueue缓冲区当中
void Logger::Log(std::pair<LogLevel,std::string> msg)
{m_lckQue.Push(msg);
}

使用:

...
LOG_INFO("NotifyService UserService success");
LOG_ERR("eeeeeerrror%d", 9999999);
LOG_INFO("NotifyService GetFriendListService success");
...查看日志文件:cat 2023-8-2-log.txt 
21-35-26 => [info]NotifyService UserService success
21-35-26 => [error]eeeeeerrror9999999
21-35-26 => [info]NotifyService GetFriendListService success

http://www.ppmy.cn/news/1000049.html

相关文章

如何通过 5 步激活策略扩大用户群

假设&#xff0c;你现在是一个“深藏功与名”的增长黑客。前期你表现非常好&#xff0c;做了一些拉新实验&#xff0c;每天都有上千用户进入到产品。团队成员和家人朋友都非常开心你们的产品增长终于有了起色。 然而&#xff0c;如果你不重视拉新&#xff08;acquisition&…

Eureka 学习笔记2:EurekaClient

版本 awsVersion ‘1.11.277’ EurekaClient 接口实现了 LookupService 接口&#xff0c;拥有唯一的实现类 DiscoveryClient 类。 LookupService 接口提供以下功能&#xff1a; 获取注册表根据应用名称获取应用根据实例 id 获取实例信息 public interface LookupService<…

Python系统学习1-2

目录 一、硬件 二、软件&#xff1a;程序文档 三、基础知识 四、python执行过程 五、Pycharm使用技巧 一、硬件 计算机五大部件&#xff1a;运算器&#xff0c;存储器&#xff0c;控制器、输入设备&#xff0c;输出设备。 运算器和控制器 集成在CPU中。 存储&#xff1a…

爬虫教程1_Xpath 入门教程

Xpath 入门教程 在编写爬虫程序的过程中提取信息是非常重要的环节&#xff0c;但是有时使用正则表达式无法匹配到想要的信息&#xff0c;或者书写起来非常麻烦&#xff0c;此时就需要用另外一种数据解析方法&#xff0c;也就是本节要介绍的 Xpath 表达式。 Xpath表达式 XPath…

每日一道面试题之Iterator 和 ListIterator 有什么区别?

Iterator 和 ListIterator 都是 Java 集合框架中用于遍历集合元素的接口&#xff0c;但它们有一些区别: 使用的范围&#xff1a;Iterator可以迭代所有集合,而ListIterator 只能用于List及其子类。 继承关系&#xff1a;ListIterator 继承 Iterator&#xff0c;并且ListIterat…

Golang之路---02 基础语法——异常机制:panic 和 recover

Golang的异常处理 Go 没有像 Java 和 .NET 那样的 try/catch 异常机制&#xff1a;不能执行抛异常操作。 在 Golang 中&#xff0c;有不少常规错误&#xff0c;在编译阶段就能提前告警&#xff0c;比如语法错误或类型错误等&#xff0c;但是有些错误仅能在程序运行后才能发生&…

vue中axios的使用

什么是Axios Axios是一个基于Promise的HTTP客户端库&#xff0c;用于发送HTTP请求并处理响应。它可以在浏览器和Node.js环境中使用&#xff0c;并提供了许多功能&#xff0c;如拦截请求和响应、转换请求和响应数据、取消请求等。 使用场景&#xff1a; 发送AJAX请求&#xf…

【JS】运算符优先级汇总

文章目录 如果 OP1 和 OP2 具有不同的优先级&#xff08;见下表&#xff09;&#xff0c;则优先级最高的运算符先执行&#xff0c;不用考虑结合性。 console.log(3 10 * 2); // 输出 23 console.log((3 10) * 2); // 输出 26 因为括号改变了优先级如果 OP1 和 OP2 具有相同的…