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

DevOps系列文章之 GitlabCICD自动化部署SpringBoot项目

一、概述

本文主要记录如何通过Gitlab CI/CD自动部署SpringBoot项目jar包。

二、前期准备

准备三台 CentOS7服务器,分别部署以下服务:

序号系统IP服务
1CentOS7192.168.56.10Gitlab
2CentOS7192.168.56.11Runner (安装Docker)
3CentOS7192.168.56.12SpringBoot 项目 jar 包(安装jdk、maven等)

上述服务也可以只用一台CentOS7,将所有程序都部署在同一机器上,但是更建议分开部署;

三、总体架构图

 

说明:

  • Gitlab Server 用于部署Gitlab远程仓库,对CPU和内存要求比较高,建议4核CPU,4GB以上内存;
  • Runner Server 用于部署执行.gitlab-ci.yml 文件中定义的 stage(阶段);需要具有访问 Gitlab 仓库的权限,可以下载代码,通过注册方式(gitlab-runner register)实现;
  • Your Laptop Server 用户部署你的应用程序,这里就是SpringBoot的 jar 包,需要提前安装 JDK 和 Maven 并配置好环境变量;

四、环境搭建

1、环境准备(可选)

三台服务器执行以下命令:

1

2

3

yum -y upgrade

yum -y install wget

yum -y install vim

2、Gitlab安装

参考地址:
https://about.gitlab.com/install/#centos-7
CentOS8.1搭建Gitlab服务器详细教程_Linux_脚本之家

(1)安装并配置必要的依赖

1

2

3

4

5

6

7

sudo yum install -y curl policycoreutils-python openssh-server

sudo systemctl enable sshd

sudo systemctl start sshd

sudo firewall-cmd --permanent --add-service=http

sudo firewall-cmd --permanent --add-service=https

sudo systemctl reload firewalld

(2)安装邮件服务

1

2

3

sudo yum install postfix

sudo systemctl enable postfix

sudo systemctl start postfix

(3)添加 gitlab 镜像

参考地址:Index of /gitlab-ce/yum/el7/ | 清华大学开源软件镜像站 | Tsinghua Open Source Mirror

1

wget https://mirrors.tuna.tsinghua.edu.cn/gitlab-ce/yum/el7/gitlab-ce-13.4.0-ce.0.el7.x86_64.rpm

(4)安装 gitlab 安装命令

1

rpm -i gitlab-ce-13.4.0-ce.0.el7.x86_64.rpm --nodeps --force

安装成功后图片:

 

(5)修改gitlab配置文件指定服务器ip和自定义端口

1

vim  /etc/gitlab/gitlab.rb

(6)重置并启动GitLab

1

2

gitlab-ctl reconfigure

gitlab-ctl restart

提示 "ok: run:"表示启动成功

(7)访问 GitLab页面

如果报502,等待一段时间后再刷新试试,一般1-2分钟左右。

GitLab登录页面

本文设置的账号:root ,新密码:11112222

3、安装 Runner

参考官方文档:https://docs.gitlab.com/runner/install/linux-manually.html#install-gitlab-runner-manually-on-gnulinux 中的 Using binary file Install

(1)下载一个二进制文件

1

sudo curl -L --output /usr/local/bin/gitlab-runner https://gitlab-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-runner-linux-amd64

(2)修改执行权限

1

sudo chmod a+x /usr/local/bin/gitlab-runner

(3)创建 GitLab CI 用户

1

sudo useradd --comment 'GitLab Runner' --create-home gitlab-runner --shell /bin/bash

(4)安装并作为服务运行

1

2

sudo gitlab-runner install --user=gitlab-runner --working-directory=/home/gitlab-runner

sudo gitlab-runner start

如果遇到提示 sudo: gitlab-runner: command not found,切换到 root 用户,可以去掉 sudo 执行上面命令。

(5)注册 Runner

参考地址:Registering runners | GitLab
执行 gitlab-runner register 命令:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

[root@localhost bin]# gitlab-runner register

Runtime platform                                    arch=amd64 os=linux pid=21527 revision=4e1f20da version=13.4.0

Running in system-mode.

Please enter the gitlab-ci coordinator URL (e.g. https://gitlab.com/):

http://192.168.56.10/

Please enter the gitlab-ci token for this runner:

PwF1sZPX_zsB-xChSKjH

Please enter the gitlab-ci description for this runner:

[localhost.localdomain]: test ci cd desc

Please enter the gitlab-ci tags for this runner (comma separated):

my-tag,other-tag

Registering runner... succeeded                     runner=PwF1sZPX

Please enter the executor: ssh, virtualbox, parallels, shell, docker-ssh, docker+machine, docker-ssh+machine, kubernetes, custom, docker:

docker

Please enter the default Docker image (e.g. ruby:2.6):

maven:3.3.9-jdk-8

Runner registered successfully. Feel free to start it, but if it's running already the config should be automatically reloaded!

注:这里选择的docker方式,所以服务器上还需要额外多安装docker
参考:史上最全(全平台)docker安装方法! - 知乎

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

#!/bin/bash

# 移除掉旧的版本

sudo yum remove docker \

                  docker-client \

                  docker-client-latest \

                  docker-common \

                  docker-latest \

                  docker-latest-logrotate \

                  docker-logrotate \

                  docker-selinux \

                  docker-engine-selinux \

                  docker-engine

# 删除所有旧的数据

sudo rm -rf /var/lib/docker

#  安装依赖包

sudo yum install -y yum-utils \

  device-mapper-persistent-data \

  lvm2

# 添加源,使用了阿里云镜像

sudo yum-config-manager \

    --add-repo \

    http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo

# 配置缓存

sudo yum makecache fast

# 安装最新稳定版本的docker

sudo yum install -y docker-ce

# 配置镜像加速器

sudo mkdir -p /etc/docker

sudo tee /etc/docker/daemon.json <<-'EOF'

{

  "registry-mirrors": ["http://hub-mirror.c.163.com"]

}

EOF

# 启动docker引擎并设置开机启动

sudo systemctl start docker

sudo systemctl enable docker

# 配置当前用户对docker的执行权限

sudo groupadd docker

sudo gpasswd -a ${USER} docker

sudo systemctl restart docker

这里注册一个全局共享的 Runner(管理员权限,复制服务器地址和 Token),所有项目都可以使用,或者也可以注册项目级别单独的 Runner (进入项目 Runner 设置页面,复制地址和 Token)。

添加GitLab-Runner

注册成功后,Runner 列表可以查看到注册的 Runner

GitLab-Runner添加成功

勾选:Run untagged jobs Indicates whether this runner can pick jobs without tags

GitLab-Runner配置

4、安装应用服务器环境

(1)允许用户远程登录(可选)

1

2

3

4

5

6

7

vi /etc/ssh/sshd_config

修改:

PasswordAuthentication yes                     

PermitRootLogin yes

重启服务:

service sshd restart

(2)安装JDK1.8

(1)下载地址:http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html
(2)解压

1

2

3

tar -zxvf jdk-8u161-linux-x64.tar.gz

重命名:

mv jdk1.8.0_161 java1.8

(3)配置环境变量

1

2

3

4

5

6

7

8

9

10

vi /etc/profile

添加以下内容:

export JAVA_HOME=/usr/local/java1.8

export PATH=$JAVA_HOME/bin:$PATH

export CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar

保存退出

source /etc/profile

java -version

(3) 安装 Maven3.3.9

(1)下载地址:http://maven.apache.org/download.cgi
(2)解压

1

2

3

4

tar -zxvf apache-maven-3.3.9-bin.tar.gz

重命名:

mv apache-maven-3.3.9 maven-3.3.9

(3)配置环境变量

1

2

3

4

5

6

7

8

9

vi /etc/profile

添加以下内容:

export MAVEN_HOME=/usr/local/maven-3.3.9

export PATH=$MAVEN_HOME/bin:$PATH

保存退出

source /etc/profile

mvn -v

五、创建 SpringBoot 项目

1、使用Gitlab Spring 模板快速创建一个 SpringBoot 项目;

GitLab创建SpringBoot项目

SpringBoot项目配置gitlab-ci.yml.jpg

如果报错,删除pom.xml中的这行

报这个错的话:
[FATAL] Non-resolvable parent POM for com.example:demo:0.0.1-SNAPSHOT: Could not transfer artifact org.springframework.boot:spring-boot-starter-parent:pom:2.0.1.RELEASE from/to central (https://repo.maven.apache.org/maven2): Connect to repo.maven.apache.org:443 [repo.maven.apache.org/151.101.40.215] failed: Connection timed out (Connection timed out) and ‘parent.relativePath' points at wrong local POM @ line 14, column 10

修改版本
1.5.9.RELEASE

2、添加环境变量(登录应用服务器密码)

注: 其中 ssh_password 这个添加到环境变量中,取消勾选 Protect Branch (仅保护分支);修改和添加都是默认勾选,需要取消,否则,其他分支不能读取到该变量;

 

先在应用服务器上创建一个目录,用于上传存放项目 jar 包:

1

mkdir gitlab-project

添加 .gitlab-ci.yml 文件时,可以先再 CI/CD Pipeline 中 的 CI Lint 中检验 .gitlab-ci.yml 文件格式

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

# 定义一些变量, 下面各阶段会使用

variables:

  server_ip: 192.168.56.12

  jar_name: demo-0.0.1-SNAPSHOT.jar

  java_path: /usr/local/java1.8/bin

  upload_path: /usr/local/gitlab-project

# 定义执行的各个阶段及顺序

stages:

  - build

  - upload

  - deploy

# 使用 maven 镜像打包项目

maven-build:

  stage: build

  image: maven:3.5.0-jdk-8

  script:

    - mvn package -B -Dmaven.test.skip=true

  cache:

    key: m2-repo

    paths:

      - .m2/repository

  artifacts:

    paths:

      - target/$jar_name

# 上传生成的 jar 包到你的应用服务器,这里使用 ictu/sshpass 这个镜像,是为了使用 sshpass 命令

upload-jar:

  stage: upload

  image: ictu/sshpass

  script:

    - ls -l target/

    - sshpass -p $ssh_password scp -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no target/$jar_name root@$server_ip:$upload_path/$jar_name

# 启动 SpringBoot jar包

deploy-test:

  stage: deploy

  image: ictu/sshpass

  script:

    - sshpass -p $ssh_password ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no root@$server_ip "nohup $java_path/java -jar $upload_path/$jar_name >/dev/null 2>&1 &"

这里使用了DockerHub上面的一个公共镜像(ictu/sshpass),主要是想使用启动自带的sshpass命令执行scp和ssh命令。
如果一切顺利的话,就会自动触发 CI/CD ;失败的话查看报错信息,可使用 Debug 模式执行调试命令 。

1

2

3

4

5

[root@localhost gitlab-project]# jps

22119 Jps

22073 demo-0.0.1-SNAPSHOT.jar

[root@localhost gitlab-project]# curl localhost:8080

Spring is here!

 

可能遇到的问题总结:

  1. 权限问题:可以先使用 root 用户看看是不是权限问题导致,如果是的话,提升执行用户的权限;并发问题:这里没有修改 Runner 的并发数,可以修改同时可以进行的任务并发数;其他问题:读取不到配置的环境变量,取消勾选仅保护分支的选项;
  2. 未执行job:没有勾选未配置 tags 也执行选项;

六、总结

使用GitLab自带的CICD功能部署SpringBoot项目非常方便,前期环境搭建可能需要花一点时间学习,但是后期部署项目可以省去很多人为操作失误,对于小型团队来说,Gitlab自带的CICD功能比Jenkins更加简单,总体思想步骤是:

搭建GitLab服务;搭建GitLab Runner服务;搭建应用服务;编写gitlab-ci.yml;
对于SpringBoot项目来说,一般分为:
(1)maven 打包;
(2)上传jar包;
(3)启动jar包;

相关文章:

DevOps系列文章之 GitlabCICD自动化部署SpringBoot项目

一、概述 本文主要记录如何通过Gitlab CI/CD自动部署SpringBoot项目jar包。 二、前期准备 准备三台 CentOS7服务器&#xff0c;分别部署以下服务&#xff1a; 序号系统IP服务1CentOS7192.168.56.10Gitlab2CentOS7192.168.56.11Runner &#xff08;安装Docker&#xff09;3Cen…...

汽车租赁管理系统/汽车租赁网站的设计与实现

摘 要 租赁汽车走进社区&#xff0c;走进生活&#xff0c;成为当今生活中不可缺少的一部分。随着汽车租赁业的发展&#xff0c;加强管理和规范管理司促进汽车租赁业健康发展的重要推动力。汽车租赁业为道路运输车辆一种新的融资服务形式、广大人民群众一种新的出行消费方式和…...

语句覆盖、条件覆盖、判定覆盖、条件-判定覆盖、路径覆盖

白盒测试是结构测试&#xff0c;主要对代码的逻辑进行验证。 逻辑覆盖率&#xff1a;语句覆盖<条件覆盖<判定覆盖<条件-判定覆盖<组合覆盖<路径覆盖 例子 一、语句覆盖 最基础的覆盖&#xff0c;只要每一个执行处理框内的语句都能执行就可&#xff0c;不用关注…...

二进制逻辑运算符

运算的优先级&#xff1a;非>与>或 1.逻辑与&#xff1a;“ ∧ \wedge ∧“&#xff0c;“ ⋅ \cdot ⋅“&#xff0c;and 在逻辑问题中与是所有的都是真结果才是真&#xff0c;比如&#xff1a; 1010101011 1010101011 1010101011和 1010110010 1010110010 1010110010…...

Bug日记-webstorm运行yarn 命令报错

在windows中输入yarn -v正确输出&#xff0c;在webstrom终端中运行yarn命令输出错误 问题&#xff1a;可能是由于 WebStorm 配置问题导致的。 解决方案&#xff1a; 检查 WebStorm 的终端配置&#xff1a;在 WebStorm 中&#xff0c;点击菜单栏的 “File”&#xff08;文件&am…...

C++11并发与多线程笔记(9) async、future、packaged_task、promise

C11并发与多线程笔记&#xff08;9&#xff09; async、future、packaged_task、promise 1、std::async、std::future创建后台任务并返回值2、std::packaged_task&#xff1a;打包任务&#xff0c;把任务包装起来3、std::promise3、小结 1、std::async、std::future创建后台任务…...

Mr. Cappuccino的第63杯咖啡——Spring之AnnotationConfigApplicationContext源码分析

Spring之AnnotationConfigApplicationContext源码分析 源码分析 源码分析 以上一篇文章《Spring之Bean的生命周期》的代码进行源码分析 AnnotationConfigApplicationContext applicationContext new AnnotationConfigApplicationContext(SpringConfig02.class); LifeCycleBe…...

opencv直方图与模板匹配

import cv2 #opencv读取的格式是BGR import numpy as np import matplotlib.pyplot as plt#Matplotlib是RGB %matplotlib inline def cv_show(img,name):cv2.imshow(name,img)cv2.waitKey()cv2.destroyAllWindows() 直方图 cv2.calcHist(images,channels,mask,histSize,ran…...

Apache Doris 入门教程31:计算节点

需求场景​ 目前Doris是一个典型Share-Nothing的架构, 通过绑定数据和计算资源在同一个节点获得非常好的性能表现. 但随着Doris计算引擎性能持续提高, 越来越多的用户也开始选择使用Doris直接查询数据湖数据. 这类场景是一种Share-Disk场景, 数据往往存储在远端的HDFS/S3上, 计…...

Nacos和GateWay路由转发NotFoundException: 503 SERVICE_UNAVAILABLE “Unable to find

问题再现&#xff1a; 2023-08-15 16:51:16,151 DEBUG [reactor-http-nio-2][CompositeLog.java:147] - [dc73b32c-1] Encoding [{timestampTue Aug 15 16:51:16 CST 2023, path/content/course/list, status503, errorService Unavai (truncated)...] 2023-08-15 16:51:16,17…...

2021年9月全国计算机等级考试真题(二级C语言)

2021年9月全国计算机等级考试真题&#xff08;二级C语言&#xff09; 第1题 下列叙述中正确的是&#xff08; &#xff09;。 A. 算法的复杂度是指算法所处理的数据量 B. 算法的复杂度是指算法程序中指令的数量 C. 算法的复杂度是指算法控制结构的复杂程度 D. 算法的复杂度包…...

串口通讯

USART是全双工同步通讯 在同步通信中&#xff0c;数据信号所传输的内容绝大多数属于有效数据&#xff0c;而异步通信中包含了各种帧的标识符&#xff0c;所以同步通讯的效率更高。但是同步通信对时钟要求苛刻&#xff0c;允许的误差小。而异步通信则允许双方的误差较大 比特率…...

自动拉取 GitHub 仓库更新的脚本

更好的阅读体验 \huge{\color{red}{更好的阅读体验}} 更好的阅读体验 由于将 HAUE-CS-WIKI 部署到了我自己的服务器上作为国内镜像站&#xff0c;每次在源站更新后都需要手动拉取镜像站的更新实在是太麻烦了&#xff0c;因此产生了编写该脚本的需求&#xff08; 读者可根据该…...

如何获得Android 14复活节彩蛋

每个新的安卓版本都有隐藏复活节彩蛋的悠久传统&#xff0c;可以追溯到以前&#xff0c;每个版本都以某种甜食命名。安卓14也不例外&#xff0c;但这一次的主题都是围绕太空构建的——还有一个复活节彩蛋。 安卓14复活节彩蛋实际上是一款很酷的小迷你游戏&#xff0c;你可以乘…...

国产32位单片机XL32F001,带1 路 12bit ADC,I2C、SPI、USART 等外设

XL32F001 系列单片机采用高性能的 32 位 ARM Cortex-M0内核&#xff0c;宽电压工作范围的 MCU。嵌入 24KbytesFlash 和 3Kbytes SRAM 存储器&#xff0c;最高工作频率 24MHz。包含多种不同封装类型多款产品。芯片集成 I2C、SPI、USART 等通讯外设&#xff0c;1 路 12bit ADC&am…...

typescript基础之null和undefined

TypeScript是一种基于JavaScript的编程语言&#xff0c;它支持静态类型检查和面向对象的特性。TypeScript中的null和undefined是两种基本类型&#xff0c;它们分别表示空值或未定义的值。在本文中&#xff0c;我将介绍TypeScript中null和undefined的含义、区别、检查方法和使用…...

php_mb_strlen指定扩展

1 中文在utf-字符集下占3个字节,所以计算出来长度为9。 2 可以引入php多字节字符的扩展&#xff0c;默认是没有的&#xff0c;需要自己配置这个函数 3 找到php.ini文件&#xff0c;去掉;extension mbstring的注释&#xff0c;接着重启apache服务 可以看到准确输出的中文的长度…...

利用OpenCV光流算法实现视频特征点跟踪

光流简介 光流&#xff08;optical flow&#xff09;是运动物体在观察成像平面上的像素运动的瞬时速度。光流法是利用图像序列中像素在时间域上的变化以及相邻帧之间的相关性来找到上一帧跟当前帧之间存在的对应关系&#xff0c;从而计算出相邻帧之间物体的运动信息的一种方法。…...

探索无限创造力的星辰大道,画出想象的浩瀚宇宙!-turtle

介绍 视频教程地址在此&#xff1a;https://www.bilibili.com/video/BV1Pm4y1H7Tb/ 大家好&#xff0c;欢迎来到本视频&#xff01;今天&#xff0c;我们将一同探索Python编程世界中的一个有趣而创意的库——Turtle库。无需专业绘画技能&#xff0c;你就可以轻松地用代码绘制…...

企业数字化转型大数据湖一体化平台项目建设方案PPT

导读&#xff1a;原文《企业数字化转型大数据湖一体化平台项目建设方案PPT》&#xff08;获取来源见文尾&#xff09;&#xff0c;本文精选其中精华及架构部分&#xff0c;逻辑清晰、内容完整&#xff0c;为快速形成售前方案提供参考。 喜欢文章&#xff0c;您可以点赞评论转发…...

IDEA运行Tomcat出现乱码问题解决汇总

最近正值期末周&#xff0c;有很多同学在写期末Java web作业时&#xff0c;运行tomcat出现乱码问题&#xff0c;经过多次解决与研究&#xff0c;我做了如下整理&#xff1a; 原因&#xff1a; IDEA本身编码与tomcat的编码与Windows编码不同导致&#xff0c;Windows 系统控制台…...

脑机新手指南(八):OpenBCI_GUI:从环境搭建到数据可视化(下)

一、数据处理与分析实战 &#xff08;一&#xff09;实时滤波与参数调整 基础滤波操作 60Hz 工频滤波&#xff1a;勾选界面右侧 “60Hz” 复选框&#xff0c;可有效抑制电网干扰&#xff08;适用于北美地区&#xff0c;欧洲用户可调整为 50Hz&#xff09;。 平滑处理&…...

【人工智能】神经网络的优化器optimizer(二):Adagrad自适应学习率优化器

一.自适应梯度算法Adagrad概述 Adagrad&#xff08;Adaptive Gradient Algorithm&#xff09;是一种自适应学习率的优化算法&#xff0c;由Duchi等人在2011年提出。其核心思想是针对不同参数自动调整学习率&#xff0c;适合处理稀疏数据和不同参数梯度差异较大的场景。Adagrad通…...

【JVM】- 内存结构

引言 JVM&#xff1a;Java Virtual Machine 定义&#xff1a;Java虚拟机&#xff0c;Java二进制字节码的运行环境好处&#xff1a; 一次编写&#xff0c;到处运行自动内存管理&#xff0c;垃圾回收的功能数组下标越界检查&#xff08;会抛异常&#xff0c;不会覆盖到其他代码…...

镜像里切换为普通用户

如果你登录远程虚拟机默认就是 root 用户&#xff0c;但你不希望用 root 权限运行 ns-3&#xff08;这是对的&#xff0c;ns3 工具会拒绝 root&#xff09;&#xff0c;你可以按以下方法创建一个 非 root 用户账号 并切换到它运行 ns-3。 一次性解决方案&#xff1a;创建非 roo…...

Cloudflare 从 Nginx 到 Pingora:性能、效率与安全的全面升级

在互联网的快速发展中&#xff0c;高性能、高效率和高安全性的网络服务成为了各大互联网基础设施提供商的核心追求。Cloudflare 作为全球领先的互联网安全和基础设施公司&#xff0c;近期做出了一个重大技术决策&#xff1a;弃用长期使用的 Nginx&#xff0c;转而采用其内部开发…...

相机从app启动流程

一、流程框架图 二、具体流程分析 1、得到cameralist和对应的静态信息 目录如下: 重点代码分析: 启动相机前,先要通过getCameraIdList获取camera的个数以及id,然后可以通过getCameraCharacteristics获取对应id camera的capabilities(静态信息)进行一些openCamera前的…...

【配置 YOLOX 用于按目录分类的图片数据集】

现在的图标点选越来越多&#xff0c;如何一步解决&#xff0c;采用 YOLOX 目标检测模式则可以轻松解决 要在 YOLOX 中使用按目录分类的图片数据集&#xff08;每个目录代表一个类别&#xff0c;目录下是该类别的所有图片&#xff09;&#xff0c;你需要进行以下配置步骤&#x…...

GruntJS-前端自动化任务运行器从入门到实战

Grunt 完全指南&#xff1a;从入门到实战 一、Grunt 是什么&#xff1f; Grunt是一个基于 Node.js 的前端自动化任务运行器&#xff0c;主要用于自动化执行项目开发中重复性高的任务&#xff0c;例如文件压缩、代码编译、语法检查、单元测试、文件合并等。通过配置简洁的任务…...

Linux 中如何提取压缩文件 ?

Linux 是一种流行的开源操作系统&#xff0c;它提供了许多工具来管理、压缩和解压缩文件。压缩文件有助于节省存储空间&#xff0c;使数据传输更快。本指南将向您展示如何在 Linux 中提取不同类型的压缩文件。 1. Unpacking ZIP Files ZIP 文件是非常常见的&#xff0c;要在 …...