msgqueue.hpp队列模块

news2024/9/21 12:24:27

一.MsgQueue相关类介绍

二.MsgQueue类的实现

成员变量

MsgQueue 结构体用于描述一个消息队列的基本属性。

std::string _name; // 队列名称
bool _durable;     // 队列是否持久化
bool _exclusive;   // 队列是否独占
bool _auto_del;    // 队列是否自动删除
google::protobuf::Map<std::string, std::string> _args; // 队列的附加参数

构造函数与析构函数

结构体提供了一个带参数的构造函数用于初始化各成员变量。同时提供了一个默认构造函数以支持无参初始化。

MsgQueue(const std::string &name, bool durable, bool exclusive, bool auto_del, const google::protobuf::Map<std::string, std::string> &_args)
    : _name(name), _durable(durable), _exclusive(exclusive), _auto_del(auto_del), _args(_args) {}

成员函数

MsgQueue 提供了两个主要的成员函数,用于设置和获取队列的附加参数。

参数设置函数 setArgs

setArgs 方法接收一个格式化的字符串参数,并将其解析为键值对,存储在 _args 中。
该方法使用了 StrHelper::split 函数来分割字符串,通过 = 符号区分键和值,并存储在 _args 中。

参数获取函数 getArgs

getArgs 方法用于将 _args 中的键值对组合成格式化字符串,返回给调用者。

std::string getArgs()
{
    std::string ret;
    for (auto &kv : _args)
    {
        ret += kv.first + "=" + kv.second + "&";
    }
    return ret;
}

三.MsgQueueMapper类的实现

成员变量

SqliteHelper sql_helper; //数据库管理句柄
  • sql_helperSqliteHelper 是一个辅助类,封装了SQLite的基本操作,如执行SQL语句和管理数据库连接。

构造函数

MsgQueueMapper(const std::string &dbname) : sql_helper(dbname)
{
    std::string path = FileHelper::getParentDirName(dbname);
    FileHelper::createDir(path);
    if (!sql_helper.open())
    {
        ELOG("MsgQueueMapper open db failed:%s", dbname.c_str());
        assert(0);
    }
    createTable();
}

构造函数接受一个数据库名称参数,初始化 sql_helper,并在必要时创建数据库目录和表。

成员函数

创建表格函数 createTable
void createTable()
{
    std::stringstream sql;
    sql << "create table if not exists msg_queue(";
    sql << "name varchar(64) primary key,";
    sql << "durable int,";
    sql << "exclusive int,";
    sql << "auto_del int,";
    sql << "args varchar(64));";
    if (!sql_helper.exec(sql.str(), nullptr, nullptr))
    {
        ELOG("MsgQueueMapper create table failed:%s", sql.str().c_str());
        assert(0);
    }
}
删除表格函数 dropTable

dropTable 方法用于删除现有的消息队列表格。该方法执行删除表格的SQL语句,如果表格存在,它将被移除。

void dropTable()
{
    std::stringstream sql;
    sql << "drop table if exists msg_queue;";
    if (!sql_helper.exec(sql.str(), nullptr, nullptr))
    {
        ELOG("MsgQueueMapper drop table failed:%s", sql.str().c_str());
        assert(0);
    }
}
插入数据函数 insert

insert 方法将 MsgQueue 对象插入到数据库中。

void insert(MsgQueue::ptr &msg_queue_ptr)
{
    std::stringstream sql;
    sql << "insert into msg_queue(name, durable, exclusive, auto_del, args) values(";
    sql << "'" << msg_queue_ptr->_name << "',";
    sql << msg_queue_ptr->_durable << ",";
    sql << msg_queue_ptr->_exclusive << ",";
    sql << msg_queue_ptr->_auto_del << ",";
    sql << "'" << msg_queue_ptr->getArgs() << "');";
    if (!sql_helper.exec(sql.str(), nullptr, nullptr))
    {
        ELOG("MsgQueueMapper insert msg_queue failed:%s", sql.str().c_str());
        assert(0);
    }
}
删除数据函数 remove

remove 方法用于从数据库中删除指定名称的消息队列。

void remove(const std::string &name)
{
    std::stringstream sql;
    sql << "delete from msg_queue where name = '" << name << "';";
    if (!sql_helper.exec(sql.str(), nullptr, nullptr))
    {
        ELOG("MsgQueueMapper remove msg_queue failed:%s", sql.str().c_str());
        assert(0);
    }
}
数据恢复函数 recover

recover 方法用于从数据库中恢复所有消息队列。该方法执行SQL查询语句,遍历结果集,并将每个消息队列恢复到内存中的 _msgQueues 容器中。

   msgqueue_map recover()
        {
            msgqueue_map ret;
            std::string sql = "select * from msg_queue;";
            if (!sql_helper.exec(sql, MsgQueueMapper::msgQueueMapCb, &ret))
            {
                ELOG("MsgQueueMapper recover failed:%s", sql.c_str());
                assert(0);
            }

            return ret;
        }
msgQueueMapCb
  • 该静态回调函数在 recover 函数中被调用,用于将数据库查询结果中的每一行转化为一个 MsgQueue对象,并将其存储到 msgqueue_map中。
      static int msgQueueMapCb(void *arg, int col_count, char **col_values, char **col_names)
        {
            msgqueue_map *ret = (msgqueue_map *)arg;
            MsgQueue::ptr msg_queue_ptr = std::make_shared<MsgQueue>();

            msg_queue_ptr->_name = col_values[0];
            msg_queue_ptr->_durable = std::stoi(col_values[1]);
            msg_queue_ptr->_exclusive = std::stoi(col_values[2]);
            msg_queue_ptr->_auto_del = std::stoi(col_values[3]);
            if (col_values[4])
            {
                msg_queue_ptr->setArgs((std::string)col_values[4]);
            }
            else
                ELOG("没有其它参数");

            ret->insert(std::make_pair(msg_queue_ptr->_name, msg_queue_ptr));
            return 0;
        }

四.MsgQueueMapper类的实现

MsgQueueManager 类用于管理内存中的消息队列对象,并与 MsgQueueMapper 协作实现消息队列的持久化。

成员变量

  • _msgQueues:一个键值对映射,用于存储当前内存中的所有消息队列。
  • _mapperMsgQueueMapper 对象,用于与数据库进行持久化操作。
  • _mutex:用于保护消息队列操作的线程安全。

构造函数

构造函数在初始化时会调用 MsgQueueMapperrecover 方法,从数据库恢复所有消息队列。

MsgQueueManager(const std::string &dbname) : _mapper(dbname)
{
    _mapper.recover(_msgQueues);
}

成员函数

声明队列函数 declareQueue

declareQueue 方法用于声明一个新的消息队列,并将其加入到内存和数据库中。

该方法首先检查队列是否已经存在,若不存在,则创建并插入新的队列。如果队列需要持久化,则还会将其插入到数据库中。

bool declareQueue(const std::string &name, bool durable, bool exclusive, bool auto_del, const google::protobuf::Map<std::string, std::string> &args)
{
    std::lock_guard<std::mutex> lock(_mutex);
    auto it = _msgQueues.find(name);
    if (it != _msgQueues.end())
    {
        ILOG("MsgQueueManager declareQueue:%s already exists", name.c_str())
        return true;
    }

    MsgQueue::ptr msg_queue_ptr = std::make_shared<MsgQueue>(name, durable, exclusive, auto_del, args);
    _msgQueues.insert(std::make_pair(name, msg_queue_ptr));
    if (msg_queue_ptr->_durable)
        _mapper.insert(msg_queue_ptr);

    return true;
}
删除队列函数 removeQueue

removeQueue 方法用于删除指定名称的消息队列。

bool removeQueue(const std::string &name)
{
    std::lock_guard<std::mutex> lock(_mutex);
    auto it = _msgQueues.find(name);
    if (it == _msgQueues.end())
    {
        ELOG("MsgQueueManager removeQueue:%s not exists", name.c_str());
        return false;
    }

    if (it->second->_durable)
        _mapper.remove(name);
    _msgQueues.erase(it);
    return true;
}
查询队列函数 selectQueue

selectQueue 方法根据队列名称查询并返回消息队列对象

该方法用于在内存中查找指定名称的队列,并返回指向该队列的智能指针。

MsgQueue::ptr selectQueue(const std::string &name)
{
    std::lock_guard<std::mutex> lock(_mutex);
    auto it = _msgQueues.find(name);
    if (it == _msgQueues.end())
    {
        ELOG("MsgQueueManager selectQueue:%s not exists", name.c_str());
        return nullptr;
    }
    return it->second;
}
查询所有队列函数 selectAll
  msgqueue_map selectAll()
        {
            return _msgQueues;
        }
队列是否存在函数 exists

exists 方法用于判断指定名称的消息队列是否存在。


bool exists(const std::string &name) { std::lock_guard<std::mutex> lock(_mutex); return _msgQueues.find(name) != _msgQueues.end(); }

该方法返回一个布尔值,指示队列是否存在。

队列数量函数 size

size 方法用于返回当前存在的消息队列数量。

size_t size() { std::lock_guard<std::mutex> lock(_mutex); return _msgQueues.size(); }

该方法返回一个整数值,表示内存中队列的数量。

清空所有队列函数 clear

clear 方法用于清空所有当前存在的消息队列。


void clear() { std::lock_guard<std::mutex> lock(_mutex); _msgQueues.clear(); _mapper.dropTable(); _mapper.createTable(); }

该方法清空内存中的所有队列,并在数据库中删除和重建消息队列表格。

五.MsgQueue.hpp所有代码

#pragma once
#include "../common_mq/helper.hpp"
#include "../common_mq/logger.hpp"
#include "../common_mq/msg.pb.h"
#include <string>
#include <unordered_map>
#include <mutex>
#include <memory>
#include <cassert>
#include <cstring>
#include <google/protobuf/map.h>

namespace mq
{
    struct MsgQueue
    {
        std::string _name;
        bool _durable;   // 是否持久化
        bool _exclusive; // 是否独占
        bool _auto_del;  // 是否自动删除
        google::protobuf::Map<std::string, std::string> _args;

        using ptr = std::shared_ptr<MsgQueue>;

        MsgQueue(const std::string &name, bool durable, bool exclusive,
                 bool auto_del, const google::protobuf::Map<std::string, std::string> &_args)
            : _name(name),
              _durable(durable),
              _exclusive(exclusive),
              _auto_del(auto_del),
              _args(_args)
        {
        }
        MsgQueue() {}
        void setArgs(const std::string &str_args)
        {
            std::vector<std::string> args;
            size_t sz = StrHelper::split(str_args, "&", args);
            for (auto &kv : args)
            {
                size_t pos = kv.find("=");
                if (pos == std::string::npos)
                {
                    ELOG("MsgQueue args format error:%s", kv.c_str());
                    assert(0);
                }
                std::string key = kv.substr(0, pos);
                std::string val = kv.substr(pos + 1);
                _args[key] = val;
            }
        }
        std::string getArgs()
        {
            std::string ret;
            for (auto &kv : _args)
            {
                ret += kv.first + "=" + kv.second + "&";
            }

            return ret;
        }
    };

    using msgqueue_map = std::unordered_map<std::string, std::shared_ptr<MsgQueue>>;

    class MsgQueueMapper
    {
    private:
        SqliteHelper sql_helper;

    public:
        MsgQueueMapper(const std::string &dbname)
            : sql_helper(dbname)
        {
            // 数据库有path即可,open时自动创建文件
            std::string path = FileHelper::getParentDirName(dbname);
            FileHelper::createDir(path);
            if (!sql_helper.open())
            {
                ELOG("MsgQueueMapper open db failed:%s", dbname.c_str());
                assert(0);
            }
            createTable();
        }
        // 1.创建,删除表
        void createTable()
        {
            std::stringstream sql;
            sql << "create table if not exists msg_queue(";
            sql << "name varchar(64) primary key,";
            sql << "durable int,";
            sql << "exclusive int,";
            sql << "auto_del int,";
            sql << "args varchar(64));";
            if (!sql_helper.exec(sql.str(), nullptr, nullptr))
            {
                ELOG("MsgQueueMapper create table failed:%s", sql.str().c_str());
                assert(0);
            }
        }
        void dropTable()
        {
            std::stringstream sql;
            sql << "drop table if exists msg_queue;";
            if (!sql_helper.exec(sql.str(), nullptr, nullptr))
            {
                ELOG("MsgQueueMapper drop table failed:%s", sql.str().c_str());
                assert(0);
            }
        }
        // 2.插入/删除数据
        void insert(MsgQueue::ptr &msg_queue_ptr)
        {
            std::stringstream sql;
            sql << "insert into msg_queue (name,durable,exclusive,auto_del,args) values(";
            sql << "'" << msg_queue_ptr->_name << "',";
            sql << msg_queue_ptr->_durable << ",";
            sql << msg_queue_ptr->_exclusive << ",";
            sql << msg_queue_ptr->_auto_del << ",";
            sql << "'" << msg_queue_ptr->getArgs() << "');";
            if (!sql_helper.exec(sql.str(), nullptr, nullptr))
            {
                ELOG("MsgQueueMapper insert failed:%s", sql.str().c_str());
                assert(0);
            }
        }
        void remove(const std::string &name)
        {
            std::stringstream sql;
            sql << "delete from msg_queue where name='" << name << "';";
            if (!sql_helper.exec(sql.str(), nullptr, nullptr))
            {
                ELOG("MsgQueueMapper remove failed:%s", sql.str().c_str());
                assert(0);
            }
        }
        // 3.recover
        msgqueue_map recover()
        {
            msgqueue_map ret;
            std::string sql = "select * from msg_queue;";
            if (!sql_helper.exec(sql, MsgQueueMapper::msgQueueMapCb, &ret))
            {
                ELOG("MsgQueueMapper recover failed:%s", sql.c_str());
                assert(0);
            }

            return ret;
        }

    private:
        static int msgQueueMapCb(void *arg, int col_count, char **col_values, char **col_names)
        {
            msgqueue_map *ret = (msgqueue_map *)arg;
            MsgQueue::ptr msg_queue_ptr = std::make_shared<MsgQueue>();

            msg_queue_ptr->_name = col_values[0];
            msg_queue_ptr->_durable = std::stoi(col_values[1]);
            msg_queue_ptr->_exclusive = std::stoi(col_values[2]);
            msg_queue_ptr->_auto_del = std::stoi(col_values[3]);
            if (col_values[4])
            {
                msg_queue_ptr->setArgs((std::string)col_values[4]);
            }
            else
                ELOG("没有其它参数");

            ret->insert(std::make_pair(msg_queue_ptr->_name, msg_queue_ptr));
            return 0;
        }
    };

    class MsgQueueManager
    {
    public:
        using ptr = std::shared_ptr<MsgQueueManager>;

    private:
        msgqueue_map _msgQueues;
        std::mutex _mutex;
        MsgQueueMapper _mapper;

    public:
        MsgQueueManager(const std::string &dbname)
            : _mapper(dbname)
        {
            _msgQueues = _mapper.recover();
        }
        // 1. 插入/删除数据
        bool declareQueue(const std::string &name,
                          bool durable,
                          bool exclusive,
                          bool auto_del,
                          const google::protobuf::Map<std::string, std::string> &args)
        {
            std::lock_guard<std::mutex> lock(_mutex);
            auto it = _msgQueues.find(name);
            if (it != _msgQueues.end())
            {
                ILOG("MsgQueueManager declareQueue:%s already exists", name.c_str())
                return true;
            }

            MsgQueue::ptr msg_queue_ptr = std::make_shared<MsgQueue>(name, durable, exclusive, auto_del, args);
            _msgQueues.insert(std::make_pair(name, msg_queue_ptr));
            if (msg_queue_ptr->_durable)
                _mapper.insert(msg_queue_ptr);
            // std::cout<<"declare队列:"<<msg_queue_ptr->_name<<std::endl;
            // std::cout<<"队列的个数:"<<_msgQueues.size()<<std::endl;
            return true;
        }
        bool removeQueue(const std::string &name)
        {
            std::lock_guard<std::mutex> lock(_mutex);
            auto it = _msgQueues.find(name);
            if (it == _msgQueues.end())
            {
                ILOG("MsgQueueManager removeQueue:%s not exists", name.c_str());
                return true;
            }
            _msgQueues.erase(it);
            if (it->second->_durable)
                _mapper.remove(name);
            return true;
        }
        // 2. 查询一个/所有 queue
        MsgQueue::ptr selectQueue(const std::string &name)
        {
            std::lock_guard<std::mutex> lock(_mutex);
            auto it = _msgQueues.find(name);
            if (it == _msgQueues.end())
            {
                ILOG("MsgQueueManager select:%s not exists", name.c_str());
                return MsgQueue::ptr();
            }

            return it->second;
        }
        msgqueue_map selectAll()
        {
            return _msgQueues;
        }
        // 3. 其它操作
        bool exists(const std::string &name)
        {
            std::lock_guard<std::mutex> lock(_mutex);
            auto it = _msgQueues.find(name);
            if (it == _msgQueues.end())
            {
                ILOG("MsgQueueManager :%s not exists", name.c_str());
                return false;
            }

            return true;
        }
        size_t size()
        {
            std::unique_lock<std::mutex> lock(_mutex);
            return _msgQueues.size();
        }
        void clear()
        {
            std::unique_lock<std::mutex> lock(_mutex);
            _mapper.dropTable();
            _msgQueues.clear();
        }
    };
};

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

版本控制基础理论

一、本地版本控制 在本地记录文件每次的更新&#xff0c;可以对每个版本做一个快照&#xff0c;或是记录补丁文件&#xff0c;适合个人使用&#xff0c;如RCS. 二、集中式版本控制&#xff08;代表SVN&#xff09; 所有的版本数据都保存在服务器上&#xff0c;协同开发者从…

在HTML中固定表格表头的简单方法

在HTML中&#xff0c;表格元素自身无法提供滚动以及固定表头的配置。借助第三方工具&#xff08;如jQuery的表头固定插件&#xff09;或者结合JavaScrip&#xff0c;是可以实现表格的表头固定的&#xff0c;除此之外&#xff0c;本文还想讨论一种更简单的方式来实现。 从思路上…

【初阶数据结构】详解顺序表(下)(顺序表的代码实现)

文章目录 前言1. 项目文件的配置1.1 顺序表的项目的文件配置(仅供参考) 2. 顺序表的代码实现2.1 SeqList.h&#xff1a;2.2 SeqList.c:2.2.1 顺序表初始化的代码实现&#xff1a;2.2.2 顺序表销毁的代码实现&#xff1a;2.2.3 顺序表尾插数据的代码实现&#xff1a;2.2.4 顺序表…

【国赛必看!】数学建模python基础教学及常用算法代码包分享

一、内容介绍 Python在各个编程语言中比较适合新手学习&#xff0c;Python解释器易于扩展&#xff0c;可以使用C、C或其他可以通过C调用的语言扩展新的功能和数据类型。 Python也可用于可定制化软件中的扩展程序语言。Python丰富的标准库&#xff0c;提供了适用于各个主要系统…

opencv-python图像增强六:低光照增强

文章目录 一&#xff1a;简介二、低光照图像增强方案&#xff1a;三、算法实现步骤3.1 CLAHE直方图均衡化&#xff1a;3.2 伽马变换&#xff1a;3.3 对亮度通道做伽马变换 四&#xff1a;整体代码实现五&#xff0c;效果&#xff1a; 一&#xff1a;简介 低光照图像增强是一种…

Flink 常见问题汇总:反压积压,checkpoint报错,窗口计算,作业报错,无产出,流批不一致,调优等。

Flink 常见问题汇总 0 如何分析日志0.1作业内部重启异常&#xff0c; 作业正常运行0.2 作业内部重启&#xff0c; 但作业已经手动 kill 整个 yarn-application0.3 作业失败&#xff0c;整个 yarn application 结束运行 1 Flink 作业积压排查流程及解决思路1 反压原因2 反压的危…

Windows下搭建Telegraf+Influxdb+Grafana(详解一)

InfluxDB&#xff08;时序数据库&#xff09;&#xff0c;常用使用场景&#xff1a;监控数据统计。 grafana&#xff0c;用作监控页面的前端展示。 telegraf&#xff0c;数据采集器。 所有的安装包都上传到网盘 链接: https://pan.baidu.com/s/1Lv6UnfueK7URx7emAatoYg 提取…

oracle 数据中lsnrctl 是干啥的

突然发现lsnrctl stop 之后&#xff0c;依然可以启动数据库 就感觉怪怪的&#xff0c;一直以为这个是数据库的守护进程&#xff0c;原来不是。。。。 lsnrctl 是 Oracle 监听器控制实用程序的命令行界面工具&#xff0c;用于管理 Oracle Net 服务监听器。监听器是 Oracle 网络…

map和set的应用

map、set 1. 序列式和关联式容器2.set和multiset2.1 构造2.2 迭代器2.3 修改 3. map和multimap3.1 map3.2. multimap 1. 序列式和关联式容器 序列式容器&#xff1a;比如&#xff1a;vector、list、deque、forward_list等&#xff0c;这些容器统称为序列式容器&#xff0c;因为…

Vue3+Echarts+饼图环形图

记得给容器宽高 <div id"leftChartguawang" style"height: 28vh"></div> 配置函数 const leftChartguawang () > {const chartBox echarts.init(document.getElementById(leftChartguawang))let datas [[{ name: 居民节能建筑, value…

SmartEDA电路仿真软件革新力作:重塑电子设计界,揭秘其爆红背后的秘密武器!

在这个日新月异的科技时代&#xff0c;每一场技术革新都是推动行业进步的强劲动力。而在电子设计领域&#xff0c;一款名为SmartEDA的电路仿真软件正以前所未有的姿态&#xff0c;颠覆传统设计模式&#xff0c;成为众多工程师和设计师争相追捧的新宠。今天&#xff0c;就让我们…

硬核详解FutureTask设计与实现

写在文章开头 最近看到一篇比较不错的FutureTask实践,于是对FutureTask源码进行了研究,而本文将从实践和源码两个角度分析FutureTask的设计与实现思路,希望对你有帮助。 Hi,我是 sharkChili ,是个不断在硬核技术上作死的 java coder ,是 CSDN的博客专家 ,也是开源项目 …

从零开始搭建 EMQX 集群压测框架

从零开始搭建 EMQX 集群压测框架 架构 在设计以EMQX为中心的MQTT消息队列集群压力测试框架时&#xff0c;我们采用微服务架构模式。EMQX作为消息队列的核心&#xff0c;负责处理MQTT协议的消息发布和订阅。Nginx作为EMQX的反向代理&#xff0c;负责负载均衡和SSL/TLS终端。MQT…

农业上的目标跟踪论文汇总

文章目录 2022Multi-object tracking using Deep SORT and modified CenterNet in cotton seedling counting (Computers and Electronics in Agriculture)A novel apple fruit detection and counting methodology based on deep learning and trunk tracking in modern orcha…

模型部署 - docker

docker简介 Docker 是一种开源的容器化平台&#xff0c;允许开发者将应用程序及其依赖项打包到一个标准化的单元中&#xff0c;称为“容器”。这些容器可以在任何支持 Docker 的系统上运行&#xff0c;无需担心环境差异。 为什么需要 Docker&#xff1f; 在传统的开发中&…

ECharts数据可视化 数据集与事件 入门基础知识【2】

echarts一个基于 JavaScript 的开源可视化图表库。其有丰富的图表类型、强大的渲染引擎、专业多维度的数据分析、灵活配置的可视化设计。关于echarts的下载安装以及在项目中使用的基础知识我们前面已经回顾过了&#xff1a; ECharts 数据可视化 入门基本知识 下载安装常用的图表…

计数排序以及排序总结

技数排序 计数排序又称为鸽巢原理&#xff0c;是对哈希直接定址法的变形应用。 思路&#xff1a; 统计相同元素出现次数根据统计的结果将序列回收到原来的序列中 void CountSort(int* arr, int n) {//寻找最大、最小值int maxarr[0], min arr[0];for (int i 0; i < n;…

【网络】私有IP和公网IP的转换——NAT技术

目录 引言 NAT工作机制​编辑 NAT技术的优缺点 优点 缺点 个人主页&#xff1a;东洛的克莱斯韦克-CSDN博客 引言 公网被子网掩码划分为层状结构&#xff0c;一个公网IP的机器又可以用很多私有IP搭建内网。在日常生活场景中用的都是私有IP&#xff0c;例如手机&#xff0c;…

单元训练05:独立按键的基本操作

可以正常运行的&#xff1a; #include "stc15f2k60s2.h"#define LED(x) \{ \P0 x; \P2 P2 & 0x1f | 0x80; \P2 & 0x1f; \}void Timer0_Init(void) // 1毫秒12.000MHz {AUXR | 0x80; …

spring boot 发送微信小程序订阅消息

首先我们需要订阅一个消息&#xff1a; 订阅教程本文章并未提起&#xff0c;感兴趣的同学自行百度。 我们可以看到订阅消息中【消息内容】有很多参数&#xff0c;我们在发送消息时就需要将这些参数进行填充&#xff0c;当然填充的时候要注意格式&#xff0c;如果格式不对还是会…