大家好,我是魚皮。
項目上線是每位學(xué)編程同學(xué)必須掌握的基本技能。之前我已經(jīng)給大家分享過很多種上線單體項目的方法了,今天再出一期微服務(wù)項目的部署教程,用一種最簡單的方法,帶大家輕松部署微服務(wù)項目。
開始之前,先做個小調(diào)研啊,大家更喜歡看 “真實踩坑版” 的教程還是 “壓縮純凈版” 的教程呢?
本期教程我還是保持自己一貫的風(fēng)格,依然是保姆級教程,包含了一些踩坑過程和解決方案,大家跟著做就完事兒~
更推薦看視頻版:https://www.bilibili.com/video/BV1Cp4y1F7eA/
這次的教程以我?guī)Т蠹胰讨辈ナ职咽珠_發(fā)的、基于 Spring Cloud + Docker 代碼沙箱的 在線判題系統(tǒng) 為例,進(jìn)行演示。
這個項目的核心功能是讓用戶能夠在線選題和做題,然后由系統(tǒng)自動判題:
項目用到的核心依賴包括:MySQL 數(shù)據(jù)庫、Redis 緩存、RabbitMQ 消息隊列、Nacos 注冊中心
涉及的核心服務(wù)包括:用戶服務(wù)、題目服務(wù)、判題服務(wù)(代碼沙箱)、Gateway 網(wǎng)關(guān)服務(wù)
對于這樣一個項目,如果我們還用傳統(tǒng)單機(jī)項目的部署方式,一個個打 jar 包、用 Java 命令來啟動,會有哪些問題呢?
所以,為了解決這些問題,我們會選用一種更高效的微服務(wù)部署方式 —— Docker Compose。
在介紹 Docker Compose 前,先簡單介紹下 Docker。
Docker 是一種容器技術(shù),允許開發(fā)者將應(yīng)用程序和所有依賴項(如代碼、庫、配置等)制作為 鏡像。可以把鏡像簡單理解為軟件安裝包,可以在不同的計算機(jī)上通過它快速安裝和啟動應(yīng)用程序(容器),這些程序獨立隔離地運行,不受外部環(huán)境的影響。
如果要部署微服務(wù)項目,可能要啟動多個 Docker 容器,比如 MySQL 容器、用戶服務(wù)容器等。這時就需要 Docker Compose 了。它是一個容器編排助手,用于集中管理多個 Docker 容器的啟動和協(xié)同工作。可以在一個配置文件中集中定義所有容器以及它們的關(guān)系。然后,可以使用一行命令啟動所有容器,而不需要手動運行多個命令。
需要注意的是,Docker Compose 通常適用于把所有微服務(wù)部署在同一臺服務(wù)器的場景,在真實的企業(yè)級項目中,往往會使用 K8S 等更專業(yè)的容器編排和自動化部署工具,更方便地在多個服務(wù)器上部署容器。
了解了 Docker 和 Docker Compose 的作用后,我們來快速了解下部署流程,分為 2 大階段 —— 本地部署和服務(wù)器部署。
一、本地部署
二、服務(wù)端部署
第一階段是本地部署,也可以叫做部署準(zhǔn)備。
強(qiáng)烈建議大家,比起直接操作線上服務(wù)器,最好是先在本地把所有的流程跑通,風(fēng)險更低、效率更高。
這里我使用的是 Mac 操作系統(tǒng),已經(jīng)安裝了 Docker Desktop 軟件,管理 Docker 容器會更方便一些。
對于本地沒有 Docker 環(huán)境的同學(xué),這一階段仔細(xì)看一遍有個印象就足夠了。可以直接拿我調(diào)試好的配置文件在服務(wù)器上部署,而不用自己調(diào)試。
怎么樣,夠貼心吧!可以叫我 “保姆魚皮”,簡稱 “保姆皮”。
在部署微服務(wù)項目前,首先要規(guī)劃好要部署哪些服務(wù)、以及各服務(wù)的關(guān)鍵信息,比如服務(wù)名稱、版本號、占用端口號、關(guān)鍵配置等。
對于我的在線判題項目,梳理好的服務(wù)表格如下:
服務(wù)名稱 | 英文名 | 端口號 | 版本號 | 服務(wù)類別 |
數(shù)據(jù)庫 | mysql | 3306 | v8 | 環(huán)境依賴 |
緩存 | redis | 6379 | v6 | 環(huán)境依賴 |
消息隊列 | rabbitmq | 5672, 15672 | v3.12.6 | 環(huán)境依賴 |
注冊中心 | nacos | 8848 | v2.2.0 | 環(huán)境依賴 |
網(wǎng)關(guān)服務(wù) | gateway | 8101 | java 8 | 業(yè)務(wù)服務(wù) |
用戶服務(wù) | yuoj-backend-user-service | 8102 | java 8 | 業(yè)務(wù)服務(wù) |
題目服務(wù) | yuoj-backend-question-service | 8103 | java 8 | 業(yè)務(wù)服務(wù) |
判題服務(wù) | yuoj-backend-judge-service | 8104 | java 8 | 業(yè)務(wù)服務(wù) |
為什么這里我要劃分服務(wù)類別為 “環(huán)境依賴” 和 “業(yè)務(wù)服務(wù)” 呢?
因為在啟動服務(wù)時,必須要先啟動環(huán)境依賴,才能啟動業(yè)務(wù)服務(wù),否則就會報類似 “無法連接數(shù)據(jù)庫” 之類的錯誤。
對于微服務(wù)項目,我們通常是使用 Maven 的子父模塊功能進(jìn)行管理的。需要部署項目時,不用針對每個子服務(wù)單獨執(zhí)行 mvn package 命令進(jìn)行打包,而是可以一鍵打包所有服務(wù)。
想要實現(xiàn)這個功能,需要給子父模塊的依賴文件(pom.xml)進(jìn)行一些配置,主要包括:
在父模塊的 pom.xml 文件中引入 spring-boot-maven-plugin 即可,注意一定不要配置 configuration 和 repackage!
示例代碼如下:
<plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <version>${spring-boot.version}</version></plugin>修改所有需要啟動 Spring Boot 的服務(wù)(用戶服務(wù)、題目服務(wù)、判題服務(wù)、網(wǎng)關(guān)服務(wù))的子模塊 pom.xml 文件。
主要是增加 executions 配置,使用 spring-boot-maven-plugin 的 repackage 命令來構(gòu)建子模塊,從而自動在構(gòu)建時將公共模塊的依賴打入 jar 包。
示例代碼如下:
<plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <executions> <execution> <id>repackage</id> <goals> <goal>repackage</goal> </goals> </execution> </executions></plugin>Dockerfile 是定義 Docker 容器鏡像構(gòu)建過程的文件,包括容器鏡像使用的基礎(chǔ)環(huán)境、容器內(nèi)的依賴和文件、容器的配置、啟動命令等。
有了 Dockerfile,我們就能很輕松地制作出自己的容器鏡像。
雖然 Dockerfile 的寫法并不復(fù)雜,但我還是建議大家盡量不要自己寫,直接去網(wǎng)上找個差不多的項目,復(fù)制粘貼別人的 Dockerfile 就足夠了!
這里魚皮給大家提供 2 種常用的 Spring Boot 項目的 Dockerfile。
思路:在本地打好 jar 包后,復(fù)制 jar 包到容器中運行
示例代碼如下:
# 基礎(chǔ)鏡像FROM openjdk:8-jdk-alpine# 指定工作目錄WORKDIR /app# 將 jar 包添加到工作目錄,比如 target/yuoj-backend-user-service-0.0.1-SNAPSHOT.jarADD {本地 jar 包路徑} . # 暴露端口EXPOSE {服務(wù)端口號}# 啟動命令ENTRYPOINT ["java","-jar","/app/{jar 包名稱}","--spring.profiles.active=prod"]思路:復(fù)制本地代碼到容器中,在容器中使用 Maven 打包再運行
示例代碼如下:
# 基礎(chǔ)鏡像FROM maven:3.8.1-jdk-8-slim as builder# 指定工作目錄WORKDIR /app# 添加源碼文件COPY pom.xml .COPY src ./src# 構(gòu)建 jar 包,跳過測試RUN mvn package -DskipTests# 啟動命令ENTRYPOINT ["java","-jar","/app/target/{jar 包名稱}","--spring.profiles.active=prod"]此處由于我們的微服務(wù)項目可以一鍵打好所有子服務(wù)的 jar 包,就沒必要每個服務(wù)單獨在容器中打包了,所以選擇第一種方式的 Dockerfile。
我們需要給每個 Spring Boot 服務(wù)(用戶服務(wù)、題目服務(wù)、判題服務(wù)、網(wǎng)關(guān)服務(wù))都編寫一個 Dockerfile,放到每個子服務(wù)的根目錄下。
以用戶服務(wù)為例,示例代碼如下:
# 基礎(chǔ)鏡像FROM openjdk:8-jdk-alpine # 指定工作目錄WORKDIR /app # 將 jar 包添加到工作目錄,比如 target/yuoj-backend-user-service-0.0.1-SNAPSHOT.jarADD target/yuoj-backend-user-service-0.0.1-SNAPSHOT.jar . # 暴露端口EXPOSE 8102 # 啟動命令ENTRYPOINT ["java","-jar","/app/yuoj-backend-user-service-0.0.1-SNAPSHOT.jar","--spring.profiles.active=prod"]建議先在本地利用 IDEA 開發(fā)工具調(diào)通鏡像構(gòu)建流程,確保每個 Dockerfile 都是可以成功制作鏡像的:
查看容器的啟動日志,發(fā)現(xiàn)能夠啟動服務(wù)、看到 Spring 圖標(biāo)即可:
接下來,我們就要編寫 Docker Compose 的配置文件了,可以根據(jù)配置文件快速啟動多個服務(wù)。
之前我們已經(jīng)梳理了服務(wù)部署表格,將服務(wù)劃分為了 “環(huán)境依賴” 和 “業(yè)務(wù)服務(wù)”。
由于業(yè)務(wù)服務(wù)依賴 MySQL 等環(huán)境依賴,所以需要拆分 2 套 Docker Compose 的配置文件,分別為 docker-compose.env.yml 環(huán)境配置和 docker-compose.service.yml 業(yè)務(wù)服務(wù)配置,保證先成功啟動依賴,再啟動服務(wù)。
學(xué)過 Docker Compose 的同學(xué)可能聽說過 depends_on 配置,也能決定服務(wù)的啟動順序。但是千萬注意,depends_on 并不會等待服務(wù)完全就緒,只是確保它們在啟動時的順序,并不穩(wěn)定。
如何編寫 Docker Compose 文件呢?
和 Dockerfile 一樣,直接去網(wǎng)上找現(xiàn)成的 Docker Compose file,復(fù)制粘貼過來略做修改就能使用了~
再配合以下 2 個網(wǎng)站,完全無需記憶 Docker Compose 的寫法!
當(dāng)然,現(xiàn)在 AI 時代了,還有更簡單的方式!
直接把我們整理好的服務(wù)部署需要喂給 GPT,讓 AI 幫我們生成配置即可~
示例 prompt:
現(xiàn)在我需要用 docker compose 來部署 mysql 8(3306 端口)username=root,password=123456redis 6(無密碼,6379端口)、rabbitmq v.3.12.6( 5672 端口 password: guest,username: guest)、nacos 2.2.0(8848端口);還有 4 個本地的 springboot 服務(wù)(名稱分別為:yuoj-backend-user-service 8102端口、yuoj-backend-question-service 8103端口、yuoj-backend-judge-service 8104端口、yuoj-backend-gateway 8101 端口),每個服務(wù)本地目錄都有一個 Dockerfile,請幫我自動生成 docker compose 的 yml 配置文件,要求這些服務(wù)網(wǎng)絡(luò)能夠連通效果還是非常不錯的,只要你描述地足夠清楚,生成的配置完全可用!
由于這篇文章是教程嘛,我就帶大家通過調(diào)試的方式一步步完成 Docker Compose 文件,最后會把完整的 Docker Compose 文件給大家分享出來,大家直接用就行了~
我們要分別在 Docker Compose 中定義 4 大基礎(chǔ)依賴,包括 MySQL、Redis、RabbitMQ 和 Nacos。
我們不僅要創(chuàng)建一個 MySQL 服務(wù),還要在創(chuàng)建服務(wù)后自動創(chuàng)建我們需要的庫表結(jié)構(gòu)。
所以需要先準(zhǔn)備數(shù)據(jù)庫 SQL 腳本文件,里面包含了建庫、建表語句,我們把它放在微服務(wù)項目根目錄的 mysql-init 文件夾中:
魚皮帶大家做的每個項目都提供了現(xiàn)成的建表語句,這是一個非常好的開發(fā)習(xí)慣,便于其他人快速啟動你的項目。
由于要在本地啟動 MySQL,還需要定義一個文件夾 .mysql-data 來存放 MySQL 的持久化數(shù)據(jù),防止容器重啟后數(shù)據(jù)丟失。
做好這兩點后,就可以編寫 docker-compose.env.yml 文件了,先只寫一個 MySQL 服務(wù),示例代碼如下:
關(guān)鍵配置的含義我都寫到注釋里了
version: '3'services: mysql: image: mysql:8 # 使用的鏡像 container_name: yuoj-mysql # 啟動的實例名稱 environment: MYSQL_ROOT_PASSWORD: 123456 # root 用戶密碼 ports: - "3306:3306" # 端口映射 volumes: - ./.mysql-data:/var/lib/mysql # 將數(shù)據(jù)目錄掛載到本地目錄以進(jìn)行持久化 - ./mysql-init:/docker-entrypoint-initdb.d # 自動執(zhí)行啟動腳本 restart: always # 崩潰后自動重啟 networks: - mynetwork # 指定網(wǎng)絡(luò)networks: mynetwork: # 自定義網(wǎng)絡(luò),實現(xiàn)網(wǎng)絡(luò)互通和隔離寫好配置文件后,可以直接在 IDEA 里執(zhí)行 Docker Compose 文件,調(diào)試 MySQL 的運行:
運行成功后,我們可以在本地成功連接數(shù)據(jù)庫:
Redis 服務(wù)的定義和啟動操作和 MySQL 服務(wù)幾乎一致,Redis 的 Docker Compose 配置示例代碼如下:
version: '3'services: redis: image: redis:6 container_name: yuoj-redis ports: - "6379:6379" networks: - mynetwork volumes: - ./.redis-data:/data # 持久化networks: mynetwork:然后在本地執(zhí)行 Docker Compose 文件,啟動 Redis 服務(wù),并且嘗試進(jìn)入 Terminal 來調(diào)試 Redis:
同 MySQL 和 Redis,RabbitMQ 的 Docker Compose 配置示例代碼如下:
version: '3'services: rabbitmq: image: rabbitmq:3.12.6-management # 支持管理面板的消息隊列 container_name: yuoj-rabbitmq environment: RABBITMQ_DEFAULT_USER: guest RABBITMQ_DEFAULT_PASS: guest ports: - "5672:5672" - "15672:15672" # RabbitMQ Dashboard 端口 volumes: - ./.rabbitmq-data:/var/lib/rabbitmq # 持久化 networks: - mynetworknetworks: mynetwork:本地執(zhí)行 Docker Compose 文件,啟動 RabbitMQ 服務(wù),然后可以訪問 localhost:15672 查看到管理面板,就表示啟動成功了~
賬號密碼都是 guest
和其他服務(wù)一樣,Nacos 也需要編寫 Docker Compose 配置。
但是在選擇 Nacos 鏡像時必須要注意,建議選擇支持 linux/arm64 架構(gòu)的鏡像版本,比如 v2.2.0-slim,否則后面可能會無法運行:
Nacos 示例配置文件如下:
version: '3'services: nacos: image: nacos/nacos-server:v2.2.0-slim container_name: yuoj-nacos ports: - "8848:8848" volumes: - ./.nacos-data:/home/nacos/data networks: - mynetwork environment: - MODE=standalone # 單節(jié)點模式啟動 - PREFER_HOST_MODE=hostname # 支持 hostname - TZ=Asia/Shanghai # 控制時區(qū)networks: mynetwork:然后在本地執(zhí)行 Docker Compose 啟動 Nacos,訪問 localhost:8848/nacos 能夠看到管理頁面,就表示運行成功了~
管理頁面的賬號和密碼默認(rèn)都是 nacos
分別調(diào)試完上述服務(wù)后,我們把所有的配置拼在一起,就得到了完整的文件,文件名為 docker-compose.env.yml。
完整代碼如下:
version: '3'services: mysql: image: mysql:8 # 使用的鏡像 container_name: yuoj-mysql # 啟動的實例名稱 environment: MYSQL_ROOT_PASSWORD: 123456 # root 用戶密碼 ports: - "3306:3306" # 端口映射 volumes: - ./.mysql-data:/var/lib/mysql # 將數(shù)據(jù)目錄掛載到本地目錄以進(jìn)行持久化 - ./mysql-init:/docker-entrypoint-initdb.d # 啟動腳本 restart: always # 崩潰后自動重啟 networks: - mynetwork # 指定網(wǎng)絡(luò) redis: image: redis:6 container_name: yuoj-redis ports: - "6379:6379" networks: - mynetwork volumes: - ./.redis-data:/data # 持久化 rabbitmq: image: rabbitmq:3.12.6-management # 支持管理面板的消息隊列 container_name: yuoj-rabbitmq environment: RABBITMQ_DEFAULT_USER: guest RABBITMQ_DEFAULT_PASS: guest ports: - "5672:5672" - "15672:15672" # RabbitMQ Dashboard 端口 volumes: - ./.rabbitmq-data:/var/lib/rabbitmq # 持久化 networks: - mynetwork nacos: image: nacos/nacos-server:v2.2.0-slim container_name: yuoj-nacos ports: - "8848:8848" volumes: - ./.nacos-data:/home/nacos/data networks: - mynetwork environment: - MODE=standalone # 單節(jié)點模式啟動 - PREFER_HOST_MODE=hostname # 支持 hostname - TZ=Asia/Shanghai # 控制時區(qū)networks: mynetwork:用同樣的方式,我們可以編寫業(yè)務(wù)服務(wù)的 Docker Compose 文件,文件名稱 docker-compose.service.yml。
示例代碼如下,其中需要格外關(guān)注的配置是 build 和 depends_on:
version: '3'services: yuoj-backend-gateway: container_name: yuoj-backend-gateway build: # 服務(wù)的 Docker 構(gòu)建文件位置 context: ./yuoj-backend-gateway dockerfile: Dockerfile ports: - "8101:8101" networks: - mynetwork yuoj-backend-user-service: container_name: yuoj-backend-user-service build: context: ./yuoj-backend-user-service dockerfile: Dockerfile ports: - "8102:8102" networks: - mynetwork depends_on: # 本服務(wù)依賴的服務(wù),控制啟動先后順序 - yuoj-backend-gateway yuoj-backend-question-service: container_name: yuoj-backend-question-service build: context: ./yuoj-backend-question-service dockerfile: Dockerfile ports: - "8103:8103" networks: - mynetwork depends_on: - yuoj-backend-user-service - yuoj-backend-gateway yuoj-backend-judge-service: container_name: yuoj-backend-judge-service build: context: ./yuoj-backend-judge-service dockerfile: Dockerfile ports: - "8104:8104" networks: - mynetwork depends_on: - yuoj-backend-user-service - yuoj-backend-question-service - yuoj-backend-gateway# 網(wǎng)絡(luò),不定義的話就是默認(rèn)網(wǎng)絡(luò)networks: mynetwork:編寫好上述配置文件后,本地嘗試運行 Docker Compose 業(yè)務(wù)服務(wù),結(jié)果發(fā)現(xiàn):報錯啦!依賴服務(wù)的地址訪問不通!
這是由于之前我們的項目訪問依賴服務(wù)時,全部是使用了固定的 IP 地址(比如 localhost),而容器內(nèi)部的 localhost(或 127.0.0.1)通常指向容器本身,而不是宿主主機(jī)。所以為了在容器內(nèi)訪問其他服務(wù),程序中應(yīng)該使用服務(wù)名稱而不是 localhost。
我們給每個 Spring Boot 服務(wù)都增加一套 prod 上線配置,在配置中更改服務(wù)調(diào)用地址。
用戶服務(wù)、題目服務(wù)和判題服務(wù)的 application-prod.yml 配置修改如下:
# 生產(chǎn)環(huán)境配置文件spring: # 數(shù)據(jù)庫配置 datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://mysql:3306/yuoj # localhost 改為 mysql username: root password: 123456 # Redis 配置 redis: database: 1 host: redis # localhost 改為 redis port: 6379 timeout: 5000 cloud: nacos: discovery: server-addr: nacos:8848 # localhost 改為 nacos rabbitmq: host: rabbitmq # localhost 改為 rabbitmq port: 5672 password: guest username: guestGateway 網(wǎng)關(guān)服務(wù)的配置修改如下:
spring: cloud: nacos: discovery: server-addr: nacos:8848 # localhost 改為 nacos gateway: routes: - id: yuoj-backend-user-service uri: lb://yuoj-backend-user-service predicates: - Path=/api/user/** - id: yuoj-backend-question-service uri: lb://yuoj-backend-question-service predicates: - Path=/api/question/** - id: yuoj-backend-judge-service uri: lb://yuoj-backend-judge-service predicates: - Path=/api/judge/** application: name: yuoj-backend-gateway main: web-application-type: reactiveserver: port: 8101knife4j: gateway: enabled: true strategy: discover discover: enabled: true version: swagger2然后執(zhí)行 mvn package 命令重新打包、執(zhí)行 Docker Compose。
結(jié)果發(fā)現(xiàn)大多數(shù)服務(wù)都啟動了,但是判題服務(wù)還有報錯。
這是因為程序在創(chuàng)建消息隊列時存在硬編碼的變量,指定了 host 為 "localhost",示例代碼如下:
ConnectionFactory factory = new ConnectionFactory();factory.setHost("localhost");Connection connection = factory.newConnection();Channel channel = connection.createChannel();String EXCHANGE_NAME = "code_exchange";channel.exchangeDeclare(EXCHANGE_NAME, "direct");舉這個例子是為了告訴大家,在程序中盡量不要寫固定 IP 或域名,全部改為動態(tài)讀取配置文件,便于修改。
示例修改后的代碼如下:
/** * 用于創(chuàng)建測試程序用到的交換機(jī)和隊列(只用在程序啟動前執(zhí)行一次) */@Slf4j@Componentpublic class InitRabbitMqBean { @Value("${spring.rabbitmq.host:localhost}") private String host; @PostConstruct public void init() { try { ConnectionFactory factory = new ConnectionFactory(); factory.setHost(host); Connection connection = factory.newConnection(); Channel channel = connection.createChannel(); String EXCHANGE_NAME = "code_exchange"; channel.exchangeDeclare(EXCHANGE_NAME, "direct"); // 創(chuàng)建隊列,隨機(jī)分配一個隊列名稱 String queueName = "code_queue"; channel.queueDeclare(queueName, true, false, false, null); channel.queueBind(queueName, EXCHANGE_NAME, "my_routingKey"); log.info("消息隊列啟動成功"); } catch (Exception e) { log.error("消息隊列啟動失敗"); } }}修復(fù)上述問題后,所有服務(wù)都可以通過 Docker Compose 文件啟動了。
然后我們訪問 localhost:8101/doc.html 網(wǎng)關(guān)地址,能夠看到 Swagger 聚合接口文檔。
依次調(diào)用用戶注冊 => 登錄 => 獲取登錄用戶信息 => 創(chuàng)建題目接口,全部執(zhí)行成功。
至此,第一階段就完成啦。
在第二階段,我們的目標(biāo)就是在真實的 Linux 服務(wù)器上部署微服務(wù)項目。有了第一階段的準(zhǔn)備,第二階段簡直可以說是易如反掌!
首先,我們要有一臺 Linux 服務(wù)器。
選擇服務(wù)器前,我們必須要評估下微服務(wù)項目的內(nèi)存占用,千萬別把服務(wù)器的內(nèi)存買小了!
可以使用 Docker Desktop 直接查看內(nèi)存占用,虛擬機(jī)內(nèi)存大概占用了 8 個 G、容器實際內(nèi)存占用了 4 個 G:
那我們搞多少內(nèi)存的服務(wù)器呢?
我猜很多同學(xué)會說 8 G,奈何我天生反骨,明知山有虎偏向虎山行(主要是想省
本文鏈接:http://m.www897cc.com/showinfo-26-11808-0.html還不會部署微服務(wù)項目?保姆級教程來啦!
聲明:本網(wǎng)頁內(nèi)容旨在傳播知識,若有侵權(quán)等問題請及時與本網(wǎng)聯(lián)系,我們將在第一時間刪除處理。郵件:2376512515@qq.com
上一篇: 繼續(xù)聊聊云平臺運維規(guī)范
下一篇: 低代碼平臺組件間通信方案復(fù)盤