ThreadPoolTaskExecutor+CompletableFuture實現多線程異步數據同步和自定義線程池監控和動態調整實現

前言

ThreadPoolTaskExecutor是Spring框架提供的一個線程池實現,它是對Java標準庫中ThreadPoolExecutor的封裝,提供了更便捷的配置和集成方式,特別適合在Spring環境中使用。相關線程池概念見線程&線程池相關
在這里插入圖片描述
CompletableFuture 是 Java 8 引入的異步編程工具,實現了 Future 和CompletionStage 接口。它不僅提供了異步任務執行能力,還支持強大的函數式編程風格,允許開發者以聲明式方式組合多個異步操作,處理復雜的異步編程場景。相關概念及API使用

功能描述

創建了一個ThreadPoolTaskExecutor的管理類用于監控線程池狀態、動態調整線程池配置,定義線程池注冊為Spring Bean,創建基于分頁查詢和同步的CompletableFuture異步任務,使用自定義的核心線程池提交任務,最終主線程獲取異步結果(也可以引申主線程返回任務執行中,記錄任務ID,主動獲取任務執行結果通知主線程,實現頁面操作非阻塞性)。

代碼示例

用于記錄線程池狀態和調整線程池參數的實體類

package gov.zwfw.iam.uc.threadpoolconfig;import lombok.AllArgsConstructor;
import lombok.Data;@Data
@AllArgsConstructor
public class ThreadPoolStatusPo {private String poolName;private int corePoolSize;private int maxPoolSize;private int currentPoolSize;private int activeCount;private int largestPoolSize;private long taskCount;private long completedTaskCount;private int queueSize;private int queueRemainingCapacity;private int queueCapacity;private int keepAliveSeconds;private String rejectedHandlerType;private String threadNamePrefix;private String queueName;
}
package gov.zwfw.iam.uc.threadpoolconfig;import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;import java.util.concurrent.RejectedExecutionHandler;/*** 用于動態調整線程池配置的實體類* 包括核心線程數、最大線程數、隊列大小、拒絕策略、線程存活時間*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class ThreadPoolJudgePo {private int corePoolSize;private int maxPoolSize;private int keepAliveSeconds;private int queueCapacity;private RejectedExecutionHandler rejectedExecutionHandler;
}

用于監控線程池狀態和調整線程池參數的管理類

package gov.zwfw.iam.uc.threadpoolconfig;import org.apache.tomcat.util.threads.TaskQueue;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ThreadPoolExecutor;@Component
public class ThreadPoolManager {//存儲所有注冊的線程池private static final Map<String, ThreadPoolTaskExecutor> threadPoolMap = new ConcurrentHashMap<>();//存儲線程池原始配置(用于重置)private static final Map<String, ThreadPoolJudgePo> originalConfigMap = new ConcurrentHashMap<>();/*** 注冊線程池* @param poolName* @param threadPoolTaskExecutor* @param threadPoolJudgePo*/public void registerThreadPool(String poolName, ThreadPoolTaskExecutor threadPoolTaskExecutor, ThreadPoolJudgePo threadPoolJudgePo){threadPoolMap.put(poolName,threadPoolTaskExecutor);originalConfigMap.put(poolName, threadPoolJudgePo);}/*** 獲取所有線程池狀態* @return*/public Map<String,ThreadPoolStatusPo> getAllThreadPoolStatus(){Map<String,ThreadPoolStatusPo> statusMap = new HashMap<>();threadPoolMap.forEach((name,executor)->{statusMap.put(name,getThreadPoolStatus(name,executor));});return statusMap;}/*** 獲取單個線程池狀態* @param name* @return*/public ThreadPoolStatusPo getSingleThreadPoolStatus(String name){ThreadPoolTaskExecutor threadPoolTaskExecutor = threadPoolMap.get(name);return getThreadPoolStatus(name,threadPoolTaskExecutor);}/*** 問題:為什么有的屬性從executor(ThreadPoolTaskExecutor)獲取,有的從threadPoolTaskExecutor(ThreadPoolExecutor)獲取?** 原因分析:** ThreadPoolTaskExecutor是Spring對Java原生ThreadPoolExecutor的包裝,它提供了一些額外的配置和功能,同時內部持有一個ThreadPoolExecutor實例。* 線程池的核心狀態(如核心線程數、最大線程數、當前線程數、活躍線程數、歷史最大線程數、任務總數、已完成任務數、隊列大小等)都是ThreadPoolExecutor原生提供的,所以直接從ThreadPoolExecutor實例獲取。* 但是,Spring的ThreadPoolTaskExecutor在配置線程池時,有一些屬性是它自己擴展的,或者需要從它那里獲取配置值,例如:* keepAliveSeconds:在ThreadPoolExecutor中,存活時間是通過getKeepAliveTime(TimeUnit)方法獲取的,但是需要轉換單位。而Spring的ThreadPoolTaskExecutor直接提供了getKeepAliveSeconds()方法,返回的是以秒為單位的值,這樣更方便。* threadNamePrefix:這個前綴是Spring的ThreadPoolTaskExecutor在創建線程工廠時使用的,用于設置線程的名稱前綴,ThreadPoolExecutor本身沒有提供直接獲取線程名稱前綴的方法,所以只能從ThreadPoolTaskExecutor獲取。* 另外,拒絕策略的處理:ThreadPoolExecutor提供了getRejectedExecutionHandler()方法,可以獲取到拒絕策略處理器,然后通過getClass().getName()得到其類名。這里沒有使用Spring的包裝,因為拒絕策略處理器是直接設置在底層的ThreadPoolExecutor上的。* 因此,總結如下:** 大多數運行時狀態(動態的)都是從ThreadPoolExecutor(即threadPoolTaskExecutor)中獲取。* 而一些配置信息,特別是Spring包裝后提供的配置(如keepAliveSeconds和threadNamePrefix)則從ThreadPoolTaskExecutor(即executor)中獲取。* 注意:代碼中有一個屬性是queueCapacity(隊列總容量),它是通過queue.size() + queue.remainingCapacity()計算得到的,因為隊列的剩余容量加上當前已使用的容量就是總容量。** 所以,這樣的設計是合理的,充分利用了Spring的ThreadPoolTaskExecutor提供的便捷方法,同時也直接使用原生的ThreadPoolExecutor來獲取運行時指標。** 但是,這里有一個潛在的問題:Spring的ThreadPoolTaskExecutor的getKeepAliveSeconds()返回的是配置的存活時間(秒),而實際上ThreadPoolExecutor內部是以納秒為單位保存的。不過,由于我們在配置時也是以秒為單位,所以這里獲取的值是一致的。** 另外,關于拒絕策略,這里獲取的是處理器的類名,這樣我們可以知道具體是哪種拒絕策略。* @param name* @param executor* @return*/private ThreadPoolStatusPo getThreadPoolStatus(String name, ThreadPoolTaskExecutor executor) {ThreadPoolExecutor threadPoolTaskExecutor = executor.getThreadPoolExecutor();return new ThreadPoolStatusPo(name,threadPoolTaskExecutor.getCorePoolSize(),threadPoolTaskExecutor.getMaximumPoolSize(),threadPoolTaskExecutor.getPoolSize(),threadPoolTaskExecutor.getActiveCount(),threadPoolTaskExecutor.getLargestPoolSize(),threadPoolTaskExecutor.getTaskCount(),threadPoolTaskExecutor.getCompletedTaskCount(),threadPoolTaskExecutor.getQueue().size(),threadPoolTaskExecutor.getQueue().remainingCapacity(),threadPoolTaskExecutor.getQueue().size() + threadPoolTaskExecutor.getQueue().remainingCapacity(),executor.getKeepAliveSeconds(),threadPoolTaskExecutor.getRejectedExecutionHandler().getClass().getName(),executor.getThreadNamePrefix(),executor.getThreadPoolExecutor().getQueue().getClass().getName());}/*** 動態調整線程池* @param name* @param corePoolSize* @param maxPoolSize* @param queueCapacity*/public void adjustThreadPool(String name,Integer corePoolSize,Integer maxPoolSize,Integer queueCapacity){ThreadPoolTaskExecutor executor = threadPoolMap.get(name);if(null == executor){throw new RuntimeException(name+"線程池不存在");}ThreadPoolExecutor threadPoolExecutor = executor.getThreadPoolExecutor();//調整核心線程數if(null != corePoolSize && corePoolSize > 0){threadPoolExecutor.setCorePoolSize(corePoolSize);}//調整最大線程數if(null != maxPoolSize && maxPoolSize > 0){threadPoolExecutor.setMaximumPoolSize(maxPoolSize);}//調整隊列容量if(null != queueCapacity && queueCapacity > 0){//在Spring的ThreadPoolTaskExecutor中,我們設置隊列容量時,它實際上創建的就是TaskQueue//考慮使用Spring提供的setQueueCapacity方法(通過ThreadPoolTaskExecutor對象),這樣更安全。但是,這個方法會重新設置隊列容量,但不會改變隊列實例,因為內部會調用TaskQueue.setCapacity(如果隊列是TaskQueue的話)//所以,我們可以直接調用executor.setQueueCapacity(queueCapacity)來實現executor.setQueueCapacity(queueCapacity);}}/*** 重置線程池* @param name*/public void resetThreadPool(String name){ThreadPoolJudgePo threadPoolJudgePo = originalConfigMap.get(name);if(null == threadPoolJudgePo){throw new RuntimeException(name+"線程池初始化配置不存在");}adjustThreadPool(name,threadPoolJudgePo.getCorePoolSize(),threadPoolJudgePo.getMaxPoolSize(),threadPoolJudgePo.getQueueCapacity());}}

用于SpringBoot項目注冊線程池Bean的配置類

package gov.zwfw.iam.uc.threadpoolconfig;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;/*** 線程池配置* 用于執行異步任務*/
@Configuration
@EnableAsync
public class ThreadPoolConfig {@AutowiredThreadPoolManager threadPoolManager;/*** 核心線程池配置* @return*/@Bean(name = "coreTaskExecutor")public Executor coreTaskExecutor(){ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();//核心線程數executor.setCorePoolSize(10);//最大線程數executor.setMaxPoolSize(20);//隊列容量,在創建ThreadPoolExecutor時,如果隊列是LinkedBlockingQueue且queueCapacity>0,則將其替換為TaskQueue。executor.setQueueCapacity(500);//空閑線程存活時間executor.setKeepAliveSeconds(60);//拒絕策略,使用調用者運行策略,也可以自定義策略以增強可用性,這里只是簡單推送人員信息,量不是特別大,沒必要費勁executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());//線程名前綴executor.setThreadNamePrefix("coreTaskExecutor-");//優雅停機配置//等待所有任務結束后再關閉線程池executor.setWaitForTasksToCompleteOnShutdown(true);//等待終止時間executor.setAwaitTerminationSeconds(60);executor.initialize();//注冊到監控threadPoolManager.registerThreadPool("coreTaskExecutor",executor,new ThreadPoolJudgePo(executor.getCorePoolSize(),executor.getMaxPoolSize(),executor.getKeepAliveSeconds(),executor.getThreadPoolExecutor().getQueue().size(),executor.getThreadPoolExecutor().getRejectedExecutionHandler()));System.out.println("=================================="+executor.getThreadPoolExecutor().getQueue().getClass().getName());return executor;}@Bean(name = "commonTaskExecutor")public Executor commonTaskExecutor(){ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();//核心線程數executor.setCorePoolSize(5);//最大線程數executor.setMaxPoolSize(10);//隊列容量executor.setQueueCapacity(100);//空閑線程存活時間executor.setKeepAliveSeconds(120);//拒絕策略,拋棄策略executor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());executor.setThreadNamePrefix("commonTaskExecutor-");executor.initialize();return executor;}
}

用于獲取線程池狀態和調整線程池配置的控制類

package gov.zwfw.iam.uc.threadpoolconfig;import com.alibaba.fastjson.JSONObject;
import gov.zwfw.iam.base.util.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;@RestController
@RequestMapping("/pool")
public class ThreadPoolManageController {@AutowiredThreadPoolManager threadPoolManager;@RequestMapping(value = "/getStatus",method = RequestMethod.GET)public String getStatus(@RequestParam(value = "name",required = false) String name){if(StringUtils.isEmpty(name)){List<String>result = new ArrayList<>();Map<String, ThreadPoolStatusPo> allThreadPoolStatus = threadPoolManager.getAllThreadPoolStatus();allThreadPoolStatus.keySet().forEach(key->{ThreadPoolStatusPo threadPoolStatusPo = allThreadPoolStatus.get(key);String s = key+":"+JSONObject.toJSONString(threadPoolStatusPo);result.add(s);});return result.stream().collect(Collectors.joining("\n"));}else{ThreadPoolStatusPo singleThreadPoolStatus = threadPoolManager.getSingleThreadPoolStatus(name);return name+":"+JSONObject.toJSONString(singleThreadPoolStatus);}}@RequestMapping(value = "/adjust",method = RequestMethod.POST)public String adjust(@RequestParam(value = "name") String name,@RequestParam(value = "corePoolSize",required = false)Integer corePoolSize,@RequestParam(value = "maxPoolSize",required = false)Integer maxPoolSize,@RequestParam(value = "queueCapacity",required = false)Integer queueCapacity){threadPoolManager.adjustThreadPool(name,corePoolSize,maxPoolSize,queueCapacity);return "調整成功";}@RequestMapping(value = "/reset",method = RequestMethod.POST)public String reset(@RequestParam(value = "name") String name){threadPoolManager.resetThreadPool(name);return "重置成功";}
}

提交異步任務的業務實現,這里是分頁查詢用戶信息同步到第三方平臺,首先是頁面點擊實現的全量同步和失敗重試接口

@Value("${batch.push.size:5}")private int batchSize;@RequestMapping("/syncUser")@ResponseBodypublic Result syncUser(@RequestParam String resId) {Result result = new Result();//校驗當天是否存在失敗未重試任務String key = "FAIL_INDEX_"+resId+"_"+new SimpleDateFormat("yyyyMMdd").format(new Date());if(CodisUtil.lLen(key)>0){result.setCode("-5");result.setMsg("存在失敗未重試任務,請點擊失敗重試按鈕處理");return result;}String resUrl = "";try{resUrl = staffApi.selectUserSyncUrl(resId);if(gov.zwfw.iam.base.util.StringUtils.isEmpty(resUrl)){result.setCode("-1");result.setMsg("同步地址為空");return result;}logger.info("同步地址:{}", resUrl);//分頁查詢用戶信息,分批推送int userNum = staffApi.countUser();long startTime  = System.nanoTime();CompletableFuture<JSONObject> jsonObject = staffApi.resolveTask(resId,resUrl,userNum,batchSize, null);JSONObject futureJson = jsonObject.get();long endTime = System.nanoTime();logger.info("同步耗時:{}納秒", (endTime-startTime));result.setCode(futureJson.getString("code"));result.setMsg(futureJson.getString("msg"));}catch (Exception e){logger.error("同步失敗,同步地址:{},失敗原因:{}",  resUrl, e.getMessage());result.setCode("-1");result.setMsg(e.getMessage());}return result;}@RequestMapping("/syncUserFail")@ResponseBodypublic Result syncUserFail(@RequestParam String resId) {Result result = new Result();String key = "FAIL_INDEX_"+resId+"_"+new SimpleDateFormat("yyyyMMdd").format(new Date());if(CodisUtil.lLen(key)>0){String resUrl = "";try{resUrl = staffApi.selectUserSyncUrl(resId);if(gov.zwfw.iam.base.util.StringUtils.isEmpty(resUrl)){result.setCode("-1");result.setMsg("同步地址為空");return result;}logger.info("同步地址:{}", resUrl);List<String> failIndexList = CodisUtil.lRange(key,0,-1);CodisUtil.delKey(key);long startTime  = System.nanoTime();CompletableFuture<JSONObject> jsonObject = staffApi.resolveTask(resId,resUrl,0,batchSize,failIndexList);JSONObject futureJson = jsonObject.get();long endTime = System.nanoTime();logger.info("同步耗時:{}納秒", (endTime-startTime));result.setCode(futureJson.getString("code"));result.setMsg(futureJson.getString("msg"));}catch (Exception e){logger.error("同步失敗,同步地址:{},失敗原因:{}",  resUrl, e.getMessage());result.setCode("-1");result.setMsg(e.getMessage());}}else{result.setCode("-6");result.setMsg("不存在失敗未重試任務");}return result;}

然后是真正實現分頁任務以及提交執行的核心類

@Resource(name = "coreTaskExecutor")private Executor coreTaskExecutor;@Async("coreTaskExecutor")public CompletableFuture<JSONObject> resolveTask(String resId, String resUrl, int total, int batchSize, List<String> failIndexList) {//1、分頁任務列表List<CompletableFuture<String>> futures = new ArrayList<>();//失敗的下表存儲到redis的key,使用list類型String key = "FAIL_INDEX_"+resId+"_"+new SimpleDateFormat("yyyyMMdd").format(new Date());//2、如果是全量推送,計算起始位置,執行分頁查詢并推送;如果是失敗數據推送,那么使用失敗的下表,分頁查詢并推送if(total !=0 && null == failIndexList){for(int i=0;i<total;i+=batchSize){int startIndex = i;CompletableFuture<String> future = getFuture(startIndex, batchSize, resUrl, key);futures.add(future);}}else{for (int i = 0; i < failIndexList.size(); i++) {int startIndex = Integer.parseInt(failIndexList.get(i));CompletableFuture<String> future = getFuture(startIndex, batchSize, resUrl, key);futures.add(future);}}//5、等待所有任務執行完成并處理結果CompletableFuture<Void> allFutures = CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]));return allFutures.thenApply(r->{List<String> results = futures.stream().map(CompletableFuture::join).collect(Collectors.toList());//6、構建響應信息JSONObject resultJson = new JSONObject();int failCount = Math.toIntExact(results.stream().filter(result ->{JSONObject jsonObject = JSONObject.parseObject(result);if(jsonObject.containsKey("startIndex")){String startIndex = jsonObject.getString("startIndex");CodisUtil.lPush(key,startIndex);logger.error("失敗index:{}",startIndex);}return !jsonObject.getString("code").equals("0");}).count());resultJson.put("code",failCount>0?"-1":"0");resultJson.put("msg",failCount>0?"部分數據推送失敗,請點擊失敗重試按鈕重新推送":"推送成功");CodisUtil.expireKey(key,60*60*24);return resultJson;});}public int countUser() {return govStaffService.countUser();}public CompletableFuture<String> getFuture(int startIndex, int batchSize, String resUrl, String key){CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {try{//3、分頁查詢List<UcGovStaff> list = govStaffService.selectListByPage(startIndex, batchSize);logger.info("查詢到第"+(startIndex/batchSize+1)+"頁數據,數量為:{}", list.size());String syncRes = "";if(null == list || list.isEmpty()){JSONObject jsonObject = new JSONObject();jsonObject.put("code","-2");jsonObject.put("msg","推送數據為空");return jsonObject.toJSONString();}//4、執行推送任務syncRes = govStaffService.syncUser(startIndex,list, resUrl);return syncRes;}catch (Exception e){logger.error("分頁任務異常:{}",e.getMessage());JSONObject jsonObject = new JSONObject();jsonObject.put("code","-3");jsonObject.put("msg","任務執行失敗");CodisUtil.lPush(key,String.valueOf(startIndex));CodisUtil.expireKey(key,60*60*24);return jsonObject.toJSONString();}},coreTaskExecutor);return future;}public String syncUser(int startIndex, List<UcGovStaff> list, String resUrl) {String data = JSON.toJSONString(list);JSONObject jsonObject = new JSONObject();jsonObject.put("data", data);String s = "";try {s = WebUtils.doPost(resUrl,jsonObject);jsonObject = JSONObject.parseObject(s);if(!"0".equals(jsonObject.getString("code"))){jsonObject.put("startIndex",startIndex);}s = jsonObject.toJSONString();} catch (IOException e) {logger.error("同步人員異常:{}",e.getMessage());jsonObject = new JSONObject();jsonObject.put("code","-1");jsonObject.put("msg","網絡請求異常");jsonObject.put("startIndex",startIndex);s = jsonObject.toJSONString();}return s;}

注意,使用異步任務一定要在啟動類添加@EnableAsync注解,同時,真正執行異步任務的方法上添加@Async("coreTaskExecutor")注解,注解里的參數對應的是提交任務的線程池名稱。下面是獲取線程池狀態以及調整線程池配置的示例
在這里插入圖片描述
在這里插入圖片描述

總結

這次業務實現了基于ThreadPoolTaskExecutor+CompletableFuture的數據推送業務,可以引申為其他的多線程異步任務實現,實現了全量數據推送和失敗重試機制,對于處理大批量任務很有幫助,由于業務中主線程和異步任務是同步實現的,因此,會阻塞主線程直至異步任務執行完成,如果要實現主線程同步返回,異步執行后續任務,只需要@Async注解提交resolveTask任務即可。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/84627.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/84627.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/84627.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

一篇文章理解js閉包和作用于原理

一、js閉包的作用原理 JS閉包是指內部函數訪問外部函數變量的機制&#xff0c;常用于數據封裝和模塊化。典型應用包括創建私有變量、解決循環中的異步問題、實現函數柯里化等。案例分析展示了閉包在計數器、防抖函數等場景的使用&#xff0c;同時揭示了可能的內存泄漏風險。正…

GUI絲滑教程-python tinker

在 Tkinter GUI 應用中&#xff0c;線程可以幫助你在后臺執行長時間運行的任務&#xff0c;而不阻塞界面響應。下面是一些技巧&#xff0c;幫助你在使用線程時避免 Tkinter 界面卡頓的問題。 為什么 Tkinter 界面會卡頓&#xff1f; Tkinter 使用 主線程 來處理 UI 更新&…

第一部分-數據通信網絡基礎

目錄 一、什么是網絡通信&#xff1f; 二、網絡通信設備的基本識別 1.雙絞線 2.集線器&#xff08;物理層設備&#xff09; 3.中繼器&#xff08;物理層設備&#xff09; 4.接入交換機 5.匯聚交換機 6.核心交換機 7.路由器 8.無線路由器 9.光貓 一、什么是網絡通信&#xff1f;…

windows電腦解決筆記本搜索不到wifi問題

windows筆記本電腦明明打開了wifi功能&#xff0c;卻搜索不到wifi&#xff0c;此問題可能是網絡適配器被禁用的原因導致&#xff0c;通過以下方法也許能解決&#xff0c;無需重啟電腦 1、右鍵點擊網絡或wifi圖標&#xff0c;打開界面”網絡和internet“ 2、選擇”高級網絡設置…

C# 界面檢測顯示器移除并在可用顯示器上顯示

C# 檢測顯示器被移除&#xff0c;將界面在當前可用的顯示器上顯示&#xff0c;避免程序在任務欄點擊無響應。 using System; using System.Linq; using System.Windows.Forms;public class MonitorWatcher : IDisposable {private readonly Form _targetForm;private Screen …

JAVA實戰開源項目:青年公寓服務平臺 (Vue+SpringBoot) 附源碼

本文項目編號 T 233 &#xff0c;文末自助獲取源碼 \color{red}{T233&#xff0c;文末自助獲取源碼} T233&#xff0c;文末自助獲取源碼 目錄 一、系統介紹二、數據庫設計三、配套教程3.1 啟動教程3.2 講解視頻3.3 二次開發教程 四、功能截圖五、文案資料5.1 選題背景5.2 國內…

阿里云服務狀態監控:實時掌握云服務健康狀況

前言 在云計算時代,企業和開發者越來越依賴云服務提供商的基礎設施和服務。當我們的應用部署在云上,服務的可用性和穩定性就與云服務提供商息息相關。一旦云服務出現故障或維護,可能會對我們的業務造成直接影響。因此,實時了解云服務的運行狀態變得尤為重要。阿里云作為國…

使用VSCode開發FastAPI指南

1概述 FastAPI 是一個現代的高性能 Web 框架&#xff0c;用于使用 Python 構建 API。它旨在讓開發者輕松快速高效地構建 API&#xff0c;同時提供 API 的自動驗證、序列化和文檔記錄等功能&#xff0c;使其成為構建 Web 服務和微服務的熱門選擇。 在這個 FastAPI 教程中&#…

2025年硬件實習/秋招面試準備

前言 暑期即將到來&#xff0c;有很多研一研二以及大三大四的同學準備硬件類&#xff08;硬件研發、嵌入式硬件、layout、電源設計、射頻、硬件測試、工藝、FAE&#xff09;的實習或秋招。鑒于此&#xff0c;總結一下網友們秋招、實習中的硬件高頻考點&#xff0c;并分析他們是…

VSCode - Trae 插件關閉彈出框代碼補全

Trae 插件關閉彈出框代碼補全 彈出框代碼補全與非彈出框代碼補全 如下是彈出框代碼補全 如下是非彈出框代碼補全 關閉 / 啟用彈出框代碼補全 點擊 【管理】&#xff08;小齒輪&#xff09; -> 點擊 【設置】 取消勾選&#xff08;如果需要啟用&#xff0c;則勾選即可&…

Elasticsearch從安裝到實戰、kibana安裝以及自定義IK分詞器/集成整合SpringBoot詳細的教程ES(三)

DSL官方地址&#xff1a; DSL查詢分類 Elasticsearch提供了基于JSON的DSL&#xff08;https://www.elastic.co/docs/explore-analyze/query-filter/languages/querydsl&#xff09;來定義查詢。常見的查詢類型包括&#xff1a; 查詢所有&#xff1a;查詢出所有數據&#xff0…

我們來學mysql -- keepalive主從高可用

keepalive主從高可用 簡明扼要安裝KP場景“高可用”配置主keepalived.conf從keepalived.confmysql_check.sh 高可用驗證KP運行情況通過vip連接mysqlvip連接上創建數據庫關閉主庫所在服務器的KPvip連接上再次創建數據庫 結尾 簡明扼要 搭建mysql的主從八股文如是&#xff1a;主…

Compose筆記(二十六)--DatePicker

這一節主要了解一下Compose中的DatePicker,DatePicker是一個用于選擇日期的組件&#xff0c;它提供了直觀的界面讓用戶可以通過日歷視圖或直接輸入來選擇年、月、日。我們在開發中時常會用到日期選擇器&#xff0c;簡單總結如下: API: DatePickerDialog onDismissRequest&…

【靶場】upload-labs-文件上傳漏洞闖關

提示:文章寫完后,目錄可以自動生成,如何生成可參考右邊的幫助文檔 文章目錄 前言1.第一關1.保存html頁面2.修改頁面html3.訪問修改后的本地html文件4.上傳php文件5.訪問上傳的php2.第二關1.抓上傳包修改文件類型2.上傳成功3.第三關1.phtml php3會被解析為php原理2.上傳成功4…

基于 Transformer RoBERTa的情感分類任務實踐總結之四——PGM、EMA

整合了以下五大核心技術&#xff1a;R-Drop、PGM 對抗訓練、EMA、標簽平滑、CosineAnnealing 學習率調度。 1. R-Drop&#xff08;Regularized Dropout&#xff09; 原理&#xff1a;同一個樣本做兩次前向傳播&#xff08;同 dropout mask&#xff09;&#xff0c;計算兩次輸…

錄制mp4 rospy

ros 預覽攝像頭 #!/usr/bin/env python import rospy from sensor_msgs.msg import Image from cv_bridge import CvBridge import cv2# 初始化 bridge bridge CvBridge()def image_callback(msg):# 將ROS圖像消息轉換為OpenCV圖像cv_image bridge.imgmsg_to_cv2(msg, desir…

超簡單部署離線語音合成TTS和語音識別

一篇文章講清楚超簡單 離線語音合成TTS 和 離線語音識別 系統部署 本文只介紹兩個輕量級的 語音合成用piper, 語音識別用vosk 部署簡單,效果勉強 語音合成 推薦 piper (其他沒用過) 安裝 linux下安裝 pip install piper-tts下載模型(63M) 中文模型下載 zh_CN-huayan-medi…

【算力網】

一、算力網-DNS 1.1、核心架構設計 1.1.1 設計框架 基于SRv6的智能DNS算法設計框架&#xff0c;結合IPv6路由可編程性、動態路徑優化及業務感知能力&#xff0c;實現網絡性能與用戶體驗的雙重提升&#xff1a;? ?SRv6-DNS融合架構? ?控制平面?&#xff1a; DNS服務器集…

shell分析nginx日志的指令

shell指令 查看有多少個IP訪問&#xff1a; awk {print $1} log_file|sort|uniq|wc -l 查看某一個頁面被訪問的次數&#xff1a; grep "/index.php" log_file | wc -l 查看每一個IP訪問了多少個頁面&#xff1a; awk {S[$1]} END {for (a in S) print a,S[a]} …

CMS軟件以及常見分類

CMS&#xff08;Content Management System&#xff0c;內容管理系統&#xff09;是 讓非技術人員也能便捷創建、編輯、管理網站內容的軟件 &#xff0c;核心是 分離 “內容” 和 “頁面設計”&#xff08;內容存在數據庫&#xff0c;頁面用模板生成&#xff09;&#xff0c;無…