当前位置: 首页 > news >正文

C++基于多设计模式下的同步异步日志系统day3

C++基于多设计模式下的同步&异步日志系统day3

📟作者主页:慢热的陕西人

🌴专栏链接:C++基于多设计模式下的同步&异步日志系统

📣欢迎各位大佬👍点赞🔥关注🚓收藏,🍉留言

主要内容日志项目的输出格式化类的设计和日志落地(LogSink)类设计(简单工厂模式)

在这里插入图片描述

文章目录

  • C++基于多设计模式下的同步&异步日志系统day3
  • 1.代码设计
      • 1.1日志输出格式化类设计
      • 1.2日志落地(LogSink)类设计(简单工厂模式)

1.代码设计

1.1日志输出格式化类设计

⽇志格式化(Formatter)类主要负责格式化⽇志消息。其主要包含以下内容

  • pattern成员:保存⽇志输出的格式字符串。
    ◦ %d⽇期
    ◦ %T缩进
    ◦ %t线程id
    ◦ %p⽇志级别
    ◦ %c⽇志器名称
    ◦ %f⽂件名
    ◦ %l⾏号
    ◦ %m⽇志消息
    ◦ %n换⾏

  • std::vectorFormatItem::ptritems成员:⽤于按序保存格式化字符串对应的⼦格式化对象

FormatItem类主要负责⽇志消息⼦项的获取及格式化。其包含以下⼦类

  • MsgFormatItem:表⽰要从LogMsg中取出有效⽇志数据
  • LevelFormatItem:表⽰要从LogMsg中取出⽇志等级
  • NameFormatItem:表⽰要从LogMsg中取出⽇志器名称
  • ThreadFormatItem:表⽰要从LogMsg中取出线程ID
  • TimeFormatItem:表⽰要从LogMsg中取出时间戳并按照指定格式进⾏格式化
  • CFileFormatItem:表⽰要从LogMsg中取出源码所在⽂件名
  • CLineFormatItem:表⽰要从LogMsg中取出源码所在⾏号
  • TabFormatItem:表⽰⼀个制表符缩进
  • NLineFormatItem:表⽰⼀个换⾏
  • OtherFormatItem:表⽰⾮格式化的原始字符串

⽰例:“[%d{%H:%M:%S}]%m%n”

pattern = "[%d{%H:%M:%S}] %m%n"
items = {
{OtherFormatItem(), "["},
{TimeFormatItem(), "%H:%M:%S"},
{OtherFormatItem(), "]"},
{MsgFormatItem (), ""},
{NLineFormatItem (), ""}
}
LogMsg msg = {
size_t _line = 22;
size_t _ctime = 12345678;
std::thread::id _tid = 0x12345678;
std::string _name = "logger";
std::string _file = "main.cpp";
std::string _payload = "创建套接字失败";
LogLevel::value _level = ERROR;
};

格式化的过程其实就是按次序从Msg中取出需要的数据进⾏字符串的连接的过程。
最终组织出来的格式化消息:“[22:32:54]创建套接字失败\n”

代码实现:

#ifndef __M_FMT_H__
#define __M_FMT_H__#include"message.hpp"
#include<time.h>
#include<vector>
#include<cassert>
#include<sstream>namespace xupt
{//抽象格式化子类基类class FormatItem{public:using ptr = std::shared_ptr<FormatItem>;virtual void format(std::ostream& out, const LogMsg &msg) = 0; };//派生格式化子类项---消息,等级,时间,文件名,行号,线程ID,日志器名,制表符,换行,其他class MsgFormatItem : public FormatItem{public:virtual void format(std:: ostream& out, const LogMsg &msg){out << msg._payload;}};class LevelFormatItem : public FormatItem{public:virtual void format(std:: ostream& out, const LogMsg &msg){out << LogLevel::toString(msg._level);}};class TimeFormatItem : public FormatItem{public:TimeFormatItem(const std::string &fmt = "%H:%M:%S"):_time_fmt(fmt){}virtual void format(std:: ostream& out, const LogMsg &msg){struct tm t;localtime_r(&msg._ctime, &t);char tmp[128];strftime(tmp, 127, _time_fmt.c_str(), &t);out << tmp;}private:std::string _time_fmt;};    class FileFormatItem : public FormatItem{public:virtual void format(std:: ostream& out, const LogMsg &msg){out << msg._file;}};   class LineFormatItem : public FormatItem{public:virtual void format(std:: ostream& out, const LogMsg &msg){out << msg._line;}};   class ThreadFormatItem : public FormatItem{public:virtual void format(std:: ostream& out, const LogMsg &msg){out << msg._tid;}};   class LoggerFormatItem : public FormatItem{public:virtual void format(std:: ostream& out, const LogMsg &msg){out << msg._logger;}};  class TabFormatItem : public FormatItem{public:virtual void format(std:: ostream& out, const LogMsg &msg){out << "\t";}};    class NewLineFormatItem : public FormatItem{public:virtual void format(std:: ostream& out, const LogMsg &msg){out << "\n";}};   class OtherFormatItem : public FormatItem{public:OtherFormatItem(const std::string str):_str(str){}virtual void format(std:: ostream& out, const LogMsg &msg){out << _str;}private:std::string _str;};   class Formatter{public:Formatter(const std::string & pattern = "[%d{%H:%M:%S}][%t][%c][%f:%l][%p]%T%m%n"):_pattern(pattern){assert(parsePattern()); //如果解析失败直接跳断言}//对msg进行格式化void format(std::ostream& out ,const  LogMsg & Msg){for(auto& item : _items){item->format(out, Msg);}}std::string format(const LogMsg & Msg){std::stringstream ss;format(ss, Msg);return ss.str();}private://对格式化规则字符串进行解析bool parsePattern(){//1.对格式化规则字符串进行解析//abcd[%d{%H:%M:%S}][%p]%T%m%nstd::vector<std::pair<std::string, std::string>> fmt_order;size_t pos = 0;std::string key, val;                while(pos < _pattern.size()){//1.处理原始字符串,如果不是%,那么就是原始字符if(_pattern[pos] != '%'){val.push_back(_pattern[pos++]);continue;}//%的情况//1.双%的情况if(pos + 1 < _pattern.size() && _pattern[pos + 1] == '%'){val.push_back('%'); pos += 2; continue;}//能走到这里,代表%后是一个格式化字符串,这时候代表原始字符串处理完毕if(val.empty() == false){fmt_order.push_back(std::make_pair("", val));val.clear();}//2.%后是格式化字符的情况pos += 1;//这一步之后,pos指向格式化字符位置if(pos == _pattern.size()){std::cout << "%后没有对应的格式化字符" << std::endl;return false;}key = _pattern[pos];//2.{}的情况pos += 1;if(_pattern[pos] == '{'){pos += 1; //这时候pos指向子规则的起始位置while(pos < _pattern.size() && _pattern[pos] != '}'){val.push_back(_pattern[pos++]);}//走到了末尾跳出了循环,则代表没有遇到},代表格式是错误的if(pos == _pattern.size())  //没有找到}{std::cout << "子规则{}匹配失败" << std::endl;return false;}pos += 1;}fmt_order.push_back(std::make_pair(key, val));key.clear();val.clear();}//2.根据解析得到的数据初始化子项数组成员for(auto &it : fmt_order){_items.push_back(createItem(it.first,it.second));}return true;}//根据不同的格式化字符创建不同的格式化子项对象FormatItem::ptr createItem(const std::string & key, const std::string &val){if(key == "d") return std::make_shared<TimeFormatItem>(val);if(key == "t") return std::make_shared<ThreadFormatItem>();if(key == "c") return std::make_shared<LoggerFormatItem>();if(key == "f") return std::make_shared<FileFormatItem>();if(key == "l") return std::make_shared<LineFormatItem>();if(key == "p") return std::make_shared<LevelFormatItem>();if(key == "T") return std::make_shared<TabFormatItem>();if(key == "m") return std::make_shared<MsgFormatItem>();if(key == "n") return std::make_shared<NewLineFormatItem>();if(key.empty()) return std::make_shared<OtherFormatItem>(val);std::cout << "没有对应的格式化字符串:%" << key << std::endl;abort();return FormatItem::ptr();}private:std::string _pattern; //格式化规则字符串std::vector<FormatItem::ptr> _items; };}#endif

1.2日志落地(LogSink)类设计(简单工厂模式)

⽇志落地类主要负责落地⽇志消息到⽬的地

它主要包括以下内容:

  • Formatter⽇志格式化器:主要是负责格式化⽇志消息。
  • mutex互斥锁:保证多线程⽇志落地过程中的线程安全,避免出现交叉输出的情况。这个类⽀持可扩展,其成员函数log设置为纯虚函数,当我们需要增加⼀个log输出⽬标,可以增加⼀个类继承⾃该类并重写log⽅法实现具体的落地⽇志逻辑。

目前实现了三个不同方向上的日志落地:

  • 标准输出:StdoutSink

  • 固定文件:FileSink

  • 滚动文件:RollSinkBySize

    ◦ 滚动⽇志⽂件输出的必要性:

    ​ • 由于机器磁盘空间有限,我们不可能⼀直⽆限地向⼀个⽂件中增加数据

    ​ • 如果⼀个⽇志⽂件体积太⼤,⼀⽅⾯是不好打开,另⼀⽅⾯是即时打开了由于包含数据巨⼤,也不利于查找我们需要的信息

    ​ • 所以实际开发中会对单个⽇志⽂件的⼤⼩也会做⼀些控制,即当⼤⼩超过某个⼤⼩时(如1GB),我们就重新创建⼀个新的⽇志⽂件来滚动写⽇志。对于那些过期的⽇志,⼤部分企业内部都有专⻔的运维⼈员去定时清理过期的⽇志,或者设置系统定时任务,定时清理过期⽇志

    ◦ ⽇志⽂件的滚动思想:

    ⽇志⽂件滚动的条件有两个:⽂件⼤⼩和时间。我们可以选择:

    • ⽇志⽂件在⼤于1GB的时候会更换新的⽂件
    • 每天定点滚动⼀个⽇志⽂件

    本项⽬基于⽂件⼤⼩的判断滚动⽣成新的⽂件

    /*  日志落地模块的实现1.抽象落地基类2.派生落地子类(根据不同的落地方向)3.使用工厂模式进行使用和生产的分离
    */#ifndef __M_SIK_H__
    #define __M_SIK_H__#include"util.hpp"
    #include<memory>
    #include<fstream>
    #include<cassert>
    #include<sstream>namespace xupt 
    {class LogSink{public:using ptr = std::shared_ptr<LogSink>;LogSink() {}virtual ~LogSink() {}virtual void log(const char* data, size_t len) = 0; };  //落地方向:标准输出class StdoutSink : public LogSink{public:virtual void log(const char* data, size_t len){std::cout.write(data, len);}};//落地方向:指定文件class FileSink : public LogSink{public:FileSink(const std::string &pathname):_pathname(pathname){//1.创建日志文件所在的目录util::File::CreateDirectory(util::File::path(_pathname));//2.创建并打开日志文件_ofs.open(_pathname, std::ios::binary | std::ios::app); //以二进制追加的方式写入//断言以下是否打开成功assert(_ofs.is_open());}//写入到文件virtual void log(const char* data, size_t len){_ofs.write(data, len);assert(_ofs.good());}private:std::string _pathname;std::ofstream _ofs;};//落地方向:滚动文件(以大小进行滚动)class RollSinkBySize : public LogSink{public://构造函数,传入文件名,文件大小,并管理文件输出句柄RollSinkBySize(const std::string &basename, size_t max_size):_basename(basename), _max_size(max_size),_cur_size(0),_name_count(0){std::string pathname = createNewFile();//1.创建日志文件所在的目录                util::File::CreateDirectory(util::File::path(pathname));//2.创建并打开日志文件_ofs.open(pathname, std::ios::binary | std::ios::app); //以二进制追加的方式写入//断言以下是否打开成功assert(_ofs.is_open());                    }//将日志消息写入到文件,写入前判断文件大小,超过了最大大小,就要切换文件virtual void log(const char* data, size_t len){if(_cur_size >= _max_size){//先关闭之前的文件_ofs.close();//创建新的文件std::string pathname = createNewFile();_ofs.open(pathname,std::ios::binary | std::ios::app);//断言以下是否打开成功assert(_ofs.is_open()); //置零当前文件大小_cur_size = 0;}//写入?没有更改_cur_size_ofs.write(data, len);//断言以下是否成功写入assert(_ofs.good());   _cur_size += len;            }private://当文件大小达到限制的时候,用于切换文件使用(创建新的文件名,并不能创建文件)    std::string createNewFile(){//获取系统时间time_t t = util::Date::now();struct tm lt;localtime_r(&t, &lt);//创建一个字符串流std::stringstream filename;filename << _basename;filename << lt.tm_year + 1900;filename << lt.tm_mon + 1;filename << lt.tm_mday;filename << lt.tm_hour;filename << lt.tm_min;filename << lt.tm_sec;filename << "-";filename << _name_count++;filename << ".log";return filename.str();}private:size_t _name_count ; //用于区别文件名的std::string _basename; //存储日志信息的文件, 命名规则我们一般是加一个固定的头,尾部用时间来区分std::ofstream _ofs; //维护一个我们的文件输出句柄size_t _max_size;     //单个文件的最大大小size_t _cur_size;    //当前文件的大小,算是一个优化,这样我们就不用去频繁查看文件属性,从而降低效率};/*拓展一个以时间作为文件滚动切换类型的日志落地模块
    */  class RollSinkByTime : public LogSink{public:enum class TimeGap{GAP_SECOND,GAP_MINUTE,GAP_HOUR,GAP_DAY,};public:RollSinkByTime(const std::string &basename, TimeGap gap_type):_basename(basename){switch(gap_type) {case TimeGap::GAP_SECOND : _gap_size = 1; break;case TimeGap::GAP_MINUTE : _gap_size = 60; break;case TimeGap::GAP_HOUR : _gap_size = 3600; break;case TimeGap::GAP_DAY : _gap_size = 3600 * 24; break;}_cur_gap = _gap_size == 1 ? util::Date::now() : util::Date::now() % _gap_size;  //初始化当前的时间片util::File::CreateDirectory(util::File::path(_basename)); //1.创建日志文件所在的目录      std::string filename = createNewFile();  //2.创建文件名,并且打开文件_ofs.open(filename,std::ios::binary | std::ios::app);assert(_ofs.is_open());                 }//写入到文件,判断当前的时间段,是否是当前文件的时间段,不是则切换文件virtual void log(const char* data, size_t len){//获取系统时间time_t cur = util::Date::now();                if(cur % _gap_size != _cur_gap){_ofs.close();std::string filename = createNewFile();  _ofs.open(filename,std::ios::binary | std::ios::app);assert(_ofs.is_open());                                      }//写入文件_ofs.write(data, len);assert(_ofs.good());}private://当时间片达到限制的时候,用于切换文件使用,(创建新的文件名,并不能创建文件)   std::string createNewFile(){//获取系统时间time_t t = util::Date::now();struct tm lt;localtime_r(&t, &lt);//创建一个字符串流std::stringstream filename;filename << _basename;filename << lt.tm_year + 1900;filename << lt.tm_mon + 1;filename << lt.tm_mday;filename << lt.tm_hour;filename << lt.tm_min;filename << lt.tm_sec;filename << "-";filename << ".log";return filename.str();}private:std::string _basename;std::ofstream _ofs;size_t _cur_gap;size_t _gap_size;};class SinkFactory{public:template<typename SinkType, typename ...Args>static LogSink::ptr create(Args && ...args) //右值引用,保持原来类型的属性,用于后来的完美转发{return std::make_shared<SinkType>(std::forward<Args>(args)...);}};
    }#endif
    

到这本篇博客的内容就到此结束了。
如果觉得本篇博客内容对你有所帮助的话,可以点赞,收藏,顺便关注一下!
如果文章内容有错误,欢迎在评论区指正

在这里插入图片描述

相关文章:

C++基于多设计模式下的同步异步日志系统day3

C基于多设计模式下的同步&异步日志系统day3 &#x1f4df;作者主页&#xff1a;慢热的陕西人 &#x1f334;专栏链接&#xff1a;C基于多设计模式下的同步&异步日志系统 &#x1f4e3;欢迎各位大佬&#x1f44d;点赞&#x1f525;关注&#x1f693;收藏&#xff0c;&am…...

Cypher语句查询neo4j数据库教程

文章目录 Cypher介绍执行Cypher语句查询总结 Cypher介绍 NodeMatcher和RelationshipMatcher能够表达的匹配条件相对简单&#xff0c;更加复杂的查询还是需要用Cypher语句来表达。 Py2neo本身支持执行Cypher语句的执行&#xff0c;可以将复杂的查询写成Cypher语句&#xff0c;…...

【ESP32 IDF快速入门】点亮第一个LED灯与流水灯

文章目录 前言一、有哪些工作模式&#xff1f;1.1 GPIO的详细介绍1.2 GPIO的内部框图输入模式输出部分 二、GPIO操作函数2.1 GPIO 汇总2.2 GPIO操作函数gpio_config配置引脚reset 引脚函数设置引脚电平选中对应引脚设置引脚的方向 2.3 点亮第一个灯 三、流水灯总结 前言 ESP32…...

再见,Visual Basic——曾经风靡一时的编程语言

2020年3月&#xff0c;微软团队宣布了对Visual Basic&#xff08;VB&#xff09;的“终审判决”&#xff1a;不再进行开发或增加新功能。这意味着曾经风光无限的VB正式退出了历史舞台。 VB是微软推出的首款可视化编程软件&#xff0c;自1991年问世以来&#xff0c;便受到了广大…...

【C++精简版回顾】18.文件操作

1.文件操作头文件 2.操作文件所用到的函数 1.文件io 1.头文件 #include<fstream> 2.打开文件 &#xff08;1&#xff09;函数名 文件对象.open &#xff08;2&#xff09;函数参数 /* ios::out 可读 ios::in 可…...

【解决方案】ArcGIS Engine二次开发时,运行后出现“正尝试在 OS 加载程序锁内执行托管代码。不要尝试在 DllMain...”

我们在做ArcGIS Engine二次开发时&#xff0c;特别是新手&#xff0c;安装好了开发环境&#xff0c;满怀信心的准备将按照教程搭建好的框架在Visual Studio中进行运行。点击运行后&#xff0c;却出现了“正尝试在 OS 加载程序锁内执行托管代码。不要尝试在 DllMain 或映像初始化…...

新项目,Linux上一键安装MySQL,Redis,Nacos,Minio

大家好&#xff0c;我是 jonssonyan 分享一个我的一个开源项目&#xff0c;这是一个在 Linux 平台上一键安装各种软件的脚本项目&#xff0c;脚本使用 Shell 语言编写&#xff0c;后续还会增加更多软件的一键安装&#xff0c;代码在 GitHub 上全部开源的&#xff0c;开源地址如…...

Rust 从 PyTorch 到 Burn

一、性能轮盘赌 机器码相同&#xff0c;但放置在不同的地址上&#xff0c;性能可能截然不同。 作为软件开发人员&#xff0c;我们经常假设特定代码的性能仅由代码本身和运行它的硬件决定。这种假设让我们在优化代码以获得更好性能时感到有控制力。虽然在大多数情况下这种假设…...

Swin-Transformer网络代码实现

还是参考导师级别博主霹雳吧啦Wz的个人空间-霹雳吧啦Wz个人主页-哔哩哔哩视频 博主写的博客Swin-Transformer网络结构详解_swin transformer-CSDN博客 视频理论讲解12.1 Swin-Transformer网络结构详解_哔哩哔哩_bilibili pytorch实现12.2 使用Pytorch搭建Swin-Transformer网…...

Java ZooKeeper-RocketMQ 面试题

Java ZooKeeper-RocketMQ 面试题 前言1、谈谈你对ZooKeeper的理解 &#xff1f;2、Zookeeper的工作原理&#xff08;Zab协议&#xff09;3、谈谈你对分布式锁的理解&#xff0c;以及分布式锁的实现&#xff1f;4、 zookeeper 是如何保证事务的顺序一致性的&#xff1f;5、 zook…...

css制作瀑布流布局

CSS制作瀑布流布局的步骤如下&#xff1a; HTML结构&#xff1a;使用无序列表ul和列表项li来创建网格布局。 <ul class"grid"><li><img src"image1.jpg"></li><li><img src"image2.jpg"></li><li…...

Redis 的哨兵模式配置

1.配置 vim sentinel.conf# mymaster 给主机起的名字 # 192.168.205.128 主机的ip地址 # 6379 端口号 # 2 当几个哨兵发现主观宕机&#xff0c;则判定为客观宕机。 原则上是大于一半。比如三个哨兵&#xff0c;则设置为 2 sentinel monitor mymaster 192.168.205.128 63…...

基于单片机的继电器参数测试系统设计

摘要:由于原有测试系统在参数设置上过于单一,在对继电器测试过程中需要多次进行器件连接,导致对其测试准确度下降,会造成继电器的二次损伤,基于单片机研究继电器参数测试系统的设计方法。在硬件设计上,构建二级模式集散控制框架,利用单片机进行数据采集和处理,满足实时…...

unity 数学 空间四个点是否在同一个平面

问题&#xff1a;已知三维空间中四点A、B、C、D&#xff0c;如何知道四个点是否在同一个平面呢 首先我们知道三点确定一个平面&#xff0c;所以可以由上面四个点其中任意三点组成一个平面p&#xff08;A,B,C&#xff09;&#xff0c;另外一个点和三个任意点的形成线&#xff0…...

数据卷dockerfile

目录 一、数据卷 1. 简介 2. 数据卷和数据卷容器 1. 数据卷&#xff1a; 2. 数据卷容器&#xff1a; 二、自定义镜像 1. 作用 2. 自定义centos 3. 自定义tomcat8 一、数据卷 1. 简介 数据卷是一个可供一个或多个容器使用的特殊目录&#xff0c;它将主机操作系统目录直…...

AOP的介绍与使用

文章目录 AOP的概念AOP术语AOP的实现AspectJSpring AOP Spring AOP原理JDK动态代理CGLib动态代理 SpringAOP代码编写规则自定义切面自定义切点自定义通知在通知中获取当前请求代码实例 一些选择题 AOP的概念 • Aspect Oriented Programing&#xff0c;即面向方面&#xff08;…...

金融行业专题|期货超融合架构转型与场景探索合集(2023版)

更新内容&#xff1a; 更新 SmartX 超融合在期货行业的覆盖范围、部署规模与应用场景。新增 CTP 主席系统实践与评测、容器云资源池等场景实践。更多超融合金融核心生产业务场景实践&#xff0c;欢迎下载阅读电子书《SmartX 金融核心生产业务场景探索文章合集》。 面对不断变…...

08 yum和git

什么是软件包 安装软件&#xff0c;一个通常的办法就是下载程序的源代码进行编译。这种太麻烦&#xff0c;于是一些人把常用软件编译好&#xff0c;做成软件包放在服务器上&#xff0c;通过包管理器可以很方便的得到这个软件包安装&#xff0c;就好比手机上的应用商店 yum&am…...

JMeter元件和采样器一览

Apache JMeter是一个强大的开源负载测试工具&#xff0c;用于性能和功能测试。JMeter提供了丰富的元件和采样器&#xff0c;使得它能够模拟复杂的测试场景和高并发的用户请求。以下是JMeter中常用的一些元件和采样器的介绍和讲解&#xff1a; 测试计划元件 测试计划&#xff0…...

BF算法的优化之SPFA算法

介绍 全称Shortest Path Faster Algorithm. 优化思想&#xff1a; 1.由int path[maxn]定义的记录最短距离的容器&#xff0c;只有在path[i]value<path[j]时才会更新&#xff0c;它们两者的值相等时path的值仍保持不变。由此优化容器&#xff0c;选择用一个队列来替path数…...

【Zephyr 系列 10】实战项目:打造一个蓝牙传感器终端 + 网关系统(完整架构与全栈实现)

🧠关键词:Zephyr、BLE、终端、网关、广播、连接、传感器、数据采集、低功耗、系统集成 📌目标读者:希望基于 Zephyr 构建 BLE 系统架构、实现终端与网关协作、具备产品交付能力的开发者 📊篇幅字数:约 5200 字 ✨ 项目总览 在物联网实际项目中,**“终端 + 网关”**是…...

AI编程--插件对比分析:CodeRider、GitHub Copilot及其他

AI编程插件对比分析&#xff1a;CodeRider、GitHub Copilot及其他 随着人工智能技术的快速发展&#xff0c;AI编程插件已成为提升开发者生产力的重要工具。CodeRider和GitHub Copilot作为市场上的领先者&#xff0c;分别以其独特的特性和生态系统吸引了大量开发者。本文将从功…...

RNN避坑指南:从数学推导到LSTM/GRU工业级部署实战流程

本文较长&#xff0c;建议点赞收藏&#xff0c;以免遗失。更多AI大模型应用开发学习视频及资料&#xff0c;尽在聚客AI学院。 本文全面剖析RNN核心原理&#xff0c;深入讲解梯度消失/爆炸问题&#xff0c;并通过LSTM/GRU结构实现解决方案&#xff0c;提供时间序列预测和文本生成…...

AspectJ 在 Android 中的完整使用指南

一、环境配置&#xff08;Gradle 7.0 适配&#xff09; 1. 项目级 build.gradle // 注意&#xff1a;沪江插件已停更&#xff0c;推荐官方兼容方案 buildscript {dependencies {classpath org.aspectj:aspectjtools:1.9.9.1 // AspectJ 工具} } 2. 模块级 build.gradle plu…...

稳定币的深度剖析与展望

一、引言 在当今数字化浪潮席卷全球的时代&#xff0c;加密货币作为一种新兴的金融现象&#xff0c;正以前所未有的速度改变着我们对传统货币和金融体系的认知。然而&#xff0c;加密货币市场的高度波动性却成为了其广泛应用和普及的一大障碍。在这样的背景下&#xff0c;稳定…...

React---day11

14.4 react-redux第三方库 提供connect、thunk之类的函数 以获取一个banner数据为例子 store&#xff1a; 我们在使用异步的时候理应是要使用中间件的&#xff0c;但是configureStore 已经自动集成了 redux-thunk&#xff0c;注意action里面要返回函数 import { configureS…...

Razor编程中@Html的方法使用大全

文章目录 1. 基础HTML辅助方法1.1 Html.ActionLink()1.2 Html.RouteLink()1.3 Html.Display() / Html.DisplayFor()1.4 Html.Editor() / Html.EditorFor()1.5 Html.Label() / Html.LabelFor()1.6 Html.TextBox() / Html.TextBoxFor() 2. 表单相关辅助方法2.1 Html.BeginForm() …...

MySQL 主从同步异常处理

阅读原文&#xff1a;https://www.xiaozaoshu.top/articles/mysql-m-s-update-pk MySQL 做双主&#xff0c;遇到的这个错误&#xff1a; Could not execute Update_rows event on table ... Error_code: 1032是 MySQL 主从复制时的经典错误之一&#xff0c;通常表示&#xff…...

深度剖析 DeepSeek 开源模型部署与应用:策略、权衡与未来走向

在人工智能技术呈指数级发展的当下&#xff0c;大模型已然成为推动各行业变革的核心驱动力。DeepSeek 开源模型以其卓越的性能和灵活的开源特性&#xff0c;吸引了众多企业与开发者的目光。如何高效且合理地部署与运用 DeepSeek 模型&#xff0c;成为释放其巨大潜力的关键所在&…...

GraphQL 实战篇:Apollo Client 配置与缓存

GraphQL 实战篇&#xff1a;Apollo Client 配置与缓存 上一篇&#xff1a;GraphQL 入门篇&#xff1a;基础查询语法 依旧和上一篇的笔记一样&#xff0c;主实操&#xff0c;没啥过多的细节讲解&#xff0c;代码具体在&#xff1a; https://github.com/GoldenaArcher/graphql…...