linux基于systemd自启守护进程 systemctl自定义服务傻瓜式教程
系统服务
书接上文: linux自启任务详解
演示系统:ubuntu 20.04
开发部署项目的时候常常有这样的场景: 业务功能以后台服务的形式提供,部署完成后可以随着系统的重启而自动启动;服务异常挂掉后可以再次拉起
这个功能在ubuntu系统中通常由systemd提供
如果仅仅需要达成上述的场景功能,则systemd的自定义服务就可以满足
什么是systemd
systemd:系统和服务管理器
- 功能:
systemd 是一个初始化系统(init system)和服务管理器,它负责在 Linux 系统启动时启动系统的核心服务和进程。它的任务是管理系统引导、服务管理、进程监控、资源管理等。
systemd 提供了服务启动、停止、重启、日志记录等功能,并管理系统的运行状态。 - 作用:
启动和管理系统服务:systemd 会在系统启动时根据配置文件(服务单元文件)启动必要的系统服务(例如网络、日志记录、定时任务等)。
管理进程和依赖关系:systemd 确保服务按照正确的顺序启动,并且根据需要重启或停止。
资源管理:通过 cgroups(控制组)和其他技术,systemd 能够限制服务对 CPU、内存等资源的使用。 - 配置文件:
systemd 使用以 .service 结尾的单元文件(unit files)来定义服务。每个服务有一个单独的配置文件,这些文件描述了服务如何启动、停止、重启等。
例如,/etc/systemd/system/ 和 /lib/systemd/system/ 目录下存放着这些单元文件。
什么是systemctl
systemctl:管理 systemd 的命令行工具
- 功能:
systemctl 是与 systemd 配合使用的命令行工具,用于启动、停止、重新启动、查看、启用或禁用 systemd 管理的服务。它是用户与 systemd 交互的主要方式。 - 作用:
启动和停止服务:通过 systemctl 命令,你可以启动、停止或重启任何由 systemd 管理的服务。
查看服务状态:systemctl status 命令可以用来查看服务的当前状态,帮助管理员诊断服务是否正常运行。
管理系统:systemctl 也可用于关闭、重启、挂起系统等操作。
启用/禁用服务:systemctl enable 用于设置服务开机启动,systemctl disable 用于禁止服务开机启动。 - 常见命令示例:
- 启动服务:systemctl start <service_name>
- 停止服务:systemctl stop <service_name>
- 查看服务状态:systemctl status <service_name>
- 重启服务:systemctl restart <service_name>
- 设置服务开机启动:systemctl enable <service_name>
- 设置服务不开机启动:systemctl disable <service_name>
关系
- systemd 是基础,systemctl 是工具:
systemd 是系统和服务的管理器,它负责实际的服务管理、进程监控、资源分配等。而 systemctl 是一个命令行工具,用户通过它与 systemd 进行交互,执行启动、停止、查看状态等操作。
可以理解为,systemd 是背后的系统管理框架,而 systemctl 是用户与其交互的接口。 - systemctl 控制 systemd:
systemctl 是通过向 systemd 发送指令来管理服务和系统。例如,当你通过 systemctl start <service_name> 启动一个服务时,systemctl 会告诉 systemd 启动该服务,systemd 会根据服务的配置文件启动服务并管理它。
自定义自启动服务
linux自启任务详解
想要自定义一个自启服务,需要两个东西:可执行程序(我们自己的后台业务程序)和systemd的服务脚本
假设我们自己的业务程序名为:test_demo,服务脚本名为:test_demo.service
当然了这个程序仅做演示比较简单,仅有一个test_demo_main.c文件,代码如下:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <unistd.h>const char * filePath = "/home/lijilei/1.txt";
const char * text = "hello world\n";
const char * textend = "end lalala\n";
int g_count = 0;int main(int argc,char**argv)
{FILE *fp = NULL;fp = fopen(filePath,"a+");assert(fp > 0);while(true){sleep(6);fwrite(text, strlen(text),1,fp);fflush(fp); ++g_count;if(g_count > 10){fwrite(textend, strlen(textend),1,fp);break;}}fprintf(fp,"我要写东西: %s","东西");fflush(fp);fclose(fp);return 0;}
使用cc -o test_demo test_demo_main.c 可编译出test_demo程序
该演示程序逻辑相当简单:打开一个文件/home/lijilei/1.txt,向文件中分10次写入内容,然后退出
test_demo.service文件也相当简单
#move this file to /etc/systemd/system/
[Unit]
Description=Start up test_demo[Service]
Type=simple
ExecStart=/home/lijilei/xlib_xdnd/test_demo
Restart=on-failure[Install]
WantedBy=multi-user.target
脚本被systemd执行的时候会拉起ExecStart指定路径下的/home/lijilei/xlib_xdnd/test_demo程序;
将脚本放到/etc/systemd/system/目录下,按循序执行如下指令:
- sudo systemctl enable test_demo.service 启用服务,以便在系统启动时自动启动
- sudo systemctl start test_demo.service 启动test_demo.service服务,也就是变相的拉起配置的ExecStart=/home/lijilei/xlib_xdnd/test_demo程序
- sudo systemctl status test_demo.service 停止服务
当修改.service文件后执行
- sudo systemctl daemon-reload 当有修改.service文件时,需重新加载
上述的配置已经可以实现开机自启一个服务运行
自定义自启动守护进程
自启动守护进程的业务场景
在上述自启服务的基础上,将业务服务程序改为守护进程程序,使用守护进程去守护目标业务程序会更方便的控制业务程序的生命周期;
比如将守护进程改为看门狗程序,业务程序一直给看门狗发指令(喂狗),当业务程序因为业务崩溃了,则守护进程(看门狗主动拉起)业务程序,当然了我这里不会演示如何写一个看门狗程序,这里用定时查看进程快照的方式检测目标业务程序是否在执行,如果不在执行则拉起
什么是守护进程
守护进程是个孤儿进程,它的运行脱离了进程组的管控,无法接受进程退出信号,会一直运行在后台直到本身发生崩溃退出
为什么使用守护进程
守护进程的特性决定了它不会因为任何退出信号而关闭,所以适合用来执行监控任务,只要守护进程自带的业务逻辑足够简单,那守护进程将永远运行,直到系统关机,能让守护进程退出的方法只有三种
- 系统关机
- 找到守护进程的pid,手动kill
- 守护进程因自己的运行bug崩溃退出
因为systemd的功能,我们可以克服第一个方法跟第三个方法导致的守护进程因关机或崩溃而无法再次运行的问题
怎么写一个守护进程
这里创建一个名为daemond.c的文件,文件内容如下:
// daemon.c
#include <stdio.h>
#include <signal.h>
#include <sys/param.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <time.h>
#include <syslog.h>
#include <errno.h>
#include <string.h>
#include <assert.h>static FILE *g_fp = NULL;
static time_t g_now;
static const char* PIDFile = "/var/daemond.pid";
//static const char* LOCKDir = "/var/run/daemond";
static const char* LOGFile = "/var/log/daemond.txt";//看护的程序名字,可以是多个
static const char* PROCESSName1 = "test_demo";
static const char* PROCESSName2 = NULL;static int init_daemon(void)
{pid_t pid;int i;pid = fork();if(pid > 0){//第一步,结束父进程,使得子进程成为后台exit(0);}else if(pid < 0){return -1;}/*第二步建立一个新的进程组,在这个新的进程组中,子进程成为这个进程组的首进程,以使该进程脱离所用终端*/setsid();/*再次新建一个子进程,退出父进程,保证该进程不是进程组长,同时让该进程无法再打开一个新的终端*/pid = fork();if(pid > 0){exit(0);}else if(pid < 0){return -1;}//第三步:关闭所用从父进程继承的不再需要的文件描述符for(i = 0;i < NOFILE;close(i++));//第四步:改变工作目录,使得进程不与任何文件系统联系chdir("/");//第五步:将文件屏蔽字设置为0umask(0);//第六步:忽略SIGCHLD信号 执行第二步后就不需要执行该步骤signal(SIGCHLD,SIG_IGN);// 1. 忽略其他异常信号// 忽略子进程结束信号,防止产生僵尸进程//signal(SIGCLD, SIG_IGN);// 忽略管道破裂信号,防止程序因向已关闭的管道写入而异常退出//signal(SIGPIPE, SIG_IGN);// 忽略停止信号,守护进程通常不应被外部信号随意停止//signal(SIGSTOP, SIG_IGN);return 0;
}static int program_running_number(const char *prog)
{if(prog == NULL) {return 0;}FILE *fp;int count = 0;char buf[8] = {0};char command[128];snprintf(command, sizeof(command), \"ps -ef | grep -v grep | grep -w -c %s", prog);command[sizeof(command) - 1] = '\0';fp = popen(command, "r");if (fp == NULL) {time(&g_now);fprintf(g_fp,"系统时间:\t%s\t\t execute %s failed: %s",ctime(&g_now),command, strerror(errno));fflush(g_fp);return 0;}if (fgets(buf, sizeof(buf), fp)) {count = atoi(buf);}pclose(fp);return count;
}static int createPIDFile(const char* File)
{umask(000);FILE *pidfile = fopen(File, "w");if (pidfile) {fprintf(pidfile, "%d", getpid());fclose(pidfile);return 0;} else {return -1;}
}static int createLOCKDir(const char* dir)
{char cmd[256] = {0};sprintf(cmd,"mkdir %s",dir);int ret = system(cmd);if (ret == 0) {return 0;} else {return -1;}
}static void watchProcess(const char** prcsessList)
{for (const char **prog = prcsessList; *prog; prog++) {if (program_running_number(*prog) > 0) {//fprintf(g_fp,"%s is running.\n", *prog);} else {time(&g_now);fprintf(g_fp,"系统时间:%s %s isn't running.\n",ctime(&g_now),*prog);fflush(g_fp);//再次执行唤起目标程序指令(可替换拉起进程指令)char cmd[256] = {0};sprintf(cmd,"sudo systemctl start %s.service",*prog);fprintf(g_fp,"执行命令: %s\n",cmd);int value = system(cmd);if (value == -1) {time(&g_now);fprintf(g_fp,"系统时间:%s %s : system() failed\n",ctime(&g_now),cmd);fflush(g_fp);} else if (WIFEXITED(value)) {time(&g_now);fprintf(g_fp,"系统时间:%s %s executed successfully with exit code %d: succeed\n",ctime(&g_now),cmd,WEXITSTATUS(value));fflush(g_fp);} else if (WIFSIGNALED(value)) {time(&g_now);fprintf(g_fp,"系统时间:%s %s : terminated by signal %d\n",ctime(&g_now),cmd,WTERMSIG(value));fflush(g_fp);} else {time(&g_now);fprintf(g_fp,"系统时间:%s %s : Unknown status\n",ctime(&g_now),cmd);fflush(g_fp);printf("Unknown status\n");}} }
}int main()
{init_daemon(); createPIDFile(PIDFile);//createLOCKDir(LOCKDir);while(1) {sleep(3);g_fp = fopen(LOGFile,"a+");if(g_fp == NULL) {return -1;}const char *program_name_list[] = {PROCESSName1, PROCESSName2};//这里修改进程看护逻辑watchProcess(program_name_list);fflush(g_fp);fclose(g_fp);}return 0;
}
使用cc -o daemond daemon.c 可编译出daemond守护进程程序
该daemond逻辑比较简单,就是负责监视test_demo程序,如果test_demo程序退出了就调用systemctl指令,执行test_demo.service,再次拉起test_demo
daemond.service的写法就稍微跟test_demo.service不同了
#move this file to /etc/systemd/system/
[Unit]
Description=Start up daemond
After=network.target
[Service]
User=root
Group=root
ExecStart=/home/lijilei/xlib_xdnd/daemond --single-instance
#当进程退出时自动重启
Restart=always
#适用于后台运行的服务,systemd 等待父进程退出,并且通过 PID 文件确认进程启动
Type=forking
#适用于后台运行的服务,systemd 等待父进程退出,并且通过 PID 文件确认进程启动
PIDFile=/var/daemond.pid
#只终止主进程,不终止子进程
KillMode=process
#RestartSec=5 #服务崩溃后会等待 5 秒钟再重启
#StartLimitIntervalSec=10 #定义了一个 10 秒的时间窗口
#StartLimitBurst=1 #在 10 秒内,服务最多重启 1 次。如果超过这个次数,systemd 将不会再重启服务
#删除PID文件
ExecStopPost=/bin/rm -f /var/daemond.pid
#删除日志文件
ExecStopPost=/bin/rm -f /var/log/daemond.txt
[Install]
WantedBy=multi-user.target
将脚本放到/etc/systemd/system/目录下,按顺序执行如下指令:
- sudo systemctl enable daemond.service 启用服务,以便在系统启动时自动启动
- sudo systemctl start daemond.service daemond.service服务,也就是变相的拉起配置的/home/lijilei/xlib_xdnd/daemond程序
执行效果
把test_demo.service和daemond.service都加入开机自启后会出现如下现象:
- test_demo.service会拉起test_demo程序
- test_demo程序在完成打印后退出
- daemond查找进程快照发现test_demo退出,就执行systemctl脚本test_demo.service
- test_demo.service会拉起test_demo程序
- …如此反复执行
查看下daemon.service的执行状态
$ sudo systemctl status daemond.service ● daemond.service - Start up daemondLoaded: loaded (/etc/systemd/system/daemond.service; enabled; vendor preset: enabled)Active: active (running) since Fri 2024-11-22 01:43:28 UTC; 2 weeks 0 days agoMain PID: 125749 (daemond)Tasks: 1 (limit: 14203)Memory: 13.9MCGroup: /system.slice/daemond.service└─125749 /home/lijilei/xlib_xdnd/daemond --single-instanceWarning: journal has been rotated since unit was started, output may be incomplete.
发现这个服务已经连续运行两周了
查看下1.txt内容:

发现已经打印了20几万行信息了
附录
如果你在 systemd 单元文件中使用了其他不熟悉或不常见的配置项,建议通过以下命令来验证服务单元文件的正确性:
- sudo systemd-analyze verify /etc/systemd/system/your_service.service
这个框架有个问题就是daemon在调用system()函数时能执行但是返回值是-1,猜测是由systemctl导致的.后面我再研究研究
以上
相关文章:
linux基于systemd自启守护进程 systemctl自定义服务傻瓜式教程
系统服务 书接上文: linux自启任务详解 演示系统:ubuntu 20.04 开发部署项目的时候常常有这样的场景: 业务功能以后台服务的形式提供,部署完成后可以随着系统的重启而自动启动;服务异常挂掉后可以再次拉起 这个功能在ubuntu系统中通常由systemd提供 如果仅仅需要达成上述的场…...
HTTP协议和接口测试详解
介绍接口测试前我们先来介绍一下HTTP协议,为什么先要介绍HTTP协议呢因为因为我们做接口测试其实就是用测试工具(postman,fiddler,jmeter等等)或代码来模拟用户使用软件的场景,在我们模拟的时候不像平时功能测试时我们有已经开发完…...
vue3【实战】定义全局方法(两种方案)
以全局方法 calculate 为例 src/utils/calculate.ts export default {sum: function (a: number, b: number) {return a b} }方案1: 依赖注入 provide inject main.ts import calculate from ./utils/calculateapp.provide(calculate, calculate)页面中 // esl…...
基于JavaScript的DBUtils增删改查操作实验
1、实验目的 学习和掌握数据库连接池的配置与管理。使用DBUtils进行增删改查操作。按照步骤,掌握并实现使用DBUtils实现增删改查的全过程。 2、实验所用方法 上机实践 3、实验步骤及截图 创建一个数据库表,使用下面sql语句创建数据库表并插入数据&#x…...
初学stm32 --- 系统时钟配置
众所周知,时钟系统是 CPU 的脉搏,就像人的心跳一样。所以时钟系统的重要性就不言而喻了。 STM32 的时钟系统比较复杂,不像简单的 51 单片机一个系统时钟就可以解决一切。于是有人要问,采用一个系统时钟不是很简单吗?为…...
实现星星评分系统
使用HTML、CSS和JavaScript实现星星评分系统 本文将详细讲解如何使用 HTML、CSS 和 JavaScript 实现一个简单的星星评分系统。用户可以通过点击星星进行评分,并且还能够看到星星的悬浮效果和已选中状态。 1. HTML 结构 我们首先在 HTML 中定义了一个星星评分的结…...
数据库建模工具 PDManer
数据库建模工具 PDManer 1.PDManer简介2.PDManer使用 1.PDManer简介 PDManer(元数建模)是一款功能强大且易于使用的开源数据库建模工具。它不仅支持多种常见数据库,如MySQL、PostgreSQL、Oracle、SQL Server等,还特别支持国产数据…...
后台运维操作建议
文章目录 1.版本升级2.配置发布3.数据库/脚本操作4.发布依赖确认5.发布规范6.服务下线参考文献 1.版本升级 版本升级是软件维护和演进中的关键环节,但它可能带来一系列问题。这些问题涉及兼容性、功能、性能、安全性等方面。 【强制】版本管理:使用版本…...
NX二次开发调用内部函数设置对象穿透显示DSS_ATTR_set_show_through
获取动态库libdisp.dll的路径 void TcharToChar(const TCHAR* tchar, char* _char) {int iLength; #if UNICODE//获取字节长度 iLength = WideCharToMultiByte(CP_ACP, 0, tchar, -1, NULL, 0, NULL, NULL);//将tchar值赋给_char WideCharToMultiByte(CP_ACP, 0, tchar, …...
ubuntu16.04ros-用海龟机器人仿真循线系统
下载安装sudo apt-get install ros-kinetic-turtlebot ros-kinetic-turtlebot-apps ros-kinetic-turtlebot-interactions ros-kinetic-turtlebot-simulator ros-kinetic-kobuki-ftdi sudo apt-get install ros-kinetic-rocon-*echo "source /opt/ros/kinetic/setup.bash…...
解决Ubuntu 20.04上编译OpenCV 3.2时遇到的stdlib.h缺失错误
解决Ubuntu 20.04上编译OpenCV 3.2时遇到的stdlib.h缺失错误 您在 Ubuntu 20.04 上编译 OpenCV 3.2 时遇到的错误与 C 标准库的头文件配置问题有关。错误消息指出系统无法找到 <stdlib.h>,这通常与预编译头文件的处理、GCC 版本或者头文件搜索路径有关。下面…...
HTML综合案例
为了前端考试。 效果图: HTML代码: <!DOCTYPE html> <html lang"zh-CN"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><…...
TanStack——为现代前端开发提供高性能和灵活的工具
TanStack 是一个由社区主导的开源项目集合,专注于为现代前端开发提供高性能和灵活的工具。它包括多个流行的 JavaScript 和 TypeScript 库,主要用于处理表格、查询、虚拟化、状态管理等功能。 文章目录 1、TanStack Query:1.1 useQuery&#…...
Java爬虫️ 使用Jsoup库进行API请求有什么优势?
在Java的世界里,Jsoup库以其强大的HTML解析能力而闻名。它不仅仅是一个简单的解析器,更是一个功能齐全的工具箱,为开发者提供了从网页抓取到数据处理的一站式解决方案。本文将深入探讨使用Jsoup库进行API请求的优势,并提供代码示例…...
React源码02 - 基础知识 React API 一览
1. JSX到JavaScript的转换 <div id"div" key"key"><span>1</span><span>2</span> </div>React.createElement("div", // 大写开头会当做原生dom标签的字符串,而组件使用大写开头时,这…...
COMSOL with Matlab
文章目录 基本介绍COMSOL with MatlabCOMSOL主Matlab辅Matlab为主Comsol为辅 操作步骤常用指令mphopenmphgeommghmeshmphmeshstatsmphnavigatormphplot常用指令mphsavemphlaunchModelUtil.clear 实例教学自动另存新档**把语法套用到边界条件**把语法套用到另存新档 函数及其微分…...
【报表查询】.NET开源ORM框架 SqlSugar 系列
文章目录 前言实践一、按月统计没有为0实践二、 统计某月每天的数量实践三、对象和表随意JOIN实践四、 List<int>和表随意JOIN实践五、大数据处理实践六、每10分钟统计Count实践七、 每个ID都要对应时间总结 前言 在我们实际开发场景中,报表是最常见的功能&a…...
PostgreSQL数据库访问限制详解
pg_hba.conf 文件是 PostgreSQL 数据库系统中非常重要的一个配置文件,它用于定义哪些用户(或客户端)可以连接到 PostgreSQL 数据库服务器,以及他们可以使用哪些认证方法进行连接。 pg_hba.conf 的名称来源于 "Host-Based Aut…...
【test linux】创建一个ext4类型的文件系统
创建一个ext4类型的文件系统 dd 是一个非常强大的命令行工具,用于在Unix/Linux系统中进行低级别的数据复制和转换。这条命令的具体参数含义如下: if/dev/zero:指定输入文件(input file)为 /dev/zero,这是一…...
如何在繁忙的生活中找到自己的节奏?
目录 一、理解生活节奏的重要性 二、分析当前生活节奏 1. 时间分配 2. 心理状态 3. 身体状况 4. 生活习惯 1. 快慢适中 2. 张弛结合 3. 与目标相符 三、掌握调整生活节奏的策略 1. 设定优先级 2. 合理规划时间 3. 学会拒绝与取舍 4. 保持健康的生活方式 5. 留出…...
JVM垃圾回收机制全解析
Java虚拟机(JVM)中的垃圾收集器(Garbage Collector,简称GC)是用于自动管理内存的机制。它负责识别和清除不再被程序使用的对象,从而释放内存空间,避免内存泄漏和内存溢出等问题。垃圾收集器在Ja…...
MVC 数据库
MVC 数据库 引言 在软件开发领域,Model-View-Controller(MVC)是一种流行的软件架构模式,它将应用程序分为三个核心组件:模型(Model)、视图(View)和控制器(Controller)。这种模式有助于提高代码的可维护性和可扩展性。本文将深入探讨MVC架构与数据库之间的关系,以…...
如何在看板中有效管理突发紧急任务
在看板中有效管理突发紧急任务需要:设立专门的紧急任务通道、重新调整任务优先级、保持适度的WIP(Work-in-Progress)弹性、优化任务处理流程、提高团队应对突发情况的敏捷性。其中,设立专门的紧急任务通道尤为重要,这能…...
【android bluetooth 框架分析 04】【bt-framework 层详解 1】【BluetoothProperties介绍】
1. BluetoothProperties介绍 libsysprop/srcs/android/sysprop/BluetoothProperties.sysprop BluetoothProperties.sysprop 是 Android AOSP 中的一种 系统属性定义文件(System Property Definition File),用于声明和管理 Bluetooth 模块相…...
【论文阅读28】-CNN-BiLSTM-Attention-(2024)
本文把滑坡位移序列拆开、筛优质因子,再用 CNN-BiLSTM-Attention 来动态预测每个子序列,最后重构出总位移,预测效果超越传统模型。 文章目录 1 引言2 方法2.1 位移时间序列加性模型2.2 变分模态分解 (VMD) 具体步骤2.3.1 样本熵(S…...
(一)单例模式
一、前言 单例模式属于六大创建型模式,即在软件设计过程中,主要关注创建对象的结果,并不关心创建对象的过程及细节。创建型设计模式将类对象的实例化过程进行抽象化接口设计,从而隐藏了类对象的实例是如何被创建的,封装了软件系统使用的具体对象类型。 六大创建型模式包括…...
Bean 作用域有哪些?如何答出技术深度?
导语: Spring 面试绕不开 Bean 的作用域问题,这是面试官考察候选人对 Spring 框架理解深度的常见方式。本文将围绕“Spring 中的 Bean 作用域”展开,结合典型面试题及实战场景,帮你厘清重点,打破模板式回答,…...
comfyui 工作流中 图生视频 如何增加视频的长度到5秒
comfyUI 工作流怎么可以生成更长的视频。除了硬件显存要求之外还有别的方法吗? 在ComfyUI中实现图生视频并延长到5秒,需要结合多个扩展和技巧。以下是完整解决方案: 核心工作流配置(24fps下5秒120帧) #mermaid-svg-yP…...
使用SSE解决获取状态不一致问题
使用SSE解决获取状态不一致问题 1. 问题描述2. SSE介绍2.1 SSE 的工作原理2.2 SSE 的事件格式规范2.3 SSE与其他技术对比2.4 SSE 的优缺点 3. 实战代码 1. 问题描述 目前做的一个功能是上传多个文件,这个上传文件是整体功能的一部分,文件在上传的过程中…...
【Linux】Linux安装并配置RabbitMQ
目录 1. 安装 Erlang 2. 安装 RabbitMQ 2.1.添加 RabbitMQ 仓库 2.2.安装 RabbitMQ 3.配置 3.1.启动和管理服务 4. 访问管理界面 5.安装问题 6.修改密码 7.修改端口 7.1.找到文件 7.2.修改文件 1. 安装 Erlang 由于 RabbitMQ 是用 Erlang 编写的,需要先安…...
