RabbitMQ 部署及配置详解(集群部署)

单机部署请移步: RabbitMQ 部署及配置详解 (单机)

RabbitMQ 集群是一个或 多个节点,每个节点共享用户、虚拟主机、 队列、交换、绑定、运行时参数和其他分布式状态。

一、RabbitMQ 集群可以通过多种方式形成:

  • 通过在配置文件中列出群集节点以声明方式

  • 以声明方式使用基于 DNS 的发现

  • 以声明方式使用 AWS (EC2) 实例发现(通过插件)

  • 声明式使用 Kubernetes 发现(通过插件)

  • 以声明方式使用基于 Consul 的发现(通过插件)

  • 声明式地使用基于 etcd 的发现(通过插件)

  • 手动使用 rabbitmqctl

二、集群部署的核心概念:

1、 仲裁队列

仲裁队列是 RabbitMQ 实现持久、 基于Raft共识算法的复制FIFO队列。 它从 RabbitMQ 3.8.0 开始可用。

仲裁队列和流取代了持久镜像队列,即原始队列 复制的队列类型,现已弃用并计划删除。

仲裁队列针对数据安全性为 重中之重。这在动机中有所介绍。 仲裁队列应被视为复制队列类型的默认选项。

与传统镜像队列相比,仲裁队列在行为上也存在重要差异和一些限制, 包括特定于工作负载的,例如,当使用者重复对同一消息重新排队时。另外仲裁队列支持死信交换 (DLX)。

仲裁队列和镜像队列差异

2、节点名称

RabbitMQ 节点由节点名称标识。节点名称由两部分组成, 前缀(通常是兔子)和主机名。例如,rabbit@node1.messaging.svc.local 是一个节点名称,前缀为 rabbit,主机名为 node1.messaging.svc.local。

群集中的节点名称必须是唯一的。如果给定主机上正在运行多个节点 (这通常在开发和 QA 环境中),他们必须使用 不同的前缀,例如rabbit1@hostname和rabbit2@hostname。

在群集中,节点使用节点名称相互标识和联系。这意味着 必须解析每个节点名称的主机名部分。CLI 工具还使用节点名称识别和寻址节点。

当节点启动时,它会检查是否已为其分配节点名称。这是完成的 通过RABBITMQ_NODENAME环境变量。 如果未显式配置任何值, 节点解析其主机名,并将 Rabbit 附加到它以计算其节点名称。

如果系统使用完全限定域名 (FQDN) 作为主机名,RabbitMQ 节点 并且必须将 CLI 工具配置为使用所谓的长节点名称。 对于服务器节点,这是通过将RABBITMQ_USE_LONGNAME环境变量设置为 true 来完成的。

对于 CLI 工具,必须设置 RABBITMQ_USE_LONGNAME 或 --longnames 选项 必须指定。

3、集群形成要求

主机名解析

RabbitMQ 节点使用节点名称(组合)相互寻址 前缀和域名,可以是短的还是完全限定的 (FQDN)。因此,每个集群成员都必须能够解析主机名 对于所有其他集群成员,其自己的主机名也是如此 作为可能使用 RabbitMQCTL 等命令行工具的计算机。节点将在节点引导时尽早执行主机名解析。 在基于容器的环境中,主机名很重要 在容器启动之前,解决方案已准备就绪。 对于 Kubernetes 用户来说,这意味着 CoreDNS 的 DNS 缓存间隔值在 5-10 秒范围内。

主机名解析可以使用任何标准操作系统提供 方法:

  • 域名系统记录

  • 本地主机文件(例如 /etc/hosts)

在限制性更强的环境中,DNS 记录或 主机文件修改受到限制、无法或 不想要的,erlang可以将 VM 配置为使用备用主机名 解析方法,例如备用 DNS 服务器, 本地文件、非标准主机文件位置或混合 的方法。这些方法可以与 标准操作系统主机名解析方法。要使用 FQDN,请参阅配置指南中的RABBITMQ_USE_LONGNAME。

端口访问

RabbitMQ 节点绑定到端口(开放服务器 TCP 套接字)以接受客户端和 CLI 工具连接。 其他进程和工具(如 SELinux)可能会阻止 RabbitMQ 绑定到端口。当这种情况发生时, 节点将无法启动。CLI 工具、客户端库和 RabbitMQ 节点也打开连接(客户端 TCP 套接字)。 防火墙可以阻止节点和 CLI 工具相互通信。 以下端口与群集中的节点间通信最相关:

  • 4369:epmd,RabbitMQ 节点和 CLI 工具使用的帮助程序发现守护进程

  • 6000 到 6500:由 RabbitMQ 流复制使用

  • 25672:用于节点间和 CLI 工具通信(Erlang 分发服务器端口) 并且从动态范围分配(默认情况下仅限于单个端口, 计算为 AMQP 端口 + 20000)。除非这些端口上的外部连接确实是必要的(例如 群集使用联合身份验证或在子网外部的计算机上使用 CLI 工具), 这些端口不应公开。有关详细信息,请参阅网络指南。

  • 35672-35682:CLI 工具(Erlang 分发客户端端口)用于与节点通信 并从动态范围(计算为服务器分发端口 + 10000 到 服务器分发端口 + 10010)。

可以将 RabbitMQ 配置为使用不同的端口和特定的网络接口

对等节点

一些分布式系统 具有领导节点和从节点。对于 RabbitMQ 来说,通常并非如此。 RabbitMQ 集群中的所有节点都是平等的对等节点:RabbitMQ 核心中没有特殊的节点。 当仲裁队列和插件时,本主题变得更加微妙 被考虑在内,但出于大多数意图和目的, 应将所有群集节点视为相等

节点间的身份验证

RabbitMQ 节点和 CLI 工具(例如 rabbitmqctl)使用 cookie 以确定是否允许他们与 彼此。为了使两个节点能够通信,它们必须具有 同样的共享密钥称为 Erlang cookie。cookie只是一个字母数字字符的字符串,最大为 255 个字符。 它通常存储在本地文件中。该文件必须仅 所有者可访问(例如,具有 600 或类似的 UNIX 权限)。 每个群集节点必须具有相同的 Cookie。如果该文件不存在,Erlang VM 将尝试创建 一个在 RabbitMQ 服务器时随机生成的值 启动。在开发中使用此类生成的 cookie 文件是合适的 仅限环境。在 UNIX 系统上,cookie 通常为 位于 /var/lib/rabbitmq/.erlang.cookie

三、部署

1、启动独立节点

RabbitMQ CLI 工具(如 rabbitmq-diagnostics 和 rabbitmqctl)提供了检查资源和集群范围状态的命令。

通过重新配置现有 RabbitMQ 来设置集群 节点加入群集配置。因此,第一步 就是以正常方式在所有节点上启动 RabbitMQ:

# on rabbit1
rabbitmq-server -detached
# on rabbit2
rabbitmq-server -detached
# on rabbit3
rabbitmq-server -detached

这将创建三个独立的 RabbitMQ 代理, 每个节点上一个,如 cluster_status 命令所确认的那样:

# on rabbit1
rabbitmqctl cluster_status
# => Cluster status of node rabbit@rabbit1 ...
# => [{nodes,[{disc,[rabbit@rabbit1]}]},{running_nodes,[rabbit@rabbit1]}]
# => ...done.

# on rabbit2
rabbitmqctl cluster_status
# => Cluster status of node rabbit@rabbit2 ...
# => [{nodes,[{disc,[rabbit@rabbit2]}]},{running_nodes,[rabbit@rabbit2]}]
# => ...done.

# on rabbit3
rabbitmqctl cluster_status
# => Cluster status of node rabbit@rabbit3 ...
# => [{nodes,[{disc,[rabbit@rabbit3]}]},{running_nodes,[rabbit@rabbit3]}]
# => ...done.

从 rabbitmq-server shell 脚本启动的 RabbitMQ 代理的节点名称是 rabbit@shorthostname,其中 short 节点名称为小写(如rabbit@rabbit1, 以上)。在 Windows 上,如果使用 rabbitmq-server.bat 批处理文件,则短节点名称为大写(如 在rabbit@RABBIT1)。键入节点名称时, 大小写很重要,这些字符串必须完全匹配

2、创建集群

为了将集群中的三个节点链接起来,我们告诉 其中两个节点,例如rabbit@rabbit2和rabbit@rabbit3,加入 第三,说rabbit@rabbit1。在此之前,两者 新加入的成员必须重置。

我们首先在集群中加入rabbit@rabbit2 与rabbit@rabbit1。为此,在rabbit@rabbit2我们停止了 RabbitMQ 应用程序并加入rabbit@rabbit1集群,然后重新启动 RabbitMQ 应用程序。请注意, 必须先重置节点,然后才能加入现有群集。 重置节点会删除以前的所有资源和数据 存在于该节点上。这意味着节点不能成为成员 并同时保留其现有数据。当需要时, 使用蓝/绿部署策略或备份和还原是可用选项。

# on rabbit2
rabbitmqctl stop_app
# => Stopping node rabbit@rabbit2 ...done.

rabbitmqctl reset
# => Resetting node rabbit@rabbit2 ...

rabbitmqctl join_cluster rabbit@rabbit1
# => Clustering node rabbit@rabbit2 with [rabbit@rabbit1] ...done.

rabbitmqctl start_app
# => Starting node rabbit@rabbit2 ...done.

我们可以看到,两个节点通过 在任一节点上运行 cluster_status 命令:

# on rabbit1
rabbitmqctl cluster_status
# => Cluster status of node rabbit@rabbit1 ...
# => [{nodes,[{disc,[rabbit@rabbit1,rabbit@rabbit2]}]},
# =>  {running_nodes,[rabbit@rabbit2,rabbit@rabbit1]}]
# => ...done.

# on rabbit2
rabbitmqctl cluster_status
# => Cluster status of node rabbit@rabbit2 ...
# => [{nodes,[{disc,[rabbit@rabbit1,rabbit@rabbit2]}]},
# =>  {running_nodes,[rabbit@rabbit1,rabbit@rabbit2]}]
# => ...done.

现在我们加入rabbit@rabbit3到同一集群。步骤与上面的步骤相同,只是这次我们将集群到rabbit2,以证明选择集群到的节点无关紧要——只需提供一个在线节点就足够了,该节点将集群到指定节点所属的集群。

# on rabbit3
rabbitmqctl stop_app
# => Stopping node rabbit@rabbit3 ...done.

# on rabbit3
rabbitmqctl reset
# => Resetting node rabbit@rabbit3 ...

rabbitmqctl join_cluster rabbit@rabbit2
# => Clustering node rabbit@rabbit3 with rabbit@rabbit2 ...done.

rabbitmqctl start_app
# => Starting node rabbit@rabbit3 ...done.

我们可以看到,这三个节点通过 在任何节点上运行 cluster_status 命令:

# on rabbit1
rabbitmqctl cluster_status
# => Cluster status of node rabbit@rabbit1 ...
# => [{nodes,[{disc,[rabbit@rabbit1,rabbit@rabbit2,rabbit@rabbit3]}]},
# =>  {running_nodes,[rabbit@rabbit3,rabbit@rabbit2,rabbit@rabbit1]}]
# => ...done.

# on rabbit2
rabbitmqctl cluster_status
# => Cluster status of node rabbit@rabbit2 ...
# => [{nodes,[{disc,[rabbit@rabbit1,rabbit@rabbit2,rabbit@rabbit3]}]},
# =>  {running_nodes,[rabbit@rabbit3,rabbit@rabbit1,rabbit@rabbit2]}]
# => ...done.

# on rabbit3
rabbitmqctl cluster_status
# => Cluster status of node rabbit@rabbit3 ...
# => [{nodes,[{disc,[rabbit@rabbit3,rabbit@rabbit2,rabbit@rabbit1]}]},
# =>  {running_nodes,[rabbit@rabbit2,rabbit@rabbit1,rabbit@rabbit3]}]
# => ...done.

通过执行上述步骤,我们可以在集群运行时随时向集群添加新节点。

3、重新启动群集节点

已加入群集的节点可以随时停止。它们也可能出现故障或被操作系统终止。

通常,如果节点停止后大多数节点仍处于联机状态,这不会影响集群的其余部分,尽管集群的客户端连接分布、队列副本放置和负载分布会发生变化。

4、来自联机对等方的架构同步

重新启动的节点将在启动时同步来自其对等节点的架构和其他信息。在这个过程完成之前,节点将不会完全启动和运行。

因此,了解流程节点在停止和重新启动时所经历的过程非常重要。

停止节点在重新启动后选择要同步的联机群集成员(只考虑磁盘节点)。在重新启动时,节点将尝试默认联系该对等方10次,并有30秒的响应超时。

如果对等方在该时间间隔内可用,则节点成功启动,从对等方同步所需内容并继续运行。

如果对等节点不可用,则重新启动的节点将放弃并自动停止。这种情况可以通过日志中的超时(timeout_waiting_for_tables)警告消息来识别,这些消息最终会导致节点启动失败:


2020-07-27 21:10:51.361 [warning] <0.269.0> Error while waiting for Mnesia tables: {timeout_waiting_for_tables,[rabbit@node2,rabbit@node1],[rabbit_durable_queue]}
2020-07-27 21:10:51.361 [info] <0.269.0> Waiting for Mnesia tables for 30000 ms, 1 retries left
2020-07-27 21:11:21.362 [warning] <0.269.0> Error while waiting for Mnesia tables: {timeout_waiting_for_tables,[rabbit@node2,rabbit@node1],[rabbit_durable_queue]}
2020-07-27 21:11:21.362 [info] <0.269.0> Waiting for Mnesia tables for 30000 ms, 0 retries left

2020-07-27 21:15:51.380 [info] <0.269.0> Waiting for Mnesia tables for 30000 ms, 1 retries left
2020-07-27 21:16:21.381 [warning] <0.269.0> Error while waiting for Mnesia tables: {timeout_waiting_for_tables,[rabbit@node2,rabbit@node1],[rabbit_user,rabbit_user_permission, …]}
2020-07-27 21:16:21.381 [info] <0.269.0> Waiting for Mnesia tables for 30000 ms, 0 retries left
2020-07-27 21:16:51.393 [info] <0.44.0> Application mnesia exited with reason: stopped

2020-07-27 21:16:51.397 [error] <0.269.0> BOOT FAILED
2020-07-27 21:16:51.397 [error] <0.269.0> ===========
2020-07-27 21:16:51.397 [error] <0.269.0> Timeout contacting cluster nodes: [rabbit@node1].

当节点在关闭期间没有联机对等端时,它将在不尝试与任何已知对等端同步的情况下启动。然而,它并不是作为一个独立的节点启动的,对等节点将能够重新加入它。

因此,当整个集群关闭时,最后一个关闭的节点是唯一一个在关闭时没有任何正在运行的对等节点的节点。该节点可以在不首先联系任何对等方的情况下启动。由于节点将尝试与已知对等方联系长达5分钟(默认情况下),因此可以在这段时间内以任何顺序重新启动节点。在这种情况下,他们将成功地一个接一个地重新加入对方。此时间窗口可以使用两种配置设置进行调整:


# wait for 60 seconds instead of 30
mnesia_table_loading_retry_timeout = 60000

# retry 15 times instead of 10
mnesia_table_loading_retry_limit = 15

通过调整这些设置和调整已知对等方必须返回的时间窗口,可以考虑到可能需要5分钟以上才能完成的集群范围内的重新部署场景。

在升级过程中,有时最后一个停止的节点必须是升级后第一个启动的节点。该节点将被指定执行集群范围的模式迁移,其他节点可以从该迁移同步并在重新加入时应用。

5、重新启动和运行状况检查(就绪探测器)

在某些环境中,通过指定的运行状况检查来控制节点的重新启动。检查验证一个节点是否已启动,并且部署过程可以继续到下一个节点。如果检查不通过,则认为节点的部署不完整,部署过程通常会等待一段时间并重试。这种环境的一个流行例子是Kubernetes,在这里,当使用OrderedReady pod管理策略时,操作员定义的就绪探测可以阻止部署继续进行。使用并行吊舱管理策略的部署不会受到影响,但必须担心初始集群形成期间的自然竞争条件。

考虑到上述对等同步行为,这样的健康检查可能会阻止集群范围内的重新启动及时完成。显式或隐式地假设重新加入其群集对等体的完全启动节点将失败并阻止进一步的节点部署的检查。

大多数健康检查,甚至是相对基本的健康检查,都隐含地假设节点已经完成引导。它们不适用于正在等待来自对等方的架构表同步的节点。

这种检查的一个非常常见的例子:


# will exit with an error for the nodes that are currently waiting for
# a peer to sync schema tables from
rabbitmq-diagnostics check_running

一种不希望节点完全启动并同步架构表的运行状况检查是:


# a very basic check that will succeed for the nodes that are currently waiting for
# a peer to sync schema from
rabbitmq-diagnostics ping

这种基本检查将允许部署继续进行,并允许节点最终重新加入彼此,假设它们是兼容的。

6、在重启时修改主机名

如果节点名称或主机名更改后重新加入的节点的数据目录路径因此而更改,则该节点可以作为空白节点启动。这样的节点将无法重新加入集群。当节点处于脱机状态时,其对等节点可以重置或使用空白数据目录启动。在这种情况下,恢复节点也将无法重新加入其对等节点,因为内部数据存储集群标识将不再匹配。

考虑以下情况:

  1. 形成由A、B和C三个节点组成的集群

  1. 节点A已关闭

  1. 节点B已重置

  1. 节点A已启动

  1. 节点A试图重新加入B,但B的群集标识已更改

  1. 节点B无法将A识别为已知群集成员,因为它已重置

在这种情况下,节点B将在日志中用适当的错误消息拒绝来自A的集群尝试:

Node 'rabbit@node1.local' thinks it's clustered with node 'rabbit@node2.local', but 'rabbit@node2.local' disagrees

在这种情况下,B可以再次重置,然后能够加入A,或者A可以重置,然后成功加入B。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

技术分享 | JMeter性能测试实现与分析

导语 JMeter是由著名开源软件巨头Apache组织开发的纯Java的压力测试工具&#xff0c;它即能测试动态服务&#xff08;WebService&#xff09;&#xff0c;也能测试静态资源&#xff0c;包括Servlet服务、CGI脚本等&#xff0c;还能测试动态语言服务&#xff08;PHP、Java、ASP…

二次元通用PPT模版

二次元通用PPT模版 共&#xff1a;19页 PPT模版&#xff1a;百度网盘 请输入提取码&#xff1a;4s3f

分布式任务调度-XXL-job

源码仓库地址 http://gitee.com/xuxueli0323/xxl-job 前置环境 docker容器环境配置 拉取msyql镜像&#xff1a; docker pull mysql:5.7创建mysql容器: docker run -p 3306:3306 --name mysql57 \ -v /opt/mysql/conf:/etc/mysql \ -v /opt/mysql/logs:/var/log/mysql \ -v …

一文带您了解什么是渲染农场

很多第一次接触渲染农场的朋友可能渲染农场都有些什么服务、渲染农场怎么收费和渲染农场怎么收费这样的疑问&#xff0c;今天小编就来给大家说一下吧。 什么是渲染农场&#xff1f; 目前&#xff0c;行业内普片认为&#xff1a;渲染农场是一种渲染服务&#xff0c;可以提供强…

剑指Offer || 116.省份数量

题目 有 n 个城市&#xff0c;其中一些彼此相连&#xff0c;另一些没有相连。如果城市 a 与城市 b 直接相连&#xff0c;且城市 b 与城市 c 直接相连&#xff0c;那么城市 a 与城市 c 间接相连。 省份 是一组直接或间接相连的城市&#xff0c;组内不含其他没有相连的城市。 …

如何使用ONLYOFFICE来P惊悚特效图

如何使用ONLYOFFICE来P惊悚特效图 老朋友们可能会经常看见本号主又换头像了&#xff0c;各种各样精神分裂成一群人或者我和自己俩个人的头像&#xff0c;之前讲过的&#xff1a; 手把手教你如何自己一个人精神分裂成一群人https://mp.weixin.qq.com/s/yacKt7N3sZnarfMhXRNdBA…

JDK1.8 新特性(二)【Stream 流】

前言 上节我们学了 lambda 表达式&#xff0c;很快我就在 Flink 的学习中用到了&#xff0c;我学的是 Java 版本的 Flink&#xff0c;一开始会以为代码会很复杂&#xff0c;但事实上 Flink 中很多地方都用到了 函数接口&#xff0c;这也让我们在编写 Flink 程序的时候可以使用 …

2023亚太杯数学建模思路 - 案例:ID3-决策树分类算法

文章目录 0 赛题思路1 算法介绍2 FP树表示法3 构建FP树4 实现代码 建模资料 0 赛题思路 &#xff08;赛题出来以后第一时间在CSDN分享&#xff09; https://blog.csdn.net/dc_sinor?typeblog 1 算法介绍 FP-Tree算法全称是FrequentPattern Tree算法&#xff0c;就是频繁模…

在线协作工具都有哪些?推荐这10款

如今&#xff0c;互联网的快速发展不仅改变了我们的生活方式&#xff0c;也改变了我们的工作方式。 特别是对于一些与产品设计相关的公司或团体&#xff0c;网络不仅为其设计提供了稳定的资源和灵感&#xff0c;而且为成员之间的沟通和合作提供了更大的便利。 如果您也需要为…

【Proteus仿真】【51单片机】公交车报站系统

文章目录 一、功能简介二、软件设计三、实验现象联系作者 一、功能简介 本项目使用Proteus8仿真51单片机控制器&#xff0c;使用LCD12864显示模块、DS18B20温度传感器、DS1302时钟模块、按键、LED蜂鸣器、ULN2003、28BYJ48步进电机模块等。 主要功能&#xff1a; 系统运行后&…

【Spring】依赖注入方式,DI的方式

这里写目录标题 1. setter注入在一个类中注入引用类型在一个类中注入简单类型 2. 构造器注入在一个类中注入引用类型在一个类中注入简单类型 3. 依赖注入方式选择4. 依赖自动装配按类型注入按名称注入 5. 集合注入 1. setter注入 在一个类中注入引用类型 回顾一下之前setter注…

【深度学习实验】网络优化与正则化(七):超参数优化方法——网格搜索、随机搜索、贝叶斯优化、动态资源分配、神经架构搜索

文章目录 一、实验介绍二、实验环境1. 配置虚拟环境2. 库版本介绍 三、优化算法0. 导入必要的库1. 随机梯度下降SGD算法a. PyTorch中的SGD优化器b. 使用SGD优化器的前馈神经网络 2.随机梯度下降的改进方法a. 学习率调整b. 梯度估计修正 3. 梯度估计修正&#xff1a;动量法Momen…

万宾科技智能井盖传感器,提升市政井盖健康

市政井盖就是城市里不可或缺的基础设施之一&#xff0c;关于它的监测工作可马虎不得。它承载着保护市民的交通安全以及城市正常运转的重要使命。虽然现在城市化的速度很快&#xff0c;但是传统的市政井盖管理方式变得有些力不从心了。井盖的覆盖范围很广&#xff0c;如果单单依…

Python爬虫动态ip代理防止被封的方法

目录 前言 一、什么是动态IP代理&#xff1f; 二、如何获取代理IP&#xff1f; 1. 付费代理IP 2. 免费代理IP 3. 自建代理IP池 三、如何使用代理IP爬取数据&#xff1f; 1. 使用requests库设置代理IP 2. 使用urllib库设置代理IP 3. 使用selenium库设置代理IP 四、常…

实现MQTT协议的服务器端和客户端的双向交互

公司和第三方合作开发一个传感器项目&#xff0c;想要通过电脑或者手机去控制项目现场的传感器控制情况。现在的最大问题在于&#xff0c;现场的边缘终端设备接入的公网方式是无线接入&#xff0c;无法获取固定IP&#xff0c;所以常规的HTTP协议通信就没法做&#xff0c;现在打…

群晖7.2安装Jellyfin+alist+CloudDriver搭建无盘影院中心

群晖7.2安装JellyfinalistCloudDriver搭建无盘影音中心。 实现思路如下&#xff1a; Jellyfin&#xff1a;提供个人影院功能。 alist&#xff08;xiaoya&#xff09;&#xff1a;给影院提供海量影音资源。 CloudDriver2&#xff1a;alist的资源为网络资源&#xff0c;通过C…

lvgl 画圆弧时进入 HardFault

目录 一、现象描述 lvgl 版本 二、问题分析 lvgl 需要的资源新建mcu 工程时默认分配的资源问题解决 一、现象描述 移植完lvgl 之后&#xff0c;能正常显示label&#xff0c;但是button arc 等复杂的控件都不能正常显示。调用官方的画圆弧demo 时&#xff0c;在多次调用 _lv…

业务流程图用什么软件画?这10款流程图软件,好用到飞起!

业务流程图是什么&#xff1f; 业务流程图是一种用于表示业务过程中活动流向的图形表示方法&#xff0c;它使用标准化的图形元素&#xff08;如箭头、椭圆、方框等&#xff09;来表达一个过程中各个环节之间的关系。在这个图形表示中&#xff0c;每个元素都有特定的含义和功能…

C#WPF用户控件及自定义控件实例

本文演示C#WPF自定义控件实例 用户控件(UserControl)和自定义控件(CustomControl)都是对UI控件的一种封装方式,目的都是实现封装后控件的重用。 只不过各自封装的实现方式和使用的场景上存在差异。 1 基于UserControl 创建 创建控件最简单一个方法就是基于UserControl …

Docker在Centos7下的安装

1、卸载旧版本 执行如下指令对旧版本进行卸载&#xff1a; sudo yum remove docker \docker-client \docker-client-latest \docker-common \docker-latest \docker-latest-logrotate \docker-logrotate \docker-engine 执行完毕后&#xff0c;如果输入docker version发现do…