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

一次订单同步任务的多线程改造实践

背景最近我在维护一个订单同步任务每天需要从第三方系统同步订单数据到本地数据库。原来的代码是串行执行的按天循环一天一天地去调用 API 同步数据。java// 原来的代码串行 String date sinceDate; while (!date.equals(nowDate)) { String url buildUrl(date); batchSyncOrder(url); // 同步这一天 date nextDate; }一次同步需要跑 30 天假设每天平均耗时 2-3 秒总耗时就要 1-2 分钟。这显然有很大的优化空间。于是我决定对这两个定时任务进行多线程改造。一、为什么可以并行分析后发现每天的数据是天然隔离的每天调用独立的 API 接口每天使用独立的数据库事务SqlSession天与天之间没有任何数据依赖这就像工厂里的流水线每个工位处理不同的产品互不干扰。串行是等第 1 天做完再做第 2 天并行是 30 天同时开工。二、改造前的代码分析2.1 两个定时任务任务同步天数执行频率syncMonthOrder()最近 30 天每天凌晨 2 点syncAllOrder()最近 365 天每周日凌晨 3 点2.2 原有代码的一个隐藏 Bug改造过程中发现doGet()方法上有个Async注解javaAsync public String doGet(String url) { // 发起 HTTP 请求 return result; }由于Async生效调用方拿到的结果是null真正的 HTTP 请求在另一个线程中执行。这意味着原来的代码其实一直有问题每天的数据根本没有被正确同步我把Async去掉让doGet()变成同步方法先修复了这个 Bug。三、ThreadPoolExecutor 详解3.1 什么是 ThreadPoolExecutorThreadPoolExecutor是 Java 线程池的核心实现类位于java.util.concurrent包中。它管理着一组工作线程负责执行提交的任务。3.2 为什么要用线程池方式问题每次 new Thread()创建销毁开销大线程数量不可控容易 OOM使用线程池复用线程控制并发数管理任务队列3.3 核心构造参数javapublic ThreadPoolExecutor( int corePoolSize, // 核心线程数 int maximumPoolSize, // 最大线程数 long keepAliveTime, // 空闲线程存活时间 TimeUnit unit, // 时间单位 BlockingQueueRunnable workQueue, // 任务队列 ThreadFactory threadFactory, // 线程工厂 RejectedExecutionHandler handler // 拒绝策略 )参数一corePoolSize核心线程数核心线程会一直存活即使空闲也不会被回收除非设置allowCoreThreadTimeOut(true)。参数二maximumPoolSize最大线程数线程池允许创建的最大线程数量。当任务队列满了且当前线程数小于最大线程数时会创建新线程来处理任务。参数三keepAliveTime unit空闲存活时间非核心线程空闲超过这个时间就会被回收。参数四workQueue任务队列用于存放等待执行的任务。常见的有队列类型特点LinkedBlockingQueue链表结构的有界/无界队列ArrayBlockingQueue数组结构的有界队列SynchronousQueue不存储任务直接交给线程执行参数五threadFactory线程工厂用于创建线程可以给线程起有意义的名称javaThreadFactory threadFactory new ThreadFactory() { private final AtomicInteger threadNumber new AtomicInteger(1); Override public Thread newThread(Runnable r) { Thread t new Thread(r, order-sync-pool- threadNumber.getAndIncrement()); t.setUncaughtExceptionHandler((thread, throwable) - { log.error(线程 {} 发生未捕获异常, thread.getName(), throwable); }); return t; } };参数六handler拒绝策略当线程池和任务队列都满了时触发拒绝策略策略行为AbortPolicy默认抛出 RejectedExecutionExceptionCallerRunsPolicy让提交任务的线程自己执行DiscardPolicy静默丢弃任务DiscardOldestPolicy丢弃队列头部的任务重新提交3.4 线程池工作原理重点当一个任务被提交到线程池时执行流程如下text步骤1当前线程数 corePoolSize └─ 是 → 创建新线程执行任务流程结束 └─ 否 → 进入步骤2 步骤2任务队列未满 └─ 是 → 将任务放入队列等待 └─ 否 → 进入步骤3 步骤3当前线程数 maximumPoolSize └─ 是 → 创建新线程执行任务 └─ 否 → 触发拒绝策略图解说明text提交任务 ↓ ┌─────────────────────────────────────┐ │ 当前线程数 核心线程数 │ │ 例如当前5个线程核心线程数10 │ └─────────────────────────────────────┘ ↓ 否已经有10个核心线程在工作 ┌─────────────────────────────────────┐ │ 任务队列是否已满 │ │ 队列容量500当前有500个任务等待 │ └─────────────────────────────────────┘ ↓ 是队列满了 ┌─────────────────────────────────────┐ │ 当前线程数 最大线程数 │ │ 当前10个线程最大线程数50 │ └─────────────────────────────────────┘ ↓ 是 ┌─────────────────────────────────────┐ │ 创建新的非核心线程执行任务 │ └─────────────────────────────────────┘ ↓ 否已经有50个线程了 ┌─────────────────────────────────────┐ │ 触发拒绝策略 │ └─────────────────────────────────────┘3.5 线程回收机制核心线程默认一直存活空闲也不回收非核心线程空闲超过keepAliveTime后被回收可以通过allowCoreThreadTimeOut(true)让核心线程也能被回收3.6 如何合理设置线程数这是一个经典问题取决于任务类型任务类型特点建议线程数CPU密集型大量计算CPU 一直是 100%CPU核心数 1IO密集型网络调用、数据库操作大部分时间在等待CPU核心数 × (1 IO耗时/CPU耗时)我的任务是调用第三方 API 和操作数据库属于典型的IO 密集型所以线程数可以设置得大一些月同步30天10-50 个线程全量同步365天20-100 个线程3.7 我的线程池创建代码javaprivate ExecutorService createExecutorForMonthSync(int dateCount) { int cpuCores Runtime.getRuntime().availableProcessors(); int corePoolSize Math.min(dateCount, Math.max(10, cpuCores * 2)); int maximumPoolSize Math.min(dateCount, 50); long keepAliveTime 60L; TimeUnit unit TimeUnit.SECONDS; BlockingQueueRunnable workQueue new LinkedBlockingQueue(200); ThreadFactory threadFactory new ThreadFactory() { private final AtomicInteger threadNumber new AtomicInteger(1); Override public Thread newThread(Runnable r) { Thread t new Thread(r, month-sync-pool- threadNumber.getAndIncrement()); t.setDaemon(false); return t; } }; RejectedExecutionHandler handler new ThreadPoolExecutor.CallerRunsPolicy(); return new ThreadPoolExecutor( corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler ); }四、CompletableFuture 详解4.1 什么是 CompletableFutureCompletableFuture是 Java 8 引入的异步编程工具可以看作Future的增强版。它解决了传统Future的几个痛点痛点FutureCompletableFuture手动完成结果❌✅ complete()回调机制❌✅ thenApply/thenAccept组合多个任务❌✅ allOf/anyOf异常处理抛出✅ exceptionally超时控制需自己实现✅ get(timeout)4.2 CompletableFuture 的核心数据结构javapublic class CompletableFutureT { // 存储结果或异常volatile保证可见性 volatile Object result; // 等待栈链表结构存储依赖的回调任务 volatile Completion stack; }关键设计result任务完成后的结果未完成时为nullstack一个栈结构存储所有等待该 Future 完成的回调任务当 Future 完成时会遍历这个栈依次执行每个回调。4.3 任务的创建方式java// 无返回值 CompletableFutureVoid future1 CompletableFuture.runAsync(() - { System.out.println(执行任务); }, executor); // 有返回值 CompletableFutureString future2 CompletableFuture.supplyAsync(() - { return result; }, executor);注意如果不指定executor会使用默认的ForkJoinPool.commonPool()。4.4 核心方法详解runAsyncjavapublic static CompletableFutureVoid runAsync(Runnable runnable, Executor executor)提交一个没有返回值的任务返回CompletableFutureVoid。supplyAsyncjavapublic static U CompletableFutureU supplyAsync(SupplierU supplier, Executor executor)提交一个有返回值的任务。allOfjavapublic static CompletableFutureVoid allOf(CompletableFuture?... cfs)这是我最常用的方法。它创建一个新的 CompletableFuture当传入的所有Future 都完成时这个新 Future 才会完成。javaCompletableFutureVoid f1 CompletableFuture.runAsync(() - task1()); CompletableFutureVoid f2 CompletableFuture.runAsync(() - task2()); CompletableFutureVoid f3 CompletableFuture.runAsync(() - task3()); // 等待 f1、f2、f3 全部完成 CompletableFuture.allOf(f1, f2, f3).get();底层原理allOf()内部维护了一个计数器每有一个 Future 完成计数器减 1当计数器为 0 时结果 Future 被标记为完成。get(timeout, unit)javapublic T get(long timeout, TimeUnit unit)阻塞等待结果最多等待指定时间。超时会抛出TimeoutException。exceptionallyjavapublic CompletableFutureT exceptionally(FunctionThrowable, ? extends T fn)处理异常返回一个默认值。4.5 方法链示例javaCompletableFuture.supplyAsync(() - hello) .thenApply(s - s.toUpperCase()) // HELLO .thenApply(s - s WORLD) // HELLO WORLD .thenAccept(System.out::println) // 打印 .exceptionally(e - { log.error(处理失败, e); return null; });4.6 我在改造中的使用javaListCompletableFutureVoid futures new ArrayList(); for (String date : dateList) { CompletableFutureVoid future CompletableFuture.runAsync(() - { try { syncOneDay(date); successCount.incrementAndGet(); } catch (Exception e) { failCount.incrementAndGet(); failedDates.add(date); } }, executor); futures.add(future); } // 等待所有任务完成最多30分钟 try { CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])) .get(30, TimeUnit.MINUTES); } catch (TimeoutException e) { log.error(同步任务整体超时); futures.forEach(f - f.cancel(true)); }五、失败兜底策略多线程环境下失败处理是非常重要的环节。我设计了以下几层兜底策略5.1 单天失败不影响其他天某一天同步失败比如网络超时、API 返回错误不会影响其他天的同步。失败的日期会被记录下来最后统一告警。javaAtomicInteger successCount new AtomicInteger(0); AtomicInteger failCount new AtomicInteger(0); ListString failedDates Collections.synchronizedList(new ArrayList()); for (String date : dateList) { CompletableFutureVoid future CompletableFuture.runAsync(() - { try { syncOneDay(date); successCount.incrementAndGet(); } catch (Exception e) { failCount.incrementAndGet(); failedDates.add(date); log.error(同步失败: {}, date, e); } }, executor); futures.add(future); } // 最后输出失败的日期 if (!failedDates.isEmpty()) { log.error(失败日期列表: {}, failedDates); }5.2 整体超时控制设置最大等待时间月同步 30 分钟全量同步 60 分钟超时后主动取消未完成的任务javatry { CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])) .get(30, TimeUnit.MINUTES); } catch (TimeoutException e) { log.error(同步任务整体超时部分任务可能未完成); futures.forEach(f - f.cancel(true)); // 取消未完成的任务 }5.3 线程池优雅关闭任务完成后需要优雅地关闭线程池确保已提交的任务能执行完成javaprivate void shutdownExecutor(ExecutorService executor) { executor.shutdown(); // 禁止提交新任务 try { // 等待30秒让现有任务完成 if (!executor.awaitTermination(30, TimeUnit.SECONDS)) { log.warn(线程池未能在30秒内完成尝试强制关闭); executor.shutdownNow(); // 强制中断 if (!executor.awaitTermination(10, TimeUnit.SECONDS)) { log.error(线程池强制关闭后仍未完全终止); } } } catch (InterruptedException e) { executor.shutdownNow(); Thread.currentThread().interrupt(); } }5.4 线程安全的统计多线程环境下普通的int和ArrayList不是线程安全的。需要使用原子类或同步容器java// 计数器 AtomicInteger successCount new AtomicInteger(0); successCount.incrementAndGet(); // 原子操作 // 列表 ListString failedDates Collections.synchronizedList(new ArrayList());5.5 线程未捕获异常处理通过ThreadFactory设置全局异常处理器javaThreadFactory threadFactory new ThreadFactory() { Override public Thread newThread(Runnable r) { Thread t new Thread(r, sync-pool- counter.getAndIncrement()); t.setUncaughtExceptionHandler((thread, throwable) - { log.error(线程 {} 发生未捕获异常, thread.getName(), throwable); }); return t; } };六、踩坑记录6.1 数据库连接池不够用多线程同时操作数据库需要确保连接池大小足够。现象部分任务卡住日志显示获取连接超时。解决调整 HikariCP 连接池配置最大连接数从 20 调到 50。yamlspring: datasource: hikari: maximum-pool-size: 506.2 API 限流多个线程同时请求第三方 API触发了限流。解决用Semaphore控制并发数javaprivate final Semaphore semaphore new Semaphore(10); private void syncOneDay(String date) { semaphore.acquire(); try { // 调用 API } finally { semaphore.release(); } }七、改造效果指标改造前改造后30 天同步耗时约 1-2 分钟26 秒代码改动量-约 150 行失败影响范围某天失败会导致后续全部停止只影响当天其他继续日志输出示例text 开始同步订单最近30天 需要同步的总天数: 30 创建月同步线程池: corePoolSize20, maximumPoolSize50 ... 各线程并行执行 ... 月同步完成统计 总天数: 30 成功天数: 30 失败天数: 0 总耗时: 26秒 Update task overdue success.八、总结这次改造让我对多线程有了更深的理解知识点实践应用核心线程数 vs 最大线程数核心常驻最大应对突发有界队列防止任务积压导致 OOM拒绝策略CallerRunsPolicy保证任务不丢失CompletableFuture.runAsync()异步执行无返回值的任务CompletableFuture.allOf()等待多个异步任务全部完成get(timeout)整体超时控制AtomicInteger线程安全的计数synchronizedList线程安全的列表shutdown()awaitTermination()优雅关闭线程池核心原则任务独立性任务之间没有依赖关系才适合并行失败隔离一个任务失败不能影响其他任务超时控制避免无限等待设置合理超时资源管理连接池、线程池都要合理配置线程安全共享变量必须用线程安全的类多线程改造并不复杂关键是理解业务场景选择合适的方案。希望这篇文章对你有帮助

相关文章:

一次订单同步任务的多线程改造实践

背景最近我在维护一个订单同步任务,每天需要从第三方系统同步订单数据到本地数据库。原来的代码是串行执行的:按天循环,一天一天地去调用 API 同步数据。java// 原来的代码(串行) String date sinceDate; while (!dat…...

集鲜鲜肉核心业务模式

直连源头与终端 通过数字化平台直接对接屠宰场与下游客户(批发商、食品工厂、供应链企业、团餐等),减少中间环节,实现白条猪肉的高效流通。四大核心服务板块数智化采销 覆盖全国16个省份猪源,日均供应量超2000吨&#…...

计算机毕设论文写到崩溃?AI自动生成万字初稿,附查重降重技巧

一、论文,是压垮毕业生的最后一根稻草 我见过太多这样的场景: 代码写完了,系统跑通了,导师说"可以答辩了"然后一看论文,还有8000字没写...打开Word,盯着光标闪了半天,一个字憋不出来…...

获取安卓10或以上唯一标识

公司原先的设备运行的是安卓10以下系统,使用内部开发的方法重置设备标识时不会发生变化。但随着设备系统升级至更高版本后,我们发现原有方法已失效,必须重新开发新的解决方案。方法如下:// 需要原生插件支持 plus.android.importC…...

多模态模型体积暴增87%?SITS2026首席架构师亲授:4类跨模态冗余识别法+2种硬件感知剪枝策略

第一章:SITS2026专家:多模态模型压缩 2026奇点智能技术大会(https://ml-summit.org) 多模态压缩的核心挑战 传统单模态压缩方法(如图像剪枝、语言模型量化)难以直接迁移至多模态场景,因跨模态对齐损失、联合表征耦合…...

高熵合金强塑失衡?看行业研究如何破解这一难题

强塑失衡是金属材料研发领域的共性难题——传统合金往往难以兼顾高强度与高塑性,强度提升常伴随塑性下降,制约其工程应用范围。高熵合金以多主元、高熵效应为核心特征,打破传统合金单一主元设计理念,其最核心的研究价值便是破解这…...

VisionPro点胶检测项目复盘:我是如何用CogCopyRegionTool和极性转换搞定复杂背景干扰的?

VisionPro点胶检测实战:复杂背景干扰下的预处理与极性转换技巧 在工业视觉检测领域,点胶质量检测一直是个令人头疼的问题——尤其是当产品背景存在复杂纹理或干扰图案时。传统的二值化处理方法往往会被这些干扰因素"带偏",导致误检…...

GPT-SoVITS实战效果:高清音质语音克隆,听起来和真人一样

GPT-SoVITS实战效果:高清音质语音克隆,听起来和真人一样 1. 引言:语音克隆技术的新突破 想象一下,你只需要录制5秒钟的语音,就能让AI完美复刻你的声音——这不是科幻电影,而是GPT-SoVITS带来的真实能力。…...

告别“假性忙碌”:如何区分生产性努力与表演性努力?

目录 01 先分清两种“努力” 02 三个信号,测测你是不是在假性忙碌 信号一:你的日程表被琐事填满,核心目标纹丝不动 信号二:你害怕停下来,一有空闲就心慌 信号三:你总是在救火,但从不防火…...

一文讲透扩散模型采样器:DDPM、DDIM、Euler、Heun、DPM-Solver、UniPC、LCM 全面对比

下面我把范围先说清楚: 你问的“类似 Euler、Heun、DPM-Solver 这种定位的求解器”,我这里按扩散模型 / Flow Matching 推理阶段的通用采样器(scheduler / solver)来整理,而不把任务专用或模型专用的东西混进来,比如 RePaint、DDIM inverse、VQDiffusion、CogVideoX/Hel…...

Qwen-Image-2512-Pixel-Art-LoRA 结合YOLOv8:为生成的像素画智能添加检测框标注

Qwen-Image-2512-Pixel-Art-LoRA 结合YOLOv8:为生成的像素画智能添加检测框标注 你有没有想过,自己生成的像素画,能不能像游戏里的场景一样,自动识别出里面的房子、树木和人物?最近我在尝试一个挺有意思的组合&#x…...

Qwen3-0.6B-FP8部署全攻略:环境配置+模型调用一步到位

Qwen3-0.6B-FP8部署全攻略:环境配置模型调用一步到位 想快速体验一个轻量级但能力不俗的大语言模型吗?Qwen3-0.6B-FP8镜像为你提供了一个开箱即用的解决方案。这个镜像基于通义千问最新的Qwen3-0.6B模型,通过vLLM进行高效部署,并…...

谁能按那个按钮?——美国EAS系列(三):权限、授权链与对国内应急广播的启示

「美国EAS系列」收官篇。第一篇讲了EAS的历史与五桩事故,第二篇讲了它的分发架构和多级管理。这一篇我们把最后一层门推开:谁有资格签发一条警报、权限在端侧如何崩塌、以及中国正在落地的CDR应急广播体系能从这七十年的血泪里直接抄到哪几条作业。 一个…...

生态系统NPP及碳源、碳汇模拟:土地利用变化、未来气候变化、空间动态模拟

查看原文>>> https://mp.weixin.qq.com/s/OlIHIKED91-KI2vaXK8B9g 前言 由于全球变暖、大气中温室气体浓度逐年增加等问题的出现,“双碳”行动特别是碳中和已经在世界范围形成广泛影响。碳中和可以从碳排放(碳源)和碳固定&#xf…...

CISSP 域4知识点 无线与移动网络安全

CISSP考点速记|Domain4 无线与移动网络安全 📶🔐 官方定位:域4通信与网络安全的核心模块,占Domain4权重25%左右;对应OSG第十版第10、11章无线与移动专项内容。是企业网络边界延伸&混合办公安全的核心考…...

Node.js-安装部署

1 需求 …… 2 接口 …… 3 示例 …… 4 参考资料 https://zhuanlan.zhihu.com/p/2004975759790477711...

避坑指南:Xilinx Aurora IP核多核例化时,GT_RESET信号必须保持10个时钟周期的原因

Xilinx Aurora IP核多核例化中GT_RESET信号的时序陷阱与实战解决方案 在FPGA高速串行通信领域,Xilinx的Aurora 8B/10B协议IP核因其简洁高效的特性,成为多通道数据交互的首选方案。但当工程师尝试在单个QUAD中例化多个Aurora IP核以实现高密度链路时&…...

工业缺陷检测应用:结合YOLOv5与PyTorch 2.8实现高精度识别

工业缺陷检测应用:结合YOLOv5与PyTorch 2.8实现高精度识别 1. 工业质检的痛点与AI解决方案 在制造业生产线上,零件表面缺陷检测一直是个老大难问题。传统的人工检测方式不仅效率低下(每小时最多检测几百个零件),而且…...

STM32 FatFS连续写入SD卡数据丢失?3个常见坑点与实战修复方案

STM32 FatFS连续写入SD卡数据丢失?3个常见坑点与实战修复方案 最近在调试STM32的SD卡数据记录功能时,遇到了一个让人头疼的问题:使用FatFS库连续写入数据时,SD卡中的文件要么是空的,要么全是乱码。经过一番折腾&#x…...

面试官问你做过几层板,这么回才加分

前几天有个学生跟我吐槽,说面试的时候被问到"你做过几层板",当时脑子一片空白,随便回了句"做过4层的",结果面试官哦了一声就跳到下一个问题了。他回来后越想越慌,问我是不是答砸了。说实话&#x…...

2026 全球 AI 大模型全景榜单:国产强势崛起,国际格局重塑

2026 年,全球 AI 大模型产业正式步入技术深耕与规模化落地并行的关键阶段。国际顶尖模型持续在超大参数、全模态融合、智能体协作上突破;国产模型则凭借本土场景适配、垂直领域深耕、算力自主可控实现快速追赶,在政务、制造、教育、电商等领域…...

​从CNN到Transformer:基于PyTorch的遥感影像、无人机影像的地物分类、目标检测、语义分割和点云分类

我国高分辨率对地观测系统重大专项已全面启动,高空间、高光谱、高时间分辨率和宽地面覆盖于一体的全球天空地一体化立体对地观测网逐步形成,将成为保障国家安全的基础性和战略性资源。随着小卫星星座的普及,对地观测已具备多次以上的全球覆盖…...

宝宝益生菌,这2大品牌必须了解

引言在宝宝的成长过程中,肠道健康至关重要。益生菌作为调节肠道菌群、增强免疫力的“小卫士”,成为众多家长关注的焦点。市场上宝宝益生菌品牌众多,如何挑选成为了家长们头疼的问题。今天,就为大家深度分析两个值得了解的宝宝益生…...

Embedding算法入门到精通:拆解腾讯二面必考题,收藏这一篇就够了!

👔面试官:RAG 里用的 Embedding 算法有哪些?你了解过几代演进? 🙋‍♂️我:Embedding 算法我知道,Word2Vec 嘛,把词变成向量。 👔面试官:Word2Vec 是 2013 …...

DeepSeek-R1 1.5B使用技巧:这样提问,AI回答更准确更详细

DeepSeek-R1 1.5B使用技巧:这样提问,AI回答更准确更详细 1. 引言:提问方式决定回答质量 你是否遇到过这样的情况:向AI提问后,得到的回答要么过于简略,要么偏离主题?这很可能不是模型能力的问题…...

2026高性价比协作工具盘点:如何兼顾文件管理与数据安全?

在2026年的数字化办公时代,企业网盘早已不仅是简单的“存储仓库”,而是团队协作、文件流转和保障数据资产安全的核心基础设施。针对市面上眼花缭乱的产品,企业IT和管理者如何找到匹配业务需求且具备高性价比的云盘方案? 本文将从…...

DeerFlow深度研究助理5分钟快速上手:零基础搭建个人AI研究助手

DeerFlow深度研究助理5分钟快速上手:零基础搭建个人AI研究助手 1. 认识DeerFlow:您的智能研究伙伴 DeerFlow是一款基于LangStack技术框架开发的深度研究助理工具。它能像专业研究员一样帮您完成信息搜集、数据分析、报告撰写甚至播客制作等工作。想象一…...

企业网盘哪个品牌好用?2026年企业高效办公网盘TOP5产品全景测评

“信息创造了人类,而信息流通与共享塑造了文明。”在数字化协作成为企业命脉的今天,这句预言愈发显现其前瞻性。过去几年中,远程协同办公逐渐常态化,当团队分散在各地、项目文件版本混乱、重要数据面临泄露风险时,传统…...

W-TRS-5.5D7红外测温传感器:电饭煲智能化测温的革新力量

在健康饮食与智能烹饪需求日益增长,电饭煲作为厨房的核心设备之一,其测温技术的革新直接关系到米饭的口感、营养保留以及特殊饮食需求的满足。W-TRS-5.5D7红外测温传感器的出现,以非接触式测温技术为核心,结合定制化算法实现智能化…...

储能BMS数据语境化采集架构解析与边缘计算网关选型推荐

摘要:在新能源场站的系统集成中,面对各厂家互不兼容的BMS/PCS总线协议,传统的硬编码接入模式存在扩展性差、联调耗时长的问题。本文分享一种在底层计算节点中利用数据语境化机制与动态映射解析器实现零代码接入的高阶架构,探讨通用…...