Linux下编译Eclipse Paho库采用MQTT协议连接MQTT服务器

news2024/10/10 7:40:27

一、Eclipse Paho介绍

Eclipse Paho 是一个开源项目,由 Eclipse Foundation 主持,提供可靠的开源实现来处理 MQTT(Message Queuing Telemetry Transport)协议以及其他与物联网 (IoT) 相关的协议。MQTT 是一种轻量级的发布/订阅消息传输协议,专为具有低带宽和不可靠网络连接的设备设计。Paho 提供了多种语言的客户端库,使得开发者可以在各种平台上开发基于 MQTT 协议的应用程序。

1.1 主要特点

  1. 跨平台支持:Paho 支持多种操作系统和硬件平台,包括Windows、Linux、macOS 以及嵌入式系统。

  2. 多语言实现:Paho 客户端库提供了多种编程语言的选择,如 C、C++、Java 和 Python 等。

  3. 可靠性:Paho 能够在各种网络条件下可靠地工作,包括高延迟、丢包和间歇性连接等。

  4. 安全性:Paho 支持 TLS/SSL 加密通信,以保证数据的安全传输。

  5. 灵活性:除了基本的 MQTT 协议实现之外,Paho 还允许扩展和定制以适应特定的需求。

1.2 Eclipse Paho MQTT C客户端库特点

Eclipse Paho MQTT支持多种语言,其中的C客户端库是一个用于实现MQTT协议客户端的开源C语言库。

  1. 跨平台支持:该库设计为可移植的,支持多种操作系统和硬件平台,包括Linux、Windows、MacOS以及嵌入式系统。
  2. 易于集成:库的设计使得它易于集成到现有的C或C++项目中,为开发者提供了简单而强大的API来构建MQTT客户端。
  3. 灵活的连接选项:支持TLS/SSL加密的MQTT连接,提供安全的通信通道。同时,支持QoS(服务质量)级别0(最多一次)、1(至少一次)和2(仅一次)的消息传递,确保消息传递的可靠性。
  4. 异步操作:大多数库操作都是异步的,允许应用程序在等待网络响应时继续执行其他任务,提高了应用程序的响应性和效率。
  5. 客户端和服务器消息处理:库支持客户端到服务器的消息发布(PUBLISH)以及从服务器到客户端的消息订阅(SUBSCRIBE)和接收(RECEIVE)。
  6. 持久会话和遗嘱消息:支持持久会话,即使客户端断开连接也能保持订阅和QoS状态。同时,可以设置遗嘱消息,在客户端异常断开时发送特定消息。
  7. 回调函数:通过提供回调函数来处理连接、断开连接、消息接收等事件,使得事件处理逻辑更加灵活。

二、Eclipse Paho源码下载

官网地址:https://projects.eclipse.org/projects/iot.paho/downloads

image-20240828114024524

库的下载地址:https://github.com/eclipse/paho.mqtt.c/releases

在页面上可以看到源码下载和编译好的库文件下载。

提供了Linux下、Windows下编译好的库文件,可以直接使用。 如果你现在嵌入式平台上、其他平台上使用,那需要自己下载源码进行编译,使用。

image-20240828114346872

三、编译Eclipse Paho库文件

3.1 下载编译openSSL

如果要支持ssl加密的支持,那就需要先编译安装 openSSL。

下载地址:https://codeload.github.com/openssl/openssl/tar.gz/refs/tags/OpenSSL_1_1_1g

编译步骤:

1、解压缩

tar zxf openssl-OpenSSL_1_1_1g.tar.gz

2、进入目录,并配置输出目录和编译器

cd openssl-OpenSSL_1_1_1g/
./config no-asm shared no-async --prefix=`pwd`/ssl_result 

如果是交叉编译器,可以指定 --cross-compile-prefix=arm-linux-

image-20240828135043773

3、执行下面命令,删除Makefile文件的 -m64

(如果指定了交叉编译器)

sed -i 's/-m64//' Makefile

执行后,可以避免出现这个编译错误:arm-linux-: error: unrecognized command line option ‘-m64’

4、编译、安装

make && make install

成功编译后,在openssl-OpenSSL_1_1_1g/目录会生成一个ssl_result目录,可以看到里面生成的库.

编译过程中。

image-20240828135118170

编译完成。

image-20240828135559999

3.2 paho.mqtt.c 编译

当前下载的是 paho.mqtt.c-1.3.13.tar.gz

下载地址:https://github.com/eclipse/paho.mqtt.c/archive/refs/tags/v1.3.13.tar.gz

wget https://github.com/eclipse/paho.mqtt.c/archive/refs/tags/v1.3.13.tar.gz

image-20240828135713529

编译步骤:

1、解压缩,创建要安装目录paho.mqtt.c_result

tar zxf paho.mqtt.c-1.3.13.tar.gz

mkdir paho.mqtt.c_result/bin -p
mkdir paho.mqtt.c_result/include -p
mkdir paho.mqtt.c_result/lib -p
mkdir paho.mqtt.c_result/share/man/man1 -p

2、进入目录,交叉编译

cd paho.mqtt.c-1.3.13/
make  CC=gcc CFLAGS:="-I `pwd`/../ssl_result/include" LDFLAGS:="-L `pwd`/../ssl_result/lib"

编译过程中:

image-20240828135950732

编译完成:

image-20240828140017727

如果是交叉编译需要指定交叉编译器完整名字:

cd paho.mqtt.c-1.3.13/
make  CC=arm-linux-gcc CFLAGS:="-I `pwd`/../ssl_result/include" LDFLAGS:="-L `pwd`/../ssl_result/lib"

参数介绍:

CFLAGS:=-I `pwd`//ssl_result/include”:指定前面编译的 openssl 的头文件;
LDFLAGS:=-L `pwd`//ssl_result/lib”:指定前面编译的 openssl 的库文件路径;

3、make install,安装编译结果

make install prefix=`pwd`/../paho.mqtt.c_result 
prefix=`pwd`//paho.mqtt.c_result :指定安装目录路径;

编译完成后,会生成目录。

3.3 paho.mqtt.cpp 编译

当前下载的是paho.mqtt.cpp-1.3.2.tar.gz

下载地址:https://github.com/eclipse/paho.mqtt.cpp/archive/refs/tags/v1.3.2.tar.gz

如果你下载的版本跟我的一样,可以使用下面的脚本进行编译。

编译之前,先创建一个脚本文件,名字为paho.mqtt.cpp_install,将下面代码粘贴进去保存。

#! /bin/sh
RESULT_DIR=$(pwd)/result_dir
RESULT_SSL=${RESULT_DIR}/ssl_result
RESULT_MQTT_C=${RESULT_DIR}/paho.mqtt.c_result
RESULT_MQTT_CPP=${RESULT_DIR}/paho.mqtt.cpp_result
CROSSS_COMPILE_TOOL=arm-linux-   #如果你是需要交叉编译

编译步骤:

1、解压缩

tar zxf paho.mqtt.cpp-1.3.2.tar.gz 

2、进入目录

cd paho.mqtt.cpp-1.3.2/

3、执行 cmake

mkdir build_arm 
cd build_arm

编译不需要 openssl 的库

cmake ..  -DCMAKE_CXX_COMPILER=${CROSSS_COMPILE_TOOL}g++ \
-DCMAKE_INSTALL_PREFIX=${RESULT_MQTT_CPP} \
-DPAHO_MQTT_C_LIBRARIES=${RESULT_MQTT_C}/lib/libpaho-mqtt3a.so \
-DPAHO_MQTT_C_INCLUDE_DIRS=${RESULT_MQTT_C}/include \
-DPAHO_WITH_SSL=OFF \
-DCMAKE_CXX_FLAGS="-std=gnu++11 -mcpu=cortex-a53"

说明:如果你在PC机编译目标是PC机本身使用,-mcpu=cortex-a53 就不用写。 如果交叉编译的目标是嵌入式芯片,就如实写构架。

编译带有 openssl 的库

cmake ..  -DCMAKE_CXX_COMPILER=${CROSSS_COMPILE_TOOL}g++ \
-DCMAKE_INSTALL_PREFIX=${RESULT_MQTT_CPP} \
-DPAHO_MQTT_C_LIBRARIES=${RESULT_MQTT_C}/lib/libpaho-mqtt3a.so \
-DPAHO_MQTT_C_INCLUDE_DIRS=${RESULT_MQTT_C}/include \
-DOPENSSL_SSL_LIBRARY=${RESULT_SSL}/lib/libssl.so  \
-DOPENSSL_INCLUDE_DIR=${RESULT_SSL}/include  \
-DOPENSSL_CRYPTO_LIBRARY=${RESULT_SSL}/lib/libcrypto.so \
-DCMAKE_CXX_FLAGS="-std=gnu++11 -mcpu=cortex-a53"

说明:如果你在PC机编译目标是PC机本身使用,-mcpu=cortex-a53 就不用写。 如果交叉编译的目标是嵌入式芯片,就如实写构架。

4、编译

make && make install

5、将下载的源码包 paho.mqtt.cpp-1.3.2.tar.gz 和 上面保存的脚本paho.mqtt.cpp_install 放到同一目录,并且将前面编译好的openssl库、paho.mqtt.c库放在脚本指定的结果目录,当前是放到 result_dir 目录的。

6、执行./paho.mqtt.cpp_install.sh 编译,编译完成后,在result_dir目录下会生成一个名为paho.mqtt.cpp_result的目录。

四、代码案例

4.1 订阅—async_subscribe.cpp

这是使用了 libpaho-mqttpp3.so 进行订阅消息的源码,源码路径在源码的这个路径:paho.mqtt.cpp-1.3.2/src/samples/async_subscribe.cpp,只更改了服务器地址。

完整代码如下:

#include <iostream>
#include <cstdlib>
#include <string>
#include <cstring>
#include <cctype>
#include <thread>
#include <chrono>
#include "mqtt/async_client.h"

const std::string SERVER_ADDRESS("117.78.5.125:1883");
const std::string CLIENT_ID("paho_cpp_async_subcribe");
const std::string TOPIC("hello");

const int	QOS = 1;
const int	N_RETRY_ATTEMPTS = 5;

/

// Callbacks for the success or failures of requested actions.
// This could be used to initiate further action, but here we just log the
// results to the console.

class action_listener : public virtual mqtt::iaction_listener
{
	std::string name_;

	void on_failure(const mqtt::token& tok) override {
		std::cout << name_ << " failure";
		if (tok.get_message_id() != 0)
			std::cout << " for token: [" << tok.get_message_id() << "]" << std::endl;
		std::cout << std::endl;
	}

	void on_success(const mqtt::token& tok) override {
		std::cout << name_ << " success";
		if (tok.get_message_id() != 0)
			std::cout << " for token: [" << tok.get_message_id() << "]" << std::endl;
		auto top = tok.get_topics();
		if (top && !top->empty())
			std::cout << "\ttoken topic: '" << (*top)[0] << "', ..." << std::endl;
		std::cout << std::endl;
	}

public:
	action_listener(const std::string& name) : name_(name) {}
};

/

/**
 * Local callback & listener class for use with the client connection.
 * This is primarily intended to receive messages, but it will also monitor
 * the connection to the broker. If the connection is lost, it will attempt
 * to restore the connection and re-subscribe to the topic.
 */
class callback : public virtual mqtt::callback,
					public virtual mqtt::iaction_listener

{
	// Counter for the number of connection retries
	int nretry_;
	// The MQTT client
	mqtt::async_client& cli_;
	// Options to use if we need to reconnect
	mqtt::connect_options& connOpts_;
	// An action listener to display the result of actions.
	action_listener subListener_;

	// This deomonstrates manually reconnecting to the broker by calling
	// connect() again. This is a possibility for an application that keeps
	// a copy of it's original connect_options, or if the app wants to
	// reconnect with different options.
	// Another way this can be done manually, if using the same options, is
	// to just call the async_client::reconnect() method.
	void reconnect() {
		std::this_thread::sleep_for(std::chrono::milliseconds(2500));
		try {
			cli_.connect(connOpts_, nullptr, *this);
		}
		catch (const mqtt::exception& exc) {
			std::cerr << "Error: " << exc.what() << std::endl;
			exit(1);
		}
	}

	// Re-connection failure
	void on_failure(const mqtt::token& tok) override {
		std::cout << "Connection attempt failed" << std::endl;
		if (++nretry_ > N_RETRY_ATTEMPTS)
			exit(1);
		reconnect();
	}

	// (Re)connection success
	// Either this or connected() can be used for callbacks.
	void on_success(const mqtt::token& tok) override {}

	// (Re)connection success
	void connected(const std::string& cause) override {
		std::cout << "\nConnection success" << std::endl;
		std::cout << "\nSubscribing to topic '" << TOPIC << "'\n"
			<< "\tfor client " << CLIENT_ID
			<< " using QoS" << QOS << "\n"
			<< "\nPress Q<Enter> to quit\n" << std::endl;

		cli_.subscribe(TOPIC, QOS, nullptr, subListener_);
	}

	// Callback for when the connection is lost.
	// This will initiate the attempt to manually reconnect.
	void connection_lost(const std::string& cause) override {
		std::cout << "\nConnection lost" << std::endl;
		if (!cause.empty())
			std::cout << "\tcause: " << cause << std::endl;

		std::cout << "Reconnecting..." << std::endl;
		nretry_ = 0;
		reconnect();
	}

	// Callback for when a message arrives.
	void message_arrived(mqtt::const_message_ptr msg) override {
		std::cout << "Message arrived" << std::endl;
		std::cout << "\ttopic: '" << msg->get_topic() << "'" << std::endl;
		std::cout << "\tpayload: '" << msg->to_string() << "'\n" << std::endl;
	}

	void delivery_complete(mqtt::delivery_token_ptr token) override {}

public:
	callback(mqtt::async_client& cli, mqtt::connect_options& connOpts)
				: nretry_(0), cli_(cli), connOpts_(connOpts), subListener_("Subscription") {}
};

/

int main(int argc, char* argv[])
{
	// A subscriber often wants the server to remember its messages when its
	// disconnected. In that case, it needs a unique ClientID and a
	// non-clean session.

	mqtt::async_client cli(SERVER_ADDRESS, CLIENT_ID);

	mqtt::connect_options connOpts;
	connOpts.set_clean_session(false);

	// Install the callback(s) before connecting.
	callback cb(cli, connOpts);
	cli.set_callback(cb);

	// Start the connection.
	// When completed, the callback will subscribe to topic.

	try {
		std::cout << "Connecting to the MQTT server..." << std::flush;
		cli.connect(connOpts, nullptr, cb);
	}
	catch (const mqtt::exception& exc) {
		std::cerr << "\nERROR: Unable to connect to MQTT server: '"
			<< SERVER_ADDRESS << "'" << exc << std::endl;
		return 1;
	}

	// Just block till user tells us to quit.

	while (std::tolower(std::cin.get()) != 'q')
		;

	// Disconnect

	try {
		std::cout << "\nDisconnecting from the MQTT server..." << std::flush;
		cli.disconnect()->wait();
		std::cout << "OK" << std::endl;
	}
	catch (const mqtt::exception& exc) {
		std::cerr << exc << std::endl;
		return 1;
	}

 	return 0;
}

编译指令:

g++ async_subscribe.cpp -I result_dir/paho.mqtt.cpp_result/include/ -I result_dir/paho.mqtt.c_result/include/ -L result_dir/paho.mqtt.cpp_result/lib/ -L result_dir/paho.mqtt.c_result/lib/ -l paho-mqttpp3 -l paho-mqtt3a 

4.2 发布——async_publish.cpp

这是使用了 libpaho-mqttpp3.so 进行发布消息的源码,源码路径在源码的这个路径:paho.mqtt.cpp-1.3.2/src/samples/async_publish.cpp,只更改了服务器地址。

完整代码如下:

#include <iostream>
#include <cstdlib>
#include <string>
#include <thread>
#include <atomic>
#include <chrono>
#include <cstring>
#include "mqtt/async_client.h"

using namespace std;

const string DFLT_SERVER_ADDRESS	{ "117.78.5.125:1883" };
const string CLIENT_ID				{ "paho_cpp_async_publish" };
const string PERSIST_DIR			{ "./persist" };

const string TOPIC { "hello" };

const char* PAYLOAD1 = "Hello World!";
const char* PAYLOAD2 = "Hi there!";
const char* PAYLOAD3 = "Is anyone listening?";
const char* PAYLOAD4 = "Someone is always listening.";

const char* LWT_PAYLOAD = "Last will and testament.";

const int  QOS = 1;

const auto TIMEOUT = std::chrono::seconds(10);

/

/**
 * A callback class for use with the main MQTT client.
 */
class callback : public virtual mqtt::callback
{
public:
	void connection_lost(const string& cause) override {
		cout << "\nConnection lost" << endl;
		if (!cause.empty())
			cout << "\tcause: " << cause << endl;
	}

	void delivery_complete(mqtt::delivery_token_ptr tok) override {
		cout << "\tDelivery complete for token: "
			<< (tok ? tok->get_message_id() : -1) << endl;
	}
};

/

/**
 * A base action listener.
 */
class action_listener : public virtual mqtt::iaction_listener
{
protected:
	void on_failure(const mqtt::token& tok) override {
		cout << "\tListener failure for token: "
			<< tok.get_message_id() << endl;
	}

	void on_success(const mqtt::token& tok) override {
		cout << "\tListener success for token: "
			<< tok.get_message_id() << endl;
	}
};

/

/**
 * A derived action listener for publish events.
 */
class delivery_action_listener : public action_listener
{
	atomic<bool> done_;

	void on_failure(const mqtt::token& tok) override {
		action_listener::on_failure(tok);
		done_ = true;
	}

	void on_success(const mqtt::token& tok) override {
		action_listener::on_success(tok);
		done_ = true;
	}

public:
	delivery_action_listener() : done_(false) {}
	bool is_done() const { return done_; }
};

/

int main(int argc, char* argv[])
{
	// A client that just publishes normally doesn't need a persistent
	// session or Client ID unless it's using persistence, then the local
	// library requires an ID to identify the persistence files.

	string	address  = (argc > 1) ? string(argv[1]) : DFLT_SERVER_ADDRESS,
			clientID = (argc > 2) ? string(argv[2]) : CLIENT_ID;

	cout << "Initializing for server '" << address << "'..." << endl;
	mqtt::async_client client(address, clientID, PERSIST_DIR);

	callback cb;
	client.set_callback(cb);

	auto connOpts = mqtt::connect_options_builder()
		.clean_session()
		.will(mqtt::message(TOPIC, LWT_PAYLOAD, strlen(LWT_PAYLOAD), QOS, false))
		.finalize();

	cout << "  ...OK" << endl;

	try {
		cout << "\nConnecting..." << endl;
		mqtt::token_ptr conntok = client.connect(connOpts);
		cout << "Waiting for the connection..." << endl;
		conntok->wait();
		cout << "  ...OK" << endl;

		// First use a message pointer.

		cout << "\nSending message..." << endl;
		mqtt::message_ptr pubmsg = mqtt::make_message(TOPIC, PAYLOAD1);
		pubmsg->set_qos(QOS);
		client.publish(pubmsg)->wait_for(TIMEOUT);
		cout << "  ...OK" << endl;

		// Now try with itemized publish.

		cout << "\nSending next message..." << endl;
		mqtt::delivery_token_ptr pubtok;
		pubtok = client.publish(TOPIC, PAYLOAD2, strlen(PAYLOAD2), QOS, false);
		cout << "  ...with token: " << pubtok->get_message_id() << endl;
		cout << "  ...for message with " << pubtok->get_message()->get_payload().size()
			<< " bytes" << endl;
		pubtok->wait_for(TIMEOUT);
		cout << "  ...OK" << endl;

		// Now try with a listener

		cout << "\nSending next message..." << endl;
		action_listener listener;
		pubmsg = mqtt::make_message(TOPIC, PAYLOAD3);
		pubtok = client.publish(pubmsg, nullptr, listener);
		pubtok->wait();
		cout << "  ...OK" << endl;

		// Finally try with a listener, but no token

		cout << "\nSending final message..." << endl;
		delivery_action_listener deliveryListener;
		pubmsg = mqtt::make_message(TOPIC, PAYLOAD4);
		client.publish(pubmsg, nullptr, deliveryListener);

		while (!deliveryListener.is_done()) {
			this_thread::sleep_for(std::chrono::milliseconds(100));
		}
		cout << "OK" << endl;

		// Double check that there are no pending tokens

		auto toks = client.get_pending_delivery_tokens();
		if (!toks.empty())
			cout << "Error: There are pending delivery tokens!" << endl;

		// Disconnect
		cout << "\nDisconnecting..." << endl;
		client.disconnect()->wait();
		cout << "  ...OK" << endl;
	}
	catch (const mqtt::exception& exc) {
		cerr << exc.what() << endl;
		return 1;
	}

 	return 0;
}

编译指令:

g++ async_publish.cpp -I result_dir/paho.mqtt.cpp_result/include/ -I result_dir/paho.mqtt.c_result/include/ -L result_dir/paho.mqtt.cpp_result/lib/ -L result_dir/paho.mqtt.c_result/lib/ -l paho-mqttpp3 -l paho-mqtt3a -o async_publish

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

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

相关文章

SQL进阶技巧:Order by 中 NULLS LAST特性使用?

目录 1 需求描述 2 数据准备 3 问题分析 4 小结 如果觉得本文对你有帮助&#xff0c;想进一步学习SQL语言这门艺术的&#xff0c;那么不妨也可以选择去看看我的博客专栏 &#xff0c;部分内容如下&#xff1a; 数字化建设通关指南 专栏 原价99&#xff0c;现在活动价59…

k8s的安装和部署

配置三台主机&#xff0c;分别禁用各个主机上的swap&#xff0c;并配置解析 systemctl mask swap.target swapoff -a vim /etc/fstab配置这三个主机上的主机以及harbor仓库的主机 所有主机设置docker的资源管理模式为system [rootk8s-master ~]# vim /etc/docker/daemon.json…

k8s、prometheus、grafana数据采集和展示的链路流程

k8s集群中&#xff0c;容器级别的数据采集是由cAdvisor程序实现 cAdvisor # Container Advisor 容器顾问 cAdvisor程序是kubelet组件的一部分。 每个节点&#xff0c;包括master节点&#xff0c;都有一个kubelet系统服务&#xff0c; kukelet负责管理pod和容…

PCB在导出gerber文件时过孔盖油设置方法

目前主要使用两个电路设计软件&#xff0c;一个是Altium Designer 15.0版本&#xff0c;一个是cadence17.2版本。在设计完PCB以后需要导出加工文件发给PCB加工厂进行制板打样&#xff0c;其中需要注意的一点是过孔盖油设置。有的制板厂在提交工艺要求时写上过孔盖油即可&#x…

学习Ultralytics(data)(1)

今天我们来学习一下data文件夹下面的代码 首先有个_init_文件&#xff0c;典型的 Python 包中的 __init__.py 文件&#xff0c;用于导出 Ultralytics YOLO 项目中的一些重要类和函数。它将 base.py, build.py, 和 dataset.py 文件中的内容导入并暴露给外部使用。 看看里面有什…

息肉检测数据集 yolov5 yolov8适用于目标检测训练已经调整为yolo格式可直接训练yolo网络

息肉检测数据集 yolov5 yolov8 适用于目标检测训练 已经调整为yolo格式 可直接训练yolo网络 息肉检测数据集介绍 数据集概述 名称&#xff1a;息肉检测数据集&#xff08;基于某公开的分割数据集调整&#xff09;用途&#xff1a;适用于目标检测任务&#xff0c;特别是内窥镜…

【C/C++】错题记录(七)

题目一 题目二 C在调用函数时&#xff0c;当实参和形参的数据类型不一致时&#xff0c;会发生数据类型转换&#xff01;将低精度转换为高精度时&#xff0c;由编译器隐式完成&#xff1b;将高精度转换为低精度时&#xff0c;必须用强制类型转换运算符&#xff1b; static_cast…

Redis-缓存过期淘汰策略

缓存淘汰策略 生产上redis内存设置为多少 设置为最大内存的 3/4 redis 会占用物理机多少内存 默认大小是 0&#xff0c;64 位系统下表示不限制内存大小&#xff0c;32位系统表示 3G 如何设置修改redis内存大小 config get maxmemory 查看修改方式 配置文件 单位是字节 2.…

读数据工程之道:设计和构建健壮的数据系统04数据工程生命周期(下)

1. 获取 1.1. 在了解数据源、所用源系统的特征以及数据的存储方式之后&#xff0c;你需要收集数据 1.2. 数据工程生命周期的下一阶段是从源系统中获取数据 1.2.1. 源系统和获取代表了数据工程生命周期中最重要的瓶颈 1.2.2. 源系统通常不在你的直接控制范围内&#xff0c;可…

性能测试工具locust —— Python脚本参数化!

1.1.登录用户参数化 在测试过程中&#xff0c;经常会涉及到需要用不同的用户登录操作&#xff0c;可以采用队列的方式&#xff0c;对登录的用户进行参数化。如果数据要保证不重复&#xff0c;则取完不再放回&#xff1b;如可以重复&#xff0c;则取出后再返回队列。 def lo…

算法修炼之路之位运算

目录 一:位运算符及一些常用结论总结 1.给一个数n&#xff0c;确定它的二进制表示中的第x位是0还是1(位数从右向左0开始增加) 2.将一个数n的二进制表示形式的第x位修改成1 3.将一个数n的二进制表示的第x位修改为0 4.提取一个数n的二进制表示中最右侧的1 5.干掉一个数n的…

使用Android studio进行Unit Test中遇到的问题记录

1、模块本身代码运行不起来 提示&#xff1a; Cannot resolve method ‘getVolumes’ in ‘StorageManager’ Cannot resolve method ‘registerListener’ in ‘StorageManager’ Cannot resolve method ‘unregisterListener’ in ‘StorageManager’ 查看Android 源码&…

p20 docker自己commit一个镜像 p21 容器数据卷 p22mysql同步数据(国内镜像被封锁暂时往后放)p23具名挂载和匿名挂载

如何自己commit一个镜像 这里还是先引用一下老师的笔记 关于如何自己commit一个镜像这个问题目前因为从仓库中拉下来的Tomcat里面是没有项目的&#xff0c;所以把webapps.dist里面的拷贝到webapps里面去作为自己的镜像在commit一下 这里用Tomcat举例子首先把镜像拉取下来执…

C语言 | Leetcode C语言题解之第468题验证IP地址

题目&#xff1a; 题解&#xff1a; char * validIPAddress(char * queryIP) {int len strlen(queryIP);if (strchr(queryIP, .)) {// IPv4int last -1;for (int i 0; i < 4; i) {int cur -1;if (i 3) {cur len;} else {char * p strchr(queryIP last 1, .);if (p…

读懂RAG理论到实践

目录 LLM面临的问题RAG数据准备阶段检索生成阶段 RAG实战数据准备阶段数据提取数据清洗补充&#xff1a;去除停用词 分块&#xff08;Chunking&#xff09;分块的方法固定大小分块Sentence splitting&#xff08;句分割&#xff09;递归分割 补充&#xff1a;特殊分块 向量化&a…

Java项目实战II基于Java+Spring Boot+MySQL的作业管理系统设计与实现(源码+数据库+文档)

目录 一、前言 二、技术介绍 三、系统实现 四、文档参考 五、核心代码 六、源码获取 全栈码农以及毕业设计实战开发&#xff0c;CSDN平台Java领域新星创作者&#xff0c;专注于大学生项目实战开发、讲解和毕业答疑辅导。获取源码联系方式请查看文末 一、前言 在教育信息…

Spring Boot学习资源库:微服务架构的加速器

3 系统分析 3.1可行性分析 在进行可行性分析时&#xff0c;我们通常根据软件工程里方法&#xff0c;通过四个方面来进行分析&#xff0c;分别是技术、经济、操作和法律可行性。因此&#xff0c;在基于对目标系统的基本调查和研究后&#xff0c;对提出的基本方案进行可行性分析。…

网站集群批量管理-Ansible(playbook)

1.剧本概述 1. playbook 文件,用于长久保存并且实现批量管理,维护,部署的文件. 类似于脚本存放命令和变量 2. 剧本yaml格式,yaml格式的文件:空格,冒号 2. 区别 ans-playbookans ad-hoc共同点批量管理,使用模块批量管理,使用模块区别重复调用不是很方便,不容易重复场景部署服务…

视频流媒体解决方案,Liveweb国标GB28181视频监控汇聚平台

Liveweb视频监控国标平台指的是基于GB/T 28181协议的视频联网平台&#xff0c;可以对接各种符合国标GB/T 28181协议的视频平台、NVR录像机、网络监控摄像头、执法记录仪、应急布控球、移动单兵、无人机等设备。通过国标平台的联网&#xff0c;方便管理分布在不同地点的视频监控…

股指期货和股指期权有什么区别?

在金融衍生品的世界里&#xff0c;股权类衍生品无疑是其中的佼佼者&#xff0c;而股指期货和股指期权更是其中的佼佼者。尽管它们之间有着千丝万缕的联系&#xff0c;但它们之间的区别同样不容忽视。本文衍生股指君将详细解析股指期货和股指期权的核心区别。 一、交易的东西不…