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

仿RabbitMQ实现消息队列客户端

文章目录

  • 客⼾端模块实现
  • 订阅者模块
  • 信道管理模块
  • 异步⼯作线程实现
  • 连接管理模块
  • 生产者客户端
  • 消费者客户端

客⼾端模块实现

在RabbitMQ中,提供服务的是信道,因此在客⼾端的实现中,弱化了Client客⼾端的概念,也就是说在RabbitMQ中并不会向⽤⼾展⽰⽹络通信的概念出来,⽽是以⼀种提供服务的形式来体现。其实现思想类似于普通的功能接⼝封装,⼀个接⼝实现⼀个功能,接⼝内部完成向客⼾端请求的过程,但是对外并不需要体现出客⼾端与服务端通信的概念,⽤⼾需要什么服务就调⽤什么接⼝就⾏。

基于以上的思想,客⼾端的实现共分为四⼤模块:

在这里插入图片描述
基于以上模块,实现⼀个客⼾端的流程也就⽐较简单了

  1. 实例化异步线程对象
  2. 实例化连接对象
  3. 通过连接对象,创建信道
  4. 根据信道获取⾃⼰所需服务
  5. 关闭信道
  6. 关闭连接

订阅者模块

与服务端,并⽆太⼤差别,客⼾端这边虽然订阅者的存在感微弱了很多,但是还是有的,当进⾏队列消息订阅的时候,会伴随着⼀个订阅者对象的创建,⽽这个订阅者对象有以下⼏个作⽤:

  • 描述当前信道订阅了哪个队列的消息。
  • 描述了收到消息后该如何对这条消息进⾏处理
  • 描述收到消息后是否需要进⾏确认回复
    在这里插入图片描述
  1. 订阅者信息:
  • 订阅者标识
  • 订阅队列名
  • 是否⾃动确认标志
  • 回调处理函数(收到消息后该如何处理的回调函数对象)
    using ConsumerCallback = std::function<void(const std::string, const BasicProperties *bp, const std::string)>;struct Consumer {using ptr = std::shared_ptr<Consumer>;std::string tag;    //消费者标识std::string qname;  //消费者订阅的队列名称bool auto_ack;      //自动确认标志ConsumerCallback callback;Consumer(){DLOG("new Consumer: %p", this);}Consumer(const std::string &ctag, const std::string &queue_name,  bool ack_flag, const ConsumerCallback &cb):tag(ctag), qname(queue_name), auto_ack(ack_flag), callback(std::move(cb)) {DLOG("new Consumer: %p", this);}~Consumer() {DLOG("del Consumer: %p", this);}};

信道管理模块

同样的,客⼾端也有信道,其功能与服务端⼏乎⼀致,或者说不管是客⼾端的channel还是服务端的channel都是为了⽤⼾提供具体服务⽽存在的,只不过服务端是为客⼾端的对应请求提供服务,⽽客⼾端的接⼝服务是为了⽤⼾具体需要服务,也可以理解是⽤⼾通过客⼾端channel的接⼝调⽤来向服务端发送对应请求,获取请求的服务。
在这里插入图片描述

  1. 信道信息:
  • a. 信道ID
  • b. 信道关联的⽹络通信连接对象
  • c. protobuf协议处理对象
  • d. 信道关联的消费者
  • e. 请求对应的响应信息队列(这⾥队列使⽤<请求ID,响应>hash表,以便于查找指定的响应)
  • f. 互斥锁&条件变量(⼤部分的请求都是阻塞操作,发送请求后需要等到响应才能继续,但是muduo库的通信是异步的,因此需要我们⾃⼰在收到响应后,通过判断是否是等待的指定响应来进⾏同步)
  1. 信道操作:
  • a. 提供创建信道操作
  • b. 提供删除信道操作
  • c. 提供声明交换机操作(强断⾔-有则OK,没有则创建)
  • d. 提供删除交换机
  • e. 提供创建队列操作(强断⾔-有则OK,没有则创建)
  • f. 提供删除队列操作
  • g. 提供交换机-队列绑定操作
  • h. 提供交换机-队列解除绑定操作
  • i . 提供添加订阅操作
  • j. 提供取消订阅操作
  • k. 提供发布消息操作
  • l. 提供确认消息操作
    typedef std::shared_ptr<google::protobuf::Message> MessagePtr;using ProtobufCodecPtr = std::shared_ptr<ProtobufCodec>;using basicConsumeResponsePtr = std::shared_ptr<basicConsumeResponse>;using basicCommonResponsePtr = std::shared_ptr<basicCommonResponse>;class Channel {public:using ptr = std::shared_ptr<Channel>;Channel(const muduo::net::TcpConnectionPtr& conn, const ProtobufCodecPtr& codec):_cid(UUIDHelper::uuid()), _conn(conn), _codec(codec) {}~Channel() { basicCancel(); }std::string cid() { return _cid; }bool openChannel() {std::string rid = UUIDHelper::uuid();openChannelRequest req; req.set_rid(rid);req.set_cid(_cid);_codec->send(_conn, req);basicCommonResponsePtr resp = waitResponse(rid);return resp->ok();}void closeChannel() {std::string rid = UUIDHelper::uuid();closeChannelRequest req; req.set_rid(rid);req.set_cid(_cid);_codec->send(_conn, req);waitResponse(rid);return ;}bool declareExchange(const std::string &name,ExchangeType type, bool durable, bool auto_delete,google::protobuf::Map<std::string, std::string> &args) {//构造一个声明虚拟机的请求对象,std::string rid = UUIDHelper::uuid();declareExchangeRequest req;req.set_rid(rid);req.set_cid(_cid);req.set_exchange_name(name);req.set_exchange_type(type);req.set_durable(durable);req.set_auto_delete(auto_delete);req.mutable_args()->swap(args);//然后向服务器发送请求_codec->send(_conn, req);//等待服务器的响应basicCommonResponsePtr resp = waitResponse(rid);//返回return resp->ok();}void deleteExchange(const std::string &name) {std::string rid = UUIDHelper::uuid();deleteExchangeRequest req;req.set_rid(rid);req.set_cid(_cid);req.set_exchange_name(name);_codec->send(_conn, req);waitResponse(rid);return ;}bool declareQueue(const std::string &qname, bool qdurable, bool qexclusive,bool qauto_delete,google::protobuf::Map<std::string, std::string> &qargs) {std::string rid = UUIDHelper::uuid();declareQueueRequest req;req.set_rid(rid);req.set_cid(_cid);req.set_queue_name(qname);req.set_durable(qdurable);req.set_auto_delete(qauto_delete);req.set_exclusive(qexclusive);req.mutable_args()->swap(qargs);_codec->send(_conn, req);basicCommonResponsePtr resp = waitResponse(rid);return resp->ok();}void deleteQueue(const std::string &qname) {std::string rid = UUIDHelper::uuid();deleteQueueRequest req;req.set_rid(rid);req.set_cid(_cid);req.set_queue_name(qname);_codec->send(_conn, req);waitResponse(rid);return ;}bool queueBind(const std::string &ename, const std::string &qname, const std::string &key) {std::string rid = UUIDHelper::uuid();queueBindRequest req;req.set_rid(rid);req.set_cid(_cid);req.set_exchange_name(ename);req.set_queue_name(qname);req.set_binding_key(key);_codec->send(_conn, req);basicCommonResponsePtr resp = waitResponse(rid);return resp->ok();}void queueUnBind(const std::string &ename, const std::string &qname) {std::string rid = UUIDHelper::uuid();queueUnBindRequest req;req.set_rid(rid);req.set_cid(_cid);req.set_exchange_name(ename);req.set_queue_name(qname);_codec->send(_conn, req);waitResponse(rid);return ;}void basicPublish(const std::string &ename,const BasicProperties *bp,const std::string &body) {std::string rid = UUIDHelper::uuid();basicPublishRequest req;req.set_rid(rid);req.set_cid(_cid);req.set_body(body);req.set_exchange_name(ename);if (bp != nullptr) {req.mutable_properties()->set_id(bp->id());req.mutable_properties()->set_delivery_mode(bp->delivery_mode());req.mutable_properties()->set_routing_key(bp->routing_key());}_codec->send(_conn, req);waitResponse(rid);return ;}void basicAck(const std::string &msgid) {if (_consumer.get() == nullptr) {DLOG("消息确认时,找不到消费者信息!");return ;}std::string rid = UUIDHelper::uuid();basicAckRequest req;req.set_rid(rid);req.set_cid(_cid);req.set_queue_name(_consumer->qname);req.set_message_id(msgid);_codec->send(_conn, req);waitResponse(rid);return;}void basicCancel() {if (_consumer.get() == nullptr) {return ;}std::string rid = UUIDHelper::uuid();basicCancelRequest req;req.set_rid(rid);req.set_cid(_cid);req.set_queue_name(_consumer->qname);req.set_consumer_tag(_consumer->tag);_codec->send(_conn, req);waitResponse(rid);_consumer.reset();return;}bool basicConsume(const std::string &consumer_tag,const std::string &queue_name,bool auto_ack,const ConsumerCallback &cb) {if (_consumer.get() != nullptr) {DLOG("当前信道已订阅其他队列消息!");return false;}std::string rid = UUIDHelper::uuid();basicConsumeRequest req;req.set_rid(rid);req.set_cid(_cid);req.set_queue_name(queue_name);req.set_consumer_tag(consumer_tag);req.set_auto_ack(auto_ack);_codec->send(_conn, req);basicCommonResponsePtr resp =  waitResponse(rid);if (resp->ok() == false) {DLOG("添加订阅失败!");return false;}_consumer = std::make_shared<Consumer>(consumer_tag, queue_name, auto_ack, cb);return true;}public:   //连接收到基础响应后,向hash_map中添加响应void putBasicResponse(const basicCommonResponsePtr& resp) {std::unique_lock<std::mutex> lock(_mutex);_basic_resp.insert(std::make_pair(resp->rid(), resp));_cv.notify_all();}//连接收到消息推送后,需要通过信道找到对应的消费者对象,通过回调函数进行消息处理void consume(const basicConsumeResponsePtr& resp) {if (_consumer.get() == nullptr) {DLOG("消息处理时,未找到订阅者信息!");return;}if (_consumer->tag != resp->consumer_tag()) {DLOG("收到的推送消息中的消费者标识,与当前信道消费者标识不一致!");return ;}_consumer->callback(resp->consumer_tag(), resp->mutable_properties(), resp->body());}private:basicCommonResponsePtr waitResponse(const std::string &rid) {std::unique_lock<std::mutex> lock(_mutex);_cv.wait(lock, [&rid, this](){return _basic_resp.find(rid) != _basic_resp.end();});//while(condition()) _cv.wait();basicCommonResponsePtr basic_resp = _basic_resp[rid];_basic_resp.erase(rid);return basic_resp;}private:std::string _cid;muduo::net::TcpConnectionPtr _conn;ProtobufCodecPtr _codec;Consumer::ptr _consumer;std::mutex _mutex;std::condition_variable _cv;std::unordered_map<std::string, basicCommonResponsePtr> _basic_resp;};
  1. 信道管理:
  • a. 创建信道
  • b. 查询信道
  • c. 删除信道
    class ChannelManager {public:using ptr = std::shared_ptr<ChannelManager>;ChannelManager(){}Channel::ptr create(const muduo::net::TcpConnectionPtr &conn, const ProtobufCodecPtr &codec) {std::unique_lock<std::mutex> lock(_mutex);auto channel = std::make_shared<Channel>(conn, codec);_channels.insert(std::make_pair(channel->cid(), channel));return channel;}void remove(const std::string &cid) {std::unique_lock<std::mutex> lock(_mutex);_channels.erase(cid);}Channel::ptr get(const std::string &cid) {std::unique_lock<std::mutex> lock(_mutex);auto it = _channels.find(cid);if (it == _channels.end()) {return Channel::ptr();}return it->second;}private:std::mutex _mutex;std::unordered_map<std::string, Channel::ptr> _channels;};

异步⼯作线程实现

客⼾端这边存在两个异步⼯作线程,

  • ⼀个是muduo库中客⼾端连接的异步循环线程EventLoopThread
  • ⼀个是当收到消息后进⾏异步处理的⼯作线程池。

这两项都不是以连接为单元进⾏创建的,⽽是创建后,可以⽤以多个连接中,因此单独进⾏封装。

class AsyncWorker {public:using ptr = std::shared_ptr<AsyncWorker>;muduo::net::EventLoopThread loopthread;threadpool pool;};

连接管理模块

在客⼾端这边,RabbitMQ弱化了客⼾端的概念,因为⽤⼾所需的服务都是通过信道来提供的,因此操作思想转换为先创建连接,通过连接创建信道,通过信道提供服务这⼀流程。

这个模块同样是针对muduo库客⼾端连接的⼆次封装,向⽤⼾提供创建channel信道的接⼝,创建信道后,可以通过信道来获取指定服务。

在这里插入图片描述

    class Connection{public:using ptr = std::shared_ptr<Connection>;Connection(const std::string &sip, int sport, const AsyncWorker::ptr &worker): _latch(1), _client(worker->loopthread.startLoop(), muduo::net::InetAddress(sip, sport), "Client"),_dispatcher(std::bind(&Connection::onUnknownMessage, 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))),_worker(worker),_channel_manager(std::make_shared<ChannelManager>()){_dispatcher.registerMessageCallback<basicCommonResponse>(std::bind(&Connection::basicResponse, this,std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));_dispatcher.registerMessageCallback<basicConsumeResponse>(std::bind(&Connection::consumeResponse, this,std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));_client.setMessageCallback(std::bind(&ProtobufCodec::onMessage, _codec.get(),std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));_client.setConnectionCallback(std::bind(&Connection::onConnection, this, std::placeholders::_1));_client.connect();_latch.wait(); // 阻塞等待,直到连接建立成功}Channel::ptr openChannel(){Channel::ptr channel = _channel_manager->create(_conn, _codec);bool ret = channel->openChannel();if (ret == false){DLOG("打开信道失败!");return Channel::ptr();}return channel;}void closeChannel(const Channel::ptr &channel){channel->closeChannel();_channel_manager->remove(channel->cid());}private:void basicResponse(const muduo::net::TcpConnectionPtr &conn, const basicCommonResponsePtr &message, muduo::Timestamp){// 1. 找到信道Channel::ptr channel = _channel_manager->get(message->cid());if (channel.get() == nullptr){DLOG("未找到信道信息!");return;}// 2. 将得到的响应对象,添加到信道的基础响应hash_map中channel->putBasicResponse(message);}void consumeResponse(const muduo::net::TcpConnectionPtr &conn, const basicConsumeResponsePtr &message, muduo::Timestamp){// 1. 找到信道Channel::ptr channel = _channel_manager->get(message->cid());if (channel.get() == nullptr){DLOG("未找到信道信息!");return;}// 2. 封装异步任务(消息处理任务),抛入线程池_worker->pool.push([channel, message](){ channel->consume(message); });}void onUnknownMessage(const muduo::net::TcpConnectionPtr &conn, const MessagePtr &message, muduo::Timestamp){LOG_INFO << "onUnknownMessage: " << message->GetTypeName();conn->shutdown();}void onConnection(const muduo::net::TcpConnectionPtr &conn){if (conn->connected()){_latch.countDown(); // 唤醒主线程中的阻塞_conn = conn;}else{// 连接关闭时的操作_conn.reset();}}private:muduo::CountDownLatch _latch;       // 实现同步的muduo::net::TcpConnectionPtr _conn; // 客户端对应的连接muduo::net::TcpClient _client;      // 客户端ProtobufDispatcher _dispatcher;     // 请求分发器ProtobufCodecPtr _codec;            // 协议处理器AsyncWorker::ptr _worker;ChannelManager::ptr _channel_manager;};

生产者客户端

publish_client.cc

  1. 实例化异步工作线程对象
  2. 实例化连接对象
  3. 通过连接创建信道
  4. 通过信道提供的服务完成所需
  5. 循环向交换机发布消息
  6. 关闭信道
#include "connection.hpp"int main()
{//1. 实例化异步工作线程对象nzq::AsyncWorker::ptr awp = std::make_shared<nzq::AsyncWorker>();//2. 实例化连接对象nzq::Connection::ptr conn = std::make_shared<nzq::Connection>("127.0.0.1",8085,awp);//3. 通过连接创建信道nzq::Channel::ptr channel = conn->openChannel();//4. 通过信道提供的服务完成所需//  1. 声明一个交换机exchange1, 交换机类型为广播模式google::protobuf::Map<std::string,std::string> tmp_map;channel->declareExchange("exchange1", nzq::ExchangeType::TOPIC, true, false, tmp_map);//  2. 声明一个队列queue1channel->declareQueue("queue1", true, false, false, tmp_map);//  3. 声明一个队列queue2channel->declareQueue("queue2", true, false, false, tmp_map);//  4. 绑定queue1-exchange1,且binding_key设置为queue1channel->queueBind("exchange1", "queue1", "queue1");//  5. 绑定queue2-exchange1,且binding_key设置为news.music.#channel->queueBind("exchange1", "queue2", "news.music.#");//5. 循环向交换机发布消息for (int i = 0; i < 10; i++) {nzq::BasicProperties bp;bp.set_id(nzq::UUIDHelper::uuid());bp.set_delivery_mode(nzq::DeliveryMode::DURABLE);bp.set_routing_key("news.music.pop");channel->basicPublish("exchange1", &bp, "Hello World-" + std::to_string(i));}nzq::BasicProperties bp;bp.set_id(nzq::UUIDHelper::uuid());bp.set_delivery_mode(nzq::DeliveryMode::DURABLE);bp.set_routing_key("news.music.sport");channel->basicPublish("exchange1", &bp, "Hello linux");bp.set_routing_key("news.sport");channel->basicPublish("exchange1", &bp, "Hello chileme?");//6. 关闭信道conn->closeChannel(channel);return 0;return 0;
}

消费者客户端

同生产者客户端,如何消费需要自己设置,下面只是其中一种

void cb(nzq::Channel::ptr &channel, const std::string consumer_tag, const nzq::BasicProperties *bp, const std::string &body)
{std::cout << consumer_tag << "消费了消息:" << body << std::endl;channel->basicAck(bp->id());
}
int main(int argc, char *argv[])
{if (argc != 2) {std::cout << "usage: ./consume_client queue1\n";return -1;}//1. 实例化异步工作线程对象nzq::AsyncWorker::ptr awp = std::make_shared<nzq::AsyncWorker>();//2. 实例化连接对象nzq::Connection::ptr conn = std::make_shared<nzq::Connection>("127.0.0.1", 8085, awp);//3. 通过连接创建信道nzq::Channel::ptr channel = conn->openChannel();//4. 通过信道提供的服务完成所需//  1. 声明一个交换机exchange1, 交换机类型为广播模式google::protobuf::Map<std::string, std::string> tmp_map;channel->declareExchange("exchange1", nzq::ExchangeType::TOPIC, true, false, tmp_map);//  2. 声明一个队列queue1channel->declareQueue("queue1", true, false, false, tmp_map);//  3. 声明一个队列queue2channel->declareQueue("queue2", true, false, false, tmp_map);//  4. 绑定queue1-exchange1,且binding_key设置为queue1channel->queueBind("exchange1", "queue1", "queue1");//  5. 绑定queue2-exchange1,且binding_key设置为news.music.#channel->queueBind("exchange1", "queue2", "news.music.#");auto functor = std::bind(cb, channel, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);channel->basicConsume("consumer1", argv[1], false, functor);while(1) std::this_thread::sleep_for(std::chrono::seconds(3));conn->closeChannel(channel);return 0;
}

相关文章:

仿RabbitMQ实现消息队列客户端

文章目录 客⼾端模块实现订阅者模块信道管理模块异步⼯作线程实现连接管理模块生产者客户端消费者客户端 客⼾端模块实现 在RabbitMQ中&#xff0c;提供服务的是信道&#xff0c;因此在客⼾端的实现中&#xff0c;弱化了Client客⼾端的概念&#xff0c;也就是说在RabbitMQ中并…...

CSS | 面试题:你知道几种移动端适配方案?

目录 一、自适应和响应式 二、为什么要做移动端适配&#xff1f; 三、当前流行的几种适配方案 (1) 方案一&#xff1a;百分比设置&#xff08;不推荐&#xff09; (2) 方案二&#xff1a;rem 动态设置 font-size px 与 rem 的单位换算 手动换算 less/scss函数 webpac…...

【web安全】——XSS漏洞

1.XSS漏洞基础 1.1.漏洞成因 XSS(Cross-site scripting)被称为跨站脚本攻击&#xff0c;由于与层叠样式表的缩写一样&#xff0c;因此被缩写为XSS.XSS漏洞形成的原因是网站/程序对前端用户的输入过滤不严格&#xff0c;导致攻击者可以将恶意的is/html代码注入到网页中&#x…...

JAVA基础语法 Day11

一、Set集合 Set特点&#xff1a;无序&#xff08;添加数据的顺序和获取出的数据顺序不一致&#xff09;&#xff0c;不重复&#xff0c;无索引 public class demo1 {public static void main(String[] args) {//1.创建一个集合//HashSet特点&#xff1a;无序&#xff0c;不重…...

知识图谱入门——7:阶段案例:使用 Protégé、Jupyter Notebook 中的 spaCy 和 Neo4j Desktop 搭建知识图谱

在 Windows 环境中结合使用 Protg、Jupyter Notebook 中的 spaCy 和 Neo4j Desktop&#xff0c;可以高效地实现从自然语言处理&#xff08;NLP&#xff09;到知识图谱构建的全过程。本案例将详细论述环境配置、步骤实现以及一些扩展和不足之处。 文章目录 1. 环境准备1.1 Neo4j…...

【AIGC】VoiceControl for ChatGPT指南:轻松开启ChatGPT语音对话模式

博客主页&#xff1a; [小ᶻZ࿆] 本文专栏: AIGC | ChatGPT 文章目录 &#x1f4af;前言&#x1f4af;安装VoiceControl for ChatGPT插件&#x1f4af;如何使用VoiceControl for ChatGPT进行语音输入VoiceControl for ChatGPT快捷键注意点 &#x1f4af;VoiceControl for C…...

基于SpringCloud的微服务架构下安全开发运维准则

为什么要进行安全设计 微服务架构进行安全设计的原因主要包括以下几点&#xff1a; 提高数据保护&#xff1a;微服务架构中&#xff0c;服务间通信频繁&#xff0c;涉及到大量敏感数据的交换。安全设计可以确保数据在传输和存储过程中的安全性&#xff0c;防止数据泄露和篡改。…...

vue的图片显示

通过参数 调用方法 进行显示图片 方法一: 方法二:...

深度学习06:线性回归模型

线性回归&#xff1a;从理论到实现 1. 什么是线性回归&#xff1f; 线性回归是一种用于预测因变量&#xff08;目标值&#xff09;和自变量&#xff08;特征值&#xff09;之间关系的基本模型。它假设目标值&#xff08;y&#xff09;是特征值&#xff08;x&#xff09;的线性…...

Angular ng-state script 元素的生成机制介绍

ng-state 的生成过程是在 Angular SSR 中非常关键的部分。为了让客户端能够接管服务器渲染的页面状态&#xff0c;Angular 在服务器端需要将应用的当前状态保存下来&#xff0c;并将其嵌入到返回的 HTML 中。这样&#xff0c;客户端在接管时就可以直接使用这些状态&#xff0c;…...

小程序-全局数据共享

目录 1.什么是全局数据共享 2. 小程序中的全局数据共享方案 MboX 1. 安装 MobX 相关的包 2. 创建 MobX 的 Store 实例 3. 将 Store 中的成员绑定到页面中 4. 在页面上使用 Store 中的成员 5. 将 Store 中的成员绑定到组件中 6. 在组件中使用 Store 中的成员 1.什么是全…...

vSAN01:vSAN简介、安装、磁盘组、内部架构与调用关系

目录 传统的共享存储vSAN存储OSA的系统要求vSAN安装vSAN集群vSAN skyline healthvSAN与HA磁盘组混合磁盘架构全闪磁盘架构 vSAN对象vSAN内部架构 传统的共享存储 通过隔离的存储网络使得不同的ESXi主机访问独立的存储设备。需要前期投入较高的资金单独采购存储、网络可以单独规…...

Apache NiFi最全面试题及参考答案

目录 解释什么是Apache NiFi以及它的主要用途。 NiFi 的数据处理流程是怎样的? NiFi 的架构包括哪些组件? 解释 NiFi 的 “FlowFile” 概念及其组成部分。 NiFi 的 “Processor” 是什么?有哪些类型? 如何在 NiFi 中创建一个新的数据流? NiFi 的 “Connection” 有…...

基于Docker部署最新版本SkyWalking【10.1.0版本】

文章目录 前言前置条件一、创建Docker 网络二、部署 SkyWalking OAP 服务器三 部署 SkyWalking UI四 查看日志4.1. 查看 SkyWalking OAP 日志4.2. 查看 SkyWalking UI 日志 五 停止并删除容器结论 前言 由于本地的 JDK 版本与 SkyWalking 对应的 JDK 版本不一致&#xff0c;为…...

如何在 Ubuntu 18.04 上使用 LEMP 安装 WordPress

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站。 简介 WordPress 是互联网上最流行的 CMS&#xff08;内容管理系统&#xff09;。它允许您在 MySQL 后端和 PHP 处理的基础上轻松设置灵…...

shadcn-vue 快速入门(2)

components.json 关于项目配置 components.json 文件保存了项目的配置信息。 我们使用该文件了解项目的基本设定&#xff0c;并生成定制化的组件以适应项目需求。 注意&#xff1a;components.json 文件是可选的&#xff0c;仅在使用 CLI 向项目添加组件时才需要。如果使用复…...

Oracle数据恢复—异常断电导致Oracle数据库报错的数据恢复案例

Oracle数据库故障&#xff1a; 机房异常断电后&#xff0c;Oracle数据库启库报错&#xff1a;“system01.dbf需要更多的恢复来保持一致性&#xff0c;数据库无法打开”。数据库没有备份&#xff0c;归档日志不连续。用户方提供了Oracle数据库的在线文件&#xff0c;需要恢复zxf…...

数据结构-4.1.特殊矩阵的压缩存储

一.一维数组的存储结构&#xff1a; 1.知道一维数组的起始地址&#xff0c;就可以求出任意下标对应的元素所在的地址&#xff1b; 2.注&#xff1a;如果数组下标从1开始&#xff0c;上述公式的i就要改为i-1&#xff1b; 3.数组里的元素类型相同&#xff0c;因此所占空间也相同…...

Hive数仓操作(十四)

一、Hive的DDL语句 在 Hive 中&#xff0c;DDL&#xff08;数据定义语言&#xff09;语句用于数据库和表的创建、修改、删除等操作。以下是一些重要的 DDL 语句&#xff1a; 1. 创建数据库和表 创建数据库 CREATE DATABASE IF NOT EXISTS database_name;创建表 CREATE TABLE …...

SpringBoot技术:实现古典舞在线交流平台的秘诀

摘 要 随着互联网技术的发展&#xff0c;各类网站应运而生&#xff0c;网站具有新颖、展现全面的特点。因此&#xff0c;为了满足用户古典舞在线交流的需求&#xff0c;特开发了本古典舞在线交流平台。 本古典舞在线交流平台应用Java技术&#xff0c;MYSQL数据库存储数据&#…...

【杂谈】-递归进化:人工智能的自我改进与监管挑战

递归进化&#xff1a;人工智能的自我改进与监管挑战 文章目录 递归进化&#xff1a;人工智能的自我改进与监管挑战1、自我改进型人工智能的崛起2、人工智能如何挑战人类监管&#xff1f;3、确保人工智能受控的策略4、人类在人工智能发展中的角色5、平衡自主性与控制力6、总结与…...

React Native 导航系统实战(React Navigation)

导航系统实战&#xff08;React Navigation&#xff09; React Navigation 是 React Native 应用中最常用的导航库之一&#xff0c;它提供了多种导航模式&#xff0c;如堆栈导航&#xff08;Stack Navigator&#xff09;、标签导航&#xff08;Tab Navigator&#xff09;和抽屉…...

2025年能源电力系统与流体力学国际会议 (EPSFD 2025)

2025年能源电力系统与流体力学国际会议&#xff08;EPSFD 2025&#xff09;将于本年度在美丽的杭州盛大召开。作为全球能源、电力系统以及流体力学领域的顶级盛会&#xff0c;EPSFD 2025旨在为来自世界各地的科学家、工程师和研究人员提供一个展示最新研究成果、分享实践经验及…...

蓝桥杯 2024 15届国赛 A组 儿童节快乐

P10576 [蓝桥杯 2024 国 A] 儿童节快乐 题目描述 五彩斑斓的气球在蓝天下悠然飘荡&#xff0c;轻快的音乐在耳边持续回荡&#xff0c;小朋友们手牵着手一同畅快欢笑。在这样一片安乐祥和的氛围下&#xff0c;六一来了。 今天是六一儿童节&#xff0c;小蓝老师为了让大家在节…...

深入理解JavaScript设计模式之单例模式

目录 什么是单例模式为什么需要单例模式常见应用场景包括 单例模式实现透明单例模式实现不透明单例模式用代理实现单例模式javaScript中的单例模式使用命名空间使用闭包封装私有变量 惰性单例通用的惰性单例 结语 什么是单例模式 单例模式&#xff08;Singleton Pattern&#…...

Python实现prophet 理论及参数优化

文章目录 Prophet理论及模型参数介绍Python代码完整实现prophet 添加外部数据进行模型优化 之前初步学习prophet的时候&#xff0c;写过一篇简单实现&#xff0c;后期随着对该模型的深入研究&#xff0c;本次记录涉及到prophet 的公式以及参数调优&#xff0c;从公式可以更直观…...

[Java恶补day16] 238.除自身以外数组的乘积

给你一个整数数组 nums&#xff0c;返回 数组 answer &#xff0c;其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。 题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。 请 不要使用除法&#xff0c;且在 O(n) 时间复杂度…...

Element Plus 表单(el-form)中关于正整数输入的校验规则

目录 1 单个正整数输入1.1 模板1.2 校验规则 2 两个正整数输入&#xff08;联动&#xff09;2.1 模板2.2 校验规则2.3 CSS 1 单个正整数输入 1.1 模板 <el-formref"formRef":model"formData":rules"formRules"label-width"150px"…...

Unsafe Fileupload篇补充-木马的详细教程与木马分享(中国蚁剑方式)

在之前的皮卡丘靶场第九期Unsafe Fileupload篇中我们学习了木马的原理并且学了一个简单的木马文件 本期内容是为了更好的为大家解释木马&#xff08;服务器方面的&#xff09;的原理&#xff0c;连接&#xff0c;以及各种木马及连接工具的分享 文件木马&#xff1a;https://w…...

【7色560页】职场可视化逻辑图高级数据分析PPT模版

7种色调职场工作汇报PPT&#xff0c;橙蓝、黑红、红蓝、蓝橙灰、浅蓝、浅绿、深蓝七种色调模版 【7色560页】职场可视化逻辑图高级数据分析PPT模版&#xff1a;职场可视化逻辑图分析PPT模版https://pan.quark.cn/s/78aeabbd92d1...