【Spark源码分析】Spark的RPC通信一-初稿

Spark的RPC通信一-初稿

文章目录

  • Spark的RPC通信一-初稿
    • Spark的RPC顶层设计
      • 核心类`NettyRpcEnv`
      • 核心类`RpcEndpoint`
      • 核心类`RpcEndpointRef`
    • Spark RPC消息的发送与接收实现
      • 核心类`Inbox`
      • 核心类`Dispatcher`
      • 核心类`Outbox`

Spark的RPC顶层设计

在RpcEnv中定义了RPC通信框架的启动、停止和关闭等抽象方法,表示RPC的顶层环境。唯一的子类NettyRpcEnv。

RpcEndpoints 需要向 RpcEnv 注册自己的名称,以便接收信息。然后,RpcEnv 将处理从 RpcEndpointRef 或远程节点发送的信息,并将它们传送到相应的 RpcEndpoints。对于 RpcEnv 捕捉到的未捕获异常,RpcEnv 会使用 RpcCallContext.sendFailure 将异常发回给发送者,如果没有发送者或出现 NotSerializableException,则记录异常。

RpcEnv 还提供了一些方法来检索给定名称或 uri 的 RpcEndpointRefs。

继承
继承
继承
继承
继承
继承
继承
继承
继承
继承
继承
Association
Association
Dispatcher
-endpoints
-endpointRefs
-receivers
-stopped
-threadpool
registerRpcEndpoint(name: String, endpoint: RpcEndpoint)
getRpcEndpointRef(endpoint: RpcEndpoint)
removeRpcEndpointRef(endpoint: RpcEndpoint)
unregisterRpcEndpoint(name: String)
postToAll(message: InboxMessage)
postMessage(endpointName: String,message: InboxMessage,callbackIfStopped: (Exception) => Unit)
«trait»
RpcEndpoint
rpcEnv: RpcEnv
self()
receive()
receiveAndReply(context: RpcCallContext)
onError(cause: Throwable)
onConnected(remoteAddress: RpcAddress)
onDisconnected(remoteAddress: RpcAddress)
onNetworkError(cause: Throwable, remoteAddress: RpcAddress)
onStart()
onStop()
stop()
«trait»
ThreadSafeRpcEndpoint
«trait»
RpcEnvFactory
create(config: RpcEnvConfig)
NettyRpcEnvFactory
create(config: RpcEnvConfig)
«abstract»
RpcEndpointRef
«abstract»
RpcEnv
NettyRpcEnv
-dispatcher: Dispatcher
-streamManager:NettyStreamManager
-transportContext:TransportContext
-clientFactory:TransportClientFactory
startServer(bindAddress: String, port: Int)
setupEndpoint(name: String, endpoint: RpcEndpoint)
send(message: RequestMessage)
ask[T: ClassTag](message: RequestMessage, timeout: RpcTimeout)
postToOutbox(receiver: NettyRpcEndpointRef, message: OutboxMessage)
asyncSetupEndpointRefByURI(uri: String)
DummyMaster
ClientEndpoint
DriverEndpoint
Master
Worker
LocalEndpoint
其他
NettyRpcEndpointRef

在RpcEnvFactory中定义了创建RpcEnv的抽象方法,在NettyRpcEnv和NettyRpcEnvFactory中使用Netty对继承的方式进行了实现。

在NettRpcEnv中启动终端点方法setEndpoint中,会将RpcEndpoint和RpcEndpointRef相互以键值对的形式存储到ConcurrentHashMap中,最后在RpcEnv的object类中通过反射方式实现创建RpcEnv的实例的静态方法。

核心类NettyRpcEnv

NettyRpcEnv的核心成员和核心方法

  • transportConf:TransportConf的实例对象,加载一些关于RPC的配置项
  • dispatcher:Dispatcher的实例对象,消息转发器,将RPC消息路由到要该对此消息处理的RpcEndpoint。
  • streamManager:NettyStreamManager的实例对象,流的管理器,为NettyRpcEnv提供流式服务。
  • transportContext:TransportContext的实例对象
  • clientFactory: 用于构造发送和接收响应的TransportClient
  • fileDownloadFactory: 用于文件下载的独立客户端工厂。这样可以避免使用与主 RPC 上下文相同的 RPC 处理程序,从而将这些客户端引起的事件与主 RPC 流量隔离开来。它还允许对某些属性进行不同的配置,例如每个对等节点的连接数。
  • server:TransportServer,提供高效的底层流媒体服务。
  • ConcurrentHashMap[RpcAddress, Outbox] outboxes:远程地址与Outbox的映射map。
  • startServer(bindAddress: String, port: Int)
    • 创建一个TransportServer
    • 向消息转发器中注册RpcEndpointVerifier,RpcEndpointVerifier的注册名称为endpoint-verifier,用来校验RpcEndpoint是否存在的RpcEndpoint服务
  • send(message: RequestMessage): Unit
    • 发送消息时,将本地消息交于InBox,远程消息交于OutBox
  • ask[T: ClassTag](message: RequestMessage, timeout: RpcTimeout)
    • 若请求消息的接收者的地址与当前的NettyRpcEnv的地址相同,将消息交通过dispatcher.postLocalMessage(message, p)方法处理,p中是成功和失败的回调函数。
    • 若请求消息的的接收者的地址与当前的NettyRpcEnv的地址不同时,将消息通过postToOutbox(message.receiver, rpcMessage)方法处理,主要是将消息放入outbox,然后传输到远程地址上。
    • 在方法的最后设定了一个定时器,实现消息请求的超时机制。
  • postToOutbox(receiver: NettyRpcEndpointRef, message: OutboxMessage):将消息传到远程节点上
    • 如果receiver.client不为空,那么消息将直接通过TransportClient发送到远端节点
    • 如果receiver.client为空,则获取远端结点地址对应的Outbox,若没有则新建一个
    • 如果NettyRpcEnv已经停止,移除该Outbox并停止,否则调用Outbox.send()发送消息。

核心类RpcEndpoint

RpcEndpoint是对能够处理RPC请求,给某一特定服务提供本地调用及跨节点调用的RPC组件的抽象,所有运行于RPC框架之上的实体都应该继承RpcEndpoint。

RPC 的RpcEndpoint,它定义了给定消息时要触发的函数。保证按调用顺序为 onStart、receive 和 onStop。RpcEndpoint的生命周期为constructor -> onStart -> receive* -> onStop。receive 可以并发调用。如果希望接收是线程安全的,则需要请使用 ThreadSafeRpcEndpoint。如果 RpcEndpoint 方法(onError 除外)抛出任何错误,onError 将被调用并说明原因。如果 onError 抛出错误,RpcEnv会将忽略。

ThreadSafeRpcEndpoint是继承自RpcEndpoint的特质,需要 RpcEnv 以线程安全方式向其发送消息的特性。主要用于对消息的处理,必须是线程安全的场景。ThreadSafeRpcEndpoint对消息的处理都是串行的,即前一条消息处理完才能接着处理下一条消息。

核心类RpcEndpointRef

远程 RpcEndpoint 的引用。RpcEndpointRef 是线程安全的。用于消息发送方持有并发送消息。

核心成员

  • maxRetries:最大尝试连接次数。可以通过spark.rpc.numRetries参数指定,默认3次
  • retryWaitMs:每次尝试连接最大等待毫秒值。可以通过spark.rpc.retry.wait,默认3秒
  • defaultAskTimeout:RPC ask操作的超时时间。可以通过spark.rpc.askTimeout,默认120秒
  • address:远程RpcEndpoint引用的地址
  • name:远程RpcEndpoint引用的名称

核心方法

  • send():发送单向异步信息。只管发送,不管结果。
  • ask()系列:向远程的RpcEndpoint.receiveAndReply()方法发送消息,并带有超时机制的Future。该类方法只发送一次消息,从不重试。
  • askSync()系列:向相应的 RpcEndpoint.receiveAndReply 发送消息,并在指定超时内获取结果,如果失败则抛出异常。
    这是一个阻塞操作,可能会耗费大量时间,因此不要在 RpcEndpoint 的消息循环中调用它。

NettyRpcEndpointRef是其唯一的继承类。重写了ask()和send()方法,主要是消息封装成RequestMessage,然后通过nettyEnv的ask和send方法将消息发送出去。

客户端发送请求简单示例图

Client
Server
RpcEnv
outboxs
outbox
messages
RpcEnv
5
3
4
1
2
Dispatcher
receivers
threadpool
EndpointData
messages
1.1
1.2
1.2
1.3
EndpointData
MessageLoop
InboxMessage
Inbox
RpcEndpoint
NettyRpcEndpointRef
InboxMessage
EndpointData
MessageLoop
NettyRpcEndPointRef
outbox
outbox
TransportClient
OutboxMessage
OutboxMessage
RpcEndPoint
Dispatcher
  1. 若是向本地节点的RpcEndpoint发送消息
    1. 通过调用NettyRpcEndpointRef的send()和ask()方法向本地节点的RpcEndpoint发送消息。由于是在同一节点,所以直接调用Dispatcher的postLocalMessage()或postOneWayMessage()方法将消息放入EndpointData内部Inbox的messages中。
    2. InboxMessage放入后Inbox后,Inbox所属的endPointData就会放入receivers一旦receivers中有数据,原本阻塞的MessageLoop就可以取到数据,
    3. MessageLoop将调用inbox.process()方法消息的处理。对不同的消息类型调用endpoint的不同回调函数,即完成了消息的处理。
  2. 通过调用NettyRpcEndpointRef的send()和ask()方法向远端节点的RpcEndpoint发送消息。消息将首先被封装为OutboxMessage,然后放入到远端RpcEndpoint的地址所对应的Outbox的messages中。
  3. 每个Outbox的drainOutbox()方法通过循环,不断从messages列表中取得OutboxMessage,并通过TransportClient发送,底层依赖Netty。
  4. TransportClient和远端NettyRpcEnv的TransportServer建立了连接后,请求消息首先经过Netty管道的处理,由TransportChannelHandler将消息分发给TransportRequestHandler,最终会调用NettyRpcHandler或StreamManager处理。如果是RPC消息则会调用NettyRpcHandler.receive()方法,之后与第一步所述一致,调用Dispatcher的postRemoteMessage()或``postOneWayMessage()`方法。
  5. 如果TransportRequestHandler处理的是RpcRequest,那么server端的TransportRequestHandler处理消息时还会对client端进行响应,依赖Netty将响应消息发送给client端。client端接收到消息时由TransportChannelHandler将消息分发给TransportResponseHandler处理。

Spark RPC消息的发送与接收实现

OutboxMessage在客户端使用,是对外发送消息的封装。InboxMessage在服务端使用,是对接收消息的封装。

继承
继承
继承
继承
继承
继承
继承
继承
继承
聚合
聚合
Dispatcher
-endpoints
-endpointRefs
-receivers
-stopped
-threadpool
registerRpcEndpoint(name: String, endpoint: RpcEndpoint)
getRpcEndpointRef(endpoint: RpcEndpoint)
removeRpcEndpointRef(endpoint: RpcEndpoint)
unregisterRpcEndpoint(name: String)
postToAll(message: InboxMessage)
postMessage(endpointName: String,message: InboxMessage,callbackIfStopped: (Exception) => Unit)
EndpointData
MessageLoop
Inbox
#messages
-stopped
-enableConcurrent
-numActiveThreads
process(dispatcher: Dispatcher)
post(message: InboxMessage)
stop()
Outbox
-messages
-client
-connectFuture
-stopped
-draining
send(message: OutboxMessage)
-drainOutbox()
-launchConnectTask()
-handleNetworkFailure(e: Throwable)
-closeClient()
stop()
RpcMessage
OnStart
OnStop
RemoteProcessConnected
RemoteProcessDisconnected
RemoteProcessConnectionError
OneWayMessage
«trait»
InboxMessage
OneWayOutboxMessage
«trait»
OutboxMessage
sendWith(client: TransportClient)
onFailure(e: Throwable)
RpcOutboxMessage

InboxMessage是一个scala特质类,所有的RPC消息都继承自InboxMessage。下面是继承自InboxMessage的子类

  • OneWayMessage:RpcEndpoint处理此类型的消息后不需要向客户端回复信息。
  • RpcMessage:RpcEndpoint处理完此消息后需要向客户端回复信息。
  • OnStart:Inbox实例化后,再通知与此Inbox相关联的RpcEndpoint启动。
  • OnStop:Inbox停止后,通知与此Inbox相关联的RpcEndpoint停止。
  • RemoteProcessConnected:告诉所有的RpcEndpoint,有远端的进程已经与当前RPC服务建立了连接。
  • RemoteProcessDisconnected:告诉所有的RpcEndpoint,有远端的进程已经与当前RPC服务断开了连接。
  • RemoteProcessConnectionError:告诉所有的RpcEndpoint,与远端某个地址之间的连接发生了错误。

核心类Inbox

Inbox为RpcEndpoint存储了消息即InboxMessage,并线程安全地发送给RpcEndPoint。

private[netty] class Inbox(val endpointRef: NettyRpcEndpointRef,val endpoint: RpcEndpoint)extends Logging {//相当于给this起了一个别名为inbox,inbox =>  }

重要的属性

  • messages:所有的消息以消息盒子的方式,通过LinkedList链式存储
  • enableConcurrent:是否同时允许多线程同时处理消息
  • numActiveThreads:Inbox中正在处理消息的线程数

重要方法

  • post():将InboxMessage投递到box中,从下面的代码可以看出使用了synchronized保证线程安全,如果该box已经关闭,消息将会丢弃。

    def post(message: InboxMessage): Unit = inbox.synchronized {if (stopped) {// 日志进行warning输出onDrop(message)} else {messages.add(message)false}
    }
    
  • process():处理存储在messages中的消息。

      def process(dispatcher: Dispatcher): Unit = {var message: InboxMessage = null// 1.以synchronized进行并发检查,开启并发则取消息,numActiveThreads自增1。inbox.synchronized {if (!enableConcurrent && numActiveThreads != 0) {return}message = messages.poll()if (message != null) {numActiveThreads += 1} else {return}}while (true) {// 安全回调?处理异常的safelyCall(endpoint) {//对不同消息,通过模式匹配进行通过不同的endpoint进行处理message match {case RpcMessage(_sender, content, context) =>try {endpoint.receiveAndReply(context).applyOrElse[Any, Unit](content, { msg =>throw new SparkException(s"Unsupported message $message from ${_sender}")})} catch {case e: Throwable =>context.sendFailure(e)// Throw the exception -- this exception will be caught by the safelyCall function.// The endpoint's onError function will be called.throw e}case OneWayMessage(_sender, content) =>endpoint.receive.applyOrElse[Any, Unit](content, { msg =>throw new SparkException(s"Unsupported message $message from ${_sender}")})case OnStart =>endpoint.onStart()if (!endpoint.isInstanceOf[ThreadSafeRpcEndpoint]) {inbox.synchronized {if (!stopped) {enableConcurrent = true}}}case OnStop =>val activeThreads = inbox.synchronized { inbox.numActiveThreads }assert(activeThreads == 1,s"There should be only a single active thread but found $activeThreads threads.")dispatcher.removeRpcEndpointRef(endpoint)endpoint.onStop()assert(isEmpty, "OnStop should be the last message")case RemoteProcessConnected(remoteAddress) =>endpoint.onConnected(remoteAddress)case RemoteProcessDisconnected(remoteAddress) =>endpoint.onDisconnected(remoteAddress)case RemoteProcessConnectionError(cause, remoteAddress) =>endpoint.onNetworkError(cause, remoteAddress)}}inbox.synchronized {// 调用 `onStop` 后,"enableConcurrent "将被设置为 false,所以需要每次都检查它。if (!enableConcurrent && numActiveThreads != 1) {// 此线程退出,降低并发,最终归于一个线程处理剩下的消息numActiveThreads -= 1return}message = messages.poll()// 没有消息之后,退出当前循环if (message == null) {numActiveThreads -= 1return}}}}
    
  • stop():enableConcurrent赋值为false,保证当前是唯一活跃的线程。并在messages中添加onStop消息。

    def stop(): Unit = inbox.synchronized {// 优雅关闭,是关闭并发只留一个线程处理消息。确保OnStop为最后一个消息,这样,"RpcEndpoint.onStop "就可以安全地释放资源了。if (!stopped) {enableConcurrent = falsestopped = truemessages.add(OnStop)}
    }
    

核心类Dispatcher

Dispatcher负责将RPC消息路由到要该对此消息处理的RpcEndpoint。

内部类

  • EndpointData:包装一个Inbox类。一个RpcEndpoint与NettyRpcEndpointRef映射关联在一起。即一个Inbox只为一个映射关系服务。
  • MessageLoop:用于转发信息的循环任务类,从receivers中获取有消息的inbox进行处理。

重要属性

  • endpoints:储存name和EndpointData的映射关系。EndpointData包含了name,RpcEndpoint, NettyRpcEndpointRef和Inbox,采用ConcureentHashMap保证线程安全
  • endpointRefs:储存RpcEndpoint和RpcEndpointRef的映射关系。采用ConcureentHashMap保证线程安全
  • receivers:存储inbox中可能包含message的EndpointData。在MessageLoop中取出并处理消息。使用阻塞队列LinkedBlockingQueue存储。
  • threadpool:用于调度消息的线程池。根据spark.rpc.netty.dispatcher.numThreads创建固定大小的线程池,启动与线程池大小相同个数的MessageLoop任务。

重要方法

  • registerRpcEndpoint():在调度器中注册endpoint。由name和RpcEndpoint构建NettyRpcEndpointRef,并加入到endpoints, endpointRefs, receivers中
  • postToAll():将message投递到在注册到该Dispatcher的所有RpcEndpoint。postMessage()将message投递到注册到该Dispatcher指定name的RpcEndpoint中,并将EndpointData放入receivers中,该方法中还传入了失败回调函数
  • unregisterRpcEndpoint(), stop():注销所有已注册的RpcEndpoint,从endpoints中移除并在inbox中增加了onstop消息。在receivers中插入哨兵,等待receivers中的所有消息都处理完毕后,关闭线程池。

Dispatcher中的消息处理流程。

  1. postToAll()或者postxx()方法会调用postMessage()方法将InboxMessage放到对应endPointData里inbox的messages列表(调用inbox.post())
  2. InboxMessage放入后inbox后,inbox所属的endPointData就会放入receivers
  3. 一旦receivers中有数据,原本阻塞的MessageLoop就可以取到数据,因为receivers是一个阻塞队列
  4. MessageLoop将调用inbox.process()方法消息的处理。利用模式匹配,对不同的消息类型调用endpoint的不同回调函数,即完成了消息的处理。

核心类Outbox

OutboxMessage是一个特质,内部只有未实现的SendWith方法和onFailure方法。OneWayOutboxMessage和RpcOutboxMessage都继承自OutboxMessage特质,实现的SendWith通过调用TransportClient的sendRpc()方法发送信息,其中RpcOutboxMessage还增加了超时和发送成功的回调方法。

Outbox的重要属性

  • messages: 保存要发送的OutboxMessage。LinkedList类型,线程不安全
  • client: TransportClient
  • stopped: 当前Outbox是否停止的标识
  • draining: 表示当前Outbox内正有线程在处理messages中消息的状态

重要方法

  • send():将要发送的OutboxMessage首先保存到成员变量链表messages中,若Outbox未停止则调用drainOutbox()方法处理messages中的信息。因为messages是LinkedList类型,线程不安全,所以在添加和删除时使用了同步机制。之后调用了私有的drainOutbox()方法发送消息。发送信息。如果没有活动连接,则缓存并启动新连接。如果[[发件箱]]被停止,发送者将收到[[SparkException]]通知。

      def send(message: OutboxMessage): Unit = {val dropped = synchronized {if (stopped) {true} else {messages.add(message)false}}if (dropped) {message.onFailure(new SparkException("Message is dropped because Outbox is stopped"))} else {drainOutbox()}}
    
  • drainOutbox():先判断是否已停止,client是否空等前置条件。取出一条消息,并将draining置为true,接下来将messages中所有消息调用sendWith()方法发送。耗尽消息队列。如果有其他线程正在排空,则直接退出。如果尚未建立连接,则在 nettyEnv.clientConnectionExecutor 中启动一个任务来建立连接。

  • launchConnectTask(): 初始化client

  • stop():停止Outbox

    • 将Outbox的停止状态stopped置为true
    • 关闭TransportClient
    • 清空messages中的消息

之所以要使用这种机制来发消息,是保证并发发送消息时,所有消息依次添加到Outbox中,并依次传输,同时不会阻塞send()方法

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.tpcf.cn/news/238983.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Android应用-flutter使用Positioned将控件定位到底部中间

文章目录 场景描述示例解释 场景描述 要将Positioned定位到屏幕底部中间的位置,你可以使用MediaQuery来获取屏幕的高度,然后设置Positioned的bottom属性和left或right属性,一般我们left和right都会设置一个值让控制置于合适的位置&#xff0…

【Amazon 实验②】Amazon WAF功能增强之使用Cloudfront、Lambda@Edge阻挡攻击

文章目录 一、方案介绍二、架构图三、部署方案1. 进入Cloud9 编辑器,新打开一个teminal2. 克隆代码3. 解绑上一个实验中Cloudfront 分配绑定的防火墙4. 使用CDK部署方案5. CDK部署完成6. 关联LambdaEdge函数 四、方案效果 一、方案介绍 采用 LambdaEdge DynamoDB 架…

第一部分 数理逻辑

目录 什么是命题 注意: 例1 下列句子中那些是命题? 联结词 例2 将下列命题符号化. 注意: 例4 设 p:天冷,q:小王穿羽绒服,将下列命题符号化 例5 求下列复合命题的真值 例如 真值表: 例&#xff1…

OpenHarmony 4.0 Release发布,同步升级API 10

不久之前,OpenHarmony 正式发布了4.0 版本,开发套件也同步升级到 API 10。相比 3.2 Release 版本,4.0 版本新增 4000 多个 ArkTS API,应用开发能力更加丰富;HDF 新增 200 多个 HDI 接口,硬件适配更加便捷&a…

深度学习数据处理(一)

在PyTorch中,torch.Tensor是存储和变换数据的主要工具。如果你之前用过NumPy,你会发现Tensor和NumPy的多维数组非常类似。然而,Tensor提供GPU计算和自动求梯度等更多功能,这些使Tensor更加适合深度学习。 张量(tensor&…

C语言—每日选择题—Day61

明天更新解析 第一题 1. 下面指针所指向的内容,可以修改的是() A:const int* a B:int const* b C:int* const c D:const int* const d 答案及解析 C const 在 * 左侧,指针指向的内容…

基于ssm+jsp理发店管理系统源码和论文

随着信息化时代的到来,管理系统都趋向于智能化、系统化,理发店管理系统也不例外,但目前国内的市场仍都使用人工管理,市场规模越来越大,同时信息量也越来越庞大,人工管理显然已无法应对时代的变化&#xff0…

【万能技巧】IP知识速通与小技巧~

本文目录 前言一、网络代理IP简介二、IPIDEA 优势2.1 多种类型IP代理2.2 海量纯净代理池2.3 稳定高效数据收集架构 三、IP实操小Tips3.1 查看本地网络IP3.2 使用浏览器IP3.3 使用IPIDEA进行爬虫实操 前言 各位友友,大家好,马上就到2024年了,…

[Angular] 笔记 6:ngStyle

ngStyle 指令: 用于更新 HTML 元素的样式。设置一个或多个样式属性&#xff0c;用以冒号分隔的键值对指定。键是样式名称&#xff0c;带有可选的 .<unit> 后缀&#xff08;如 ‘top.px’、‘font-style.em’&#xff09;&#xff0c;值为待求值的表达式&#xff0c;得到…

【泛型中K T V E? Object等分别代表什么含 义】

✅ 泛型中K T V E? Object等分别代表什么含义 ✅ 典型解析✅代码示例 ✅ 典型解析 E - Element (在集合中使用&#xff0c;因为集合中存放的是元素) T-Type (Java 类) K- Key (键) V - Value (值) N - Number (数值类型) ? - 表示不确定的iava类型 (无限制通配符类型) …

鸿蒙和各大厂合作,是不是要火起来

今年9月底&#xff0c;在华为秋季全场景新品发布会上&#xff0c;华为常务董事、终端BG CEO余承东宣布&#xff0c;鸿蒙原生应用全面启动&#xff0c;HarmonyOS NEXT开发者预览版将在2024年第一季度开放。 近日&#xff0c;腾讯、阿里、美团、网易&#xff0c;外包大厂中软国际…

vivado 时钟组

时钟组 本节讨论时钟组&#xff0c;包括&#xff1a; •关于时钟组 •时钟类别 •异步时钟组 •专用时钟组 关于时钟组 Vivado IDE默认情况下会对设计中所有时钟之间的路径进行计时&#xff0c;除非通过使用时钟组或错误路径约束以其他方式指定。set_clock_groups命令禁…

微信小程序购物车页面实现

目录 32.商品加入购物车逻辑实现&#xff08;前端&#xff09; 33.购物车页面收货地址实现 34.购物车商品列表显示实现 37.购物车商品复选框选中业务处理 38.购物车全选复选框选中业务处理 39.购物车商品数量编辑实现 40.购物车商品数量为0判定是否删除 42.商品详情立即…

控制中存在的一些问题(注意事项)

控制器要阶次要降阶&#xff0c;阶数过高不行 机械臂上层控制器降阶的目的是简化控制算法&#xff0c;提高实时性能&#xff0c;并减少计算资源的消耗。当控制器的阶数过高时&#xff0c;可能会导致以下问题&#xff1a; 计算复杂性增加&#xff1a;高阶控制器需要更多的计算…

只更新软件,座椅为何能获得加热功能?——一文读懂OTA

2020年&#xff0c;特斯拉发布过一次OTA更新&#xff0c;车主可以通过这次系统更新获得座椅加热功能。当时&#xff0c;这则新闻震惊了车圈和所有车主&#xff0c;彼时的大家还没有把汽车当作可以“升级”的智能设备。 如今3年过去了&#xff0c;车主对各家车企的OTA升级早已见…

FCIS 2023网络安全创新大会-核心PPT资料下载

一、峰会简介 本次会议的主题是“AI大模型、人工智能与智能制造安全、攻击面管理与供应链安全”。 1、AI大模型 会议首先探讨了AI大模型在网络安全领域的应用。AI大模型是一种基于深度学习的模型&#xff0c;具有强大的特征提取和分类能力&#xff0c;可以用于检测和防御各种…

Unity3D移动端实现摇一摇功能

手机摇一摇功能在平时项目开发中是很常见的需求&#xff0c;利用Unity的重力感应可以很方便的实现该功能。 Unity简化了重力感应的开发&#xff0c; 通过访问Input.acceleration属性&#xff0c;取回加速度传感器的值。首先我们看一下重力传感器的方向问题。Unity3D中重量的取…

四、Spring IoC实践和应用(基于注解方式管理 Bean)

本章概要 基于注解方式管理 Bean 实验一&#xff1a; Bean注解标记和扫描 (IoC)实验二&#xff1a; 组件&#xff08;Bean&#xff09;作用域和周期方法注解实验三&#xff1a; Bean属性赋值&#xff1a;引用类型自动装配 (DI)实验四&#xff1a; Bean属性赋值&#xff1a;基本…

在MacOS上Qt配置OpenCV并进行测试

一.Qt环境准备 上一篇博客我讲了如何下载配置OpenCV库&#xff0c;但是在Qt5.15.2使用OpenCV库时&#xff0c;出现了一个问题就是我下载的Qt5.15.2是x86架构的&#xff0c;不能对OpenCV库进行链接&#xff0c;而OpenCV库是arm架构的 直接使用Qt5.15.2编译链接OpenCV库链接头文件…

二的幂数组中查询范围内的乘积

说在前面 &#x1f388;不知道大家对于算法的学习是一个怎样的心态呢&#xff1f;为了面试还是因为兴趣&#xff1f;不管是出于什么原因&#xff0c;算法学习需要持续保持。 题目描述 给你一个正整数 n &#xff0c;你需要找到一个下标从 0 开始的数组 powers &#xff0c;它包…