RocketMQ源码分析之Broker概述与同步消息发送原理与高可用设计及思考
1、Broker概述
Broker 在 RocketMQ 架构中的角色,就是存储消息,核心任务就是持久化消息,生产者发送消息给 Broker,消费者从 Broker 消费消息,其物理部署架构图如下:
备注:以上摘录自官方 RocketMQ 设计文档。
上述基本描述了消息中间件的架构设计,不仅限于 RocketMQ,不同消息中间件的最大区别之一在消息的存储上。
2、Broker存储设计概要
接下来从配置文件的角度来窥探 Broker 存储设计的关注点,对应代码(MessageStoreConfig)。
- storePathRootDir
设置Broker的存储根目录,默认为 $Broker_Home/store。 - storePathCommitLog
设置commitlog的存储目录,默认为$Broker_Home/store/commitlog。 - mapedFileSizeCommitLog
commitlog 文件的大小,默认为1G。 - mapedFileSizeConsumeQueue
consumeQueueSize,ConsumeQueue 存放的是定长的信息(20个字节,偏移量、size、tagscode),默认30w * ConsumeQueue.CQ_STORE_UNIT_SIZE。 - enableConsumeQueueExt
是否开启 consumeQueueExt,默认为 false,就是如果消费端消息消费速度跟不上,是否创建一个扩展的 ConsumeQueue文件,如果不开启,应该会阻塞从 commitlog 文件中获取消息,并且 ConsumeQueue,应该是按topic独立的。 - mappedFileSizeConsumeQueueExt
扩展consume文件的大小,默认为48M。 - flushIntervalCommitLog
刷写 CommitLog 的间隔时间,RocketMQ 后台会启动一个线程,将消息刷写到磁盘,这个也就是该线程每次运行后等待的时间,默认为500毫秒。flush 操作,调用文件通道的force()方法。 - commitIntervalCommitLog
提交消息到 CommitLog 对应的文件通道的间隔时间,原理与上面类似;将消息写入到文件通道(调用FileChannel.write方法)得到最新的写指针,默认为200毫秒。 - useReentrantLockWhenPutMessage
在put message( 将消息按格式封装成msg放入相关队列时实用的锁机制:自旋或ReentrantLock)。 - flushIntervalConsumeQueue
刷写到ConsumeQueue的间隔,默认为1s。 - flushCommitLogLeastPages
每次 flush commitlog 时最小发生变化的页数。 - commitCommitLogLeastPages
每一次 commitlog 提交任务至少需要的页数。 - flushLeastPagesWhenWarmMapedFile
用字节0填充整个文件,每多少页刷盘一次,默认4096,异步刷盘模式生效。 - flushConsumeQueueLeastPages
一次刷盘至少需要的脏页数量,默认为2,针对 consuequeue 文件。 - putMsgIndexHightWater
当前版本未使用。
接下来从如下方面去深入其实现:
1)生产者发送消息
2)消息协议(格式)
3)消息存储、检索
4)消费队列维护
5)消息消费、重试等机制
2.1 消息发送
org.apache.rocketmq.client.impl.producer.DefaultMQProducerImpl sendDefaultImpl方法源码分析
rprivate SendResult sendDefaultImpl(//Message msg, // final CommunicationMode communicationMode, //final SendCallback sendCallback, //final long timeout//) throws MQClientException, RemotingException, MQBrokerException, InterruptedException {
2.1.1 消息发送参数详解:
1、Message msg
2、communicationMode communicationMode
发送方式,SYNC(同步)、ASYNC(异步)、ONEWAY(单向,不关注返回)
3、SendCallback sendCallback
异步消息发送回调函数。
4、long timeout
消息发送超时时间。
2.2.2 消息发送流程
默认消息发送实现:
private SendResult sendDefaultImpl(//Message msg, //final CommunicationMode communicationMode, //final SendCallback sendCallback, //final long timeout//) throws MQClientException, RemotingException, MQBrokerException, InterruptedException {this.makeSureStateOK();Validators.checkMessage(msg, this.defaultMQProducer);final long invokeID = random.nextLong();long beginTimestampFirst = System.currentTimeMillis();long beginTimestampPrev = beginTimestampFirst;long endTimestamp = beginTimestampFirst;TopicPublishInfo topicPublishInfo = this.tryToFindTopicPublishInfo(msg.getTopic()); // @1if (topicPublishInfo != null && topicPublishInfo.ok()) {MessageQueue mq = null;Exception exception = null;SendResult sendResult = null;int timesTotal = communicationMode == CommunicationMode.SYNC ? 1 + this.defaultMQProducer.getRetryTimesWhenSendFailed() : 1;int times = 0;String[] brokersSent = new String[timesTotal];for (; times < timesTotal; times++) {String lastBrokerName = null == mq ? null : mq.getBrokerName();MessageQueue tmpmq = this.selectOneMessageQueue(topicPublishInfo, lastBrokerName); // @2if (tmpmq != null) {mq = tmpmq;brokersSent[times] = mq.getBrokerName();try {beginTimestampPrev = System.currentTimeMillis();sendResult = this.sendKernelImpl(msg, mq, communicationMode, sendCallback, topicPublishInfo, timeout); // @3endTimestamp = System.currentTimeMillis();this.updateFaultItem(mq.getBrokerName(), endTimestamp - beginTimestampPrev, false);switch (communicationMode) {case ASYNC:return null;case ONEWAY:return null;case SYNC:if (sendResult.getSendStatus() != SendStatus.SEND_OK) {if (this.defaultMQProducer.isRetryAnotherBrokerWhenNotStoreOK()) {continue;}}return sendResult;default:break;}} catch (RemotingException e) {endTimestamp = System.currentTimeMillis();this.updateFaultItem(mq.getBrokerName(), endTimestamp - beginTimestampPrev, true); // @4log.warn(String.format("sendKernelImpl exception, resend at once, InvokeID: %s, RT: %sms, Broker: %s", invokeID, endTimestamp - beginTimestampPrev, mq), e);log.warn(msg.toString());exception = e;continue;} catch (MQClientException e) {endTimestamp = System.currentTimeMillis();this.updateFaultItem(mq.getBrokerName(), endTimestamp - beginTimestampPrev, true); log.warn(String.format("sendKernelImpl exception, resend at once, InvokeID: %s, RT: %sms, Broker: %s", invokeID, endTimestamp - beginTimestampPrev, mq), e);log.warn(msg.toString());exception = e;continue;} catch (MQBrokerException e) {endTimestamp = System.currentTimeMillis();this.updateFaultItem(mq.getBrokerName(), endTimestamp - beginTimestampPrev, true);log.warn(String.format("sendKernelImpl exception, resend at once, InvokeID: %s, RT: %sms, Broker: %s", invokeID, endTimestamp - beginTimestampPrev, mq), e);log.warn(msg.toString());exception = e;switch (e.getResponseCode()) {case ResponseCode.TOPIC_NOT_EXIST:case ResponseCode.SERVICE_NOT_AVAILABLE:case ResponseCode.SYSTEM_ERROR:case ResponseCode.NO_PERMISSION:case ResponseCode.NO_BUYER_ID:case ResponseCode.NOT_IN_CURRENT_UNIT:continue;default:if (sendResult != null) {return sendResult;}throw e;}} catch (InterruptedException e) {endTimestamp = System.currentTimeMillis();this.updateFaultItem(mq.getBrokerName(), endTimestamp - beginTimestampPrev, false);log.warn(String.format("sendKernelImpl exception, throw exception, InvokeID: %s, RT: %sms, Broker: %s", invokeID, endTimestamp - beginTimestampPrev, mq), e);log.warn(msg.toString());log.warn("sendKernelImpl exception", e);log.warn(msg.toString());throw e;}} else {break;}}if (sendResult != null) {return sendResult;}String info = String.format("Send [%d] times, still failed, cost [%d]ms, Topic: %s, BrokersSent: %s",times,System.currentTimeMillis() - beginTimestampFirst,msg.getTopic(),Arrays.toString(brokersSent));info += FAQUrl.suggestTodo(FAQUrl.SEND_MSG_FAILED);MQClientException mqClientException = new MQClientException(info, exception);if (exception instanceof MQBrokerException) {mqClientException.setResponseCode(((MQBrokerException) exception).getResponseCode());} else if (exception instanceof RemotingConnectException) {mqClientException.setResponseCode(ClientErrorCode.CONNECT_BROKER_EXCEPTION);} else if (exception instanceof RemotingTimeoutException) {mqClientException.setResponseCode(ClientErrorCode.ACCESS_BROKER_TIMEOUT);} else if (exception instanceof MQClientException) {mqClientException.setResponseCode(ClientErrorCode.BROKER_NOT_EXIST_EXCEPTION);}throw mqClientException;}List<String> nsList = this.getmQClientFactory().getMQClientAPIImpl().getNameServerAddressList();if (null == nsList || nsList.isEmpty()) {throw new MQClientException("No name server address, please set it." + FAQUrl.suggestTodo(FAQUrl.NAME_SERVER_ADDR_NOT_EXIST_URL), null).setResponseCode(ClientErrorCode.NO_NAME_SERVER_EXCEPTION);}throw new MQClientException("No route info of this topic, " + msg.getTopic() + FAQUrl.suggestTodo(FAQUrl.NO_TOPIC_ROUTE_INFO),null).setResponseCode(ClientErrorCode.NOT_FOUND_TOPIC_EXCEPTION);
主要的核心步骤如下:
代码@1:获取topic的路由信息。
代码@2:根据topic负载均衡算法选择一个MessageQueue。
代码@3:向 MessageQueue 发送消息。
代码@4:更新失败策略,主要用于规避发生故障的 broker,下文会详细介绍。
代码@5:如果是同步调用方式(SYNC),则执行失败重试策略,默认重试两次。
2、2.2.1 获取topic的路由信息
首先我们来思考一下,topic 的路由信息包含哪些内容。
消息的发布与订阅基于topic,路由发布信息以 topic 维度进行描述。
Broker 负载消息存储,一个 topic 可以分布在多台 Broker 上(负载均衡),每个 Broker 包含多个 Queue。队列元数据基于Broker来描述(QueueData:所在 BrokerName、读队列个数、写队列个数、权限、同步或异步)。
接下来从源码分析 tryToFindTopicPublishInfo方法,详细了解获取 Topic 的路由信息。
DefaultMQProducerImpl#tryToFindTopicPublishInfo
private TopicPublishInfo tryToFindTopicPublishInfo(final String topic) {TopicPublishInfo topicPublishInfo = this.topicPublishInfoTable.get(topic); // @1if (null == topicPublishInfo || !topicPublishInfo.ok()) {this.topicPublishInfoTable.putIfAbsent(topic, new TopicPublishInfo());this.mQClientFactory.updateTopicRouteInfoFromNameServer(topic); // @2topicPublishInfo = this.topicPublishInfoTable.get(topic);}if (topicPublishInfo.isHaveTopicRouterInfo() || topicPublishInfo.ok()) { //@3return topicPublishInfo;} else {this.mQClientFactory.updateTopicRouteInfoFromNameServer(topic, true, this.defaultMQProducer); //@4topicPublishInfo = this.topicPublishInfoTable.get(topic);return topicPublishInfo;}
代码@1:从本地缓存(ConcurrentMap< String/* topic */, TopicPublishInfo>)中尝试获取,第一次肯定为空,走代码@2的流程。
代码@2:尝试从 NameServer 获取配置信息并更新本地缓存配置。
代码@3:如果找到可用的路由信息并返回。
代码@4:如果未找到路由信息,则再次尝试使用默认的 topic 去找路由配置信息。
接下来我们重点关注updateTopicRouteInfoFromNameServer方法。
MQClientInstance#updateTopicRouteInfoFromNameServer
public boolean updateTopicRouteInfoFromNameServer(final String topic, boolean isDefault, DefaultMQProducer defaultMQProducer) {try {if (this.lockNamesrv.tryLock(LOCK_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)) { // @1try {TopicRouteData topicRouteData;if (isDefault && defaultMQProducer != null) { //@2topicRouteData = this.mQClientAPIImpl.getDefaultTopicRouteInfoFromNameServer(defaultMQProducer.getCreateTopicKey(),1000 * 3);if (topicRouteData != null) {for (QueueData data : topicRouteData.getQueueDatas()) {int queueNums = Math.min(defaultMQProducer.getDefaultTopicQueueNums(), data.getReadQueueNums());data.setReadQueueNums(queueNums);data.setWriteQueueNums(queueNums);}}} else {topicRouteData = this.mQClientAPIImpl.getTopicRouteInfoFromNameServer(topic, 1000 * 3); //@3}if (topicRouteData != null) {TopicRouteData old = this.topicRouteTable.get(topic); //@4boolean changed = topicRouteDataIsChange(old, topicRouteData); //@5if (!changed) {changed = this.isNeedUpdateTopicRouteInfo(topic); //@6} else {log.info("the topic[{}] route info changed, old[{}] ,new[{}]", topic, old, topicRouteData);}if (changed) { //@7TopicRouteData cloneTopicRouteData = topicRouteData.cloneTopicRouteData();for (BrokerData bd : topicRouteData.getBrokerDatas()) {this.brokerAddrTable.put(bd.getBrokerName(), bd.getBrokerAddrs());}// Update Pub info //@8{TopicPublishInfo publishInfo = topicRouteData2TopicPublishInfo(topic, topicRouteData);publishInfo.setHaveTopicRouterInfo(true);Iterator<Entry<String, MQProducerInner>> it = this.producerTable.entrySet().iterator();while (it.hasNext()) {Entry<String, MQProducerInner> entry = it.next();MQProducerInner impl = entry.getValue();if (impl != null) {impl.updateTopicPublishInfo(topic, publishInfo);}}}// Update sub info //@9{Set<MessageQueue> subscribeInfo = topicRouteData2TopicSubscribeInfo(topic, topicRouteData);Iterator<Entry<String, MQConsumerInner>> it = this.consumerTable.entrySet().iterator();while (it.hasNext()) {Entry<String, MQConsumerInner> entry = it.next();MQConsumerInner impl = entry.getValue();if (impl != null) {impl.updateTopicSubscribeInfo(topic, subscribeInfo);}}}log.info("topicRouteTable.put. Topic = {}, TopicRouteData[{}]", topic, cloneTopicRouteData);this.topicRouteTable.put(topic, cloneTopicRouteData);return true;}} else {log.warn("updateTopicRouteInfoFromNameServer, getTopicRouteInfoFromNameServer return null, Topic: {}", topic);}} catch (Exception e) {if (!topic.startsWith(MixAll.RETRY_GROUP_TOPIC_PREFIX) && !topic.equals(MixAll.DEFAULT_TOPIC)) {log.warn("updateTopicRouteInfoFromNameServer Exception", e);}} finally {this.lockNamesrv.unlock();}} else {log.warn("updateTopicRouteInfoFromNameServer tryLock timeout {}ms", LOCK_TIMEOUT_MILLIS);}} catch (InterruptedException e) {log.warn("updateTopicRouteInfoFromNameServer Exception", e);}return false;
代码@1:为了避免重复从 NameServer 获取配置信息,在这里使用了ReentrantLock,并且设有超时时间。固定为3000s。
代码@2,@3的区别,一个是获取默认 topic 的配置信息,一个是获取指定 topic 的配置信息,该方法在这里就不跟踪进去了,具体的实现就是通过与 NameServer 的长连接 Channel 发送 GET_ROUTEINTO_BY_TOPIC (105)命令,获取配置信息。注意,次过程的超时时间为3s,由此可见,NameServer的实现要求高效。
代码@4、@5、@6:从这里开始,拿到最新的 topic 路由信息后,需要与本地缓存中的 topic 发布信息进行比较,如果有变化,则需要同步更新发送者、消费者关于该 topic 的缓存。
代码@7:更新发送者的缓存。
代码@8:更新订阅者的缓存(消费队列信息)。
至此 tryToFindTopicPublishInfo 运行完毕,从 NameServe r获取 TopicPublishData,继续消息发送的第二个步骤,选取一个消息队列。
相关文章:
RocketMQ源码分析之Broker概述与同步消息发送原理与高可用设计及思考
1、Broker概述 Broker 在 RocketMQ 架构中的角色,就是存储消息,核心任务就是持久化消息,生产者发送消息给 Broker,消费者从 Broker 消费消息,其物理部署架构图如下: 备注:以上摘录自官方 RocketMQ 设计文档…...
K8s常见面试题总结
部分内容来自:k8s面试题大全(持续更新中) 目录 k8s常考面试题 1.1.什么是k8s? 1.2.简述Docker和Kubernetes的关系 1.3.k8s的组件有哪些,作用分别是什么? 1.4.简述kubelet的功能和作用 1.5.简述pod是…...
OpenFeign 自定义解码器Decoder 失效
问题描述 项目上开发了OpenFeign的自定义解码器,用来统一处理返回结果。 开发完后测试已经生效了,过两天后,这块代码没有变动的情况下,发现请求结果突然又不走自定义的解码器了。 代码如下 解码器 BaseResponseFeignDecoder …...
c++练习题8
1.在do-while循环中,循环由do开始,用while结束;必须注意的是:在while表达式后面的 不能丢,它表示do-while语句的结束。 A)0 B)1 C);…...
Python循环语句代码详解:while、for、break
目录 1 while循环 1 while循环 循环语句是程序设计中常用的语句之一。任何编程语言都有while循环,Python也不例外。while循环的格式如下所示。 while(表达式): … else: … while循环的执行过程:当循环表达式为真时,依次执行whi…...
vue父子组件传值不能实时更新
最近做项目,遇到个大坑,这会爬出来了,写个总结,避免下次掉坑。 vue父子组件传值不能实时更新问题,父组件将值传给了子组件,但子组件显示的值还是原来的初始值,并没有实时更新,为什么…...
2023美赛A题思路数据代码分享
文章目录赛题思路2023年美国大学生数学建模竞赛选题&论文一、关于选题二、关于论文格式三、关于论文提交四、论文提交流程注意不要手滑美赛A题思路数据代码【最新】赛题思路 (赛题出来以后第一时间在CSDN分享) 最新进度在文章最下方卡片,加入获取一手资源 202…...
【蓝桥杯集训·每日一题】AcWing 3768. 字符串删减
文章目录一、题目1、原题链接2、题目描述二、解题报告1、思路分析2、时间复杂度3、代码详解三、知识风暴双指针一、题目 1、原题链接 3768. 字符串删减 2、题目描述 给定一个由 n 个小写字母构成的字符串。 现在,需要删掉其中的一些字母,使得字符串中不…...
Python|每日一练|树|深度优先搜索|数组|二分查找|链表|双指针|单选记录:填充每个节点的下一个右侧节点指针|搜索插入位置|旋转链表
1、填充每个节点的下一个右侧节点指针(树,深度优先搜索) 给定一个 完美二叉树 ,其所有叶子节点都在同一层,每个父节点都有两个子节点。二叉树定义如下: struct Node { int val; Node *left; Node *rig…...
降雨量实时监测系统压电式雨量计
压电式雨量传感器由上盖、外壳和下盖组成,壳体内部有压电片和电路板,可以固定在外径50mm立柱上和气象站横杆上。传感器采用冲击测量原理对单个雨滴重量进行测算,进而计算降雨量。雨滴在降落过程中受到雨滴重量和空气阻力的作用,到…...
滑动相关的原理以及用滤波器实现滑动相关(匹配滤波器捕获DMF)
目录滑动相关匹配滤波器捕获(DMF)滑动相关 滑动相关属于一种时域捕获方法,其具体原理是是通过本地序列与接收信号在固定窗长内滑动累加得到相关结果。 一般滑动相关算法可以用于对自相关性非常好的伪码进行同步判决。 我们首先生成一组自相关…...
计算机网络笔记(三)—— 数据链路层
数据链路层概述 数据链路层以帧为单位传输数据。 封装成帧:给网络层提供的协议数据单元添加帧头帧尾 差错检测:检错码封装在帧尾 可靠传输:尽管误码不能避免,但如果可以实现发送什么就接受什么,就叫可靠传输 封装成…...
【日常】矩阵正态分布参数检验问题
最近给凯爹做的一个苦力活,统计检验这个东西说实话也挺有趣,跟算法设计一样,好的检验真的是挺难设计的,就有近似算法的那种感觉,检验很难保证size和power都很理想,所以就要做tradeoff,感觉这个假…...
QML矩形(Rectangle)
Rectangle 用于绘制矩形 常见的属性: 填充颜色:纯色:color 渐变 :Gradient类 渐变的优先级大于纯色Gradient(渐变色): 渐变由多种颜色定义,这些颜色将无缝混合,…...
CSS自定义鼠标样式
CSS自定义鼠标样式 属性值 属性描述url需使用的自定义光标的 URLdefault默认光标(通常是一个箭头)auto默认。浏览器设置的光标crosshair光标呈现为十字线pointer光标呈现为指示链接的指针(一只手)move此光标指示某对象可被移动e…...
春招Leetcode刷题日记-D4-双指针算法-滑动窗口快慢指针
D4-双指针算法-滑动窗口&&快慢指针快慢指针算力扣141. 环形链表思路代码力扣142. 环形链表 II思路代码滑动窗口力扣76. 最小覆盖子串思路代码力扣424. 替换后的最长重复字符思路代码快慢指针算 快慢指针算法,多用于链表当中,常见的如࿱…...
【go】结合一个go开源项目分析谷歌浏览器cookie为什么不安全 附go项目导包失败怎么解决教程
本文创作背景 源于谷歌浏览器提示密码被泄露 并且某站很快收到了异地企图登录的提醒。 当即怀疑是不是谷歌浏览器保存的密码不安全,最后查阅诸多资料 并找到一个go语言编写的开源项目进行研究,虽然最终不能确定密码是如何泄露的 但研究结论还是让人不由感…...
Windows瘦身方法
一、快速删除系统盘临时文件方法, 1、winr打开运行对话框,输入%temp%命令,如图1 图1 2、打开temp文件夹,如图2,选择所有文件,鼠标右键删除或按Del键删除。 图2 二、磁盘清理 1、winr,输入cleanmgr&#x…...
19. 删除链表的倒数第 N 个结点
题目链接:https://leetcode.cn/problems/remove-nth-node-from-end-of-list/进阶:你能尝试使用一趟扫描实现吗?解题思路:最简单的方法是先遍历一次链表,得到链表的长度len,然后再一次遍历链表,遍…...
【Linux】网络编程 - 基础概念
目录 一.OSI七层模型vsTCP/IP五层模型 1.一些周边概念 2.OSI七层模型 3.TCP/IP五层模型 4.网络传输流程图 二.什么是MAC地址 三.什么是IP/IP地址 1.什么是IP 2.什么是IP地址 四.什么是端口号 一.OSI七层模型vsTCP/IP五层模型 1.一些周边概念 局域网vs广域网 网络互…...
【SpringBoot】100、SpringBoot中使用自定义注解+AOP实现参数自动解密
在实际项目中,用户注册、登录、修改密码等操作,都涉及到参数传输安全问题。所以我们需要在前端对账户、密码等敏感信息加密传输,在后端接收到数据后能自动解密。 1、引入依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId...
深入理解JavaScript设计模式之单例模式
目录 什么是单例模式为什么需要单例模式常见应用场景包括 单例模式实现透明单例模式实现不透明单例模式用代理实现单例模式javaScript中的单例模式使用命名空间使用闭包封装私有变量 惰性单例通用的惰性单例 结语 什么是单例模式 单例模式(Singleton Pattern&#…...
2.Vue编写一个app
1.src中重要的组成 1.1main.ts // 引入createApp用于创建应用 import { createApp } from "vue"; // 引用App根组件 import App from ./App.vue;createApp(App).mount(#app)1.2 App.vue 其中要写三种标签 <template> <!--html--> </template>…...
DAY 26 函数专题1
函数定义与参数知识点回顾:1. 函数的定义2. 变量作用域:局部变量和全局变量3. 函数的参数类型:位置参数、默认参数、不定参数4. 传递参数的手段:关键词参数5 题目1:计算圆的面积 任务: 编写一…...
sshd代码修改banner
sshd服务连接之后会收到字符串: SSH-2.0-OpenSSH_9.5 容易被hacker识别此服务为sshd服务。 是否可以通过修改此banner达到让人无法识别此服务的目的呢? 不能。因为这是写的SSH的协议中的。 也就是协议规定了banner必须这么写。 SSH- 开头,…...
学习 Hooks【Plan - June - Week 2】
一、React API React 提供了丰富的核心 API,用于创建组件、管理状态、处理副作用、优化性能等。本文档总结 React 常用的 API 方法和组件。 1. React 核心 API React.createElement(type, props, …children) 用于创建 React 元素,JSX 会被编译成该函数…...
GC1808:高性能音频ADC的卓越之选
在音频处理领域,高质量的音频模数转换器(ADC)是实现精准音频数字化的关键。GC1808,一款96kHz、24bit立体声音频ADC,以其卓越的性能和高性价比脱颖而出,成为众多音频设备制造商的理想选择。 GC1808集成了64倍…...
【字节拥抱开源】字节团队开源视频模型 ContentV: 有限算力下的视频生成模型高效训练
本项目提出了ContentV框架,通过三项关键创新高效加速基于DiT的视频生成模型训练: 极简架构设计,最大化复用预训练图像生成模型进行视频合成系统化的多阶段训练策略,利用流匹配技术提升效率经济高效的人类反馈强化学习框架&#x…...
前端异步编程全场景解读
前端异步编程是现代Web开发的核心,它解决了浏览器单线程执行带来的UI阻塞问题。以下从多个维度进行深度解析: 一、异步编程的核心概念 JavaScript的执行环境是单线程的,这意味着在同一时间只能执行一个任务。为了不阻塞主线程,J…...
Unity VR/MR开发-开发环境准备
视频讲解链接: 【XR马斯维】UnityVR/MR开发环境准备【UnityVR/MR开发教程--入门】_哔哩哔哩_bilibili...
