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

Docker Compose构建lnmp

目录

Compose的优点

编排和部署

Compose原理

Compose应用案例

安装docker-ce

阿里云镜像加速器

安装docker-compose

docker-compose用法

Yaml简介

验证LNMP环境


Compose的优点

先来了解一下我们平时是怎么样使用docker的?把它进行拆分一下:

1、docker search 镜像,是不是先查找一个镜像;

2、docker run -itd 镜像名称 ,然后在运行这个镜像;

3、然后如果你要在运行第二个镜像、第三个镜像.....等等,你是不是又要docker search、docker run运行。

上面“ docker run -itd 镜像名称 ”这只是最小的动作,如果你要映射硬盘,设置nat网络或者映射端口等等。就要做更多的 docker 操作,这显然是非常没有效率的,况且如果你要大规模部署,是不是觉得就很麻烦了。

但是我们写在docker-compose里面就很好了。你只需要写好后只运行一句:

docker-compose up -d

编排和部署

编排,即orchestration,它根据被部署的对象之间的耦合关系,以及被部署对象环境的依赖,制定部署流程中各个动作的执行顺序,部署过程所需要的依赖文件的存储位置和获取方式,以及如何验证部署成功。这些信息都会在编排工具中以指定的格式(比如配置文件或者特定的代码)来要求运维人员定义并保存起来,从而保证这个流程能够随时在全新的环境中可靠有序地重现出来。

部署,即deployment,它是指按照编排所指定的内容和流程 ,在目标机器上执行编排指定环境初始化,存放指定的依赖和文件,运行指定的部署动作,最终按照编排中的规则来确认联署成功。

这么来解释吧,编排是一个指挥家,他的大脑里存储了整个乐曲的演奏流程,对于每一个小节每一段音乐的演奏方式、开始、结束他都了然于胸;部署就是整个乐队,他们严格按照指挥家的意图用乐器来完成乐谱的执行,在需要时开始演奏,又在适当的时机停止演奏。最终,两者通过协作就能把每一位演奏者独立的演奏通过组合、重叠、衔接来形成高品位的交响乐。

Compose原理

docker-compose的调用过程扁平的像一张纸,仅用一张简单的模块图就足够解释明白,如下图所示:

 

 

首先,用户执行的docker-compose up -d指令调用了命令行中的启动方法。功能很简单明了,一个docker-compose.yml定义了一个docker-compose的project,docker-compose操作提供的命令行参数则作为这个project的启动参数交由project模块去处理。

其次,如果当前宿主机已经存在与该应用对应的容器,docker-compose将进行行为逻辑判断。如果用户指定可以重新启动已有服务,docker-compose就会执行service模块的容器重启方法,否则就将直接启动已有容器。这两种操作的区别在于前者会停止旧的容器,创建启动新的容器,并把旧容器移除掉。在这个过程中创建容器的各项定义参数都是从docker-compose up 指令和docker-compose.yml中传入的。

接下来,启动容器的方法也很简洁,这个方法中完成了一个Docker容器启动所需的主要参数的封装,并在container模块执行启动。该方法所支持的参数我想大多数朋友过是有所了解的。

最后,container模块会调用docker-py客户端执行向Docker daemon发起创建容器的POST请求,再往后就是Docker处理的范畴了,相信看过我这篇文章 Docker:架构拆解请的朋友就明白了。

为了能够说明compose如何实现上述编排与部署的原理,下面和大家分享一个通过compose来编排部署LNMP服务来更好的理解它。

Compose应用案例

安装docker-ce

[root@localhost ~]# wget -O /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo[root@localhost ~]# yum -y install yum-utils device-mapper-persistent-data lvm2
[root@localhost ~]# yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo[root@localhost ~]# ls /etc/yum.repos.d/
backup  Centos-aliyun.repo  CentOS-Media.repo  docker-ce.repo[root@localhost ~]# yum -y install docker-ce
[root@localhost ~]# systemctl start docker
[root@localhost ~]# systemctl enable docker

阿里云镜像加速器

https://cr.console.aliyun.com/

 

[root@localhost ~]# cat << END > /etc/docker/daemon.json
{"registry-mirrors":[ "https://nyakyfun.mirror.aliyuncs.com" ]
}
END
[root@localhost ~]# systemctl daemon-reload
[root@localhost ~]# systemctl restart docker
[root@localhost ~]# docker version
Client: Docker Engine - CommunityVersion:           19.03.8API version:       1.40Go version:        go1.12.17Git commit:        afacb8bBuilt:             Wed Mar 11 01:27:04 2020OS/Arch:           linux/amd64Experimental:      falseServer: Docker Engine - CommunityEngine:Version:          19.03.8API version:      1.40 (minimum version 1.12)Go version:       go1.12.17Git commit:       afacb8bBuilt:            Wed Mar 11 01:25:42 2020OS/Arch:          linux/amd64Experimental:     falsecontainerd:Version:          1.2.13GitCommit:        7ad184331fa3e55e52b890ea95e65ba581ae3429runc:Version:          1.0.0-rc10GitCommit:        dc9208a3303feef5b3839f4323d9beb36df0a9dddocker-init:Version:          0.18.0GitCommit:        fec3683

安装docker-compose

下载最新版本安装,下载时间可能比较长

[root@docker ~]# curl -L https://github.com/docker/compose/releases/download/2.17.2/docker-compose-$(uname -s)-$(uname -m) -o /usr/local/bin/docker-compose% Total    % Received % Xferd  Average Speed   Time    Time     Time  CurrentDload  Upload   Total   Spent    Left  Speed
100     9  100     9    0     0      4      0  0:00:02  0:00:02 --:--:--     4[root@compose ~]# chmod +x /usr/local/bin/docker-compose
[root@docker ~]# docker-compose --version
docker-compose version 1.21.1, build 5a3f1a3
或者用pip安装:pip install docker-compose

docker-compose用法

docker-compose  [-f <arg>...] [options] [COMMAND] [ARGS...]

docker-compose 常用选项:

  1. --verbose 输出更多调试信息。
  2. --version 打印版本并退出。
  3. -f, --file FILE 使用特定的 compose 模板文件,默认为docker-compose.yml。
  4. -p, --project-name NAME 指定项目名称,默认使用目录名称。

docker-compose常用命令:

  1. build               构建或重建服务
  2. kill             杀掉容器
  3. logs             显示容器的输出内容
  4. port             打印绑定的开放端口
  5. ps              显示容器
  6. pull              拉取服务镜像
  7. restart            重启服务
  8. rm               删除停止的容器
  9. run              运行一个一次性命令
  10. scale               设置服务的容器数目
  11. exec                切换到容器内
  12. start               开启服务
  13. stop                停止服务
  14. up                创建并启动容器

其实这些常用命令用docker的命令功能是一样的。

Yaml简介

YAML是一种标记语言,可读性很强。类似于XML数据描述语言,语法比XML简单的多。YAML数据结构通过缩进来表示,连续的项目通过减号来表示,键值对用冒号分割,数组用括号括起来,hash用花括号括起来。

YAML文件格式注意事项:

在缩排中空白字符的数目并不是非常重要,只要相同阶层的元素左侧对齐就可以了(不过不能使用TAB字符);

  1. 通常开头缩进2个空格;
  2. 字符的后面缩进1个空格,比如冒号、逗号、横杆;
  3. 支持#注释;
  4. 允许在文件中加入选择性的空行,以增加可读性;

docker-compose中YAML常用的字段:

目录结构

[root@docker ~]# tree compose_lnmp
compose_lnmp
├── docker-compose.yml
├── mysql
│   ├── conf
│   │   └── my.cnf
│   └── data
├── nginx
│   ├── Dockerfile
│   ├── nginx-1.12.1.tar.gz
│   ├── nginx.conf
│   └── run.sh
├── php
│   ├── Dockerfile
│   ├── php-5.6.39.tar.gz
│   └── php.ini
└── wwwroot├── index.html└── index.php6 directories, 11 files

 编写compose文件

[root@docker compose_lnmp]# vim docker-compose.yml
version: '3'
services:nginx:hostname: nginxbuild:context: ./nginxdockerfile: Dockerfileports:- 80:80networks:- lnmpvolumes:- ./wwwroot:/usr/local/nginx/htmlphp:hostname: phpbuild:context: ./phpdockerfile: Dockerfileports:- 9000:9000networks:- lnmpvolumes:- ./wwwroot:/usr/local/nginx/htmlmysql:hostname: mysqlimage: mysql:5.6ports:- 3306:3306networks:- lnmpvolumes:- ./mysql/conf:/etc/mysql/conf.d- ./mysql/data:/var/lib/mysqlcommand: --character-set-server=utf8environment:MYSQL_ROOT_PASSWORD: 123456MYSQL_DATABASE: wordpressMYSQL_USER: userMYSQL_PASSWORD: user123networks:lnmp:

可以看到一份标准配置文件应该包含 version、services、networks 三大部分,共有三级标签,每一级都是缩进两个空格。下面来详细说明一下里面的内容:

version: '3'  这是定义compose的版本号为version 3,可以参考官方文档详细了解具体有哪些版本 Overview | Docker Documentation

services:

nginx:这是services下面的二级标签,名字用户自己定义,它将是服务运行后的名称;
hostname: nginx 这是定义容器的主机名,将写入到/etc/hostname中;

build:

 context: ./nginx 指定nginx服务的上下文路径;

 dockerfile:Dockerfile 指定通过上面指定路径中的Dockerilfe来构建;

ports:

 - 80:80 端口映射没什么好说的;

networks:

 -lnmp 指定的网络环境
volumes:把宿主机的/wwwroot目录绑定到容器中的/usr/local/nginx/html目录;

php:这个二级标签服务和下面的内容跟nginx差不多;

mysql:这个二级标签服务也和nginx、php差不多,唯一不同的是多了个images标签、还有定义了些环境变量。

image: mysql:5.6 它是通过mysql:5.6镜像来构建mysql服务器,前面nginx、php都指定了上下文通过Dockerfile来构建的。

  environment:

   MYSQL_ROOT_PASSWORD:定义root用户密码变量为123456;

   MYSQL_DATABASE:定义了数据变量为wordpress;

   MYSQL_USER:定义了普通用户变量为user;

   MYSQL_PASSWORD:定义了普通用户密码变量为user123;

3、networks:

   lnmp: 相当于执行docker network create lnmp命令了;

最后来运行docker-compose命令来启动:

[root@docker ~]# cat centos-7-x86_64.tar.gz | docker import - centos:7
sha256:711eccab16302fcb8bb969f08f8c56741a549d8289f3590e083438689f6db2b5
[root@docker ~]# docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
centos              7                   711eccab1630        18 seconds ago      589 MB[root@docker compose_lnmp]# docker-compose -f docker-compose.yml up -d
Creating compose_lnmp_mysql_1 ... done
Creating compose_lnmp_php_1   ... done
Creating compose_lnmp_nginx_1 ... done
[root@docker compose_lnmp]# docker-compose psName                      Command               State           Ports         
--------------------------------------------------------------------------------------
compose_lnmp_mysql_1   docker-entrypoint.sh --cha ...   Up      0.0.0.0:3306->3306/tcp
compose_lnmp_nginx_1   /run.sh                          Up      0.0.0.0:80->80/tcp    
compose_lnmp_php_1     ./sbin/php-fpm -c /usr/loc ...   Up      0.0.0.0:9000->9000/tcp

验证LNMP环境

客户端使用浏览器验证 docker-compose 创建的 lnmp 环境

 

PS:如果访问报 Access denined.,手动进入 nginx 容器添加读权限。

docker-compose解决的问题局限在“编排”二字,甚至连“部署”范畴都涉足甚少,而在一个能够服务于大众的云平台中,编排与部署也仅仅是其中的一个组成部分而已。来一起分析一下它的局限制会有哪些:

  1. docker-compse是面向单宿主机部署的,这是一种部署能力的欠缺。在更多的场合下,管理员需要面对大量物理服务器(或者虚拟机),这时如果要实现基于docker-compose的容器自动化编排与部署,管理员就得借助成熟的自动化运维工具(ansible、puppet、chef、saltstack)来负责管理多个目标主机,将docker-compose所需的所有资源(配置文件、用户代码)交给目标主机,然后在目标主机上执行docker-compose指令。
  2. 同样网络和存储也比较棘手,Docker不能提供跨宿主机的网络,完全面向Docker daemon的docker-compose当然也不支持。这意味着管理员必须部署一套类似于Open vSwich的独立网络工具,而且管理员还需要完成集成工作。当好不容易把容器编排都安排妥当之后,又会发现容器还处在内网环境中,于是负载均衡、服务发现等一堆问题就面临而来了,这些问题很快能消耗掉工程师所有的耐心。

那么,是否有一种能够提供完善的面向服务器集群的Docker编排和部署方案呢?Docker官方给出的答案是Compose同Machine和Swarm联动,其实还有大家近期经常听到了kubernetes(k8s)。

相关文章:

Docker Compose构建lnmp

目录 Compose的优点 编排和部署 Compose原理 Compose应用案例 安装docker-ce 阿里云镜像加速器 安装docker-compose docker-compose用法 Yaml简介 验证LNMP环境 Compose的优点 先来了解一下我们平时是怎么样使用docker的&#xff1f;把它进行拆分一下&#xff1a; 1…...

Flutter开发问题记录

1. Q:Mac电脑通过AndroidStudio运行软件到iphone报错 automatically assigning platform iOS with version 10.0 on target Runner because no platform was specified. A:项目中ios目录下,Podfile文件第2行 platform :ios, ‘11.0’&#xff0c;取消注释 2. Q:Mac电脑通过And…...

如何使用本地mock数据

当后端同事接口数据还未完成&#xff0c;我们前端开发需要使用数据时&#xff0c;怎么办呢&#xff1f;这里可以自己本地mock数据先用着啦&#xff01;仅在开发时使用 1. 创建一个 xxx.js文件&#xff0c;对外暴露一个数组&#xff1b; 对新建js文件编写导出&#xff0c;返回数…...

XXL-JOB定时任务框架(Oracle定制版)

特点 xxl-job是一个轻量级、易扩展的分布式任务调度平台&#xff0c;能够快速开发和简单学习。开放源代码并被多家公司线上产品使用&#xff0c;开箱即用。尽管其确实非常好用&#xff0c;但我在工作中使用的是Oracle数据库&#xff0c;因为xxl-job是针对MySQL设计的&#xff…...

SpringBoot + ajax 实现分页和增删查改

0目录 1.SpringBoot 2.SpringBoot分页&#xff1b;增删改查 1.SpringBoot分页 创建数据库和表 创建SpringBoot工程&#xff0c;引入springboot下的分页依赖 配置application.yml 实体类 Mapper接口 Mapper.xml Service接口 Service实现类 控制层 测试 加…...

ProxyGenerator-代理类生成器

ProxyGenerator是JDK-sun包下提供的用于生成动态代理类信息的类&#xff0c;其唯一向外透出的是其静态方法-generateProxyClass(…)。 public class ProxyGenerator { ... }学习本篇文章&#xff0c;就是想学习ProxyGenerator如何生成代理类信息的过程。 一、唯一入口-公开静…...

ARM 内存屏障指令

ARM 内存屏障指令 1. dmb (Data Memory Barrier) 数据内存栅栏2. dsb (Data Synchronization Barrier) 数据同步栅栏3. isb (Instruction Synchronization Barrier) 指令同步栅栏4. ARM 内存屏障指令如何选择使用&#xff1f;5. 使用示例5.1. DMB指令示例&#xff1a;5.2. DSB指…...

了解Linux 的 mmap --- 笔记

学习这篇博客&#xff0c;进行了一些归纳Linux下mmap_linux mmap_一个山里的少年的博客-CSDN博客https://blog.csdn.net/qq_56999918/article/details/127070280 >>读取文件 读取文件方法&#xff1a;由操作系统提供的两个方法&#xff0c;read和write来读写文件。 由…...

docker删除容器(步骤详解)

要在Docker中删除容器&#xff0c;需要使用命令docker rm。 下面是详细步骤&#xff1a; 1. 首先&#xff0c;使用docker ps命令查看当前正在运行的容器。这个命令会列出所有正在运行的容器的ID、名称、状态等信息。 如果没有正在运行的容器可以通过docker ps -a 查看当前所…...

boost beast http server 测试

boost beast http client boost http server boost beast 是一个非常好用的库&#xff0c;带上boost的协程&#xff0c;好很多东西是比较好用的&#xff0c;以下程序使用四个线程开启协程处理进入http协议处理。协议支持http get 和 http post #include <boost/beast/cor…...

Android 10.0 系统开启禁用adb push和adb pull传输文件功能

1.使用场景 在进行10.0的系统开发中,在一些产品中由于一些开发的功能比较重要,防止技术点外泄在出货产品中,禁用 adb pull 和adb push等命令 来获取系统system下的jar 和apk 等文件,所以需要禁用这些命令 2.系统开启禁用adb push和adb pull传输文件功能的分析 看了下系统…...

浙大数据结构第七周之07-图4 哈利·波特的考试

基础知识&#xff1a;&#xff08;最短路的前提都是在图中两条边之间的权值非定值&#xff09; &#xff08;一&#xff09;Dijkstra方法 算法实现&#xff1a; …...

vue2-vue项目中你是如何解决跨域的?

1、跨域是什么&#xff1f; 跨域本质是浏览器基于同源策略的一种安全手段。 同源策略&#xff08;sameoriginpolicy&#xff09;&#xff0c;是一种约定&#xff0c;它是浏览器最核心也是最基本的安全功能。 所谓同源&#xff08;即指在同一个域&#xff09;具有以下三个相同点…...

【Paper Reading】DETR:End-to-End Object Detection with Transformers

背景 Transformer已经在NLP领域大展拳脚&#xff0c;逐步替代了LSTM/GRU等相关的Recurrent Neural Networks&#xff0c;相比于传统的RNN&#xff0c;Transformer主要具有以下几点优势 可解决长时序依赖问题&#xff0c;因为Transformer在计算attention的时候是在全局维度进行…...

【rust/入门】windows安装rust gnu环境(折腾)

说在前面 首先说明&#xff0c;我是rust入门选手&#xff0c;之前都是在wsl写rust&#xff0c;突然想在windows下装下rust。windows版本&#xff1a;windows11 22H2原文换源 心路历程 看到教程我陷入了沉默&#xff0c;(官方推荐) 打开Microsoft C Build Tools我开始不解&…...

java面试---字符串相关内容

字符串 1. 什么是Java中的字符串池&#xff08;String Pool&#xff09;&#xff1f;2. String、StringBuilder和StringBuffer之间的区别是什么&#xff1f;3. 如何比较两个字符串的内容是否相等&#xff1f;4、equals和的区别5. String类有哪些常用的方法&#xff1f; 1. 什么…...

MYSQL进阶-事务的基础知识

1.什么是数据库事务&#xff1f; 就是把好几个sql语句打包成一个整体执行&#xff0c;要么全部成功&#xff0c;要么全部失败&#xff01;&#xff01;&#xff01; 事务是一个不可分割的数据库操作序列&#xff0c;也是数据库并发控制的基本单位&#xff0c;其执 行的结果必…...

【C++】C++面向对象,泛型编程总结篇(封装,继承,多态,模板)|(秋招篇)

文章目录 前言如何理解面向对象&#xff1f;如何理解泛型编程&#xff1f;C面向对象的三大特性是什么构造函数有哪几种&#xff1f;讲一下移动构造函数当我们定义一个类 系统会自动帮我们生成哪些函数&#xff1f;标题讲一下类中三类成员&#xff08;公有私有保护&#xff09;三…...

【Github】作为程序员不得不知道的几款Github加速神器

文章目录 背景推荐1&#xff1a;FastGithub推荐2&#xff1a;dev-sidecar推荐3&#xff1a;Watt Toolkit推荐4&#xff1a;篡改猴插件用户脚本1&#xff09;下载安装-->篡改猴 Tampermonkey 插件2&#xff09;下载安装-->Github 增强 - 高速下载 用户脚本 推荐5&#xff…...

react18之08自定义hook (简单的axios-get、修改浏览器title、localStorage、获取滚动条位置、img转换为base64)

目录 react18之自定义hook ()01&#xff1a;自定义一个 简单的axios hook 发起get请求useHttp.jsx使用useHttp hook效果 02&#xff1a;自定义一个 修改浏览器title hook03&#xff1a;自定义一个 localStorage(获取、存储、移除) hookuseLocalStorage.jsx使用hook效果 04&…...

mRNA疫苗序列生物信息学分析:从密码子优化到免疫原性预测

1. 项目概述&#xff1a;解码两大mRNA疫苗的“核心蓝图”作为一名在生物信息学和基因组学领域摸爬滚打了十多年的“老码农”&#xff0c;我见过太多令人兴奋的数据集&#xff0c;但当我第一次在GitHub上看到这个名为“Assemblies-of-putative-SARS-CoV2-spike-encoding-mRNA-se…...

低温预警!固化慢、易开裂……密封胶冬季施工手册

低温预警!固化慢、易开裂……密封胶冬季施工手册 硅酮耐候密封胶主要作用是保障幕墙的气密性、水密性。其出现问题,可能会导致耐候密封失效,从而造成幕墙漏水漏气,影响幕墙的正常使用。耐候密封胶由于考虑到现场施工,几乎都是单组分硅酮密封胶产品。进入冬季,气候变化明…...

Windows Android子系统深度优化:WSABuilds项目架构解析与实战部署指南

Windows Android子系统深度优化&#xff1a;WSABuilds项目架构解析与实战部署指南 【免费下载链接】WSABuilds Run Windows Subsystem For Android on your Windows 10 and Windows 11 PC using prebuilt binaries with Google Play Store (MindTheGapps) and/or Magisk or Ker…...

树莓派扩展板EYESPI Pi Beret:简化硬件连接,加速原型开发

1. 项目概述&#xff1a;为什么我们需要EYESPI Pi Beret&#xff1f;玩树莓派的朋友&#xff0c;尤其是喜欢捣鼓屏幕和传感器的&#xff0c;肯定都经历过那个阶段&#xff1a;面对一堆杜邦线&#xff0c;对照着屏幕驱动板的引脚定义&#xff0c;一个个数着树莓派的GPIO针脚&…...

CircuitPython嵌入式游戏开发:基于TileGrid的迷宫寻蛋与JSON数据持久化实践

1. 项目概述与核心价值如果你和我一样&#xff0c;对嵌入式开发充满热情&#xff0c;同时又对游戏开发抱有好奇心&#xff0c;那么将两者结合——在微控制器上编写一个完整的2D游戏——绝对是一次令人兴奋的挑战。这不仅仅是让LED闪烁或读取传感器数据&#xff0c;而是要在资源…...

LLVM开发实战指南:从入门到精通编译器与程序分析

1. 项目概述&#xff1a;为什么你需要一份LLVM指南&#xff1f;如果你是一名C开发者&#xff0c;或者对编译器、程序分析、代码优化这些底层技术感兴趣&#xff0c;那么“LLVM”这个名字对你来说一定不陌生。它早已不是象牙塔里的学术玩具&#xff0c;而是驱动着从iOS、macOS到…...

跨平台鼠标控制库ez-cursor-free:原理、实现与自动化实战

1. 项目概述与核心价值如果你是一名开发者&#xff0c;尤其是经常需要处理跨平台UI自动化、游戏脚本或者桌面应用交互的开发者&#xff0c;那么你一定对“鼠标控制”这个基础但又充满细节的环节感到过头疼。不同的操作系统&#xff08;Windows, macOS, Linux&#xff09;提供了…...

柔性3D打印与生物仿生设计:从TPU材料到空气喷涂的完整实践

1. 项目概述&#xff1a;当柔性3D打印遇上生物仿生美学如果你和我一样&#xff0c;玩3D打印玩久了&#xff0c;总会对那些千篇一律的硬质塑料件感到一丝审美疲劳。我们总在追求更高的精度、更强的结构&#xff0c;却常常忽略了材料本身可以带来的、截然不同的体验。直到我开始接…...

U64JSON编码技术解析与Iris框架性能优化

1. Iris框架与U64JSON编码技术解析 在嵌入式系统和高性能计算领域&#xff0c;数据交换效率直接影响整体系统性能。传统JSON虽然具有可读性好、跨平台等优势&#xff0c;但其文本特性带来的解析开销和带宽占用成为性能瓶颈。Arm Iris框架采用的U64JSON编码方案&#xff0c;通过…...

渠道输水控制系统模型在环测试【附仿真】

✨ 长期致力于渠道输水、水动力数值模拟、控制系统、模型在环测试、胶东调水工程研究工作&#xff0c;擅长数据搜集与处理、建模仿真、程序编写、仿真设计。 ✅ 专业定制毕设、代码 ✅ 如需沟通交流&#xff0c;点击《获取方式》 &#xff08;1&#xff09;Preissmann四点隐式格…...