【RabbitMQ 项目】服务端:服务器模块

news2024/12/23 18:16:05

文章目录

  • 一.编写思路
  • 二.代码实践
  • 三.服务端模块关系总结

一.编写思路

成员变量:

  1. muduo 库中的 TCP 服务器
  2. EventLoop 对象:用于主线程循环监控连接事件
  3. 协议处理句柄
  4. 分发器:用于初始化协议处理器,便于把不同请求派发给不同的业务处理函数
  5. 连接管理句柄
  6. 虚拟机句柄
  7. 消费者管理句柄
  8. 线程池管理句柄
    成员方法:
    向外提供的只有 2 个方法:
  9. start:启动服务
  10. 构造函数:
  • 完成各项成员的初始化,
  • 注册 TCP 服务器的两个回调函数:
    OnMessage:从接收缓冲区把数据读到用户缓冲区后的回调函数
    OnConnection:Tcp 连接建立或断开时的回调函数
  • 给分发器注册业务处理函数(私有成员方法,共 12 个)
    信道打开与与关闭;交换机,队列,绑定添加与删除,订阅与取消订阅,发布与确认消息
    私有成员(业务处理函数):
    如果是创建或关闭信道,直接用连接管理句柄新增或删除信道,然后构建响应返回
    如果是其他请求,先用连接管理句柄找到信道(请求中携带了信道 id),再使用信道提供的服务

二.代码实践

BrokerServer.hpp:

#pragma once
#include "muduo/protobuf/codec.h"
#include "muduo/protobuf/dispatcher.h"
#include "muduo/base/Logging.h"
#include "muduo/base/Mutex.h"
#include "muduo/net/EventLoop.h"
#include "muduo/net/TcpServer.h"

#include "VirtualHost.hpp"
#include "Connection.hpp"
#include "Consumer.hpp"
#include <functional>
#include <stdio.h>
#include <unistd.h>

namespace ns_server
{
    using ConnectionManagerPtr = std::shared_ptr<ns_connection::ConnectionManager>;
    using VirtualHostPtr = std::shared_ptr<ns_data::VirtualHost>;
    using ConsumerManagerPtr = std::shared_ptr<ns_consumer::ConsumerManager>;
    using ThreadPoolPtr = std::shared_ptr<ns_tp::ThreadPool>;
    using ProtobufCodecPtr = std::shared_ptr<ProtobufCodec>;

    /************
     * 定义协议的结构化数据的智能指针(在分发器中注册时需要的格式)
     * *************/
    using OpenChannelRequestPtr = std::shared_ptr<ns_protocol::OpenChannelRequest>;
    using CloseChannelRequestPtr = std::shared_ptr<ns_protocol::CloseChannelRequest>;
    using DeclareExchangeRequestPtr = std::shared_ptr<ns_protocol::DeclareExchangeRequest>;
    using DeleteExchangeRequestPtr = std::shared_ptr<ns_protocol::DeleteExchangeRequest>;
    using DeclareMsgQueueRequestPtr = std::shared_ptr<ns_protocol::DeclareMsgQueueRequest>;
    using DeleteMsgQueueRequestPtr = std::shared_ptr<ns_protocol::DeleteMsgQueueRequest>;
    using BindRequestPtr = std::shared_ptr<ns_protocol::BindRequest>;
    using UnbindRequestPtr = std::shared_ptr<ns_protocol::UnbindRequest>;
    using PublishMessageRequestPtr = std::shared_ptr<ns_protocol::PublishMessageRequest>;

    using SubscribeQueueRequestPtr = std::shared_ptr<ns_protocol::SubscribeQueueRequest>;
    using CancelSubscribeRequestPtr = std::shared_ptr<ns_protocol::CancelSubscribeRequest>;
    using AckRequestPtr = std::shared_ptr<ns_protocol::AckRequest>;

    class BrokerServer
    {
    public:
    private:
        muduo::net::EventLoop _baseLoop;
        muduo::net::TcpServer _server;
        ProtobufDispatcher _dispatcher;
        ProtobufCodecPtr _codecPtr;
        VirtualHostPtr _vhPtr;
        ConsumerManagerPtr _consumerManagerPtr;
        ConnectionManagerPtr _connManagerPtr;
        ThreadPoolPtr _threadPoolPtr;

    public:
        BrokerServer(int serverPort, const std::string &dbName, const std::string &msgDir)
            : _baseLoop(),
              _server(&_baseLoop, muduo::net::InetAddress("0.0.0.0", serverPort), "TcpServer", muduo::net::TcpServer::kReusePort),
              _dispatcher(std::bind(&BrokerServer::onUnknownMessage, this, std::placeholders::_1,
                                    std::placeholders::_2, std::placeholders::_3))
        {
            // 初始化成员
            _codecPtr = std::make_shared<ProtobufCodec>(std::bind(&ProtobufDispatcher::onProtobufMessage,
                                                                  &_dispatcher, std::placeholders::_1,
                                                                  std::placeholders::_2, std::placeholders::_3));
            _vhPtr = std::make_shared<ns_data::VirtualHost>(dbName, msgDir);

            _threadPoolPtr = std::make_shared<ns_tp::ThreadPool>();
            _threadPoolPtr->start();

            std::vector<std::string> qnames;
            _vhPtr->getAllQueueName(&qnames);
            _consumerManagerPtr = std::make_shared<ns_consumer::ConsumerManager>(qnames);

            _connManagerPtr = std::make_shared<ns_connection::ConnectionManager>();

            // 给_server注册两个回调函数
            _server.setConnectionCallback(std::bind(&BrokerServer::onConnection, this, std::placeholders::_1));
            _server.setMessageCallback(std::bind(&ProtobufCodec::onMessage, _codecPtr.get(), std::placeholders::_1,
                                                 std::placeholders::_2, std::placeholders::_3));

            // 给分发器注册业务处理函数
            _dispatcher.registerMessageCallback<ns_protocol::OpenChannelRequest>(std::bind(&BrokerServer::onOpenChannel,
                                                                                           this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));

            _dispatcher.registerMessageCallback<ns_protocol::CloseChannelRequest>(std::bind(&BrokerServer::onCloseChannel,
                                                                                            this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));

            _dispatcher.registerMessageCallback<ns_protocol::DeclareExchangeRequest>(std::bind(&BrokerServer::onDeclareExchange,
                                                                                               this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));

            _dispatcher.registerMessageCallback<ns_protocol::DeleteExchangeRequest>(std::bind(&BrokerServer::onDeleteExchange,
                                                                                              this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));

            _dispatcher.registerMessageCallback<ns_protocol::DeclareMsgQueueRequest>(std::bind(&BrokerServer::onDeclareMsgQueue,
                                                                                               this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));

            _dispatcher.registerMessageCallback<ns_protocol::DeleteMsgQueueRequest>(std::bind(&BrokerServer::onDeleteMsgQueue,
                                                                                              this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));

            _dispatcher.registerMessageCallback<ns_protocol::BindRequest>(std::bind(&BrokerServer::onBind,
                                                                                    this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));

            _dispatcher.registerMessageCallback<ns_protocol::UnbindRequest>(std::bind(&BrokerServer::onUnbind,
                                                                                      this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));

            _dispatcher.registerMessageCallback<ns_protocol::SubscribeQueueRequest>(std::bind(&BrokerServer::onSubScribe,
                                                                                              this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));

            _dispatcher.registerMessageCallback<ns_protocol::CancelSubscribeRequest>(std::bind(&BrokerServer::onCancelSubScribe,
                                                                                               this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));

            _dispatcher.registerMessageCallback<ns_protocol::PublishMessageRequest>(std::bind(&BrokerServer::onPublishMessage,
                                                                                              this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));

            _dispatcher.registerMessageCallback<ns_protocol::AckRequest>(std::bind(&BrokerServer::onAck,
                                                                                   this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
        }

        void start()
        {
            // 开启监听状态
            _server.start();
            // 开始循环监控事件
            _baseLoop.loop();
        }

    private:
        // 给TcpServer设置的回调函数
        void onConnection(const muduo::net::TcpConnectionPtr &connPtr)
        {
            if (connPtr->connected())
            {
                _connManagerPtr->newConnection(connPtr, _codecPtr, _vhPtr, _consumerManagerPtr, _threadPoolPtr);
            }
            else
            {
                _connManagerPtr->deleteConnection(connPtr);
            }
        }

        // 业务处理函数
        void onUnknownMessage(const muduo::net::TcpConnectionPtr &connPtr, MessagePtr msgPtr, muduo::Timestamp time)
        {
            cout << "未知消息" << endl;
            connPtr->shutdown();
        }

        /************
         * 信道创建与删除
         * ***************/
        void onOpenChannel(const muduo::net::TcpConnectionPtr &connPtr, const OpenChannelRequestPtr &reqPtr, muduo::Timestamp time)
        {
            auto myConnPtr = _connManagerPtr->getConnection(connPtr);
            if (myConnPtr == nullptr)
            {
                LOG(WARNING) << "打开信道时, 未找到Connection" << endl;
                return;
            }
            myConnPtr->openChannel(*reqPtr);
            LOG(DEBUG) << "create new channel, channelId: " << reqPtr->channel_id() << endl;
        }

        void onCloseChannel(const muduo::net::TcpConnectionPtr &connPtr, const CloseChannelRequestPtr &reqPtr, muduo::Timestamp time)
        {
            auto myConnPtr = _connManagerPtr->getConnection(connPtr);
            if (myConnPtr == nullptr)
            {
                LOG(WARNING) << "关闭信道时, 未找到Connection" << endl;
                return;
            }
            myConnPtr->closeChannel(*reqPtr);
            LOG(DEBUG) << "close channel, channelId: " << reqPtr->channel_id() << endl;
        }

        /*********
         * 交换机声明与删除
         * ********/
        void onDeclareExchange(const muduo::net::TcpConnectionPtr &connPtr, const DeclareExchangeRequestPtr &reqPtr, muduo::Timestamp time)
        {
            auto myConnPtr = _connManagerPtr->getConnection(connPtr);
            if (myConnPtr == nullptr)
            {
                LOG(WARNING) << "声明交换机时, 未找到Connection" << endl;
                return;
            }

            auto channelPtr = myConnPtr->getChannel(reqPtr->channel_id());
            if (channelPtr == nullptr)
            {
                LOG(WARNING) << "没有找到信道" << endl;
                return;
            }

            channelPtr->declareExchange(*reqPtr);
            LOG(DEBUG) << "声明交换机, exchangeName: " << reqPtr->exchange_name() << endl;
        }

        void onDeleteExchange(const muduo::net::TcpConnectionPtr &connPtr, const DeleteExchangeRequestPtr &reqPtr, muduo::Timestamp time)
        {
            auto myConnPtr = _connManagerPtr->getConnection(connPtr);
            if (myConnPtr == nullptr)
            {
                LOG(WARNING) << "删除交换机时, 未找到Connection" << endl;
                return;
            }

            auto channelPtr = myConnPtr->getChannel(reqPtr->channel_id());
            if (channelPtr == nullptr)
            {
                LOG(WARNING) << "没有找到信道" << endl;
                return;
            }

            channelPtr->deleteExchange(*reqPtr);
            LOG(DEBUG) << "删除信道, exchangeName: " << reqPtr->exchange_name() << endl;
        }

        /************
         * 队列声明与删除
         * ***************/
        void onDeclareMsgQueue(const muduo::net::TcpConnectionPtr &connPtr, const DeclareMsgQueueRequestPtr &reqPtr, muduo::Timestamp time)
        {
            auto myConnPtr = _connManagerPtr->getConnection(connPtr);
            if (myConnPtr == nullptr)
            {
                LOG(WARNING) << "声明队列时, 未找到Connection" << endl;
                return;
            }

            auto channelPtr = myConnPtr->getChannel(reqPtr->channel_id());
            if (channelPtr == nullptr)
            {
                LOG(WARNING) << "没有找到信道" << endl;
                return;
            }

            channelPtr->declareMsgQueue(*reqPtr);
            LOG(DEBUG) << "声明队列, queueName: " << reqPtr->queue_name() << endl;
        }

        void onDeleteMsgQueue(const muduo::net::TcpConnectionPtr &connPtr, const DeleteMsgQueueRequestPtr &reqPtr, muduo::Timestamp time)
        {
            auto myConnPtr = _connManagerPtr->getConnection(connPtr);
            if (myConnPtr == nullptr)
            {
                LOG(WARNING) << "删除队列时, 未找到Connection" << endl;
                return;
            }

            auto channelPtr = myConnPtr->getChannel(reqPtr->channel_id());
            if (channelPtr == nullptr)
            {
                LOG(WARNING) << "没有找到信道" << endl;
                return;
            }

            channelPtr->deleteMsgQueue(*reqPtr);
            LOG(DEBUG) << "删除队列, queueName: " << reqPtr->queue_name() << endl;
        }

        /**********
         * 绑定与解绑
         * ***********/
        void onBind(const muduo::net::TcpConnectionPtr &connPtr, const BindRequestPtr &reqPtr, muduo::Timestamp time)
        {
            auto myConnPtr = _connManagerPtr->getConnection(connPtr);
            if (myConnPtr == nullptr)
            {
                LOG(WARNING) << "添加绑定时, 未找到Connection" << endl;
                return;
            }

            auto channelPtr = myConnPtr->getChannel(reqPtr->channel_id());
            if (channelPtr == nullptr)
            {
                LOG(WARNING) << "没有找到信道" << endl;
                return;
            }

            channelPtr->bind(*reqPtr);
            LOG(DEBUG) << "绑定: " << reqPtr->ename() << "->" << reqPtr->qname() << ": " << reqPtr->binding_key() << endl;
        }
        void onUnbind(const muduo::net::TcpConnectionPtr &connPtr, const UnbindRequestPtr &reqPtr, muduo::Timestamp time)
        {
            auto myConnPtr = _connManagerPtr->getConnection(connPtr);
            if (myConnPtr == nullptr)
            {
                LOG(WARNING) << "删除绑定时, 未找到Connection" << endl;
                return;
            }

            auto channelPtr = myConnPtr->getChannel(reqPtr->channel_id());
            if (channelPtr == nullptr)
            {
                LOG(WARNING) << "没有找到信道" << endl;
                return;
            }

            channelPtr->unbind(*reqPtr);
            LOG(DEBUG) << "解绑: " << reqPtr->ename() << "->" << reqPtr->qname() << endl;
        }

        /*************
         * 订阅与取消订阅
         * ************/
        void onSubScribe(const muduo::net::TcpConnectionPtr &connPtr, const SubscribeQueueRequestPtr &reqPtr, muduo::Timestamp time)
        {
            auto myConnPtr = _connManagerPtr->getConnection(connPtr);
            if (myConnPtr == nullptr)
            {
                LOG(WARNING) << "订阅队列时, 未找到Connection" << endl;
                return;
            }

            auto channelPtr = myConnPtr->getChannel(reqPtr->channel_id());
            if (channelPtr == nullptr)
            {
                LOG(WARNING) << "没有找到信道" << endl;
                return;
            }

            channelPtr->subscribeQueue(*reqPtr);
            LOG(DEBUG) << "订阅队列" << ", qname: " << reqPtr->qname() << endl;
        }

        void onCancelSubScribe(const muduo::net::TcpConnectionPtr &connPtr, const CancelSubscribeRequestPtr &reqPtr, muduo::Timestamp time)
        {
            auto myConnPtr = _connManagerPtr->getConnection(connPtr);
            if (myConnPtr == nullptr)
            {
                LOG(WARNING) << "取消订阅队列时, 未找到Connection" << endl;
                return;
            }

            auto channelPtr = myConnPtr->getChannel(reqPtr->channel_id());
            if (channelPtr == nullptr)
            {
                LOG(WARNING) << "没有找到信道" << endl;
                return;
            }

            channelPtr->cancelSubscribe(*reqPtr);
            LOG(DEBUG) << "取消订阅队列" << ", qname: " << reqPtr->qname() << endl;
        }

        /********
         * 发布与应答
         * **************/
        void onPublishMessage(const muduo::net::TcpConnectionPtr &connPtr, const PublishMessageRequestPtr &reqPtr, muduo::Timestamp time)
        {
            auto myConnPtr = _connManagerPtr->getConnection(connPtr);
            if (myConnPtr == nullptr)
            {
                LOG(WARNING) << "发布消息时, 未找到Connection" << endl;
                return;
            }

            auto channelPtr = myConnPtr->getChannel(reqPtr->channel_id());
            if (channelPtr == nullptr)
            {
                LOG(WARNING) << "没有找到信道" << endl;
                return;
            }

            channelPtr->publishMessage(*reqPtr);
            LOG(DEBUG) << "publish message: " << reqPtr->msg().saved_info().body() << endl;
        }

        void onAck(const muduo::net::TcpConnectionPtr &connPtr, const AckRequestPtr &reqPtr, muduo::Timestamp time)
        {
            auto myConnPtr = _connManagerPtr->getConnection(connPtr);
            if (myConnPtr == nullptr)
            {
                LOG(WARNING) << "确认消息时, 未找到Connection" << endl;
                return;
            }

            auto channelPtr = myConnPtr->getChannel(reqPtr->channel_id());
            if (channelPtr == nullptr)
            {
                LOG(WARNING) << "没有找到信道" << endl;
                return;
            }

            channelPtr->ackMessage(*reqPtr);
            LOG(DEBUG) << "应答消息, msgId: " << reqPtr->msg_id() << endl;
        }

        void sendCommonResponse(const muduo::net::TcpConnectionPtr &connPtr, const std::string &channelId,
                                const std::string &responseId, bool ok)
        {
            ns_protocol::CommomResponse resp;
            resp.set_channel_id(channelId);
            resp.set_response_id(responseId);
            resp.set_ok(ok);
            _codecPtr->send(connPtr, resp);
        }
    };
}

三.服务端模块关系总结

在这里插入图片描述

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

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

相关文章

大语言模型在构建UNSPSC 分类数据中的应用

UNSPSC 是联合国标准产品和服务代码。UNSPSC由联合国开发计划署&#xff08;UNDP&#xff09;和Dun & Bradstreet公司&#xff08;D & B&#xff09;于1998年联合制定&#xff0c;自2003年以来一直由GS1 US管理。GS1 US 将在 2024 年底前将 UNSPSC 的管理权移交给 UNDP…

【HarmonyOS】TaskPool非阻塞UI

TaskPool方法不会阻塞UI&#xff0c;如果做上传图片的功能加载Loading记得使用TaskPool&#xff0c;Promise、Async/Await都会阻塞UI 【引言】 发现Promise可能会阻塞UI&#xff0c;尝试使用async或await&#xff0c;但发现它们仍然会导致阻塞。后来看到chaoxiaoshu回复的Tas…

数字孪生平台,助力制造设备迈入超感知与智控新时代!

痛点剖析 当前&#xff0c;制造业面临系统分散导致的数据孤岛问题&#xff0c;严重阻碍了有效监管与统计分析&#xff1b;同时&#xff0c;设备多样化且兼容性不足&#xff0c;增加了管理难度&#xff1b;台账记录方式混乱&#xff0c;工单审批流程繁琐且效率低下&#xff1b;…

electron使用npm install出现下载失败的问题

我在使用electron进行下载时&#xff0c;经常出现一个错误。 HTTPError: Response code 404 (Not Found) for https://registry.npmmirror.com/v21.4.4/electron-v21.4.4-win32-x64.zip 这个时候需要修改一些npm的配置。使用命令npm config list -ls 滑到下面&#xff0c;找到一…

第一个maven web工程(eclipse)

1、点击file--》new--》Maven Project&#xff0c;如下&#xff1a; 2、直接next&#xff0c;如下 3、搜索web原型&#xff0c;如下 4、填写项目的坐标&#xff0c;如下 5、创建完成后&#xff0c;需要自己补一个文件夹&#xff0c;名称为java&#xff0c;如下&#xff1a; …

class 023 随机快速排序

这篇文章是看了“左程云”老师在b站上的讲解之后写的, 自己感觉已经能理解了, 所以就将整个过程写下来了。 这个是“左程云”老师个人空间的b站的链接, 数据结构与算法讲的很好很好, 希望大家可以多多支持左程云老师, 真心推荐. https://space.bilibili.com/8888480?spm_id_f…

云中红队系列 | 使用 AWS API 配置Fireprox进行 IP轮换

在渗透测试评估期间&#xff0c;某些活动需要一定程度的自动化&#xff0c;例如从 LinkedIn 等网站抓取网页以收集可用于社会工程活动、密码喷洒登录门户或测试时盲注的有效员工姓名列表网络应用程序。但是&#xff0c;从单个源 IP 地址执行这些活动可能会导致在测试期间被拦截…

【TabBar嵌套Navigation案例-新特性页面-代码位置 Objective-C语言】

一、接下来,我们来说这个新特性页面 1.首先,看一下我们的示例程序,这里改一下,加一个叹号, command + R, 好,首先啊,这里边有一个新特性页面,当我这个程序是第一次安装、第一次运行、还有呢、就是当这个应用程序更新的时候,我应该去加载这个新特性页面, 然后呢,这…

JPEG图像的DCT(Discrete Cosine Transform)变换公式代码详解

引 言 网络上图像在传输过程中为节省内存空间主要采用jpeg格式。jpeg图属于有损压缩图像的一种。在图像篡改检测过程中&#xff0c;可以利用jpeg图像的单双压缩伪影的不同而判别图像为伪造图并可以定位伪造区域。RGB图像变成jpeg图像过程中涉及从RGB图变成YCbCr图像&#xff0c…

使用离火插件yoloV8数据标注,模型训练

1. 启动 2.相关配置 2.1 data.yaml path: D:/yolo-tool/yaunshen-yolov8/YOLOv8ys/YOLOv8-CUDA10.2/1/datasets/ceshi001 train: images val: images names: [蔡徐坤,篮球] 2.2 cfg.yaml # Ultralytics YOLOv8, GPL-3.0 license # Default training settings and hyp…

物联网行业中通信断线重连现象介绍以及如何实现

01 概述 断线重连是指在计算机网络中&#xff0c;当网络连接遇到异常中断或者断开时&#xff0c;系统会自动尝试重新建立连接&#xff0c;以保证网络通信的连续性和稳定性。这是一种常见的网络通信技术&#xff0c;广泛应用于各种计算机网络场景&#xff0c;包括互联网、局域…

蓝队技能-应急响应篇Web内存马查杀Spring框架型中间件型JVM分析Class提取

知识点&#xff1a; 1、应急响应-Web框架内存马-分析&清除 2、应急响应-Web中间件内存马-分析&清除 注&#xff1a;框架型内存马与中间件内存马只要网站重启后就清除了。 目前Java内存马具体分类&#xff1a; 1、传统Web应用型内存马 Servlet型内存马&#xff1a;…

探索EasyCVR视频融合平台:在视频编解码与转码领域的灵活性优势

随着视频监控技术的飞速发展&#xff0c;各类应用场景对视频数据的处理需求日益复杂多样。从公共安全到智慧城市&#xff0c;再到工业监控&#xff0c;高效、灵活的视频处理能力成为衡量视频融合平台性能的重要标准。在众多解决方案中&#xff0c;EasyCVR视频融合平台凭借其在视…

Java面试题之JVM20问

1、说说 JVM 内存区域 这张图就是一个 JVM 运行时数据图&#xff0c;「紫色区域代表是线程共享的区域」&#xff0c;JAVA 程序在运行的过程中会把他管理的内存划分为若干个不同的数据区域&#xff0c;「每一块儿的数据区域所负责的功能都是不同的&#xff0c;他们也有不同的创建…

Django设计批量导入Excel数据接口(包含图片)

Django设计批量导入Excel数据接口(包含图片) 目录 Django设计批量导入Excel数据接口(包含图片)示例xlsx文件接口详情前端上传FormData后端APIView调用函数 Django 4.2.7 openpyxl 3.1.5示例xlsx文件 接口详情 前端上传FormData …

2-104 基于MATLAB的动态模式分解(Dynamic Mode Decomposition,DMD)

基于MATLAB的动态模式分解&#xff08;Dynamic Mode Decomposition&#xff0c;DMD&#xff09;,从人类步行数据中提取信息.动态模式分解是一种降维算法&#xff0c;在流体力学领域引入的。与提供内部坐标系和相应投影的SVD相似&#xff0c;DMD为您提供随不同时间行为演变的特定…

【架构】前台、中台、后台

文章目录 前台、中台、后台1. 前台&#xff08;Frontend&#xff09;特点&#xff1a;技术栈&#xff1a; 2. 中台&#xff08;Middleware&#xff09;特点&#xff1a;技术栈&#xff1a; 3. 后台&#xff08;Backend&#xff09;特点&#xff1a;技术栈&#xff1a; 示例场景…

PMOS的原理

PMOS&#xff08;金属氧化物半导体场效应晶体管&#xff09;是一种以空穴为主要载流子的场效应管&#xff0c;它的D极&#xff08;漏极&#xff09;、S极&#xff08;源极&#xff09;和G极&#xff08;栅极&#xff09;的工作原理如下&#xff1a; 1. D极&#xff08;漏极&am…

已存在的Python项目使用依赖管理工具UV

1. 文档 uv文档 2. 如何转换 初始化 uv initrequirements.txt转换成pyproject.toml uv add $(cat requirements.txt)删除requirements.txt 如果更新pyproject.toml之后&#xff0c;使用命令 uv sync替换项目环境 如果有库没有加入依赖&#xff0c;自己手动加一下&am…

详解电力物联网通常使用哪些通信规约?

在电力物联网行业中&#xff0c;通信规约是关键的技术之一&#xff0c;用于实现电网设备与控制中心之间的数据通信和信息管理。本篇就为大家简单说明电力物联网通常使用哪些通信规约。 1、IEC 60870-5-101/104 这是由国际电工委员会&#xff08;IEC&#xff09;制定的一系列标…