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

SpringCloud Alibaba 深入源码 - Nacos 和 Eureka 的区别(健康检测、服务的拉取和订阅)

目录

一、Nacos 和 Eureka 的区别

1.1、以 Nacos 注册流程来解析区别


一、Nacos 和 Eureka 的区别


1.1、以 Nacos 注册流程来解析区别

a)首先,我们的服务启动时。都会把自己的信息提交给注册中心,然后注册中心就会把信息保存下来.

注册的信息实际上就是一个嵌套 Map,结构为 Map<String, Map<String, Service>>,第一层 key 就是 namespace_id,起到环境隔离的作用. value 由是一个嵌套 Map<String, Service>.

第二层的 key 表示 group 分组,key 就是分组名,value 就是分组下的某一个服务,实际上就是一个类,内部又维护了一个  Map<String,Cluster> .

第三层的 key 就是集群的名称,value 就是  Cluster ,也是一个类,包含了集群的具体信息.  

因为一个集群中可能包含多个实例,也就是具体的节点信息(例如实例的IP、Port、健康状态),那么 Cluster 这个类中又维护了 两个 Set<Instance>,分别是临时实例和非临时实例(此处,Eureka 就没有做区分,只有临时实例).

b)那么当服务消费者要去消费时,就可以从注册中心拉取服务信息.  这个过程也被称为“服务发现”.  但是他这个拉去动作不是每次都要做的(压力太大),而是将拉取到的服务信息缓存到一个列表中,这样接下来的一段时间里,就不用去拉去了,而是直接从缓存列表中拿. 

当然这个缓存一直不更新也不行,因此会每隔 30 秒取重新拉取一次(多长时间不用记,都是可以配置的),进行更新.

c)消费者拿到服务列表后,就可以通过 负载均衡(LoadBalancer)从列表中挑选一个发起远程调用就可以了. 

d)截至目前为止,Nacos 和 Eureka 还没什么太大的差别,那差别在哪呢?差别就在于服务提供者的健康检测机制.

e)在 nacos 中,将服务分成了临时实例和非临时实例:

  • 临时实例:当临时实例进行心跳检测的时候,如果心跳不跳了,nacos 就会把它从服务中直接剔除.  (这里的心跳检测机制和 Eureka 是一样的,但非临时实例就不一样了)
  • 非临时实例:nacos 就不会要求你给我发心跳了,而是通过 nacos 主动发请求询问,定时向实例发送请求:“你还活着吗?”,即使真的挂了,nacos 也仅仅只是把它标记为 不健康,不会剔除,而是等待它恢复健康.

而 Eureka 只提供了心跳模式的健康监测,而没有主动检测功能。

主动询问进行健康检测,效率岂不是很低?

对于非临时实例,所有的健康检测任务都不是立即执行的,都会被放入一个阻塞队列中,如下源码

@Override
public void process(HealthCheckTask task) {// 获取所有 非临时实例的 集合List<Instance> ips = task.getCluster().allIPs(false);if (CollectionUtils.isEmpty(ips)) {return;}for (Instance ip : ips) {// 封装健康检测信息到 BeatBeat beat = new Beat(ip, task);// 放入一个阻塞队列中taskQueue.add(beat);MetricsMonitor.getTcpHealthCheckMonitor().incrementAndGet();}
}

可以看出,检测任务不是立即执行,这里也采用了异步指定的策略,会把任务放到线程池中取执行,如下:

public void run() {while (true) {try {// 处理任务processTask();// ...} catch (Throwable e) {SRV_LOG.error("[HEALTH-CHECK] error while processing NIO task", e);}}
}

 通过 processTask 来处理健康检测的任务:

private void processTask() throws Exception {// 将任务封装为一个 TaskProcessor,并放入集合Collection<Callable<Void>> tasks = new LinkedList<>();do {Beat beat = taskQueue.poll(CONNECT_TIMEOUT_MS / 2, TimeUnit.MILLISECONDS);if (beat == null) {return;}tasks.add(new TaskProcessor(beat));} while (taskQueue.size() > 0 && tasks.size() < NIO_THREAD_COUNT * 64);// 批量处理集合中的任务for (Future<?> f : GlobalExecutor.invokeAllTcpSuperSenseTask(tasks)) {f.get();}
}

 任务被封装到了TaskProcessor中去执行了,TaskProcessor是一个Callable,其中的call方法:

@Override
public Void call() {// 获取检测任务已经等待的时长long waited = System.currentTimeMillis() - beat.getStartTime();if (waited > MAX_WAIT_TIME_MILLISECONDS) {Loggers.SRV_LOG.warn("beat task waited too long: " + waited + "ms");}SocketChannel channel = null;try {// 获取实例信息Instance instance = beat.getIp();// 通过NIO建立TCP连接channel = SocketChannel.open();channel.configureBlocking(false);// only by setting this can we make the socket close event asynchronouschannel.socket().setSoLinger(false, -1);channel.socket().setReuseAddress(true);channel.socket().setKeepAlive(true);channel.socket().setTcpNoDelay(true);Cluster cluster = beat.getTask().getCluster();int port = cluster.isUseIPPort4Check() ? instance.getPort() : cluster.getDefCkport();channel.connect(new InetSocketAddress(instance.getIp(), port));// 注册连接、读取事件SelectionKey key = channel.register(selector, SelectionKey.OP_CONNECT | SelectionKey.OP_READ);key.attach(beat);keyMap.put(beat.toString(), new BeatKey(key));beat.setStartTime(System.currentTimeMillis());GlobalExecutor.scheduleTcpSuperSenseTask(new TimeOutTask(key), CONNECT_TIMEOUT_MS, TimeUnit.MILLISECONDS);} catch (Exception e) {beat.finishCheck(false, false, switchDomain.getTcpHealthParams().getMax(),"tcp:error:" + e.getMessage());if (channel != null) {try {channel.close();} catch (Exception ignore) {}}}return null;
}

这差别就像是亲生儿子和非亲生儿子,亲生儿子我还会去主动关怀一下,诶,你还活着么?而非临时实例,就是你不心跳了,就把你扔了~

f)还有一个差别在于服务消费者,Eureka 采用的是定时拉取(每 30 秒一次),那如果在 30 秒内服务提供者挂了,消费肯定是不知道的,因此 Eureka 这里更新的时效性也比较差.

我们的 微服务 定时拉取的基本逻辑就是先从本地缓存读:

  • 如果本地缓存没有,就通过 Nacos 客户端构造请求去 nacos 服务器中读取.
  • 如果本地缓存有,就开启定时更新功能(就是创建一个定时任务,每隔一段时间去拉取一次),并返回缓存结果.

核心源码如下:

public ServiceInfo getServiceInfo(final String serviceName, final String clusters) {NAMING_LOGGER.debug("failover-mode: " + failoverReactor.isFailoverSwitch());// 由 服务名@@集群名拼接 keyString key = ServiceInfo.getKey(serviceName, clusters);if (failoverReactor.isFailoverSwitch()) {return failoverReactor.getService(key);}// 读取本地服务列表的缓存,缓存是一个Map,格式:Map<String, ServiceInfo>ServiceInfo serviceObj = getServiceInfo0(serviceName, clusters);// 判断缓存是否存在if (null == serviceObj) {// 不存在,创建空ServiceInfoserviceObj = new ServiceInfo(serviceName, clusters);// 放入缓存serviceInfoMap.put(serviceObj.getKey(), serviceObj);// 放入待更新的服务列表(updatingMap)中updatingMap.put(serviceName, new Object());// 立即更新服务列表updateServiceNow(serviceName, clusters);// 从待更新列表中移除updatingMap.remove(serviceName);} else if (updatingMap.containsKey(serviceName)) {// 缓存中有,但是需要更新if (UPDATE_HOLD_INTERVAL > 0) {// hold a moment waiting for update finish 等待5秒中,待更新完成synchronized (serviceObj) {try {serviceObj.wait(UPDATE_HOLD_INTERVAL);} catch (InterruptedException e) {NAMING_LOGGER.error("[getServiceInfo] serviceName:" + serviceName + ", clusters:" + clusters, e);}}}}// 开启定时更新服务列表的功能scheduleUpdateIfAbsent(serviceName, clusters);// 返回缓存中的服务信息return serviceInfoMap.get(serviceObj.getKey());
}

定时更新方法如下:

public void updateService(String serviceName, String clusters) throws NacosException {ServiceInfo oldService = getServiceInfo0(serviceName, clusters);try {// 基于ServerProxy发起远程调用,查询服务列表String result = serverProxy.queryList(serviceName, clusters, pushReceiver.getUdpPort(), false);if (StringUtils.isNotEmpty(result)) {// 处理查询结果processServiceJson(result);}} finally {if (oldService != null) {synchronized (oldService) {oldService.notifyAll();}}}
}public String queryList(String serviceName, String clusters, int udpPort, boolean healthyOnly)throws NacosException {// 准备请求参数final Map<String, String> params = new HashMap<String, String>(8);params.put(CommonParams.NAMESPACE_ID, namespaceId);params.put(CommonParams.SERVICE_NAME, serviceName);params.put("clusters", clusters);params.put("udpPort", String.valueOf(udpPort));params.put("clientIP", NetUtils.localIP());params.put("healthyOnly", String.valueOf(healthyOnly));// 发起请求,地址与API接口一致return reqApi(UtilAndComs.nacosUrlBase + "/instance/list", params, HttpMethod.GET);
}

 而 nacos 这里的消费者不光进行服务的定时拉取,nacos 还会主动进行消息的订阅推送,一旦发现有服务挂了,就立刻推送一条消息给服务消费者,告诉你服务要更新了.

Nacos 具体是通过什么实现消息订阅推送机制呢?

a)首先 PushPeceiver 这个类(我们自己的微服务配置的 Nacos 客户端),会以 UDP 的方式与 Nacos 服务端建立连接,监听 Nacos 服务端推送的服务变更数据.

b)一旦 Nacos 服务列表发生变更,就会发送 UDP 广播给所有的微服务订阅者.

c)当订阅者接收到通知以后,就可以将接收到的服务信息缓存到本地缓存列表.

d)那么之后再拉取服务的时候,会优先从缓存里读取,缓存里有就直接返回缓存,如果没有,再去拉取或者订阅.

PushPeceiver 构造函数如下:

public PushReceiver(HostReactor hostReactor) {try {this.hostReactor = hostReactor;// 创建 UDP客户端String udpPort = getPushReceiverUdpPort();if (StringUtils.isEmpty(udpPort)) {this.udpSocket = new DatagramSocket();} else {this.udpSocket = new DatagramSocket(new InetSocketAddress(Integer.parseInt(udpPort)));}// 准备线程池this.executorService = new ScheduledThreadPoolExecutor(1, new ThreadFactory() {@Overridepublic Thread newThread(Runnable r) {Thread thread = new Thread(r);thread.setDaemon(true);thread.setName("com.alibaba.nacos.naming.push.receiver");return thread;}});// 开启线程任务,准备接收变更数据this.executorService.execute(this);} catch (Exception e) {NAMING_LOGGER.error("[NA] init udp socket failed", e);}
}

PushReceiver 构造函数中基于线程池来运行任务。这是因为 PushReceiver 本身也是一个Runnable,其中的run方法业务逻辑就是:

@Override
public void run() {while (!closed) {try {// byte[] is initialized with 0 full filled by defaultbyte[] buffer = new byte[UDP_MSS];DatagramPacket packet = new DatagramPacket(buffer, buffer.length);// 接收推送数据udpSocket.receive(packet);// 解析为json字符串String json = new String(IoUtils.tryDecompress(packet.getData()), UTF_8).trim();NAMING_LOGGER.info("received push data: " + json + " from " + packet.getAddress().toString());// 反序列化为对象PushPacket pushPacket = JacksonUtils.toObj(json, PushPacket.class);String ack;if ("dom".equals(pushPacket.type) || "service".equals(pushPacket.type)) {// 交给 HostReactor去处理hostReactor.processServiceJson(pushPacket.data);// send ack to server 发送ACK回执,略。。} catch (Exception e) {if (closed) {return;}NAMING_LOGGER.error("[NA] error while receiving push data", e);}}
}

通知数据的处理由交给了 HostReactor 的 processServiceJson 方法:

public ServiceInfo processServiceJson(String json) {// 解析出ServiceInfo信息ServiceInfo serviceInfo = JacksonUtils.toObj(json, ServiceInfo.class);String serviceKey = serviceInfo.getKey();if (serviceKey == null) {return null;}// 查询缓存中的 ServiceInfoServiceInfo oldService = serviceInfoMap.get(serviceKey);// 如果缓存存在,则需要校验哪些数据要更新boolean changed = false;if (oldService != null) {// 拉取的数据是否已经过期if (oldService.getLastRefTime() > serviceInfo.getLastRefTime()) {NAMING_LOGGER.warn("out of date data received, old-t: " + oldService.getLastRefTime() + ", new-t: "+ serviceInfo.getLastRefTime());}// 放入缓存serviceInfoMap.put(serviceInfo.getKey(), serviceInfo);// 中间是缓存与新数据的对比,得到newHosts:新增的实例;remvHosts:待移除的实例;// modHosts:需要修改的实例if (newHosts.size() > 0 || remvHosts.size() > 0 || modHosts.size() > 0) {// 发布实例变更的事件NotifyCenter.publishEvent(new InstancesChangeEvent(serviceInfo.getName(), serviceInfo.getGroupName(),serviceInfo.getClusters(), serviceInfo.getHosts()));DiskCache.write(serviceInfo, cacheDir);}} else {// 本地缓存不存在changed = true;// 放入缓存serviceInfoMap.put(serviceInfo.getKey(), serviceInfo);// 直接发布实例变更的事件NotifyCenter.publishEvent(new InstancesChangeEvent(serviceInfo.getName(), serviceInfo.getGroupName(),serviceInfo.getClusters(), serviceInfo.getHosts()));serviceInfo.setJsonFromServer(json);DiskCache.write(serviceInfo, cacheDir);}// 。。。return serviceInfo;
}

相关文章:

SpringCloud Alibaba 深入源码 - Nacos 和 Eureka 的区别(健康检测、服务的拉取和订阅)

目录 一、Nacos 和 Eureka 的区别 1.1、以 Nacos 注册流程来解析区别 一、Nacos 和 Eureka 的区别 1.1、以 Nacos 注册流程来解析区别 a&#xff09;首先&#xff0c;我们的服务启动时。都会把自己的信息提交给注册中心&#xff0c;然后注册中心就会把信息保存下来. 注册的…...

Java复习_3

填空题 课程推荐的 jdk 下载网址为 jdk.java.net 使用命令行编译程序&#xff1a;javac -d bin stc*.java 使用命令行运行程序&#xff1a; java -cp bin 类名 java 语言标识符&#xff1a;字母、数字、下划线和美元符号&#xff0c;数字不能做首字母 java 语言中标识符区…...

分类预测 | Matlab实现KPCA-EBWO-SVM分类预测,基于核主成分分析和改进的白鲸优化算法优化支持向量机分类预测

分类预测 | Matlab实现KPCA-EBWO-SVM分类预测&#xff0c;基于核主成分分析和改进的白鲸优化算法优化支持向量机分类预测 目录 分类预测 | Matlab实现KPCA-EBWO-SVM分类预测&#xff0c;基于核主成分分析和改进的白鲸优化算法优化支持向量机分类预测分类效果基本描述程序设计参…...

力扣hot100 找到字符串中所有字母异位词 滑动窗口 双指针 一题双解

Problem: 438. 找到字符串中所有字母异位词 文章目录 思路滑动窗口 数组滑动窗口 双指针 思路 &#x1f469;‍&#x1f3eb; 参考题解 滑动窗口 数组 ⏰ 时间复杂度: O ( n ) O(n) O(n) &#x1f30e; 空间复杂度: O ( 1 ) O(1) O(1) class Solution { // 滑动窗口 …...

PG DBA培训21:PostgreSQL性能优化之基准测试

本课程由风哥发布的基于PostgreSQL数据库的系列课程&#xff0c;本课程属于PostgreSQL Performance Benchmarking&#xff0c;学完本课程可以掌握PostgreSQL性能基准测试基础知识,基准测试介绍,基准测试相关指标,TPCC基准测试基础,PostgreSQL测试工具介绍,PostgreSQL性能基准测…...

使用excel从1-2048中随机选择1个整数,并展示与其对应的单词

在Excel中&#xff0c;你可以使用以下指令来从1到2048之间随机选择一个整数&#xff0c;并展示其对应的单词&#xff1a; 1. 首先&#xff0c;在一个空白单元格中输入以下公式&#xff1a; INDEX(单词列表范围, RANDBETWEEN(1, 2048)) 这里的"单词列表范围"是一个包…...

c++可调用对象、function类模板与std::bind

函数调用与函数调用运算符 先写一个简单的函数&#xff0c;如下&#xff1a; /*函数的定义*/ int func(int i) {cout<<"这是一个函数\t"<<i<<endl; }void test() {func(1);//函数的调用 } 通过这个普通的函数可以看到&#xff0c;调用一个函数很…...

【高危】Apache Solr 环境变量信息泄漏漏洞

漏洞描述 Apache Solr 是一款开源的搜索引擎。 在 Apache Solr 受影响版本中&#xff0c;由于 Solr Metrics API 默认输出所有未单独配置保护策略的环境变量。在默认无认证或具有 metrics-read 权限的情况下&#xff0c;攻击者可以通过向 /solr/admin/metrics 端点发送恶意请…...

Python中的卷积神经网络(CNN)入门

卷积神经网络&#xff08;Convolutional Neural Networks, CNN&#xff09;是一类特别适用于处理图像数据的深度学习模型。在Python中&#xff0c;我们可以使用流行的深度学习库TensorFlow和Keras来创建和训练一个CNN模型。在本文中&#xff0c;我们将介绍如何使用Keras创建一个…...

vulnhub靶机HotelWW

下载地址&#xff1a;https://download.vulnhub.com/worstwesternhotel/HotelWW.ova 主机发现 目标142 端口扫描 服务版本扫描 漏洞扫描 看一下web 好好好这么玩改host 啥也没有先做个目录爆破 扫描太慢我就没看了看几个重点的txt&#xff08;robot,config&#xff0c;readme&…...

ArcGIS Pro 标注牵引线问题

ArcGIS Pro 标注 模仿CAD坐标牵引线问题 右键需要标注的要素&#xff0c;进入标注属性。 选择背景样式 在这里有可以选择的牵引线样式 选择这一个&#xff0c;可以根据调整间距来进行模仿CAD标注样式。 此图为cad样式 此为调整后gis样式 此处可以调整牵引线的样式符号 …...

Java8的Stream最佳实践

从这一篇文章开始&#xff0c;我们会由浅入深&#xff0c;全面的学习stream API的最佳实践&#xff08;结合我的使用经验&#xff09;&#xff0c;本想一篇写完&#xff0c;但写着写着发现需要写的内容太多了&#xff0c;所以分成一个系列慢慢来说。给大家分享我的经验的同时&a…...

Spark SQL函数定义

目录 窗口函数 SQL函数分类 Spark原生自定义UDF函数 Pandas的UDF函数 Apache Arrow框架基本介绍 基于Arrow完成Pandas DataFrame和Spark DataFrame互转 基于Pandas完成UDF函数 自定义UDF函数 自定义UDAF函数 窗口函数 分析函数 over(partition by xxx order by xxx [as…...

触摸屏监控双速电动机-PLC I/O电路设计

PLC的输入接线电路图 PLC的输入接线电路如图1-21所示。24VDC电源选用0.7mm2的棕色和蓝色软铜导线&#xff0c;弱电信号线用0.5~0.7mm2的黑色或者白色软铜导线。 PLC输入接线图 PLC的输出接线电路图 PLC的输出接线电路如图1-22所示。AC220V接触器型号为CJX2-12&#xff0c;线…...

idea中使用git提交代码报 Nothing To commit No changes detected

问题描述 在idea中右键&#xff0c;开始将变更的代码进行提交的时候&#xff0c;【Commit Directory】点击提交的时候 报 Nothing To commit No changes detected解决方案 在这里点击Test 看看是不是能下面显示git版本&#xff0c;不行的话 会显示一个 fix的字样&#xff0c;行…...

基于长短期神经网络的回归分析,基于LSTM的回归预测

​目录 背影 摘要 LSTM的基本定义 LSTM实现的步骤 基于长短期神经网络LSTM的回归分析 MATALB代码:基于长短期神经网络的回归分析,基于LSTM的回归预测资源-CSDN文库 https://download.csdn.net/download/abc991835105/88184633 效果图 结果分析 展望 参考论文 背影 LSTM神经…...

mac查看maven版本报错:The JAVA_HOME environment variable is not defined correctly

终端输入mvn -version报错: The JAVA_HOME environment variable is not defined correctly, this environment variable is needed to run this program. Java环境变量的问题,打开bash_profile查看 open ~/.bash_profile export JAVA_8_HOME/Library/Java/JavaVirtualMachine…...

蓝桥杯省赛无忧 编程9

#include<bits/stdc.h> using namespace std; int main() {int n,k,ans0;cin>>n>>k;while(n--){int a;cin>>a;ansa&1;}if(ans&1) cout<<"Alice"<<\n;else cout<<"Bob"; return 0; }这个游戏是基于数…...

Spring data都包含哪些内容

Spring Data是一个涵盖了对多种数据库访问技术的支持的项目集合&#xff0c;旨在提供一致的数据访问方式&#xff0c;简化数据访问层&#xff08;DAO层&#xff09;的开发工作。Spring Data项目为许多不同类型的数据存储提供了易于使用的接口和模式。主要包括以下几个方面&…...

unity 利用Graphics.Blit来制作图片效果

c# 的代码 using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI;public class GraphicsBlitTest : MonoBehaviour {public Texture2D source;//原纹理public Material material;//效果材质public RawImage rawImage;// Sta…...

告别终端!为OpenWrt打造Web版脚本管家:Luci插件开发实战与全功能解析

1. 为什么我们需要Web版脚本管家&#xff1f; 每次在OpenWrt上折腾脚本都要打开终端&#xff0c;这对新手来说简直是噩梦。记得我第一次给路由器写脚本时&#xff0c;光是学会用vi编辑器就花了半小时&#xff0c;保存退出时还差点把系统搞崩。后来发现用WinSCP上传脚本还要改权…...

免费开源乐谱识别神器Audiveris:三步将纸质乐谱转为数字格式

免费开源乐谱识别神器Audiveris&#xff1a;三步将纸质乐谱转为数字格式 【免费下载链接】audiveris Latest generation of Audiveris OMR engine 项目地址: https://gitcode.com/gh_mirrors/au/audiveris 你是否曾面对一叠纸质乐谱&#xff0c;渴望将它们转换成可编辑的…...

告别键盘连击烦恼:KeyboardChatterBlocker 智能解决方案详解

告别键盘连击烦恼&#xff1a;KeyboardChatterBlocker 智能解决方案详解 【免费下载链接】KeyboardChatterBlocker A handy quick tool for blocking mechanical keyboard chatter. 项目地址: https://gitcode.com/gh_mirrors/ke/KeyboardChatterBlocker 你是否曾经在打…...

【实用程序】基于 Java 的简易HTTP 反向代理

本站内的程序及源代码下载地址。 第一章 概述 本项目是一个基于 Java 的简易 HTTP 反向代理实现。反向代理(Reverse Proxy)的核心职责是代表客户端向目标服务器发起请求,并将目标服务器的响应透明地返回给客户端。客户端感知不到后端真实服务的存在,所有交互都通过代理层…...

AI行业的“伦理困境”:隐私保护、算法偏见与失业问题

在人工智能技术飞速发展的今天&#xff0c;软件测试行业正经历着前所未有的变革。AI测试工具的广泛应用&#xff0c;极大提升了测试效率&#xff0c;改变了传统测试流程。然而&#xff0c;技术进步的同时&#xff0c;一系列伦理困境也随之而来&#xff0c;隐私保护、算法偏见与…...

C# 环境:深入解析与应用

C# 环境:深入解析与应用 引言 C#(读作“C Sharp”)是一种由微软开发的高级编程语言,广泛应用于Windows平台的应用程序开发。自从2002年推出以来,C#已经成为了全球开发者喜爱的编程语言之一。本文将深入解析C#环境,包括其特点、应用场景以及开发环境搭建等。 C#环境概述…...

从伺服电机到总线端子:手把手教你用EtherCAT搭建一个简易的‘两轴’运动控制Demo

从伺服电机到总线端子&#xff1a;手把手构建EtherCAT两轴运动控制原型 在工业自动化领域&#xff0c;精确的运动控制往往意味着更高的生产效率和更稳定的产品质量。想象一下&#xff0c;当你需要让两个机械臂协同完成装配任务时&#xff0c;如何确保它们的动作像钟表齿轮般精准…...

中兴B862AV3.2M盒子救砖记:免拆机、免ADB,一根双公头USB线搞定刷机

中兴B862AV3.2M盒子救砖实战&#xff1a;零门槛线刷方案详解 当你的中兴B862AV3.2M电视盒子突然黑屏、卡在开机LOGO或完全无法响应时&#xff0c;那种焦虑感与技术无助感往往让人手足无措。不同于常规的系统升级&#xff0c;设备"变砖"状态下的恢复操作需要更谨慎的步…...

影像技术实战11:视频封面生成黑屏、模糊、重复?FFmpeg + OpenCV 构建高质量缩略图自动优选方案

影像技术实战11&#xff1a;视频封面生成黑屏、模糊、重复&#xff1f;FFmpeg OpenCV 构建高质量缩略图自动优选方案 一、问题场景&#xff1a;封面不是“随便截一帧” 在视频平台、素材管理系统、内容审核后台、AI 剪辑工具里&#xff0c;视频上传后自动生成封面是一个很常见…...

HT4182:5V 输入 1.6A 同步升压双节锂电充电器,高集成全保护可 P2P 替代

在便携式音箱、POS 机、电子烟、对讲机等采用双节串联锂电池供电的设备中&#xff0c;5V USB 输入升压充电是最主流的方案&#xff0c;市场对充电效率、集成度和可靠性的要求越来越高。HT4182 作为一款专为 5V 输入优化的同步升压型双节锂电池充电器&#xff0c;凭借高转换效率…...