Broker服务器模块

一.Broker模块介绍

二.Broker模块具体实现

1. 类的成员变量与构造函数

成员变量

  • 事件循环和TCP服务器:

    • muduo::net::EventLoop _baseloop;
    • muduo::net::TcpServer _server; 这些是muduo库提供的核心组件,负责处理网络事件和管理TCP连接。
  • 消息分发和编码:

    • muduo::net::ProtobufDispatcher _dispatcher;
    • muduo::net::ProtobufCodec _codec; 这些是muduo库中用于处理Protocol Buffers消息的工具,_dispatcher负责根据消息类型调用对应的回调函数,_codec用于处理消息的序列化与反序列化。
  • 连接管理器:

    • ConnectionManager _connectionManager; 管理所有的客户端连接,确保在处理消息时能获取到正确的连接信息。
  • 消费者管理器:

    • ConsumerManager _consumerManager; 管理所有的消费者,处理消息的订阅与消费逻辑。
  • 虚拟机:

    • VirtualHost _virtualHost; 在消息队列系统中,虚拟机管理交换机、队列等资源。
  • 线程池:

    • muduo::ThreadPool _threadPool; 用于处理耗时的任务,避免阻塞主线程的事件循环。

构造函数

 Broker(int port, std::string dbname, std::string msgdir): _server(&_baseloop, muduo::net::InetAddress("127.0.0.1", port), "MyBroker", muduo::net::TcpServer::kReusePort),_dispatcher(std::bind(&Broker::onUnknowMessage, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)),_codec(std::make_shared<ProtobufCodec>(std::bind(&ProtobufDispatcher::onProtobufMessage, &_dispatcher, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3))),_connections(std::make_shared<ConnectionManager>()),_consumers(std::make_shared<ConsumerManager>()),_pool(std::make_shared<ThreadPool>(1)),_vhost(std::make_shared<VirtualHost>(virtualHost, dbname, msgdir)){// 1.将所有的消息回调函数注册到_dispatcher中//_dispatcher.registerMessageCallback<msg::OpenChannelReq>(std::bind(&Broker::onOpenChannel,this,std::placeholders::_1,std::placeholders::_2,std::placeholders::_3));_dispatcher.registerMessageCallback<msg::OpenChannelReq>(std::bind(&Broker::onOpenChannel, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));_dispatcher.registerMessageCallback<msg::CloseChannelReq>(std::bind(&Broker::onCloseChannel, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));_dispatcher.registerMessageCallback<msg::DeclareExchangeReq>(std::bind(&Broker::onDeclareExchange, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));_dispatcher.registerMessageCallback<msg::RemoveExchangeReq>(std::bind(&Broker::onRemoveExchange, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));_dispatcher.registerMessageCallback<msg::DeclareQueueReq>(std::bind(&Broker::onDeclareQueue, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));_dispatcher.registerMessageCallback<msg::RemoveQueueReq>(std::bind(&Broker::onRemoveQueueReq, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));_dispatcher.registerMessageCallback<msg::BindReq>(std::bind(&Broker::onBind, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));_dispatcher.registerMessageCallback<msg::UnbindReq>(std::bind(&Broker::onUnBind, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));_dispatcher.registerMessageCallback<msg::BasicPublishReq>(std::bind(&Broker::onBasicPublish, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));_dispatcher.registerMessageCallback<msg::BasicAckReq>(std::bind(&Broker::onBasicAck, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));_dispatcher.registerMessageCallback<msg::BasicSubscribeReq>(std::bind(&Broker::onBasicSubscribe, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));_dispatcher.registerMessageCallback<msg::BasicCancelReq>(std::bind(&Broker::onBasicCancel, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));// 2._server设置连接回调函数和消息回调函数_server.setConnectionCallback(std::bind(&Broker::onConnection, this, std::placeholders::_1));_server.setMessageCallback(std::bind(&ProtobufCodec::onMessage, _codec.get(), std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));}
  • 初始化_server: 创建一个TCP服务器,监听指定的地址,并绑定了连接回调函数和消息回调函数。
  • 初始化_dispatcher_codec: 负责消息的分发和编码处理。
  • 注册消息回调函数: 将各种消息类型的处理函数注册到_dispatcher,确保接收到特定类型的消息时能够调用对应的处理逻辑。

2. 服务器的启动与连接管理

服务器启动

void start() {_server.start();_baseloop->loop();
}
  • _server.start(): 启动TCP服务器,开始监听并接受客户端连接。
  • _baseloop->loop(): 进入事件循环,处理所有的网络事件和定时任务

连接管理

void onConnection(const muduo::net::TcpConnectionPtr& conn) {if (conn->connected()) {// 处理新连接std::shared_ptr<Connection> connection = std::make_shared<Connection>(conn);_connectionManager.addConnection(connection);} else {// 处理连接关闭_connectionManager.removeConnection(conn);}
}
  • 新连接的建立: 当有新的连接建立时,创建一个Connection对象,并将其添加到_connectionManager
  • 连接的关闭: 当连接断开时,从_connectionManager中移除该连接。
  void onUnknowMessage(const muduo::net::TcpConnectionPtr &conn,const MessagePtr &message,muduo::Timestamp receiveTime){LOG_INFO << "unknow message: " << message->GetTypeName();conn->shutdown();}

3.消息回调函数的实现

接收到来自客户端的不同类型的请求时,通过_dispatcher调用对应的消息处理回调函数

打开/关闭信道

拿到连接,然后打开信道即可.

其它消息处理回调函数

拿到连接+打开信道+利用信道调用服务端对应的函数即可.

4.线程池的实现

1. 成员变量

    using Task = std::function<void(void)>;

ThreadPool类的成员变量主要包括以下几部分:

  • 任务队列 (std::vector<Task> _tasks):

    • 用于存储待执行的任务,每个任务都是一个std::function<void(void)>类型的可调用对象。
  • 线程相关 (std::vector<std::thread> _threads):

    • 用于存储线程池中的所有线程,这些线程会循环执行任务队列中的任务。
  • 同步机制 (std::mutex _mutexstd::condition_variable _cond):

    • _mutex用于保护任务队列,防止多个线程同时访问任务队列时出现竞争条件。
    • _cond用于在线程等待和唤醒时进行同步控制,当任务队列中有新任务时,唤醒等待的线程。
  • 线程池状态 (std::atomic<bool> _is_stop):

    • 这是一个原子变量,用于标志线程池是否停止工作。当设置为true时,线程池将停止接受新任务并退出。

2. 构造函数与析构函数

构造函数,用于初始化线程池并启动指定数量的线程。

析构函数,用于停止线程池并等待所有线程退出。

3.push的实现

push函数用于向线程池提交新任务。该函数是模板函数,能够接受任意类型的函数和参数,并将其封装为异步任务。

  • 任务封装:使用std::bind将函数和参数绑定在一起,然后使用std::packaged_task将其封装成异步任务。
  • push到任务队列:将任务添加到任务队列_tasks中。
  • 唤醒线程:通过_cond.notify_one()唤醒一个等待中的线程去执行任务。
  • 返回std::future:返回一个std::future对象,用户可以通过它获取任务的执行结果。

4.entry

  • 等待任务:线程通过_cond.wait等待,直到任务队列中有任务或线程池停止。
  • 任务处理:取出所有任务并执行。通过交换_tasks和临时任务列表tmp,避免频繁加锁解锁。

5.stop

  • 设置停止标志:将_is_stop设置为true,标志着线程池将停止工作。
  • 唤醒所有线程:通过_cond.notify_all()唤醒所有等待中的线程,以便它们可以检查停止标志并退出。
  • 等待线程退出:调用join()等待每个线程退出。

6.线程池全部代码

#pragma once
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <vector>
#include <string>
#include <memory>
#include <atomic>
#include <functional>
#include <future>class ThreadPool
{
public:using ptr = std::shared_ptr<ThreadPool>;ThreadPool(int thread_num = 1){// 初始化线程池,创建thread_num个线程,每个线程执行entry函数_is_stop = false;for (int i = 0; i < thread_num; i++){_threads.emplace_back(&ThreadPool::entry, this);//用这些参数来构造对象}}~ThreadPool(){stop();}using Task = std::function<void(void)>;// 用户传入要执行的函数和参数,push内部封装成packaged_task异步任务,利用lambda生成可调用对象并放入队列中template <typename F, typename... Args>auto push(F &&func, Args &&...args) -> std::future<decltype(func(args...))>{using return_type = decltype(func(args...));auto obj = std::bind(std::forward<F>(func), std::forward<Args>(args)...);auto ptask = std::make_shared<std::packaged_task<return_type()>>(obj);std::future<return_type> future = ptask->get_future();{std::unique_lock<std::mutex> lock(_mutex);// 将可调用对象放入队列_tasks.push_back([ptask](){ (*ptask)(); });// 唤醒一个线程_cond.notify_one();}return future;}void stop(){if (_is_stop == true)return;_is_stop = true;_cond.notify_all();for (auto &t : _threads){t.join();}}private:void entry() // 线程入口函数{while (_is_stop == false){// 一个线程一次将队列中的所有任务都取出,避免频繁加锁解锁std::vector<Task> tmp;{std::unique_lock<std::mutex> lock(_mutex);_cond.wait(lock, [this](){ return !_tasks.empty() || _is_stop; });tmp.swap(_tasks);}for(auto &task:tmp){task();}}}private:// 任务队列std::vector<Task> _tasks;// 同步互斥,锁相关std::mutex _mutex;std::condition_variable _cond;// 一批线程std::vector<std::thread> _threads;// 结束标志std::atomic<bool> _is_stop;
};

三.全部代码

#pragma once#include "connection.hpp"
#include "../include/muduo/net/Buffer.h"
#include "../include/muduo/net/EventLoop.h"
#include "../include/muduo/net/TcpServer.h"
#include "../include/muduo/base/noncopyable.h"
#include "../include/muduo/net/Callbacks.h"
#include "proto/dispatcher.h"
#include "proto/codec.h"
#include "../include/muduo/base/Logging.h"
#include "../include/muduo/base/Mutex.h"
#include <google/protobuf/message.h>
#include <functional>namespace mq
{class Broker{private:const std::string virtualHost = "defaultHost"; // 默认虚拟机名称// 1.muduo库服务器相关// 服务器和事件循环muduo::net::EventLoop _baseloop;muduo::net::TcpServer _server;// Protobuf消息分发器ProtobufDispatcher _dispatcher;// Protobuf协议处理器,解决粘包等问题,提取出一个完整的请求报文,并交给_dispatcher处理mq::ProtobufCodecPtr _codec;// 2.消息队列相关ConnectionManager::ptr _connections; // 连接管理器VirtualHost::ptr _vhost;             // 虚拟机ConsumerManager::ptr _consumers;     // 消费者管理器ThreadPool::ptr _pool;               // 线程池public:Broker(int port, std::string dbname, std::string msgdir): _server(&_baseloop, muduo::net::InetAddress("127.0.0.1", port), "MyBroker", muduo::net::TcpServer::kReusePort),_dispatcher(std::bind(&Broker::onUnknowMessage, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)),_codec(std::make_shared<ProtobufCodec>(std::bind(&ProtobufDispatcher::onProtobufMessage, &_dispatcher, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3))),_connections(std::make_shared<ConnectionManager>()),_consumers(std::make_shared<ConsumerManager>()),_pool(std::make_shared<ThreadPool>(1)),_vhost(std::make_shared<VirtualHost>(virtualHost, dbname, msgdir)){// 1.将所有的消息回调函数注册到_dispatcher中//_dispatcher.registerMessageCallback<msg::OpenChannelReq>(std::bind(&Broker::onOpenChannel,this,std::placeholders::_1,std::placeholders::_2,std::placeholders::_3));_dispatcher.registerMessageCallback<msg::OpenChannelReq>(std::bind(&Broker::onOpenChannel, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));_dispatcher.registerMessageCallback<msg::CloseChannelReq>(std::bind(&Broker::onCloseChannel, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));_dispatcher.registerMessageCallback<msg::DeclareExchangeReq>(std::bind(&Broker::onDeclareExchange, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));_dispatcher.registerMessageCallback<msg::RemoveExchangeReq>(std::bind(&Broker::onRemoveExchange, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));_dispatcher.registerMessageCallback<msg::DeclareQueueReq>(std::bind(&Broker::onDeclareQueue, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));_dispatcher.registerMessageCallback<msg::RemoveQueueReq>(std::bind(&Broker::onRemoveQueueReq, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));_dispatcher.registerMessageCallback<msg::BindReq>(std::bind(&Broker::onBind, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));_dispatcher.registerMessageCallback<msg::UnbindReq>(std::bind(&Broker::onUnBind, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));_dispatcher.registerMessageCallback<msg::BasicPublishReq>(std::bind(&Broker::onBasicPublish, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));_dispatcher.registerMessageCallback<msg::BasicAckReq>(std::bind(&Broker::onBasicAck, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));_dispatcher.registerMessageCallback<msg::BasicSubscribeReq>(std::bind(&Broker::onBasicSubscribe, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));_dispatcher.registerMessageCallback<msg::BasicCancelReq>(std::bind(&Broker::onBasicCancel, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));// 2._server设置连接回调函数和消息回调函数_server.setConnectionCallback(std::bind(&Broker::onConnection, this, std::placeholders::_1));_server.setMessageCallback(std::bind(&ProtobufCodec::onMessage, _codec.get(), std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));}void start(){_server.start();_baseloop.loop();}private:void onConnection(const muduo::net::TcpConnectionPtr &conn){if (conn->connected()){DLOG("new connection from:%s ", conn->peerAddress().toIpPort().c_str());_connections->addConnection(std::make_shared<ChannelManager>(), _vhost, _consumers, conn, _codec, _pool);}else{DLOG("connection from:%s closed", conn->peerAddress().toIpPort().c_str());_connections->removeConnection(conn);}}void onUnknowMessage(const muduo::net::TcpConnectionPtr &conn,const MessagePtr &message,muduo::Timestamp receiveTime){LOG_INFO << "unknow message: " << message->GetTypeName();conn->shutdown();}//-----------------------一系列消息回调函数---------------------------------// 1.打开/关闭信道'void onOpenChannel(const muduo::net::TcpConnectionPtr &conn,const OpenChannelPtr &req,muduo::Timestamp receiveTime){auto connection = _connections->getConnection(conn);if (connection.get() == nullptr){DLOG("onOpenChannel: connection is null");conn->shutdown();return;}connection->openChannel(req);}void onCloseChannel(const muduo::net::TcpConnectionPtr &conn,const CloseChannelReqPtr &req,muduo::Timestamp receiveTime){auto connection = _connections->getConnection(conn);if (connection.get() == nullptr){DLOG("onCloseChannel: connection is null");conn->shutdown();return;}connection->closeChannel(req);}// 2.声明/移除交换机void onDeclareExchange(const muduo::net::TcpConnectionPtr &conn,const DeclareExchangeReqPtr &req,muduo::Timestamp receiveTime){auto connection = _connections->getConnection(conn);if (connection.get() == nullptr){DLOG("onDeclareExchange: connection is null");conn->shutdown();return;}auto channel = connection->getChannel(req->chid());if (channel.get() == nullptr){DLOG("onDeclareExchange: channel is null");conn->shutdown();return;}channel->declareExchange(req);}void onRemoveExchange(const muduo::net::TcpConnectionPtr &conn,const RemoveExchangeReqPtr &req,muduo::Timestamp receiveTime){auto connection = _connections->getConnection(conn);if (connection.get() == nullptr){DLOG("onRemoveExchange: connection is null");conn->shutdown();return;}auto channel = connection->getChannel(req->chid());if (channel.get() == nullptr){DLOG("onRemoveExchange: channel is null");conn->shutdown();return;}channel->removeExchange(req);}// 3.声明/移除队列void onDeclareQueue(const muduo::net::TcpConnectionPtr &conn,const DeclareQueueReqPtr &req,muduo::Timestamp receiveTime){auto connection = _connections->getConnection(conn);if (connection.get() == nullptr){DLOG("onDeclareQueue: connection is null");conn->shutdown();return;}auto channel = connection->getChannel(req->chid());if (channel.get() == nullptr){DLOG("onDeclareQueue: channel is null");conn->shutdown();return;}channel->declareQueue(req);}void onRemoveQueueReq(const muduo::net::TcpConnectionPtr &conn,const RemoveQueueReqPtr &req,muduo::Timestamp receiveTime){auto connection = _connections->getConnection(conn);if (connection.get() == nullptr){DLOG("onRemoveQueueReq: connection is null");conn->shutdown();return;}auto channel = connection->getChannel(req->chid());if (channel.get() == nullptr){DLOG("onRemoveQueueReq: channel is null");conn->shutdown();return;}channel->removeQueue(req);}// 4.绑定/解除绑定void onBind(const muduo::net::TcpConnectionPtr &conn,const BindReqPtr &req,muduo::Timestamp receiveTime){auto connection = _connections->getConnection(conn);if (connection.get() == nullptr){DLOG("onBind: connection is null");conn->shutdown();return;}auto channel = connection->getChannel(req->chid());if (channel.get() == nullptr){DLOG("onBind: channel is null");conn->shutdown();return;}channel->bind(req);}void onUnBind(const muduo::net::TcpConnectionPtr &conn,const UnbindReqPtr &req,muduo::Timestamp receiveTime){auto connection = _connections->getConnection(conn);if (connection.get() == nullptr){DLOG("onUnBind: connection is null");conn->shutdown();return;}auto channel = connection->getChannel(req->chid());if (channel.get() == nullptr){DLOG("onUnBind: channel is null");conn->shutdown();return;}channel->unbind(req);}// 5.发布/确认消息void onBasicPublish(const muduo::net::TcpConnectionPtr &conn,const BasicPublishReqPtr &req,muduo::Timestamp receiveTime){auto connection = _connections->getConnection(conn);if (connection.get() == nullptr){DLOG("onBasicPublish: connection is null");conn->shutdown();return;}auto channel = connection->getChannel(req->chid());if (channel.get() == nullptr){DLOG("onBasicPublish: channel is null");conn->shutdown();return;}channel->basicPublish(req);}void onBasicAck(const muduo::net::TcpConnectionPtr &conn,const BasicAckReqPtr &req,muduo::Timestamp receiveTime){auto connection = _connections->getConnection(conn);if (connection.get() == nullptr){DLOG("onBasicAck: connection is null");conn->shutdown();return;}auto channel = connection->getChannel(req->chid());if (channel.get() == nullptr){DLOG("onBasicAck: channel is null");conn->shutdown();return;}channel->basicAck(req);}// 6.订阅和取消订阅void onBasicSubscribe(const muduo::net::TcpConnectionPtr &conn,const BasicSubscribeReqPtr &req,muduo::Timestamp receiveTime){auto connection = _connections->getConnection(conn);if (connection.get() == nullptr){DLOG("onBasicSubscribe: connection is null");conn->shutdown();return;}auto channel = connection->getChannel(req->chid());if (channel.get() == nullptr){DLOG("onBasicSubscribe: channel is null");conn->shutdown();return;}channel->basicSubscribe(req);}void onBasicCancel(const muduo::net::TcpConnectionPtr &conn,const BasicCancelReqPtr &req,muduo::Timestamp receiveTime){auto connection = _connections->getConnection(conn);if (connection.get() == nullptr){DLOG("onBasicCancel: connection is null");conn->shutdown();return;}auto channel = connection->getChannel(req->chid());if (channel.get() == nullptr){DLOG("onBasicCancel: channel is null");conn->shutdown();return;}channel->basicCancel(req);}};
};

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

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

相关文章

Spring Security 认证源码超详细分析

Spring Security 认证源码超详细分析 认证&#xff08;Authentication&#xff09;是系统确认用户信息的重要途径&#xff0c;用户通过认证之后&#xff0c;系统才能明确用户的身份&#xff0c;进而才可以为该用户分配一定的权限&#xff0c;这个过程也叫授权&#xff08;Auth…

项目实战--Sa-Token详细方案笔记

Sa-Token权限系统设计 一、前言二、认证授权的概念三、Sa-Token简介3.1 Sa-Token使用方式3.2 踢人下线3.3 全局异常处理3.4 二级认证3.5 同端互斥登录3.6 Http Basic/Digest 认证3.6.1 HttpBasic认证3.6.2 Http Digest 认证 四、Sa-Token授权&#xff08;鉴权&#xff09;4.1 权…

详说 类和对象

类怎么定义 类是什么呢&#xff1f;类就是我们上篇文说的命名空间&#xff0c;单独创建一个域&#xff0c;自己有自己的生命空间&#xff0c;那么类怎么定义呢&#xff1f;C规定&#xff0c;假设 stack就是他的类名&#xff0c;那么前面要加个class&#xff0c;换行之后就是他…

汽车乘客热舒适度大挑战,如何利用仿真技术提高汽车环境舒适度

舒适性在人们选择汽车的决定性方面占比越来越重&#xff0c;而汽车乘员舱环境的舒适性是指为乘员提供舒适愉快便利的乘坐环境与条件&#xff0c;包括良好的平顺性、车内的低噪声、适宜的空气环境以及良好的驾驶操作性能。 舒适性 经济性 安全性、动力性 典型的乘员舱热舒适性模…

laravel的队列的使用

laravel队列 laravel的特性&#xff1a;laravel队列可以基于不同的后台存储服务提供统一的api&#xff0c;后台存储服务包括 Redis MySQL等。队列实现了业务解耦&#xff0c;异步处理&#xff0c;错误重试的功能。比如调用第三方api&#xff0c;无法保证api的可靠性&#xff0…

Transformer 与传统模型Informer

Transformer 与传统模型:Informer 如何改变时间序列预测的规则 Transformers 是那些聪明的注意力构建者,它们在机器学习的各个领域掀起了波澜。但在时间序列预测领域,它们才真正大显身手。你可能会问,为什么?想象一下,有一个水晶球,它不仅能看到未来,还能理解导致未来的…

TCP协议 配合 Wireshark 分析数据

在TCP连接中&#xff0c;无论是客户端还是服务端&#xff0c;都有可能成为发送端或接收端&#xff0c;这是因为TCP是一个全双工协议&#xff0c;允许数据在同一连接中双向流动 客户端&#xff08;Client&#xff09;&#xff1a;通常是指主动发起连接请求的一方。例如&#xf…

宠物空气净化器有用吗?为什么养宠家庭要买宠物空气净化器?

身为一个鼻炎患者&#xff0c;却喜欢猫咪&#xff0c;所以毅然决然的养了两只宠物&#xff0c;而且还是长毛猫&#xff0c;不要问为什么鼻炎还买两只猫咪&#xff0c;因为怕一只猫咪孤单&#xff0c;所以养了两只。对于很多人来说&#xff0c;猫咪就像焦虑不安时的精神搭子&…

如何让私域服务赢得用户的心?

私域流量的概念在当今的商业环境中已经变得极为重要&#xff0c;许多品牌和企业都投入大量资源尝试通过各种策略吸引并保留用户。然而&#xff0c;单纯的流量积累并不足以确保商业成功。当面对用户的沉默、缺乏活跃度以及无法变现的困境时&#xff0c;我们必须重新审视私域流量…

语音控制开关的语音识别ic芯片方案

语音控制开关是一种基于语音识别技术的设备&#xff0c;它通过内置的语音识别芯片&#xff0c;将用户的语音指令转化为电信号&#xff0c;从而实现对设备的控制。例如在智能家居设备上的应用&#xff0c;通常需要连接到家庭的Wi-Fi网络上&#xff0c;以便与智能手机或智能音箱等…

Java之初始泛型

1 包装类 在Java中&#xff0c;由于基本类型不是继承自Object&#xff0c;为了在泛型代码中可以支持基本类型&#xff0c;Java给每个基本类型都对应了一个包装类型。 1.1 基本数据类型和对应的包装类 基本数据类型包装类byteByteshortShortintIntegerlongLongfloatFloatdoub…

FaceFormer嘴形同步论文复现

一、项目地址 https://github.com/EvelynFan/FaceFormer 二、复现过程 1、项目环境 系统&#xff1a;Ubuntu 18.04.1 python版本&#xff1a;Python 3.7 使用conda创建一个虚拟环境&#xff0c;安装requirements.txt中所需要的库 2、安装ffmpeg 教程网址&#xff1a;http…

8个Python编程进阶常用技巧!

介绍 Python 炫酷功能&#xff08;例如&#xff0c;变量解包&#xff0c;偏函数&#xff0c;枚举可迭代对象等&#xff09;的文章层出不穷。但是还有很多 Python 的编程小技巧鲜被提及。因此&#xff0c;本文会试着介绍一些其它文章没有提到的小技巧&#xff0c;这些小技巧也是…

Selenium+Python自动化测试环境搭建

1. 什么是Selenium&#xff1f; Selenium主要用于web应用程序的自动化测试&#xff0c;但并不局限于此&#xff0c;它还支持所有基于web的管理任务自动化。 2、selenium 自动化流程如下&#xff1a; 自动化程序调用Selenium 客户端库函数&#xff08;比如点击按钮元素&#xff…

【计算机组成原理】六、总线:3.操作和定时

5.操作和定时 文章目录 5.操作和定时5.1总线传输的四个阶段5.2总线定时5.2.1同步通信5.2.2异步通信5.2.3半同步通信5.2.4分离式通信 2.3按时序控制方式 同步总线异步总线 5.1总线传输的四个阶段 总线周期&#xff1a; 申请分配阶段&#xff1a;由需要使用总线的主模块&#…

计算机组成原理:实验一运算器组成实验

一、实验目的 1.掌握算术逻辑运算加、减、乘、与的工作原理。 2.熟悉简单运算器的数据传送通路。 3.验证实验台运算器的8位加、减、与、直通功能。 4.验证实验台的4位乘4位功能。 5.按给定数据&#xff0c;完成几种指定的算术和逻辑运算。 二、实验电路 图1.1 运算器数据…

约瑟夫环和一元多项式

约瑟夫环 一、问题描述 假设有 n 个人围成一圈&#xff0c;从第一个人开始报数&#xff0c;报数到 m 的人将被淘汰出圈&#xff0c;然后从下一个人开始继续从 1 报数&#xff0c;如此重复&#xff0c;直到最后只剩下一个人。求最后剩下的这个人的编号。 二、问题分析 可…

DDR3详解

1.DDR3简介 DDR3 SDRAM&#xff0c;全称第三代双倍速率同步动态随机存取存储器&#xff0c;简称 DDR3&#xff0c;双倍速率&#xff08;double-data-rate&#xff09;&#xff0c;是指时钟的上升沿和下降沿都发生数据传输&#xff1b;同步&#xff0c;是指DDR3数据的读取写入是…

使用 nuxi build-module 命令构建 Nuxt 模块

title: 使用 nuxi build-module 命令构建 Nuxt 模块 date: 2024/8/31 updated: 2024/8/31 author: cmdragon excerpt: nuxi build-module 命令是构建 Nuxt 模块的核心工具,它将你的模块打包成适合生产环境的格式。通过使用 --stub 选项,你可以在开发过程中加快模块构建速度…

linux Vim的安装和基本使用

Vim 什么是 Vim Vim是一个高度可定制的文本编辑器&#xff0c;源自Unix系统的vi编辑器。它被广泛用于类Unix系统中&#xff0c;包括Linux、Mac OS和Windows平台。Vim特别受到程序员的青睐&#xff0c;因为它提供了丰富的编程功能&#xff0c;如代码补全、编译及错误跳转等。这…