ROS入门之通信机制及常用API

news2024/11/23 19:03:46

文章目录

  • 前言
  • 一、话题通信
    • 1.话题通信理论
    • 2.话题通信常用API
      • 🍗发布者
        • advertise
        • publish
      • 🍖订阅者
        • subscribe
    • 3.自定义msg
  • 二、服务通信
    • 1.服务通信理论
    • 2.服务通信常用API
      • 🎆服务端
        • advertiseService
      • 🎇客户端
        • serviceClient
        • ros::service::waitForService
        • waitForExistence
        • call
    • 3.自定义srv
  • 三、参数服务器
    • 1.参数服务器通信理论
    • 2.参数服务器常用API
      • 🎉ros::NodeHandle
      • 🎊ros::param
  • 四、其他常用API
    • ros::init
    • ros::NodeHandle
    • ros::spinOnce
    • ros::spin
    • ros::ok
    • shutdown
    • 日志相关
      • ROS_DEBUG
      • ROS_INFO
      • ROS_WARN
      • ROS_ERROR
      • ROS_FATAL
    • 时间相关
      • ros::Time::now
      • ros::Duration
      • ros::Rate
      • createTimer
  • 结束语


  • 💂 个人主页:风间琉璃
  • 🤟 版权: 本文由【风间琉璃】原创、在CSDN首发、需要转载请联系博主
  • 💬 如果文章对你有帮助欢迎关注点赞收藏(一键三连)订阅专栏

前言

提示:这里可以添加本文要记录的大概内容:

ROS的通信架构是ROS的灵魂,也是整个ROS正常运行的关键所在。ROS通信架构包括各种数据的处理,进程的运行,消息的传递等等。ROS中的通信方式有:基于发布/订阅的话题通信基于客户端/服务器的服务通信基于RPC的参数服务器,每个通信方式都有自己的特点。这里主要记录了通信架构的基础通信方式和相关概念、各种通信常用API以及其他常用API。


一、话题通信

话题通信是ROS中使用频率最高的一种通信模式,话题通信是基于发布/订阅模式,一个节点发布信息,另一个节点订阅信息。这种通信方式可实现不同节点之间数据交互的通信模型,用于实时更新、少逻辑处理的数据传送场景。

1.话题通信理论

话题通信模型主要包含三个角色分别是:

  • ROS Master(管理者):负责保管Talker和Listener注册的信息,并匹配话题相同的Talker与Listener,并建立两者之间的连接
  • Talker(发布者):发布信息
  • Listener(订阅者):接收Talker的消息
    在这里插入图片描述
    具体两者之间建立连接和通信的流程可参考这个笔记:话题通信,了解即可,在ROS中我们只需要编程实现发布者和订阅者。

2.话题通信常用API

🍗发布者

advertise

advertise创建一个发布者对象,用于向 ROS 系统中的指定主题发布消息。其函数原型如下所示,这是一个函数模板

template <class M>
Publisher advertise(const std::string& topic, uint32_t queue_size, bool latch = false)
{
  AdvertiseOptions ops;
  ops.template init<M>(topic, queue_size);
  ops.latch = latch;
  return advertise(ops);
}
  • 模板参数 : 表示这个函数可以接受任意类型 M 的消息。在 ROS 中,消息类型通常是由 std_msgs 命名空间中的消息类型之一表示,比如 std_msgs::String、geometry_msgs或者自定义消息类型。
  • topic:指定发布的主题名称
  • queue_size:指定发布队列的大小。
  • latch:(可选参数)如果设置为 true,则发布器会将最新的消息保存并在新的订阅者连接时立即发送给它,否则只在有新消息发布时才发送。
publish

publish函数是ros::Publisher类中的一个成员函数,用于将消息发布到相应的主题。函数原型如下所示

template <typename M>
void publish(const M& message) const
  • 模板参数: 这是消息的类型参数,表示要发布的具体消息类型,例如std_msgs::String。

  • message: 这是要发布的具体消息对象。你需要创建一个相应类型的消息对象并填充它的数据,然后将其作为参数传递给publish函数

下面是一个例子,演示如何使用advertise和publish函数发布一个std_msgs::String类型的消息:

#include <ros/ros.h>
#include <std_msgs/String.h>

int main(int argc, char **argv)
{
    // 初始化 ROS 节点
    ros::init(argc, argv, "example_node");
    ros::NodeHandle nh;

    // 创建一个发布者,发布 std_msgs::String 类型的消息到主题 "example_topic",队列大小为 10
    ros::Publisher pub = nh.advertise<std_msgs::String>("example_topic", 10);

    // 创建并填充一个 std_msgs::String 类型的消息对象
    std_msgs::String msg;
    msg.data = "Hello, ROS!";

    // 设置发送频率为10Hz
    ros::Rate r(10);

    // 循环发送消息
    while (ros::ok())
    {
        // 使用 publish 函数发布消息
        pub.publish(msg);

        // 处理ROS回调函数
        ros::spinOnce();

        // 以指定频率休眠,以满足发送频率
        r.sleep();
    }

    return 0;
}

使用rostopic echo打印输出如下
在这里插入图片描述

🍖订阅者

subscribe

subscribe函数用于创建消息订阅者,有两个作用,其一是创建一个消息订阅者,用于订阅特定主题的消息;其二是当订阅的主题上有新消息时,调用指定的回调函数来处理消息。函数原型如下所示:

template<class M>
Subscriber subscribe(const std::string& topic, uint32_t queue_size, void(*fp)(const boost::shared_ptr<M const>&), const TransportHints& transport_hints = TransportHints())
  • M(模板参数):指定消息的类型。例如,如果你想要订阅std_msgs::String类型的消息,那么M将被实例化为std_msgs::String。
  • topic:指定要订阅的主题名称
  • queue_size:指定订阅者的消息队列大小。队列用于存储尚未被处理的消息,以便稍后进行处理。
  • fp:是一个函数指针,指定一个回调函数该回调函数将在接收到新消息时被调用。回调函数的参数是一个boost::shared_ptr指向常量M类型的消息
  • transport_hints(可选参数):提供关于传输的提示,一般默认即可,例如指定使用的传输协议等。

下面是一个使用subscribe函数的简单示例,配合上面的发布者程序使用:

#include <ros/ros.h>
#include <std_msgs/String.h>

// 回调函数的定义
void messageCallback(const boost::shared_ptr<std_msgs::String const>& msg)
{
    ROS_INFO("Received message: %s", msg->data.c_str());
}

int main(int argc, char** argv)
{
    // 初始化 ROS 节点
    ros::init(argc, argv, "example_node2");
    ros::NodeHandle nh;

    // 创建一个订阅者,订阅 std_msgs::String 类型的消息,回调函数为 messageCallback
    ros::Subscriber sub = nh.subscribe<std_msgs::String>("example_topic", 10, messageCallback);

    // 进入 ROS 主循环
    ros::spin();

    return 0;
}

运行结果如下:
在这里插入图片描述

3.自定义msg

在ROS通信协议中,数据载体是通信中的核心组成部分。ROS通过std_msgs库封装了一些原生的数据类型,如String、Int32、Int64、Char、Bool、Empty等。然而,这些数据类型一般只包含一个data字段,结构相对简单,对于一些复杂的数据传输,比如激光雷达的信息等,其功能上显得有些局限。

在ROS中,可以通过创建自定义消息来定义你自己的消息类型。自定义消息允许你在ROS系统中使用特定的数据结构来进行通信。以下是如何创建和使用自定义消息的一般步骤:

①创建自定义消息msg文件

功能包下新建 msg 目录,添加文件 一个.msg该文件包含自定义消息的定义。例如,假设你想创建一个名为MyCustomMsg的自定义消息,文件名可以是MyCustomMsg.msg。示例内容如下:

# MyCustomMsg.msg
string name
int64 age
int64 height

上面的例子定义了一个包含姓名、年龄和身高的自定义消息。
在这里插入图片描述

②修改package.xml文件
package.xml中添加编译依赖与执行依赖

 <build_depend>message_generation</build_depend>
<exec_depend>message_runtime</exec_depend>

在这里插入图片描述

③编辑CMakeLists.txt文件
在ROS工作空间的CMakeLists.txt文件中,确保包含以下内容,以编译并生成自定义消息:

find_package(catkin REQUIRED COMPONENTS
  roscpp
  rospy
  std_msgs
  message_generation
)
# 需要加入 message_generation,必须有 std_msgs

## 配置 msg 源文件
add_message_files(
  FILES
  <自定义名称>.msg
)

# 生成消息时依赖于 std_msgs
generate_messages(
  DEPENDENCIES
  std_msgs
)

#执行时依赖,添加message_runtime
catkin_package(
#  INCLUDE_DIRS include
#  LIBRARIES demo02_talker_listener
  CATKIN_DEPENDS roscpp rospy std_msgs message_runtime
#  DEPENDS system_lib
)

注意: 官网没有在catkin_package中配置 message_runtime,经测试配置也可以。

最后,需要添加 add_dependencies 用以设置所依赖的消息相关的中间文件。

add_dependencies(可执行程序名称 ${PROJECT_NAME}_generate_messages_cpp)

编译后的中间文件查看:C++ 需要调用的中间文件(…/工作空间/devel/include/包名/xxx.h)。后续调用相关 msg 时,是从这些中间文件调用的。
在这里插入图片描述
生成的中间文件是在devel目录下,为了方便vscode编写程序,还需要设置其中间文件的路径,如下图所示,点击头文件报错的那行,然后点击小黄灯泡即可快速完成。
在这里插入图片描述
④调用中间文件

发布者代码:

#include <ros/ros.h>
#include <turtle_pos/MyCustomMsg.h>

int main(int argc, char **argv)
{
    // 初始化 ROS 节点
    ros::init(argc, argv, "example_node");
    ros::NodeHandle nh;

    // 创建一个发布者,发布turtle_pos::MyCustomMsg类型的消息到主题 "example_topic",队列大小为 10
    ros::Publisher pub = nh.advertise<turtle_pos::MyCustomMsg>("example_topic", 10);

    // 创建并填充一个turtle_pos::MyCustomMsg类型的消息对象
    turtle_pos::MyCustomMsg msg;
    msg.age = 18;
    msg.height = 170;
    msg.name = "sue";

    // 设置发送频率为1000Hz
    ros::Rate r(1000);

    // 循环发送消息
    while (ros::ok())
    {
        // 使用 publish 函数发布消息
        pub.publish(msg);

        // 处理ROS回调函数
        ros::spinOnce();

        // 以指定频率休眠,以满足发送频率
        r.sleep();
    }

    return 0;
}

订阅者代码:

#include <ros/ros.h>
#include <turtle_pos/MyCustomMsg.h>

// 回调函数的定义
void messageCallback(const boost::shared_ptr<turtle_pos::MyCustomMsg const>& msg)
{
    // 打印接收到的消息内容
    ROS_INFO("Received message: name:%s, age:%d, height:%d", msg->name.c_str(), msg->age, msg->height);
}

int main(int argc, char** argv)
{
    // 初始化 ROS 节点
    ros::init(argc, argv, "example_node2");
    ros::NodeHandle nh;

    // 创建一个订阅者,订阅turtle_pos::MyCustomMsg类型的消息,回调函数为 messageCallback
    ros::Subscriber sub = nh.subscribe<turtle_pos::MyCustomMsg>("example_topic", 10, messageCallback);

    // 进入 ROS 主循环,等待接收消息并调用回调函数
    ros::spin();

    return 0;
}

结果如下:
在这里插入图片描述

二、服务通信

服务通信也是ROS中一种极其常用的通信模式,服务通信是基于请求响应模式的,是一种应答机制。即: 一个节点A向另一个节点B发送请求,B接收处理请求并产生响应结果返回给A

1.服务通信理论

服务通信较于话题通信比较简单,理论模型如下图所示,该模型中涉及到三个角色:

  • ROS Master:负责记录 Server 和 Client 注册的信息,并匹配话题相同的 Server 与 Client ,帮助 Server 与 Client 建立连接
  • Talker(Server,服务端):返回响应信息
  • Listener(Client,客户端):发送请求信息

在这里插入图片描述
其整个流程可以参考如下文章:服务通信理论。

注意:

1.客户端请求被处理时,需要保证服务器已经启动;(在客户端中可以使用代码解决,等待服务器启动后在进行连接)
2.服务端和客户端都可以存在多个。

2.服务通信常用API

🎆服务端

advertiseService

advertiseService 函数是创建一个ROS服务服务器对象它允许其他节点请求服务并接收相应的结果。下面是对该函数的作用和参数介绍:

template<class MReq, class MRes>
ServiceServer advertiseService(const std::string& service, bool(*srv_func)(MReq&, MRes&))

service:服务的名称,是一个字符串,用于唯一标识服务
srv_func:是一个函数指针指向处理服务请求的回调函数当有节点请求该服务时, 函数将被调用来处理请求并生成响应。该回调函数应该有两个参数,一个是请求消息的引用(MReq&),另一个是响应消息的引用(MRes&)。这个函数应该返回一个布尔值,表示服务处理是否成功。

-MReq:请求消息类型(Request Message Type),表示服务的请求消息类型
-MRes:响应消息类型(Response Message Type),表示服务的响应消息类型

注意,虽然这是一个模板函数,但是其模板参数是可选的,如果服务的请求消息和响应消息类型可以从回调函数的参数中推断出来,就可以省略模板参数。这样的情况下,ROS会通过回调函数的参数类型来自动推断消息类型。

🎇客户端

serviceClient

serviceClient 函数是 ros::NodeHandle 类中的一个成员函数,用于创建 ROS 服务客户端,用于向指定服务发送请求。下面是对这个函数的作用和参数的解释:

template<class Service>
ServiceClient serviceClient(const std::string& service_name, bool persistent = false, 
                            const M_string& header_values = M_string())

  • service_name:是服务的名称用于唯一标识服务
  • persistent:是一个布尔值,表示是否创建一个持久化的服务客户端。如果为 true,则客户端将在网络断开时自动重新连接。
  • header_values:是一个 std::map<std::string, std::string> 类型的参数,用于指定服务调用的头部信息。这可以包括认证信息、附加的元数据等。
  • 模板参数:Service是服务的类型,表示服务的请求和响应消息类型
ros::service::waitForService

waitForService 函数是 ros::NodeHandle 类中的成员函数,用于等待指定服务启动成功通常用于服务客户端在调用服务之前等待服务启动。以下是对这个函数的作用和参数的介绍:

ROSCPP_DECL bool waitForService(const std::string& service_name, ros::Duration timeout = ros::Duration(-1));
  • service_name:是服务的名称,用于唯一标识服务
  • timeout:是一个可选参数,表示等待服务的超时时间。默认值为 ros::Duration(-1),表示无限等待,直到服务启动成功或节点关闭。如果指定了超时时间,函数将在超时时返回 false。
  • 返回一个布尔值,表示等待服务是否成功启动。如果返回 true,表示服务启动成功;如果返回 false,表示等待超时或节点关闭。
waitForExistence

waitForExistence 函数是 ros::ServiceClient 类中的成员函数,用于等待服务存在,通常用于服务客户端在调用服务之前等待服务的注册。以下是对这个函数的作用和参数的介绍:

bool waitForExistence(ros::Duration timeout = ros::Duration(-1));
  • timeout:是一个可选参数,表示等待服务存在的超时时间。默认值为 ros::Duration(-1),表示无限等待,直到服务存在或节点关闭。如果指定了超时时间,函数将在超时时返回 false。
  • 返回一个布尔值,表示等待服务是否成功存在。如果返回 true,表示服务存在;如果返回 false,表示等待超时或节点关闭。
call

call 函数是 ros::ServiceClient 类中的成员函数,用于向服务发送请求并等待响应。下面是对这个函数的作用和参数介绍:

template<class Service>
bool call(Service& service)
  • Service:表示服务的请求和响应消息类型
  • service:是服务请求的对象,它包含了请求消息的数据。在调用该函数时,这个对象将被填充为服务的请求消息,同时也可以包含用于接收服务响应的字段
  • 返回一个布尔值,表示服务调用是否成功。如果返回 true,表示服务调用成功;如果返回 false,表示服务调用失败。

调用流程:

  • 调用 client.call(service) 时,客户端将发送请求消息(service 对象中的数据)给服务端,并阻塞等待服务端的响应

  • 如果服务调用成功(服务端成功处理了请求),call 函数返回 true,同时服务端的响应消息被填充到 service 对象中,你可以通过 service.response 获取响应的数据。

  • 如果服务调用失败,call 函数返回 false,通常表示服务端未能成功处理请求或服务不存在等原因。

3.自定义srv

srv 文件内的可用数据类型msg 文件一致,且定义 srv 实现流程与自定义 msg 实现流程类似:

①定义srv文件
服务通信中,数据分成两部分,请求响应,在 srv 文件中请求和响应使用---分割,具体实现如下:
功能包下新建 srv 目录,添加 xxx.srv 文件,内容:

# 客户端请求时发送的两个数字
int32 num1
int32 num2
---
# 服务器响应发送的数据
int32 sum

在这里插入图片描述
②修改package.xml文件
package.xml中添加编译依赖执行依赖

  <build_depend>message_generation</build_depend>
  <exec_depend>message_runtime</exec_depend>

在这里插入图片描述
③编辑CMakeLists.txt文件

在CMakeLists.txt编辑 srv 相关配置。

find_package(catkin REQUIRED COMPONENTS
  roscpp
  rospy
  std_msgs
  message_generation
)
# 需要加入 message_generation,必须有 std_msgs

## Generate services in the 'srv' folder
add_service_files(
  FILES
  addint.srv
)
## Generate added messages and services with any dependencies listed here
generate_messages(
  DEPENDENCIES
  std_msgs
)

注意: 官网没有在catkin_package中配置 message_runtime,经测试配置也可以,具体配置可见自定义msg,这里就懒得配置啦。

最后,需要添加 add_dependencies 用以设置所依赖的消息相关的中间文件,注意后缀名与msg的区别

add_dependencies(可执行程序名称${PROJECT_NAME}_gencpp)

编译后的中间文件查看:C++ 需要调用的中间文件(…/工作空间/devel/include/包名/xxx.h)。后续调用相关 srv 时,是从这些中间文件调用的。
在这里插入图片描述
生成的中间文件是在devel目录下,为了方便vscode编写程序,还需要设置其中间文件的路径,如下图所示,点击头文件报错的那行,然后点击小黄灯泡即可快速完成。具体见自定义msg。

④调用中间文件

服务端代码:

#include <ros/ros.h>
#include <serve_client/addint.h>

// 回调函数的定义
bool addIntsCallback(serve_client::addint::Request& req, serve_client::addint::Response& res)
{
    // 打印服务器接收到的请求数据
    ROS_INFO("服务器接收到的请求数据为: num1 = %d, num2 = %d", req.num1, req.num2);

    // 执行服务请求的操作(在这个例子中,将两个整数相加)
    res.sum = req.num1 + req.num2;
    
    // 返回 true 表示服务处理成功
    return true;
}

int main(int argc, char** argv)
{
    setlocale(LC_ALL, "");

    // 初始化 ROS 节点
    ros::init(argc, argv, "example_node");
    ros::NodeHandle nh;

    // 创建一个服务服务器,服务名称为 "AddInts",回调函数为 addIntsCallback,二选一即可,具体原因见advertiseService那里
    //ros::ServiceServer server = nh.advertiseService("AddInts", addIntsCallback);
    ros::ServiceServer server = nh.advertiseService<serve_client::addint::Request, serve_client::addint::Response>("AddInts", addIntsCallback);

    // 输出服务器启动成功的消息
    ROS_INFO("服务器启动成功");

    // 进入 ROS 主循环,等待服务请求
    ros::spin();

    return 0;
}

客户端代码:

#include <ros/ros.h>
#include <serve_client/addint.h>

int main(int argc, char** argv)
{
    setlocale(LC_ALL, "");

    // 调用时动态传值,如果通过 launch 的 args 传参,需要传递的参数个数 +3
    if (argc != 3)
    // if (argc != 5) //launch 传参(0-文件路径 1传入的参数 2传入的参数 3节点名称 4日志路径)
    {
        ROS_ERROR("请提交两个整数");
        return 1;
    }

    // 初始化 ROS 节点
    ros::init(argc, argv, "example_client");

    // 创建 ROS 句柄
    ros::NodeHandle nh;

    // 创建服务客户端,服务名称为 "AddInts"
    ros::ServiceClient client = nh.serviceClient<serve_client::addint>("AddInts");

    // 等待服务启动成功
    //client.waitForExistence();
    ros::service::waitForService("AddInts");

    // 组织请求数据
    serve_client::addint srv;
    srv.request.num1 = atoi(argv[1]);
    srv.request.num2 = atoi(argv[2]);

    // 调用服务并等待响应
    if (client.call(srv))
    {
        // 请求正常处理,输出响应结果
        ROS_INFO("请求正常处理,响应结果: %d", srv.response.sum);
    }
    else
    {
        // 请求处理失败
        ROS_ERROR("请求处理失败");
        return 1;
    }

    return 0;
}

运行结果如下:
在这里插入图片描述
如果先启动客户端,那么会导致运行失败。在客户端发送请求前添加:client.waitForExistence()或:ros::service::waitForService(“AddInts”);这是一个阻塞式函数只有服务启动成功后才会继续执行。此处可以使用 launch 文件优化,但是需要注意 args 传参特点。

三、参数服务器

参数服务器在ROS中主要用于实现不同节点之间的数据共享。参数服务器相当于是独立于所有节点的一个公共容器,可以将数据存储在该容器中,被不同的节点调用,当然不同的节点也可以往其中存储数据。

参数服务器是ROS参数系统的一部分,它允许在运行时存储和检索参数值。参数服务器是一个分布式的键-值存储系统,用于在ROS系统中共享和存储配置参数、配置文件、常量值等信息。它是一种以共享的方式实现不同节点之间数据交互的通信模式,其主要作用是存储多节点共享的数据,类似于全局变量

1.参数服务器通信理论

参数服务器实现是最为简单的,该模型如下图所示,该模型中涉及到三个角色:

  • ROS Master (管理者),作为一个公共容器保存参数
  • Talker (参数设置者),可以向容器中设置参数
  • Listener (参数调用者),可以获取参数
    在这里插入图片描述
    整个流程可以参考如下文章:参数服务器理论。

参数可使用数据类型:32-bit integers,booleans,strings,doubles,iso8601 dates,lists,base64-encoded binary data,字典。

注意:参数服务器不是为高性能而设计的,因此最好用于存储静态的非二进制的简单数据。

2.参数服务器常用API

在 C++ 中实现参数服务器数据的增删改查,可以通过两套 API 实现。

🎉ros::NodeHandle

🎊ros::param

四、其他常用API

ros::init

ros::NodeHandle

ros::spinOnce

ros::spin

ros::ok

shutdown

日志相关

ROS_DEBUG

ROS_INFO

ROS_WARN

ROS_ERROR

ROS_FATAL

时间相关

ros::Time::now

ros::Duration

ros::Rate

createTimer

小结:
ROS 架构中的主要组件:

  • 节点(Nodes): 节点是 ROS 架构的基本单位,通常是一个执行特定运算任务的独立进程。每个节点可以执行感知、控制、规划等不同功能。节点之间通过消息进行通信。

  • 主题(Topics): 主题是节点之间进行消息传递的通道。一个节点可以发布(publish)消息到一个主题,而其他节点可以订阅(subscribe)这个主题来接收该消息。主题是一种异步通信机制,多个节点可以同时发布和订阅同一个主题。

  • 消息(Messages): 消息是在节点之间传递的数据单元。节点可以根据定义的消息类型创建消息实例,并通过主题发布或接收这些消息。消息可以是包含任意数据的结构化数据类型,如图像、点云、传感器数据等,每一个消息都是一种严格的数据结构,支持标准数据类型(整型、浮点型、布尔型等),也支持嵌套结构和数组(类似于C语言的结构体struct),还可以根据需求由开发者自主定义。

  • 服务(Services): 服务提供了节点之间的一对一通信机制,类似于传统的客户端-服务器模型。一个节点可以提供一个服务,而其他节点可以请求该服务并等待响应。与主题不同,服务是一种同步通信机制,请求节点会等待直到收到响应。

  • 参数服务器(Parameter Server): 参数服务器也称节点管理器是一个全局的、分布式的键值存储系统,用于存储节点的配置参数。节点可以读取和写入参数服务器中的值,以便在运行时调整系统的行为。

ROS 架构实现了高度的灵活性和可扩展性。开发人员可以将不同的节点组合在一起,构建复杂的机器人系统,并实现各种功能和算法的集成和协同工作。

重点标记性文字设置
具体的标记性正文
具体的标记性正文
具体的标记性正文
具体的标记性正文
具体的标记性正文
具体的标记性正文

结束语

感谢阅读吾之文章,今已至此次旅程之终站 🛬。

吾望斯文献能供尔以宝贵之信息与知识也 🎉。

学习者之途,若藏于天际之星辰🍥,吾等皆当努力熠熠生辉,持续前行。

然而,如若斯文献有益于尔,何不以三连为礼?点赞、留言、收藏 - 此等皆以证尔对作者之支持与鼓励也 💞。

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

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

相关文章

[大厂实践] Pinterest通用计算平台实践

Pinterest平台团队开发实现了名为PinCompute的高性能通用计算平台&#xff0c;支持Pinterest的大量异构用例和服务。本文介绍了团队在开发这一平台过程中的经验和实践&#xff0c;对于其他平台团队来说&#xff0c;具有很好的参考意义。原文: PinCompute: A Kubernetes Backed …

RabbitMQ面试必备:基本概念、组件原理、消息传递模型,一网打尽。解密高可用性、负载均衡,深入了解安全性配置和性能优化

一、RabbitMQ的基本概念&#xff1a; 1.什么是消息队列&#xff1f; 消息队列是一种在分布式系统中用于在不同组件之间传递消息的通信机制。它允许应用程序和服务通过异步方式进行通信&#xff0c;提高了系统的可伸缩性和松耦合性。消息队列通常包括生产者&#xff08;Produc…

node-sass版本与NodeJS版本不匹配的问题

npm install 报错如下 npm ERR! code 1 npm ERR! path D:\Project\git_Product\YYYY\user\node_modules\node-sass npm ERR! command failed npm ERR! command C:\WINDOWS\system32\cmd.exe /d /s /c node scripts/build.js 问题原因 node-sass 与 node 版本不匹配 卸载Node…

PM圆桌派:同事不愿意告诉你的职场套路有哪些?

职场是社会的缩影&#xff0c;想要崭露头角&#xff0c;获得更多升职加薪的机会&#xff0c;就不要做着和多数人一样的事情&#xff0c;却期待着不一样的结果。 职场上有很多潜在的规则&#xff0c;要会做事&#xff0c;也要会说话&#xff0c;更要会做人。如果不懂规则&#…

day43_jdbc

今日内容 0 复习昨日 1 SQL注入问题 2 PreparedStatement 3 完成CRUD练习 4 ORM 5 DBUtil (properties) 6 事务操作 0 复习昨日 已经找人提问… 1 SQL注入 1.1 什么是SQL注入 用户输入的数据中有SQL关键词,导致在执行SQL语句时出现一些不正常的情况.这就是SQL注入! 出现SQL注入…

springcloud-gateway升级版本allowedOrigins要改allowedOriginPatterns

前言 报错: java.lang.IllegalArgumentException: When allowCredentials is true,allowedOrigins cannot contain the special value "*"since that cannot be set on the "Access-Control-Allow-Origin"response header. To allow credentials to a se…

AI大模型专题:OWASP大语言模型应用程序十大风险V1.0

今天分享的是AI大模型系列深度研究报告&#xff1a;《AI大模型专题&#xff1a;OWASP大语言模型应用程序十大风险V1.0》。 &#xff08;报告出品方&#xff1a;OWASP&#xff09; 报告共计&#xff1a;14页 LM01:2023_ 提示词注入 描述&#xff1a;提示词注入包括绕过过滤器…

【Linux】解决:为什么重复创建同一个【进程pid会变化,而ppid父进程id不变?】

前言 大家好吖&#xff0c;欢迎来到 YY 滴Linux 系列 &#xff0c;热烈欢迎&#xff01; 本章主要内容面向接触过Linux的老铁 主要内容含&#xff1a; 欢迎订阅 YY滴C专栏&#xff01;更多干货持续更新&#xff01;以下是传送门&#xff01; YY的《C》专栏YY的《C11》专栏YY的…

Java八大常用排序算法

1冒泡排序 对于冒泡排序相信我们都比较熟悉了&#xff0c;其核心思想就是相邻元素两两比较&#xff0c;把较大的元素放到后面&#xff0c;在一轮比较完成之后&#xff0c;最大的元素就位于最后一个位置了&#xff0c;就好像是气泡&#xff0c;慢慢的浮出了水面一样 Jave 实现 …

解决IntellIJ Idea内存不足

突然有一天我在IDEA打开两个项目时&#xff0c;发生了报错&#xff0c;说我内存不足&#xff0c;我这电脑内存16G怎么会内存不足。下面是我的解决方案。 IntelliJ IDEA 报告内存不足的原因通常与以下几个因素有关&#xff1a; 项目规模较大&#xff1a;如果您正在开发的项目非…

Linux 配置路由转发功能测试

测试Linux配置路由转发功能。 参考 手把手带你将 Linux 主机配置为静态路由器tcpdump详解&实战 环境 操作系统 Centos7.9 网络环境 1. 三台主机的网卡 enp0s5 均在 10.211.55.0/24 网段&#xff0c;且网络可以通讯 centos7-18的IP 10.211.55.18&#xff0c;作为路由…

【退役之重学前端】vite, vue3, vue-router, vuex, ES6学习日记

学习使用vitevue3的所遇问题总结&#xff08;2024年2月1日&#xff09; 组件中使用<script>标签忘记加 setup 这会导致Navbar 没有暴露出来&#xff0c;导致使用不了&#xff0c;出现以下报错 这是因为&#xff0c;如果不用setup&#xff0c;就得使用 export default…

一次enq: TM - contention分析

客户反映前台业务卡住了&#xff0c;怀疑是有锁表&#xff0c;锁表时查询等待事件是enq: TM - contention&#xff0c;因为是业务高峰期&#xff0c;所以直接把锁杀掉了&#xff0c;事后想要查一下锁表原因。 客户数据库环境是11g rac&#xff0c;我采集了锁表时间段两节点的a…

C++拷贝构造函数、赋值运算符重载

1.拷贝构造函数 拷贝构造函数的写法如图所示 调用方式如下 接下来我来说说它的特征 1.1特征 拷贝构造函数&#xff1a;只有单个形参&#xff0c;该形参是对本类类型对象的引用(一般常用const修饰)&#xff0c;在用已存在的类类型对象创建新对象时由编译器自动调用。 拷贝构造函…

算法学习——华为机考题库9(HJ56 - HJ63)

算法学习——华为机考题库9&#xff08;HJ56 - HJ63&#xff09; HJ56 完全数计算 描述 完全数&#xff08;Perfect number&#xff09;&#xff0c;又称完美数或完备数&#xff0c;是一些特殊的自然数。 它所有的真因子&#xff08;即除了自身以外的约数&#xff09;的和&…

Android配置GitLab CI/CD持续集成,Shell版本的gitlab-runner,FastLane执行,上传蒲公英

mac环境下&#xff0c; 首选需要安装gitlab-runner和fastlane brew install gitlab-runner brew install fastlane 安装完成&#xff0c;来到我们在gitlab下新建的Android项目&#xff0c;我们开始创建gitlab-runner 1、创建runner 点开runner&#xff0c;点击新建runner …

map容器的基本概念及常用成员函数

map&#xff08;字典&#xff09;的基本概念 map是一个将一个值映射到另一个值的一种数据结构。&#xff08;即两个值之间有一一对应关系&#xff09;。 map的所有元素都是pair类型&#xff0c;pair中的第一个元素称为键值&#xff08;key&#xff09;第二个元素称为实值&…

Vue工程引入Element-ui

npm 安装ELement-ui npm i element-ui -S 于package.json中发现有“element-ui”版本号即可 引入 Element 在 main.js 中写入以下内容&#xff1a; import element-ui/lib/theme-chalk/index.css; import ElementUI from element-ui;Vue.use(ElementUI);之后根据自己的需求设计…

【Apache POI库读取Excel文件,并使用HashMap来存储和检查重复项】

目录 下面是一个示例代码,使用Apache POI库读取Excel文件,并使用HashMap来存储和检查重复项。 import org.apache.poi.ss.usermodel.*; import org.apache.poi.xssf.usermodel.XSSFWorkbook;import java.io.FileInputStream; import java.io.IOException; import java.util.…

Web html

目录 1 前言2 HTML2.1 元素(Element)2.1.1 块级元素和内联(行级)元素2.1.2 空元素 2.2 html页面的文档结构2.3 常见标签使用2.3.1 注释2.3.2 标题2.3.3 段落2.3.4 列表2.3.5 超链接2.3.6 图片2.3.7 内联(行级)标签2.3.8 换行 2.4 属性2.4.1 布尔属性 2.5 实体引用2.6 空格2.7 D…