【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 还提供了一些方法来检索给定名称或 uriRpcEndpointRefs

继承
继承
继承
继承
继承
继承
继承
继承
继承
继承
继承
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的抽象方法,在NettyRpcEnvNettyRpcEnvFactory中使用Netty对继承的方式进行了实现。

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

核心类NettyRpcEnv

NettyRpcEnv的核心成员和核心方法

  • transportConfTransportConf的实例对象,加载一些关于RPC的配置项
  • dispatcherDispatcher的实例对象,消息转发器,将RPC消息路由到要该对此消息处理的RpcEndpoint
  • streamManagerNettyStreamManager的实例对象,流的管理器,为NettyRpcEnv提供流式服务。
  • transportContextTransportContext的实例对象
  • clientFactory: 用于构造发送和接收响应的TransportClient
  • fileDownloadFactory: 用于文件下载的独立客户端工厂。这样可以避免使用与主 RPC 上下文相同的 RPC 处理程序,从而将这些客户端引起的事件与主 RPC 流量隔离开来。它还允许对某些属性进行不同的配置,例如每个对等节点的连接数。
  • serverTransportServer,提供高效的底层流媒体服务。
  • ConcurrentHashMap[RpcAddress, Outbox] outboxes:远程地址与Outbox的映射map。
  • startServer(bindAddress: String, port: Int)
    • 创建一个TransportServer
    • 向消息转发器中注册RpcEndpointVerifierRpcEndpointVerifier的注册名称为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,它定义了给定消息时要触发的函数。保证按调用顺序为 onStartreceiveonStopRpcEndpoint的生命周期为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秒
  • defaultAskTimeoutRPC ask操作的超时时间。可以通过spark.rpc.askTimeout,默认120秒
  • address:远程RpcEndpoint引用的地址
  • name:远程RpcEndpoint引用的名称

核心方法

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

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

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

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. 通过调用NettyRpcEndpointRefsend()ask()方法向本地节点的RpcEndpoint发送消息。由于是在同一节点,所以直接调用DispatcherpostLocalMessage()postOneWayMessage()方法将消息放入EndpointData内部Inboxmessages中。
    2. InboxMessage放入后Inbox后,Inbox所属的endPointData就会放入receivers一旦receivers中有数据,原本阻塞的MessageLoop就可以取到数据,
    3. MessageLoop将调用inbox.process()方法消息的处理。对不同的消息类型调用endpoint的不同回调函数,即完成了消息的处理。
  2. 通过调用NettyRpcEndpointRefsend()ask()方法向远端节点的RpcEndpoint发送消息。消息将首先被封装为OutboxMessage,然后放入到远端RpcEndpoint的地址所对应的Outboxmessages中。
  3. 每个OutboxdrainOutbox()方法通过循环,不断从messages列表中取得OutboxMessage,并通过TransportClient发送,底层依赖Netty
  4. TransportClient和远端NettyRpcEnvTransportServer建立了连接后,请求消息首先经过Netty管道的处理,由TransportChannelHandler将消息分发给TransportRequestHandler,最终会调用NettyRpcHandlerStreamManager处理。如果是RPC消息则会调用NettyRpcHandler.receive()方法,之后与第一步所述一致,调用DispatcherpostRemoteMessage()或``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的子类

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

核心类Inbox

InboxRpcEndpoint存储了消息即InboxMessage,并线程安全地发送给RpcEndPoint

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

重要的属性

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

重要方法

  • 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储存nameEndpointData的映射关系EndpointData包含了nameRpcEndpoint, NettyRpcEndpointRefInbox,采用ConcureentHashMap保证线程安全
  • endpointRefs储存RpcEndpointRpcEndpointRef的映射关系。采用ConcureentHashMap保证线程安全
  • receivers存储inbox中可能包含message的EndpointData。在MessageLoop中取出并处理消息。使用阻塞队列LinkedBlockingQueue存储。
  • threadpool用于调度消息的线程池。根据spark.rpc.netty.dispatcher.numThreads创建固定大小的线程池,启动与线程池大小相同个数的MessageLoop任务。

重要方法

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

Dispatcher中的消息处理流程。

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

核心类Outbox

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

Outbox的重要属性

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

重要方法

  • send():将要发送的OutboxMessage首先保存到成员变量链表messages中,若Outbox未停止则调用drainOutbox()方法处理messages中的信息。因为messagesLinkedList类型,线程不安全,所以在添加和删除时使用了同步机制。之后调用了私有的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.mzph.cn/news/238983.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈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;它包…