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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈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;无…