您好,登錄后才能下訂單哦!
這篇文章主要介紹了SpringBoot Schedule調度任務的動態管理方法是什么的相關知識,內容詳細易懂,操作簡單快捷,具有一定借鑒價值,相信大家閱讀完這篇SpringBoot Schedule調度任務的動態管理方法是什么文章都會有所收獲,下面我們一起來看看吧。
定時任務動態管理分為兩種方式:
方式一:Web前臺配置Trigger觸發器(關聯Cron)、ThreadPoolTaskScheduler類創建Scheduler方式下進行Schedule調度任務的動態管理
方式二:基于已創建的Schedule調度任務的動態管理,即以組件類 @Scheduled注解聲明Schedule調度,在啟動程序前一次性初始化,如:
@Component public class TestTask { private DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); @Scheduled(cron = "0/2 * * * * ?") public void robReceiveExpireTask() { System.out.println(df.format(LocalDateTime.now()) + "測試測試"); } }
缺陷:目前無法在運行期間增加Schedule以及stop、Start、Reset等管理。
架構為SpringBoot + Spring + mybatis-plus
pom.xml
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <parent> <artifactId>merak-hyper-automation-boot</artifactId> <groupId>com.merak.automation</groupId> <version>1.0.0</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>automation-quartz</artifactId> <packaging>jar</packaging> <repositories> <repository> <id>aliyun</id> <name>aliyun Repository</name> <url>http://maven.aliyun.com/nexus/content/groups/public</url> <snapshots> <enabled>false</enabled> </snapshots> </repository> </repositories> <dependencies> <!-- Spring框架基本的核心工具 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context-support</artifactId> </dependency> <!-- SpringWeb模塊 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> </dependency> <!-- mysql --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <!-- druid數據連接池 --> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> </dependency> <dependency> <groupId>jakarta.validation</groupId> <artifactId>jakarta.validation-api</artifactId> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> </dependency> <dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-annotations</artifactId> </dependency> <!--引入quartz定時框架--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-quartz</artifactId> <version>2.2.5.RELEASE</version> </dependency> </dependencies> <build> <plugins> <!-- 打包跳過測試 --> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
resources目錄下文件/application.yml:
spring:
profiles:
active: dev
resources目錄下文件/application-dev.yml:
server:
port: 12105
servlet:
context-path: /automation-quartzmanagement:
endpoints:
web:
exposure:
include: '*'# Spring配置
spring:
resources:
static-locations: classpath:/static/,classpath:/templates/
mvc:
throw-exception-if-no-handler-found: true
static-path-pattern: /**
application:
name: automation-workflow
main:
allow-bean-definition-overriding: true
# 文件上傳
servlet:
multipart:
# 單個文件大小
max-file-size: 2000MB
# 設置總上傳的文件大小
max-request-size: 4000MB
#json 時間戳統一轉換
jackson:
date-format: yyyy-MM-dd HH:mm:ss
time-zone: GMT+8
aop:
proxy-target-class: true
autoconfigure:
exclude: com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure
datasource:
dynamic:
druid:
# 全局druid參數,絕大部分值和默認保持一致。(現已支持的參數如下,不清楚含義不要亂設置)
# 連接池的配置信息
# 初始化大小,最小,最大
initial-size: 1
min-idle: 1
maxActive: 20
# 配置獲取連接等待超時的時間
maxWait: 60000
# 配置間隔多久才進行一次檢測,檢測需要關閉的空閑連接,單位是毫秒
timeBetweenEvictionRunsMillis: 60000
# 配置一個連接在池中最小生存的時間,單位是毫秒
minEvictableIdleTimeMillis: 300000
validationQuery: SELECT 1
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
# 打開PSCache,并且指定每個連接上PSCache的大小
poolPreparedStatements: true
maxPoolPreparedStatementPerConnectionSize: 20
# 配置監控統計攔截的filters,去掉后監控界面sql無法統計,'wall'用于防火墻
filters: stat,wall,slf4j
# 通過connectProperties屬性來打開mergeSql功能;慢SQL記錄
connectionProperties: druid.stat.mergeSql\=true;druid.stat.slowSqlMillis\=5000
datasource:
master:
url: jdbc:mysql://127.0.0.1:3308/merak_dev?characterEncoding=UTF-8&useUnicode=true&useSSL=false
username: root
password: root
driver-class-name: com.mysql.jdbc.Driver#mybatis plus 設置
mybatis-plus:
mapper-locations: classpath*:com/merak/hyper/automation/persist/**/xml/*Mapper.xml
global-config:
# 關閉MP3.0自帶的banner
banner: false
db-config:
id-type: ID_WORKER_STR
# 默認數據庫表下劃線命名
table-underline: true
configuration:
log-impl: org.apache.ibatis.logging.slf4j.Slf4jImpl
# log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
logging:
level:
com.merar.hyper: debug
com.merak.hyper.automation.persist.**.mapper: debug
org.springframework: warn
啟動MerakQuartzApplication類
package com.merak.hyper.automation; import org.mybatis.spring.annotation.MapperScan; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration; import org.springframework.context.annotation.Bean; import org.springframework.scheduling.annotation.EnableAsync; import org.springframework.scheduling.annotation.EnableScheduling; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; import java.util.concurrent.Executor; import java.util.concurrent.ThreadPoolExecutor; /** * @author chenjun * @version 1.0 * @ClassName: MerakQuartzApplication * @description: 工單任務調度 * @date 2022/9/22 10:30 */ @EnableScheduling @EnableAsync @MapperScan(basePackages = {"com.merak.hyper.automation.persist.**.mapper"}) @SpringBootApplication(scanBasePackages = {"com.merak.hyper.automation.**"}, exclude = {SecurityAutoConfiguration.class}) public class MerakQuartzApplication { public static final Logger log = LoggerFactory.getLogger(MerakQuartzApplication.class); public static void main(String[] args) { SpringApplication.run(MerakQuartzApplication.class, args); } private int taskSchedulerCorePoolSize = 15; private int awaitTerminationSeconds = 60; private String threadNamePrefix = "taskExecutor-"; /** * @description: 實例化ThreadPoolTaskScheduler對象,用于創建ScheduledFuture<?> scheduledFuture */ @Bean public ThreadPoolTaskScheduler threadPoolTaskScheduler() { ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler(); taskScheduler.setPoolSize(taskSchedulerCorePoolSize); taskScheduler.setThreadNamePrefix(threadNamePrefix); taskScheduler.setWaitForTasksToCompleteOnShutdown(false); taskScheduler.setAwaitTerminationSeconds(awaitTerminationSeconds); /**需要實例化線程*/ taskScheduler.initialize(); // isinitialized = true; log.info("初始化ThreadPoolTaskScheduler ThreadNamePrefix=" + threadNamePrefix + ",PoolSize=" + taskSchedulerCorePoolSize + ",awaitTerminationSeconds=" + awaitTerminationSeconds); return taskScheduler; } /** * @description: 實例化ThreadPoolTaskExecutor對象,管理asyncTask啟動的線程,應用類為 ScheduledHelper */ @Bean("asyncTaskExecutor") public Executor taskExecutor() { ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor(); taskExecutor.setCorePoolSize(5); taskExecutor.setMaxPoolSize(50); taskExecutor.setQueueCapacity(200); taskExecutor.setKeepAliveSeconds(60); taskExecutor.setThreadNamePrefix("asyncTaskExecutor-"); taskExecutor.setWaitForTasksToCompleteOnShutdown(true); taskExecutor.setAwaitTerminationSeconds(60); //修改拒絕策略為使用當前線程執行 taskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); //初始化線程池 taskExecutor.initialize(); return taskExecutor; } }
一、啟動時項目啟動時,加載任務關聯的觸發器,并全量執行流程。
initLineRunner類:
package com.merak.hyper.automation.Scheduling; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.merak.hyper.automation.persist.entity.AutoTriggerInfo; import com.merak.hyper.automation.persist.entity.BusWorkflow; import com.merak.hyper.automation.persist.service.IAutoTriggerInfoService; import com.merak.hyper.automation.persist.service.IBusWorkflowService; import com.merak.hyper.automation.util.CommonUtil; import com.merak.hyper.automation.util.ScheduleUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.CommandLineRunner; import org.springframework.core.annotation.Order; import org.springframework.stereotype.Component; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.Iterator; import java.util.List; import java.util.Map; /** * 項目啟動時,加載數字員工關聯的觸發器,并全量執行 * @Date: 2020/12/25:16:00 **/ @Component @Order(1) public class initLineRunner implements CommandLineRunner { public static final Logger log = LoggerFactory.getLogger(initLineRunner.class); private DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); @Autowired private TaskService taskService; @Autowired private IAutoTriggerInfoService triggerInfoService; @Autowired private IBusWorkflowService workflowService; @Override public void run(String... args) { log.info("項目啟動:加載數字員工關聯的觸發器信息并全量執行," + df.format(LocalDateTime.now())); QueryWrapper<BusWorkflow> wrapper = new QueryWrapper<>(); wrapper.eq("wf_type", "3");//3:云托管 wrapper.eq("wf_state", "1"); List<BusWorkflow> busWorkflows = workflowService.list(wrapper); List<AutoTriggerInfo> triggerInfos = triggerInfoService.list(); if( 0 == busWorkflows.size() || 0 == triggerInfos.size() ){ log.info("數字員工關聯的觸發器信息不正確,員工記錄數:"+busWorkflows.size()+",觸發器記錄數:"+triggerInfos.size()); } else{ //數字員工關聯的觸發器信息 Map<String,AutoTriggerInfo> loadWfidAndTriggerInfo = CommonUtil.loadWfidAndTriggerInfo(busWorkflows,triggerInfos); Iterator<Map.Entry<String, AutoTriggerInfo>> entries = loadWfidAndTriggerInfo.entrySet().iterator(); while (entries.hasNext()) { Map.Entry<String, AutoTriggerInfo> entry = entries.next(); String wfId = entry.getKey(); BusWorkflow workflow = busWorkflows.stream().filter( t -> wfId.equals(t.getWfId()) ).findAny().orElse(null); if( null != workflow ){ ScheduleUtil.start(new ScheduleTask(wfId,String.valueOf(workflow.getWfCreateuserId()),taskService), entry.getValue()); } } log.info("數字員工關聯的觸發器信息全量執行完成,數字員工定時個數:"+loadWfidAndTriggerInfo.size()+","+df.format(LocalDateTime.now())); } } } 核心代碼: ```java ScheduleUtil.start(new ScheduleTask(wfId,String.valueOf(workflow.getWfCreateuserId()),taskService), entry.getValue());
Scheduler管理工具類:啟動、取消、修改等管理
package com.merak.hyper.automation.util; import com.merak.hyper.automation.Scheduling.ScheduleTask; import com.merak.hyper.automation.persist.entity.AutoTriggerInfo; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; import org.springframework.scheduling.support.CronTrigger; import java.util.HashMap; import java.util.Map; import java.util.concurrent.ScheduledFuture; /** * @version 1.0 * @ClassName: ScheduleUtil * @description: Scheduler管理工具類:啟動、取消、修改等管理 */ public class ScheduleUtil { public static final Logger log = LoggerFactory.getLogger(ScheduleUtil.class); private static ThreadPoolTaskScheduler threadPoolTaskScheduler = SpringContextUtils.getBean(ThreadPoolTaskScheduler.class); //存儲[數字員工wfI,dScheduledFuture]集合 private static Map<String, ScheduledFuture<?>> scheduledFutureMap = new HashMap<>(); /** * 啟動 * * @param scheduleTask 定時任務 * @param triggerInfo */ public static boolean start(ScheduleTask scheduleTask, AutoTriggerInfo triggerInfo) { String wfId = scheduleTask.getId(); log.info("啟動數字員工"+wfId+"定時任務線程" + scheduleTask.getId()); ScheduledFuture<?> scheduledFuture = threadPoolTaskScheduler.schedule(scheduleTask, new CronTrigger(triggerInfo.getLogicConfig())); scheduledFutureMap.put(wfId, scheduledFuture); return true; } /** * 取消 * * @param scheduleTask 定時任務 */ public static boolean cancel(ScheduleTask scheduleTask) { log.info("關閉定時任務線程 taskId " + scheduleTask.getId()); ScheduledFuture<?> scheduledFuture = scheduledFutureMap.get(scheduleTask.getId()); if (scheduledFuture != null && !scheduledFuture.isCancelled()) { scheduledFuture.cancel(false); } scheduledFutureMap.remove(scheduleTask.getId()); return true; } /** * 修改 * * @param scheduleTask 定時任務 * @param triggerInfo */ public static boolean reset(ScheduleTask scheduleTask, AutoTriggerInfo triggerInfo) { //先取消定時任務 cancel(scheduleTask); //然后啟動新的定時任務 start(scheduleTask, triggerInfo); return true; } }
ScheduleTask類:ScheduleTask任務類
package com.merak.hyper.automation.Scheduling; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * @version 1.0 * @ClassName: ScheduleTask * @description: ScheduleTask,關聯任務id、用戶id和具體執行的TaskService類,實現Runnable類 */ public class ScheduleTask implements Runnable { private static final int TIMEOUT = 30000; private String id; private String userId; private TaskService service; public static final Logger log = LoggerFactory.getLogger(ScheduleTask.class); public String getId() { return id; } /** * @param id 任務ID * @param service 業務類 */ public ScheduleTask(String id, String userId, TaskService service) { this.id = id; this.userId = userId; this.service = service; } @Override public void run() { log.info("ScheduleTask-執行數字員工消息的發送,id:"+ this.id + ",用戶id:"+userId); service.work(this.id,this.userId); } }
/** * @version 1.0 * @ClassName: TaskService * @description: TaskService */ public interface TaskService { /** * 業務處理方法 * @param keyword 關鍵參數 * @param userId */ void work(String keyword,String userId); } /** * @description: TaskService實現類,具體執行定時調度的業務 */ @Service public class TaskServiceImpl implements TaskService { public static final Logger log = LoggerFactory.getLogger(TaskServiceImpl.class); @Autowired private IAutoDeviceInfoService deviceInfoService; @Override public void work(String wfId,String userId) { try { log.info("定時任務:根據數字員工wfId"+ wfId +",用戶id:"+userId+",發送消息..."); //sendRobotMsg(wfId,userId); Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } }
二、通過WEB配置的變更,動態管理定時任務
ScheduledController類:scheduled Web業務層:啟動、取消、修改等管理schedule
調度任務信息變更(如1:Trigger Cron變更 2:任務停止 3:任務新增加等)
package com.merak.hyper.automation.controller; import com.merak.hyper.automation.common.core.domain.AjaxResult; import com.merak.hyper.automation.common.core.vo.ScheduledApiVo; import com.merak.hyper.automation.persist.entity.AutoTriggerInfo; import com.merak.hyper.automation.persist.service.IAutoTriggerInfoService; import com.merak.hyper.automation.util.ScheduledHelper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; /** * @version 1.0 * @ClassName: ScheduledController * @description: scheduled Web業務層:啟動、取消、修改等管理schedule */ @RestController @RequestMapping("/api/scheduled") public class ScheduledController { public static final Logger log = LoggerFactory.getLogger(ScheduledController.class); @Autowired private IAutoTriggerInfoService triggerInfoService; @Autowired private ScheduledHelper scheduledHelper; @PostMapping("/add") public AjaxResult addScheduleds(@RequestBody ScheduledApiVo scheduledApiVo){ AutoTriggerInfo autoTriggerInfo = triggerInfoService.getById(scheduledApiVo.getTriggerId()); scheduledHelper.addScheduleds(scheduledApiVo,autoTriggerInfo); return AjaxResult.success(); } @PostMapping("/reset") public AjaxResult resetScheduleds(@RequestBody ScheduledApiVo scheduledApiVo){ AutoTriggerInfo autoTriggerInfo = triggerInfoService.getById(scheduledApiVo.getTriggerId()); scheduledHelper.resetScheduleds(scheduledApiVo,autoTriggerInfo); return AjaxResult.success(); } @PostMapping("/stop") public AjaxResult stopScheduleds(@RequestBody ScheduledApiVo scheduledApiVo){ AutoTriggerInfo autoTriggerInfo = triggerInfoService.getById(scheduledApiVo.getTriggerId()); scheduledHelper.stopScheduleds(scheduledApiVo); return AjaxResult.success(); } } ScheduledHelper類:對外提供ScheduledHelper管理:創建、變更、停止 ```java package com.merak.hyper.automation.util; import com.merak.hyper.automation.Scheduling.ScheduleTask; import com.merak.hyper.automation.Scheduling.TaskService; import com.merak.hyper.automation.common.core.vo.ScheduledApiVo; import com.merak.hyper.automation.persist.entity.AutoTriggerInfo; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Component; /** * @version 1.0 * @ClassName: ScheduledHelper * @description:對外提供ScheduledHelper管理:創建、變更、停止 */ @Component public class ScheduledHelper { public static final Logger log = LoggerFactory.getLogger(ScheduledHelper.class); /** * @description: 對外(Web)提供異步的Scheduleds增加操作 */ @Async("asyncTaskExecutor") public void addScheduleds(ScheduledApiVo scheduledApiVo, AutoTriggerInfo triggerInfo) { //addSchedule任務 log.warn("創建原數字員工["+scheduledApiVo.getWfId()+"],同步啟動Schedule任務"); TaskService taskService = SpringContextUtils.getBean(TaskService.class); ScheduleUtil.start(new ScheduleTask(scheduledApiVo.getWfId(), scheduledApiVo.getUserId(), taskService), triggerInfo); } @Async("asyncTaskExecutor") public void resetScheduleds(ScheduledApiVo scheduledApiVo,AutoTriggerInfo triggerInfo) { //cron值改變,變更Schedule任務 log.warn("數字員工["+scheduledApiVo.getWfId()+"]關聯的觸發器信息cron值改變,變更Schedule任務"); TaskService taskService = SpringContextUtils.getBean(TaskService.class); ScheduleUtil.reset(new ScheduleTask(scheduledApiVo.getWfId(), scheduledApiVo.getUserId(), taskService), triggerInfo); } @Async("asyncTaskExecutor") public void stopScheduleds(ScheduledApiVo scheduledApiVo) { //移除Wfid,停止原Schedule任務 log.warn("原數字員工["+scheduledApiVo.getWfId()+"]無效,同步停止Schedule任務"); TaskService taskService = SpringContextUtils.getBean(TaskService.class); ScheduleUtil.cancel(new ScheduleTask(scheduledApiVo.getWfId(), scheduledApiVo.getUserId(), taskService)); } }
SpringContextUtils類:
package com.merak.hyper.automation.util; import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.stereotype.Component; /** * @version 1.0 * @ClassName: SpringContextUtils * @description: 加載Class對象 */ @Component public class SpringContextUtils implements ApplicationContextAware { private static ApplicationContext applicationContext; @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { SpringContextUtils.applicationContext = applicationContext; } public static Object getBean(String name) { return applicationContext.getBean(name); } public static <T> T getBean(Class<T> requiredType) { return applicationContext.getBean(requiredType); } public static <T> T getBean(String name, Class<T> requiredType) { return applicationContext.getBean(name, requiredType); } public static boolean containsBean(String name) { return applicationContext.containsBean(name); } public static boolean isSingleton(String name) { return applicationContext.isSingleton(name); } public static Class<? extends Object> getType(String name) { return applicationContext.getType(name); } }
ScheduledApiVo類:
import java.io.Serializable; /** * @version 1.0 * @ClassName: ScheduledApiVo * @description: scheduled Web業務層Api傳遞參數Vo類 */ public class ScheduledApiVo implements Serializable { private String wfId; private String userId; private String triggerId; //set get 略 }
最終:Web端通過發送Http請求 ,調用ScheduledHelper管理類接口,實現Scheduled創建、變更、停止操作
log.info("3:云托管更新啟動數字員工操作"); ScheduledApiVo scheduledApiVo = new ScheduledApiVo(); scheduledApiVo.setWfId(wfId); scheduledApiVo.setUserId(String.valueOf(updateUserId)); scheduledApiVo.setTriggerId(newTriggerInfo.getId()); String webHookBody = JSON.toJSONString(scheduledApiVo); EmsApiUtil.SendQuartzMessage(url, "add", webHookBody); ******************** 分隔 ************************ public static boolean SendQuartzMessage(String quartzUrl, String method, String webHookBody){ boolean result = false; try{ //org.apache.httpcomponents.httpclient sendPost,pom依賴如下dependency String resp = HttpClientUtil.sendPostByJson(quartzUrl+"/"+method, webHookBody,0); if( "error".equals(resp) || resp.contains("405 Not Allowed")){ log.error("調用任務調度中心消息發送失敗,地址:"+quartzUrl); } else { JSONObject jsonObject = JSON.parseObject(resp); if( "200".equals(String.valueOf(jsonObject.get("code"))) ){ result = true; } else{ log.error("調用任務調度中心失敗,msg:"+String.valueOf(jsonObject.get("msg"))); } } }catch (Exception e){ log.error("調用任務調度中心失敗,msg:"+e.getMessage()); } return result; }
<dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> <version>4.5.2</version> </dependency>
關于“SpringBoot Schedule調度任務的動態管理方法是什么”這篇文章的內容就介紹到這里,感謝各位的閱讀!相信大家對“SpringBoot Schedule調度任務的動態管理方法是什么”知識都有一定的了解,大家如果還想學習更多知識,歡迎關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。