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

Java面试实战系列【并发篇】- CompletableFuture异步编程实战

文章目录

    • 一、引言:异步编程的必要性
      • 1.1 传统同步编程的痛点
      • 1.2 异步编程的优势
      • 1.3 Java异步编程发展历程
    • 二、Future的局限性与CompletableFuture的诞生
      • 2.1 Future接口的设计缺陷
      • 2.2 传统异步编程方案对比
      • 2.3 CompletableFuture的设计目标
    • 三、CompletableFuture核心原理解析
      • 3.1 类结构设计(Future + CompletionStage)
      • 3.2 内部数据结构(result字段、stack链表)
      • 3.3 异步任务执行机制
    • 四、CompletableFuture基础API详解
      • 4.1 异步任务创建
      • 4.2 串行任务编排(thenApply、thenAccept、thenRun)
    • 五、线程池与性能优化
      • 5.1 默认线程池ForkJoinPool分析
      • 5.2 自定义线程池的重要性
      • 5.3 避免常见性能陷阱
    • 六、实战案例分析
      • 6.1 用户信息聚合查询优化
      • 6.2 微服务间并行调用实现

一、引言:异步编程的必要性

在现代高并发系统中,异步编程已经成为提升系统性能的重要手段。随着微服务架构的普及和用户对响应速度要求的不断提高,掌握异步编程技术变得至关重要。

1.1 传统同步编程的痛点

传统的同步编程模式存在以下问题:

// 传统同步调用示例
public OrderInfo getOrderInfo(String orderId) {// 串行调用,总耗时 = 所有调用时间之和UserInfo user = userService.getUserInfo(orderId);     // 耗时100msProductInfo product = productService.getProduct(orderId); // 耗时200msLogisticsInfo logistics = logisticsService.getLogistics(orderId); // 耗时150msreturn new OrderInfo(user, product, logistics); // 总耗时450ms
}

主要痛点:

  • 性能低下:串行执行导致总响应时间过长
  • 资源浪费:线程在等待I/O时处于阻塞状态
  • 扩展性差:难以应对高并发场景

1.2 异步编程的优势

异步编程通过非阻塞的方式执行任务,带来显著优势:

客户端请求
异步任务1
异步任务2
异步任务3
结果汇总
返回响应

主要优势:

  • 提升性能:并行执行减少总响应时间
  • 提高吞吐量:更好地利用系统资源
  • 增强用户体验:快速响应用户请求

1.3 Java异步编程发展历程

img

二、Future的局限性与CompletableFuture的诞生

2.1 Future接口的设计缺陷

Java 5引入的Future接口虽然提供了异步能力,但存在明显局限:

// Future的局限性示例
ExecutorService executor = Executors.newFixedThreadPool(3);Future<String> future = executor.submit(() -> {Thread.sleep(1000);return "Hello World";
});// 问题1:只能通过阻塞方式获取结果
String result = future.get(); // 阻塞等待// 问题2:无法设置回调
// 无法在任务完成时自动执行后续操作// 问题3:难以组合多个Future
// 无法简单地将多个异步任务串联或并行组合

Future的主要局限:

img

2.2 传统异步编程方案对比

在CompletableFuture出现之前,业界有多种异步编程解决方案:

方案优势劣势适用场景
Guava ListenableFuture支持回调依赖第三方库需要回调机制的场景
RxJava强大的响应式编程学习成本高复杂的异步流处理
Netty Future高性能与Netty绑定网络编程
原生Thread简单直接管理复杂简单异步任务

2.3 CompletableFuture的设计目标

CompletableFuture的设计目标是解决Future的局限性:

设计目标
非阻塞回调
链式编程
优雅异常处理
丰富的回调API
Future局限性
CompletableFuture解决方案
阻塞获取
无法组合
异常处理复杂
无回调机制

三、CompletableFuture核心原理解析

3.1 类结构设计(Future + CompletionStage)

CompletableFuture同时实现了Future和CompletionStage接口:

public class CompletableFuture<T> implements Future<T>, CompletionStage<T> {// 核心字段volatile Object result;       // 存储计算结果或异常volatile Completion stack;    // 依赖任务栈
}

接口职责分析:

Future
+get() : T
+cancel(boolean) : boolean
+isCancelled() : boolean
+isDone() : boolean
CompletionStage
+thenApply(Function) : CompletionStage
+thenAccept(Consumer) : CompletionStage
+thenRun(Runnable) : CompletionStage
+thenCompose(Function) : CompletionStage
+thenCombine(CompletionStage, BiFunction) : CompletionStage
CompletableFuture
-Object result
-Completion stack
+complete(T) : boolean
+completeExceptionally(Throwable) : boolean
  • Future接口:提供基本的异步计算能力
  • CompletionStage接口:提供异步任务编排能力
  • CompletableFuture类:具体实现,结合两种能力

3.2 内部数据结构(result字段、stack链表)

CompletableFuture内部维护了两个关键字段:

public class CompletableFuture<T> {// 存储计算结果、异常或特殊标记volatile Object result;// 依赖任务栈,形成链表结构volatile Completion stack;
}

数据结构图解:

Completion结构
依赖的CompletableFuture
Completion1
thenApply
执行函数
执行器Executor
CompletableFuture
result: 计算结果
stack: Completion链表头
Completion2
thenAccept
Completion3
thenRun
null

详细说明:

上图展示了CompletableFuture的核心数据结构,让我们逐一解析:

1. result字段(计算结果存储)

volatile Object result;  // 使用volatile保证可见性
  • 作用:存储异步计算的最终结果,可能是正常值、异常或特殊标记
  • 类型:Object类型,可以存储任何对象
  • 状态值:
    • null:任务尚未完成
    • 实际值:任务成功完成,存储计算结果
    • AltResult包装对象:存储异常或取消标记
  • 线程安全:使用volatile修饰,确保多线程环境下的可见性

2. stack字段(依赖任务链表)

volatile Completion stack;  // 依赖任务栈的头节点
  • 作用:维护所有依赖当前CompletableFuture的后续任务
  • 结构:单向链表,采用栈的形式(后进先出)
  • 节点类型:Completion对象,代表一个待执行的依赖任务
  • 执行时机:当前CompletableFuture完成时,会遍历整个链表执行所有依赖任务

3. Completion对象结构 每个Completion对象包含以下核心信息:

  • 依赖的CompletableFuture:指向下一个需要完成的CompletableFuture
  • 执行函数:具体的业务逻辑(如thenApply中的Function)
  • 执行器Executor:指定任务运行的线程池
  • next指针:指向链表中的下一个Completion节点

4. 链表构建过程示例

CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "hello").thenApply(s -> s.toUpperCase())     // 创建Completion1,加入stack.thenAccept(System.out::println)     // 创建Completion2,加入stack  .thenRun(() -> System.out.println("done")); // 创建Completion3,加入stack

在这个例子中:

  • 初始CompletableFuture的stack指向Completion3
  • Completion3.next指向Completion2
  • Completion2.next指向Completion1
  • Completion1.next为null(链表尾部)

result字段的状态变化:

初始状态(null)
complete(value)
completeExceptionally(throwable)
cancel()
Pending
Completed
Exception
Cancelled

状态变化说明:

1. Pending状态(待完成)

  • result字段为null
  • 任务正在执行中或尚未开始
  • 可以接受依赖任务的注册

2. Completed状态(正常完成)

  • result字段存储实际的计算结果
  • 调用complete(value)方法进入此状态
  • 触发所有依赖任务的执行

3. Exception状态(异常完成)

  • result字段存储AltResult对象,包装异常信息
  • 调用completeExceptionally(throwable)进入此状态
  • 异常会传播到依赖任务链

4. Cancelled状态(取消)

  • result字段存储特殊的取消标记
  • 调用cancel()方法进入此状态
  • 依赖任务收到CancellationException

3.3 异步任务执行机制

CompletableFuture的任务执行涉及多个组件:

ClientCompletableFutureExecutorWorkerThreadsupplyAsync(supplier)submit(task)executesupplier.get()complete(result)postComplete()触发回调链ClientCompletableFutureExecutorWorkerThread

执行流程说明:

  1. 客户端调用异步方法创建CompletableFuture
  2. 任务提交到线程池执行
  3. 工作线程执行具体业务逻辑
  4. 执行完成后调用complete方法设置结果
  5. 触发postComplete()执行依赖任务链

四、CompletableFuture基础API详解

4.1 异步任务创建

CompletableFuture提供了多种创建异步任务的方式:

// 1. runAsync - 无返回值的异步任务
CompletableFuture<Void> future1 = CompletableFuture.runAsync(() -> {System.out.println("执行异步任务");
});// 2. supplyAsync - 有返回值的异步任务
CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> {return "Hello CompletableFuture";
});// 3. 使用自定义线程池
ExecutorService executor = Executors.newFixedThreadPool(10);
CompletableFuture<String> future3 = CompletableFuture.supplyAsync(() -> {return "使用自定义线程池";
}, executor);// 4. 手动创建并完成
CompletableFuture<String> future4 = new CompletableFuture<>();
future4.complete("手动完成");

创建方式对比:

CompletableFuture创建方式
runAsync
supplyAsync
手动创建
无返回值
默认ForkJoinPool
可指定Executor
有返回值
默认ForkJoinPool
可指定Executor
new CompletableFuture
手动调用complete
完全控制执行时机

4.2 串行任务编排(thenApply、thenAccept、thenRun)

串行编排是最常用的任务组合方式,后续任务依赖前一个任务的完成:

public class SerialComposition {public static void main(String[] args) {CompletableFuture.supplyAsync(() -> {System.out.println("步骤1:获取用户ID");return "user123";}).thenApply(userId -> {System.out.println("步骤2:根据用户ID获取用户信息 - " + userId);return new User(userId, "张三");}).thenAccept(user -> {System.out.println("步骤3:处理用户信息 - " + user.getName());}).thenRun(() -> {System.out.println("步骤4:清理资源");});}
}

五、线程池与性能优化

5.1 默认线程池ForkJoinPool分析

CompletableFuture默认使用ForkJoinPool.commonPool():

// 默认线程池信息
ForkJoinPool commonPool = ForkJoinPool.commonPool();
System.out.println("并行度: " + commonPool.getParallelism());
System.out.println("活跃线程数: " + commonPool.getActiveThreadCount());
System.out.println("线程工厂: " + commonPool.getFactory());

ForkJoinPool特点:

ForkJoinPool.commonPool
工作窃取算法
并行度 = CPU核心数-1
全局共享
每个线程有自己的任务队列
空闲线程可以窃取其他线程的任务
提高CPU利用率
适合CPU密集型任务
避免线程过多的上下文切换
所有CompletableFuture共享
可能造成线程饥饿
I/O密集型任务会阻塞线程池

5.2 自定义线程池的重要性

在生产环境中,建议使用自定义线程池:

public class CustomExecutorExample {// CPU密集型任务线程池private static final ExecutorService CPU_EXECUTOR = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());// I/O密集型任务线程池private static final ExecutorService IO_EXECUTOR = Executors.newFixedThreadPool(100);// 业务线程池(可配置)private static final ExecutorService BUSINESS_EXECUTOR = new ThreadPoolExecutor(10,                          // 核心线程数50,                          // 最大线程数60L, TimeUnit.SECONDS,       // 空闲时间new LinkedBlockingQueue<>(1000),  // 工作队列new ThreadFactory() {        // 线程工厂private final AtomicInteger counter = new AtomicInteger(1);@Overridepublic Thread newThread(Runnable r) {Thread t = new Thread(r, "business-thread-" + counter.getAndIncrement());t.setDaemon(false);return t;}},new ThreadPoolExecutor.CallerRunsPolicy()  // 拒绝策略);public static void main(String[] args) {// 使用不同线程池执行不同类型任务// CPU密集型任务CompletableFuture<Integer> cpuTask = CompletableFuture.supplyAsync(() -> {// 模拟CPU密集型计算return fibonacci(40);}, CPU_EXECUTOR);// I/O密集型任务CompletableFuture<String> ioTask = CompletableFuture.supplyAsync(() -> {// 模拟I/O操作try {Thread.sleep(1000);return "I/O完成";} catch (InterruptedException e) {throw new RuntimeException(e);}}, IO_EXECUTOR);// 业务逻辑任务CompletableFuture<String> businessTask = CompletableFuture.supplyAsync(() -> {return "业务处理完成";}, BUSINESS_EXECUTOR);}private static int fibonacci(int n) {if (n <= 1) return n;return fibonacci(n - 1) + fibonacci(n - 2);}
}

线程池选择策略:

任务类型分析
是否为I/O密集型?
使用I/O线程池
线程数 = 2 * CPU核心数
是否为CPU密集型?
使用CPU线程池
线程数 = CPU核心数
使用业务线程池
根据业务特点配置
考虑队列大小
监控线程池状态

5.3 避免常见性能陷阱

在使用CompletableFuture时要注意避免常见陷阱:

public class PerformancePitfalls {// ❌ 陷阱1:过度使用默认线程池public static void badExample1() {// 所有任务都用默认线程池,可能导致线程饥饿List<CompletableFuture<String>> futures = IntStream.range(0, 1000).mapToObj(i -> CompletableFuture.supplyAsync(() -> {// I/O密集型任务占用CPU线程池try {Thread.sleep(1000);} catch (InterruptedException e) {Thread.currentThread().interrupt();}return "result" + i;})).collect(Collectors.toList());}// ✅ 改进:使用专门的I/O线程池private static final ExecutorService IO_POOL = Executors.newFixedThreadPool(100);public static void goodExample1() {List<CompletableFuture<String>> futures = IntStream.range(0, 1000).mapToObj(i -> CompletableFuture.supplyAsync(() -> {try {Thread.sleep(1000);} catch (InterruptedException e) {Thread.currentThread().interrupt();}return "result" + i;}, IO_POOL))  // 使用专门的I/O线程池.collect(Collectors.toList());}// ❌ 陷阱2:无限制的并发public static void badExample2() {List<String> items = Collections.nCopies(10000, "item");// 创建10000个并发任务,可能导致OOMList<CompletableFuture<String>> futures = items.stream().map(item -> CompletableFuture.supplyAsync(() -> process(item))).collect(Collectors.toList());}// ✅ 改进:控制并发数量public static CompletableFuture<List<String>> goodExample2(List<String> items) {int maxConcurrency = 50;  // 限制并发数return CompletableFuture.supplyAsync(() -> {return items.parallelStream().limit(maxConcurrency).map(PerformancePitfalls::process).collect(Collectors.toList());});}// ❌ 陷阱3:忘记异常处理public static void badExample3() {CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {if (Math.random() > 0.5) {throw new RuntimeException("随机错误");}return "success";});// 没有异常处理,异常会在get()时抛出future.join();  // 可能抛出未处理的异常}// ✅ 改进:完善的异常处理public static void goodExample3() {CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {if (Math.random() > 0.5) {throw new RuntimeException("随机错误");}return "success";}).exceptionally(ex -> {logger.error("任务执行失败", ex);return "default_value";}).whenComplete((result, ex) -> {if (ex != null) {// 记录异常指标metrics.incrementCounter("task.failure");} else {metrics.incrementCounter("task.success");}});}private static String process(String item) {// 模拟处理逻辑return "processed: " + item;}
}

六、实战案例分析

6.1 用户信息聚合查询优化

在微服务架构中,一个用户详情页面通常需要聚合多个服务的数据:

public class UserInfoAggregationService {private final UserService userService;private final OrderService orderService;private final PointService pointService;private final RecommendService recommendService;// 线程池配置private final ExecutorService queryExecutor = new ThreadPoolExecutor(20, 100, 60L, TimeUnit.SECONDS,new LinkedBlockingQueue<>(500),r -> new Thread(r, "user-query-" + System.currentTimeMillis()));/*** 串行方式(优化前)- 总耗时约800ms*/public UserDetailVO getUserDetailSerial(String userId) {// 串行调用各个服务UserInfo userInfo = userService.getUserInfo(userId);        // 200msList<Order> orders = orderService.getUserOrders(userId);    // 300msPointInfo points = pointService.getUserPoints(userId);      // 150msList<Product> recommendations = recommendService.getRecommendations(userId);            // 150msreturn new UserDetailVO(userInfo, orders, points, recommendations);}/*** 并行方式(优化后)- 总耗时约300ms*/public CompletableFuture<UserDetailVO> getUserDetailParallel(String userId) {// 并行调用各个服务CompletableFuture<UserInfo> userFuture = CompletableFuture.supplyAsync(() -> userService.getUserInfo(userId), queryExecutor);CompletableFuture<List<Order>> ordersFuture = CompletableFuture.supplyAsync(() -> orderService.getUserOrders(userId), queryExecutor);CompletableFuture<PointInfo> pointsFuture = CompletableFuture.supplyAsync(() -> pointService.getUserPoints(userId), queryExecutor);CompletableFuture<List<Product>> recommendationsFuture = CompletableFuture.supplyAsync(() -> recommendService.getRecommendations(userId), queryExecutor);// 等待所有服务调用完成并组装结果return CompletableFuture.allOf(userFuture, ordersFuture, pointsFuture, recommendationsFuture).thenApply(v -> new UserDetailVO(userFuture.join(),ordersFuture.join(),pointsFuture.join(),recommendationsFuture.join())).exceptionally(ex -> {logger.error("获取用户详情失败: userId={}", userId, ex);// 返回默认值或抛出业务异常throw new BusinessException("用户信息暂时无法获取");});}/*** 带超时和降级的版本*/public CompletableFuture<UserDetailVO> getUserDetailWithFallback(String userId) {CompletableFuture<UserInfo> userFuture = CompletableFuture.supplyAsync(() -> userService.getUserInfo(userId), queryExecutor).orTimeout(500, TimeUnit.MILLISECONDS)  // 超时控制.exceptionally(ex -> {// 降级逻辑:返回缓存数据或默认数据return userService.getUserInfoFromCache(userId);});CompletableFuture<List<Order>> ordersFuture = CompletableFuture.supplyAsync(() -> orderService.getUserOrders(userId), queryExecutor).orTimeout(800, TimeUnit.MILLISECONDS).exceptionally(ex -> Collections.emptyList());  // 失败时返回空列表CompletableFuture<PointInfo> pointsFuture = CompletableFuture.supplyAsync(() -> pointService.getUserPoints(userId), queryExecutor).orTimeout(300, TimeUnit.MILLISECONDS).exceptionally(ex -> new PointInfo(0));  // 失败时返回0积分CompletableFuture<List<Product>> recommendationsFuture = CompletableFuture.supplyAsync(() -> recommendService.getRecommendations(userId), queryExecutor).orTimeout(600, TimeUnit.MILLISECONDS).exceptionally(ex -> getDefaultRecommendations());  // 失败时返回默认推荐return CompletableFuture.allOf(userFuture, ordersFuture, pointsFuture, recommendationsFuture).thenApply(v -> new UserDetailVO(userFuture.join(),ordersFuture.join(),pointsFuture.join(),recommendationsFuture.join()));}
}

6.2 微服务间并行调用实现

在微服务架构中,一个业务请求往往需要调用多个下游服务:

@Service
public class CompositeService {private final UserServiceClient userServiceClient;private final ProductServiceClient productServiceClient;private final InventoryServiceClient inventoryServiceClient;private final PriceServiceClient priceServiceClient;// 配置不同类型的线程池private final ExecutorService rpcExecutor = new ThreadPoolExecutor(50, 200, 60L, TimeUnit.SECONDS,new LinkedBlockingQueue<>(1000),r -> new Thread(r, "rpc-call-" + System.currentTimeMillis()));/*** 商品详情页数据聚合*/public CompletableFuture<ProductDetailResponse> getProductDetail(String productId, String userId) {// 并行调用多个服务// 1. 获取商品基本信息(必需)CompletableFuture<ProductInfo> productFuture = CompletableFuture.supplyAsync(() -> productServiceClient.getProductInfo(productId), rpcExecutor).orTimeout(1000, TimeUnit.MILLISECONDS);  // 1秒超时// 2. 获取库存信息(必需)CompletableFuture<InventoryInfo> inventoryFuture = CompletableFuture.supplyAsync(() -> inventoryServiceClient.getInventory(productId), rpcExecutor).orTimeout(800, TimeUnit.MILLISECONDS);   // 800ms超时// 3. 获取价格信息(必需)CompletableFuture<PriceInfo> priceFuture = CompletableFuture.supplyAsync(() -> priceServiceClient.getPrice(productId, userId), rpcExecutor).orTimeout(500, TimeUnit.MILLISECONDS);   // 500ms超时// 4. 获取用户信息(可选,用于个性化)CompletableFuture<UserInfo> userFuture = CompletableFuture.supplyAsync(() -> userServiceClient.getUserInfo(userId), rpcExecutor).orTimeout(600, TimeUnit.MILLISECONDS).exceptionally(ex -> {logger.warn("获取用户信息失败,使用默认配置: userId={}", userId, ex);return UserInfo.defaultUser();  // 降级处理});// 组合所有结果return CompletableFuture.allOf(productFuture, inventoryFuture, priceFuture, userFuture).thenApply(v -> {ProductInfo product = productFuture.join();InventoryInfo inventory = inventoryFuture.join();PriceInfo price = priceFuture.join();UserInfo user = userFuture.join();return ProductDetailResponse.builder().productInfo(product).inventoryInfo(inventory).priceInfo(price).personalizedInfo(buildPersonalizedInfo(product, user)).build();}).exceptionally(ex -> {logger.error("获取商品详情失败: productId={}, userId={}", productId, userId, ex);throw new ServiceException("商品信息暂时无法获取", ex);});}/*** 购物车价格计算(批量处理)*/public CompletableFuture<CartPriceResponse> calculateCartPrice(List<CartItem> cartItems, String userId) {// 按商品ID分组,批量调用价格服务Map<String, List<CartItem>> itemsByProduct = cartItems.stream().collect(Collectors.groupingBy(CartItem::getProductId));List<CompletableFuture<ProductPriceInfo>> priceFutures = itemsByProduct.entrySet().stream().map(entry -> {String productId = entry.getKey();List<CartItem> items = entry.getValue();return CompletableFuture.supplyAsync(() -> {// 批量获取商品价格和优惠信息PriceInfo priceInfo = priceServiceClient.getPrice(productId, userId);DiscountInfo discountInfo = priceServiceClient.getDiscount(productId, userId);return new ProductPriceInfo(productId, items, priceInfo, discountInfo);}, rpcExecutor);}).collect(Collectors.toList());// 并行获取用户的优惠券信息CompletableFuture<List<CouponInfo>> couponFuture = CompletableFuture.supplyAsync(() -> userServiceClient.getUserCoupons(userId), rpcExecutor);// 等待所有价格计算完成return CompletableFuture.allOf(Stream.concat(priceFutures.stream(),Stream.of(couponFuture)).toArray(CompletableFuture[]::new)).thenApply(v -> {List<ProductPriceInfo> productPrices = priceFutures.stream().map(CompletableFuture::join).collect(Collectors.toList());List<CouponInfo> coupons = couponFuture.join();// 计算总价return calculateTotalPrice(productPrices, coupons);});}/*** 服务健康检查*/public CompletableFuture<Map<String, ServiceHealth>> checkServiceHealth() {Map<String, CompletableFuture<ServiceHealth>> healthChecks = Map.of("user-service", checkServiceHealth("user-service", userServiceClient::healthCheck),"product-service", checkServiceHealth("product-service", productServiceClient::healthCheck),"inventory-service", checkServiceHealth("inventory-service", inventoryServiceClient::healthCheck),"price-service", checkServiceHealth("price-service", priceServiceClient::healthCheck));CompletableFuture<Void> allChecks = CompletableFuture.allOf(healthChecks.values().toArray(new CompletableFuture[0]));return allChecks.thenApply(v -> healthChecks.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey,entry -> entry.getValue().join())));}private CompletableFuture<ServiceHealth> checkServiceHealth(String serviceName, Supplier<Boolean> healthCheck) {return CompletableFuture.supplyAsync(() -> {try {boolean isHealthy = healthCheck.get();return new ServiceHealth(serviceName, isHealthy, null);} catch (Exception e) {return new ServiceHealth(serviceName, false, e.getMessage());}}, rpcExecutor).orTimeout(2000, TimeUnit.MILLISECONDS)  // 健康检查超时时间.exceptionally(ex -> new ServiceHealth(serviceName, false, "检查超时"));}
}

微服务调用时序图:
在这里插入图片描述

http://www.dtcms.com/a/343953.html

相关文章:

  • Node.js中Express框架入门教程
  • vue/react使用h5player对接海康ws视频流实时播放,监控回放
  • 快速入门Vue3——初体验
  • CS创世SD NAND在北京君正平台和瑞芯微RK平台的应用
  • 高压、高功率时代,飞机电气系统如何保障安全?
  • 安全运维过程文档体系规范
  • 2025软件供应链安全技术路线未来趋势预测
  • Docker的安装
  • Docker Hub 镜像一键同步至阿里云 ACR
  • 如何在Windows 10/11家庭版安装组策略编辑器
  • nanoGPT 部署
  • 解决 SymPy Lambdify 中的符号覆盖与语法错误问题
  • 本地组策略编辑器图形化工具
  • STM32 - Embedded IDE - GCC - 重定向printf到串口
  • pytorch 网络可视化
  • 网易云音乐歌曲导出缓存为原始音乐文件。低调,低调。。。
  • 爬虫逆向之易盾文字点选分析
  • Kafka消息丢失的场景有哪些
  • 漏洞分析 | Kafka Connect 任意文件读取漏洞(CVE-2025-27817)
  • selenium爬虫
  • 开源 vs 商业 DevOps 平台:如何选择最适合你的方案?
  • Elasticsearch高能指南
  • 学习:uniapp全栈微信小程序vue3后台(3)
  • 嵌入式Linux学习 -- 网络1
  • StarRocks启动失败——修复全流程
  • 姓名重名查询抖音快手微信小程序看广告流量主开源
  • 恢复性测试:定义、重要性及实施方法
  • Linux设备模型交互机制详细分析
  • 分段渲染加载页面
  • 第9课:本地功能集成