这两年我一个人维护着好几个线上服务——有给客户端提供数据的API网关有跑定时任务的Worker还有几个对外提供查询的数据库实例。说实话最怕的不是功能写不出来而是某个服务在凌晨三点悄悄挂掉然后第二天被用户在群里 才后知后觉。PLFM_RADAR 这个项目就是因为这种狼狈而产生的。PLFM 取自 Platform 的缩写RADAR 就是雷达的意思。合起来它是一套“平台运行状态雷达”系统——定时去探测各个服务的存活状态、延迟、错误率、资源占用把这些数据汇总成可视化的面板并且在指标异常时通过钉钉、邮件等方式第一时间通知我。它的定位很明确不抢 APM 全家桶的活只做一件事那就是让你知道你的服务现在健不健康、什么时候开始不健康、为什么不健康。这个项目适合谁参考我建议两类人重点看看一类是自己手里有几个服务但没精力搭整套 Prometheus Grafana 的独立开发者另一类是团队里想快速实现“监测闭环”但不太想被复杂技术栈绑架的小组。文中的所有方案我都是在真实部署环境下跑过一段时间的踩过的坑、绕过的弯都会尽量写清楚。1. PLFM_RADAR 项目概述与设计思路1.1 核心需求解析你到底需要“监”什么动手之前先把需求掰开。PLFM_RADAR 想解决的问题有三层第一层是存活检测。服务是否还在运行端口能不能连通健康检查接口是否返回 200。这是最基础但也是最重要的线上 80% 的事故都始于“某个服务默默退出了”。第二层是性能监控。响应延迟有没有变慢错误率是不是在攀升系统资源 CPU、内存、磁盘是不是快打满了。这些指标是缓慢恶化的过程等到用户报障时往往已经影响业务了雷达需要在恶化初期就把异常信号拉出来。第三层是联动分析。单个服务的指标异常未必能说明问题但如果多个服务同时延迟升高、错误率上扬那大概率是下游数据库出问题了。PLFM_RADAR 把各层服务的指标放在同一个时间轴上对齐方便快速定位故障层级。这三层需求对应到系统设计就是三个模块采集模块、规则引擎、展示面板。我见过很多人一上来就追求大而全把消息队列、分布式链路追踪全塞进来结果项目烂尾。其实监控系统最核心的价值是“先响起来”至于定位根因那是后续一步步精细化的事。所以 PLFM_RADAR 一开始的定位就是克制的能探测、能告警、能看趋势就够了。1.2 为什么选择“自研轻量探针 规则引擎”而不是直接上 Prometheus先给结论如果你已经有成熟的 Kubernetes 环境直接上 Prometheus Operator 是合理的。但我自己的服务分散在几台普通云服务器上有的跑在 Docker 里有的干脆是 systemd 托管的裸进程强行引入 Prometheus 体系反而要维护一大堆 exporter学习成本和运维成本都不低。PLFM_RADAR 的做法是用 Python 写一个轻量探针直接通过 HTTP 轮询各服务的健康接口同时用 psutil 采集本机的 CPU、内存、磁盘、网络指标。这个探针采集到的数据写入一个时序数据库规则引擎定期扫描数据一旦触发阈值就走告警通知。这个方案的优点是核心逻辑完全是可控的指标口径、告警规则、通知渠道都捏在自己手里出了问题你在 200 行代码内就能定位而不是去翻一堆 YAML 配置和 operator 的文档。当然这个方案也有明显的短板最主要是性能上限。HTTP 轮询的实时性远不如推模式秒级监控想都不要想我实际用下来 30 秒到 1 分钟粒度的探测比较合适。另外如果未来服务数量到了几百上千这个架构会先遇到瓶颈。所以我的建议是服务数量在 30 个以内、追求快速见效的团队用 PLFM_RADAR 的思路如果规模更大再平滑迁移到 Prometheus 体系不迟。1.3 技术选型背后的几个关键决定做技术选型的时候我反复权衡了三组矛盾这里直接分享结论。时间序列存储我最后用了 InfluxDB 1.8 的免费单机版。原因很简单它自带数据保留策略可以自动清理一个月前的旧数据而且查询语法对“最近一小时的平均延迟”“最近五分钟的错误率”这类聚合计算支持得非常好。对比的话用 SQLite 起步确实更轻但数据一多查询越来越慢过期数据还得自己写脚本清理我踩过这个坑不建议你走弯路。前端可视化我用的是 ECharts 的雷达图和折线图。雷达图天生适合展示多维度指标的综合健康度一眼就能看出来哪个维度“塌”了折线图则适合看趋势变化。我见过有人用 Grafana 做同样的事当然很好但 Grafana 那种表格化、搜索式的交互和“雷达图”这种直观形态相比少了一种“整个平台的健康状况尽收眼底”的感觉。而且 ECharts 可以直接嵌到一个轻量 Flask 页面里部署简单很多。告警通知我接的是钉钉群机器人。用 Webhook 发一条 JSON 就行不需要申请额外的 SDK 权限而且手机端推送很及时基本能做到 10 秒内送达。如果你团队用企业微信或者飞书原理完全一样改下请求地址和报文格式而已。2. 核心功能拆解数据采集、指标计算与告警规则2.1 数据采集探针的实现细节探针是整个 PLFM_RADAR 的地基。我把它设计成一个常驻进程每 30 秒执行一次采集任务。采集分两个维度服务维度和主机维度。服务维度最关键的是延迟和错误率。我用 requests 库请求每个服务的健康检查接口记录从发出请求到收到响应的耗时同时把 HTTP 状态码记录下来。这里有一个很多新手容易忽略的点健康检查接口要区分“探测成功”和“业务正常”。比如一个接口返回 200 但业务逻辑已经异常这时候 200 没有任何意义。所以我在被探测服务侧额外暴露了一个/health接口内部会去检查数据库连接池、缓存连接等关键依赖依赖异常时返回 503。这样雷达拿到的状态才是真实的服务健康度。主机维度的采集相对简单直接用 psutil 拿 CPU 使用率、内存占用率、磁盘剩余空间、网络收发速率。这里要特别提一下磁盘很多监控系统把磁盘空间当成普通指标来展示但我建议把根分区使用率的告警阈值设在 80%因为日志文件、镜像、临时文件都会在一个晚上把磁盘塞满一旦满了服务写不了日志直接就僵死连监控本身的日志也会丢。下面是一段简化后的探针代码可以感受一下整体结构import time import requests import psutil SERVICES [ {name: api-gateway, url: https://api.example.com/health, timeout: 3}, {name: worker, url: https://worker.example.com/health, timeout: 5}, ] def probe_services(): results [] for svc in SERVICES: start time.time() try: resp requests.get(svc[url], timeoutsvc[timeout]) status resp.status_code except Exception: status 0 elapsed_ms (time.time() - start) * 1000 results.append({ service: svc[name], status: status, latency_ms: round(elapsed_ms, 2), ts: int(time.time()), }) return results def probe_host(): return { cpu_percent: psutil.cpu_percent(interval1), mem_percent: psutil.virtual_memory().percent, disk_percent: psutil.disk_usage(/).percent, } def main(): services_data probe_services() host_data probe_host() write_to_influxdb(services_data [host_data])这套探针我在三台不同供应商的云服务器上跑过稳定性没有问题。唯一要注意的是psutil.cpu_percent(interval1)这个调用会有 1 秒的阻塞如果你把采集间隔压到 10 秒以下探针自身消耗会变大所以采集间隔别低于 15 秒。2.2 指标计算为什么不能直接拿“原始值”做告警探针写进数据库的是每分钟的多条原始样本但告警规则要用的往往是聚合后的指标。这中间有两个关键计算滑动窗口平均值和同比环比变化率。滑动窗口平均值的逻辑很简单取最近 5 分钟或最近 15 分钟的延迟数据算一个平均值或者 P95 分位数。为什么不用单次探测值直接做告警因为单值抖动太厉害了。我自己就遇到过某个服务本来很健康一次 GC 暂停让请求延迟从 50ms 跳到 2 秒如果用单值做阈值一晚上能收到二三十条误报但用 5 分钟窗口平均值那次抖动会被平均掉只有持续恶化才会触发告警。所以 PLFM_RADAR 所有告警规则的输入都是聚合值而不是原始值。同比环比变化率要稍微复杂一点。比如某个定时任务每天凌晨固定跑一个小时这期间 CPU 飙升是正常现象又比如某个电商页面每天早上 10 点迎来流量高峰错误率略涨也在预期内。这种情况下固定阈值完全失效。我的做法是把当前窗口的平均值和前 7 天同一时段的平均值做对比算出一个变化倍率超过正常波动范围才告警。def calc_ratio(current_avg, baseline_avg): if baseline_avg 0: return None return current_avg / baseline_avg说实话这个逻辑一开始我做得比较简单只是硬编码了一个“前移 24 小时”的窗口。后来发现节假日和高峰时段会误报才改成“前 7 天同时段”数据量够大之后准确率提升非常明显。2.3 告警分级与通知策略的实战配置告警最怕的是“狼来了”。如果规则不准每天推一堆无关紧要的消息大家很快就麻木了真正出事的时候反而没人当回事。所以我设计了一个三级告警体系。P0 级别服务完全不可用或者错误率持续超过 10%。这个级别的告警会在 1 分钟内重复推送直到人工确认才停止。P1 级别延迟超过阈值、资源接近瓶颈。推送一次如果 15 分钟内没有恢复再追推一次。P2 级别趋势性预警比如磁盘使用率超过 75%、错误率缓慢上升。合并成每日摘要早上推送。告警规则我直接写在 Python 字典里而不是搞一套 DSL。很多人觉得规则配置得做成页面但实际我用了很长时间发现改配置走代码发布流程反而更严谨至少每次改动都有记录不会出现“谁在页面上偷偷改了阈值”这种事。ALERT_RULES [ { name: api_gateway_down, metric: service_status, condition: , value: 0, window: 5m, severity: P0, target: api-gateway, }, { name: api_gateway_latency_p95, metric: latency_p95, condition: , value: 800, window: 5m, severity: P1, target: api-gateway, }, ]3. 实操过程从零到一搭建 PLFM_RADAR 完整流程3.1 环境准备与模块划分我实际的部署环境是两台 2C4G 的云服务器一台放被探测的业务服务一台跑 PLFM_RADAR 本身。系统都是 Ubuntu 22.04。如果你设备资源紧张甚至可以把雷达部署在同一台机器上只是要提防一个坑雷达自身占用资源过高影响被监控服务那你看到的数据就不真实了。整个项目可以拆成四个子模块probe/独立运行的采集探针写好之后直接 nohup 启动。storage/InfluxDB 的初始化脚本负责建库建表和数据保留策略。engine/规则引擎定期查询 InfluxDB 并执行告警判断。dashboard/Flask 应用提供 HTTP 接口把数据渲染到前端页面。模块之间通过数据库和 HTTP 接口解耦互不依赖。这也方便后续扩展比如你想新增一台业务服务器只需在 probe 的配置里加一条服务地址完全不用碰其他模块。3.2 第一次部署冒烟测试的完整过程第一次跑通整套流程我大概花了半小时这里把步骤原样贴出来。第一步安装基础依赖apt update apt install -y python3 python3-pip pip3 install influxdb requests psutil flask第二步初始化 InfluxDB 数据库。我用的是 InfluxDB 1.8因为它可以直接通过 HTTP API 打数据不需要额外的写入协议字段。influx -execute CREATE DATABASE plfm_radar influx -execute CREATE RETENTION POLICY \rp_30d\ ON \plfm_radar\ DURATION 30d REPLICATION 1 DEFAULT数据保留 30 天足够了。由于告警和看板都只要最近的数据超过 30 天的旧数据不仅没用还在消耗磁盘这条策略建议无论如何都要加上。第三步编写探针配置。先在一个独立的目录里把services.json写好把你要监控的所有服务地址列进去。我当时的清单大概是API 网关、用户服务、订单服务、定时任务 Worker、MySQL 实例、Redis 实例。第四步启动规则引擎。注意一定要让探针先跑几分钟攒下至少一个窗口期的数据再启动引擎否则引擎查不到数据会一直空转。第五步打开看板。Flask 默认跑在 5000 端口访问首页就能看到雷达图。第一次看到所有维度都亮起来的时候还是挺有成就感的。3.3 看板页面与雷达图的核心实现ECharts 雷达图适合展示“多维指标是否均衡”。我把每个服务定义成一个雷达图上的多边形维度包括可用性、延迟健康度、错误率健康度、资源健康度。每个维度先把它原始值映射到 0~100 分分数越高越健康。映射逻辑要解释一下延迟健康度的映射不是线性的而是“在 100ms 以内满分500ms 以上零分中间线性递减”。为什么这么设计因为用户对延迟的敏感度不是线性的从 10ms 涨到 100ms 用户几乎没感觉但从 400ms 涨到 800ms 已经非常卡顿了。所以我把映射曲线做得“低区平坦、高区陡峭”能更早反映体验恶化。前端核心代码大概长这样var chart echarts.init(document.getElementById(radar)); chart.setOption({ series: [{ type: radar, data: [{ value: [score.availability, score.latency, score.error, score.resource], name: serviceName }] }] });说实话前端这块在我的项目里是最“轻”的部分因为数据已经在后端算成了打分前端只负责渲染连状态管理都不需要。如果你不想纠结前端直接用 ECharts 官方实例改改就行十分钟就能出一个能看的版本。3.4 通知渠道接入钉钉机器人的配置示例钉钉自定义机器人的接入非常直接。先在群里添加一个自定义机器人拿到一个 Webhook 地址然后向这个地址 POST 一段 JSON。import requests def send_alert(rule_name, message): webhook https://oapi.dingtalk.com/robot/send?access_tokenxxxx body { msgtype: markdown, markdown: { title: PLFM_RADAR 告警, text: ### rule_name \n\n message, }, } requests.post(webhook, jsonbody, timeout5)这里有两个细节一是机器人安全设置里最好启用“加签”把密钥拼进 URL防止 Webhook 地址泄露后被恶意刷消息二是通知内容里一定要带上当前时间、目标服务名、实际值、阈值四个信息不然你收到消息还得再登服务器查一遍数据多耽误事。4. 常见问题与排查技巧实录4.1 时间窗口错位导致的误报这个坑是我上线第一周就遇到的。探针每 30 秒写一条数据规则引擎每 5 分钟执行一次窗口聚合。因为数据写入到 InfluxDB 有网络延迟和时钟偏差窗口切分的瞬间会出现“上一分钟的数据还没写进来”的问题导致窗口内数据点稀疏平均值被低估或直接没有数据。排查思路并不难把告警日志和 InfluxDB 里的实际数据放在一起对比就会发现告警触发的那一刻窗口数据其实是空的。解决办法是我在规则引擎里加了一个“延迟 60 秒再聚合”的策略。也就是查询窗口的时候只查当前时刻往前推 6 分钟到 1 分钟之间的数据给写入留出缓冲。这个修正解决了我大量的误报问题。4.2 高基数指标导致查询变慢PLFM_RADAR 初期为了图方便把服务名作为 tag 写入 InfluxDB然后在每一条测量记录里都带上service标签。随着服务增多tag 组合膨胀得很快查询性能肉眼可见地下降看板打开一次要好几秒。后来我做了两个优化第一把不需要聚合的标签比如 Master 节点名从 tag 挪到 field减少索引量第二合理设置保留策略旧数据压缩周期调短。优化之后查询基本回到毫秒级。你的系统如果规模不大这个问题可能不会触发但一旦开始卡优先查 tag 的基数准没错。4.3 告警风暴一个根因引发连环告警最吓人的一次是整个平台挂了PLFM_RADAR 在五分钟内推了 40 条告警。原因是一条数据库连接线程池耗尽导致所有的 API 服务都延迟超标、错误率飙升每个服务各触发一条 P1。那阵子我真的被手机震到心慌。后来我加了依赖抑制规则如果数据库节点已经触发 P0 告警那么在数据库恢复前上游服务的 P1 告警自动进入“静默”状态改成记录日志不推送。实现逻辑也不复杂就是一个优先级队列引擎在推送前先检查是否有更严重级别的告警处于未恢复状态有就先打断。这个机制上线后告警量降了 80%真正出事的时候反而更容易聚焦根因。4.4 探针进程静默退出有段时间雷达面板上的数据突然变稀了链路健康度迟迟不更新。我查了半天发现是探针进程因为未捕获异常退出——具体是某个被探测的服务返回了非法 JSON我的探针在解析时抛了 ValueError。这个问题很愚蠢但是很典型监控系统自身没有守护机制反而成了最先失明的那双眼睛。解决方案有两条我都做了。一是在探针代码的所有外部调用外层加 try except保证单点失败不会拖垮整个采集进程二是用 systemd 托管探针进程并设置Restartalways确保挂掉后能自动拉起。[Unit] DescriptionPLFM RADAR Probe [Service] ExecStart/usr/bin/python3 /opt/plfm_radar/probe/main.py Restartalways RestartSec10 [Install] WantedBymulti-user.target这条建议真的别省监控系统本身的高可用和故障恢复和业务系统同等重要。4.5 指标口径不统一越到后面越头疼团队里不同人写的服务健康检查接口返回的字段名不一样有的叫status有的叫success甚至有一个服务把健康状态放在响应头里我用 requests 接的时候差点没骂人。前期我都是单独适配代码里全是 if 分支丑到没法维护。后来我统一了一个约定所有被 PLFM_RADAR 纳管的服务健康检查接口一律返回 JSON格式固定为{status: ok, db: true, cache: true}。新服务上线前必须对照这个格式调整。这是整个项目里最有价值的一条“行政命令”它让探针代码简洁了非常多。以后你要做类似系统建议第一条就立这个规矩。5. 后续演进让 PLFM_RADAR 更智能、更开放5.1 从监控到预测接入趋势外推当前系统的逻辑是“事后告警”数据异常了才通知你。下一步我想加一个简单的趋势预测模块基于过去 7 天同一时段的指标曲线用线性回归或者简单移动平均预测未来 30 分钟的值如果预测值会超过阈值提前发一个 P2 级别的预警。这个功能听起来高大上其实实现起来很轻。监控数据本身是强周期性的工作日和周末、白天和凌晨的曲线形状大不相同只要按“礼拜几 时段”分组预测准确率就不会太差。我不建议一开始就上 LSTM 之类的深度学习模型数据量不够训练成本高还不如一个带季节性的统计学模型实在。5.2 从平台到生态把日志与告警联动起来告警只能告诉你“服务延迟高了”但延迟高到底是 SQL 慢查询、GC 停顿还是流量超限下一次迭代我打算在告警消息里附上关键日志片段具体做法是把服务日志接入 Loki告警触发时根据时间窗口去 Loki 拉取对应的 WARN 和 ERROR 级别日志拼进通知正文。这一步做完告警就不再只是一句“狼来了”而是顺手告诉你狼从哪个方向来的。5.3 给想要落地这套系统的朋友几个建议第一先跑通最小闭环再优化细节。我见过太多人第一版就想做到分布式、高可用、可视化酷炫结果迟迟上不了线。你的第一步目标应该只是“某个服务挂了我能收到通知”哪怕页面丑得像命令行输出都行。第二保持指标体系稳定比追求丰富重要。指标不是越多越好每多一个指标就多一份采集和计算的维护成本。先把“延迟、错误率、资源使用率”这几个核心指标守住再图扩张。第三告警规则要定期复盘。每周花 20 分钟看一遍这周触发的告警把那些“事后看完全没必要”的规则改掉或者删掉。监控系统的价值不在于告警条数多而在于每一条都值得被点开。第四尽量别用单点部署。PLFM_RADAR 本身就是用来守护服务的结果它自己挂了整个监控体系就瞎了。我的实践是用 systemd 拉起探针和规则引擎同时把 InfluxDB 定期备份到对象存储一旦服务器硬件故障能在半小时内恢复到新机器上。说实话PLFM_RADAR 并不是一个技术难度多高的项目。它真正的价值在于把“监控、告警、可视化”这个闭环用最简单可靠的方式串了起来。我从开发、部署到上线迭代前后也就花了一个周末的时间但之后一年多的时间里它至少帮我提前发现了十几次可能酿成事故的隐患——建连慢、磁盘吃紧、内存泄漏每一个都是这种轻量监控系统能轻松抓住的东西。我个人体会最深的一点是监控系统要设计得足够“笨”。不要依赖任何外部复杂组件不要搞花哨的分布式链路一个探针、一张表、一组规则就足够解决大部分服务守护问题。业务在增长系统在演进等哪天真需要更重的监控体系时PLFM_RADAR 留下的统一健康检查口径和告警分级模型也会是一个很不错的起点。
企业数字化 ERP 产品动态
相关推荐
PIC32MZ与MAX14431的工业SPI隔离设计:从选型到实战 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/2 14:52:09
工业SPI隔离实战:基于MAX14431与STM32F303ZE的完整设计与调试指南 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/2 14:52:09
工业SPI通信隔离方案:MAX14431BAEE+与STM32F405RG实战设计 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/2 14:52:09
OpenClaw与LINE集成提升养虾业沟通效率 1. OpenClaw与LINE集成的核心价值OpenClaw作为一款多平台即时通讯聚合工具,其与LINE的深度整合为养虾行业带来了前所未有的沟通效率提升。传统养虾场管理中,技术人员、养殖工人、销售团队之间的信息传递往往存在滞后性,而通过OpenClaw接入LIN… · 2026/11/2 23:59:44
Java record 规范构造器能否被反序列化或反射绕过 问题现象
record 类型通常靠规范构造器来把守不变式,比如禁止名字为空:
public record User(String name) {public User {if (name == null || name · 2026/11/2 23:59:44
Unity Package Manager刷新错误排查与解决方案 1. Unity Package Manager刷新资源报错问题解析最近在Unity开发过程中,Package Manager提示"Error Refreshing Asset"的问题困扰了不少开发者。作为Unity项目依赖管理的核心组件,Package Manager一旦出现问题,轻则影响资源加载&… · 2026/11/2 23:59:44
淘宝商品详情API开发实战与性能优化指南 1. 淘宝商品详情API核心功能解析淘宝开放平台的taobao.item.get接口是电商开发者最常调用的核心接口之一,它提供了获取单个商品完整信息的能力。这个接口的独特之处在于它能返回商品的基础信息、SKU数据、促销信息等全维度数据,且响应速度控制在200ms以内… · 2026/11/2 23:59:44
JJencode编码解密工具:前端JS代码混淆与源码还原实操 做前端的人大概都遇到过这种场面:交付给客户的是一个纯前端跑的小工具,或者一个 H5 小游戏,代码打包压缩完往服务器一放,懂行的人按 F12 就能把逻辑看个七七八八,改个变量名、换个皮肤就成他自己的了。这时候就会有人翻… · 2026/11/2 23:57:44
Apache HttpAsyncClient选型指南:Default与Minimal对比 1. 项目概述在构建高性能HTTP客户端时,Apache HttpAsyncClient提供了两个核心实现:DefaultHttpAsyncClient和MinimalHttpAsyncClient。这两个客户端的选择往往让开发者陷入纠结,特别是在生产环境部署时。作为在分布式系统领域深耕多年的架构师… · 2026/11/2 23:55:43
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/2 13:10:28
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/2 4:42:45
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/2 5:17:18
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/2 13:10:28
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/2 4:42:45
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/2 5:17:18