flink源码系列:RPC通信

这里写目录标题

  • 1. 本节课目的
  • 2.开始本节内容
    • 2.1.RPC概念
    • 3.2.大数据组件常见的RPC实现技术
    • 3.3.Pekko(Akka)
      • 3.3.1. Akka、Pekko基本概念
      • 3.3.2.Pekko Demo事例
        • 3.3.2.1.PekkoData 类
        • 3.3.2.2.PekkoRpcReceiverActor类
        • 3.3.2.3.PekkoRpcSenderActor 类
        • 3.3.2.4. Demo 类
    • 3.4.Flink RPC通信
      • 3.4.1Flink RPC整体架构
      • 3.4.2.RpcGateway
      • 3.4.3.RpcEndpoint
      • 3.4.4.RpcService
      • 3.3.5.RpcServer
      • 3.3.6.PekkoRpcActor
      • 3.3.7.场景引导方式查看源码
    • 3.5.TaskExecutor向ResourceManager注册 debug

https://qwr78tzaus4.feishu.cn/drive/folder/Geumf5oe7lKrnTdAZLzcQn9Bnqc
https://qwr78tzaus4.feishu.cn/docx/K1fsdEbrgo6C0lxYktkc7FOEnQb
https://www.bilibili.com/video/BV16z421m741/?p=2&vd_source=4fd37f941817afccac1a77e31fec6be7

1. 本节课目的

核心点
1.精通Flink RPC框架整体设计
2.彻底理解Flink RPC底层是如何通信的
用到的知识点
1.ResourceManager:主要负责Flink集群中的计算资源,其中计算资源主要来自TaskManager注册。
2.TaskManager(TaskExecutor):TaskManager负责向整个集群提供Slot计算资源。TaskManager会调用registerTaskExecutor()方法向ResourceManager注册

2.开始本节内容

2.1.RPC概念

RPC,即远程过程调用(Remote Procedure Call),是一种通过网络从远程计算机程序上请求服务的技术,而无需了解底层网络技术的协议。在RPC中,客户机和服务器位于不同的机器上,客户端通过网络调用在服务器端运行的过程,并将结果发送回客户机。这种技术允许程序像调用本地过程一样调用远程过程,使得跨平台、跨机器的服务调用成为可能。
1.两个进程间的相互调用
2.集群中不同节点服务的通信

3.2.大数据组件常见的RPC实现技术

序号生态圈技术RPC实现
1HadoopNIO
2SparkSpark1(Akka),Spark2(Netty)
3FlinkAkka+Netty(Pekko+Netty)

3.3.Pekko(Akka)

3.3.1. Akka、Pekko基本概念

Flink1.18版本内部RPC通信封装用的是Apache Pekko。Apache Pekko是Akka 2.6.x的一个分支。为什么会改因为Akka将来Apache许可证更改为Business Source License (BSL) v1.1,该协议不是开源的。
Akka、Pekko 用于构建高并发、分布式、可容错、事件驱动的开发库。
1、提供基于异步非阻塞、高性能的事件驱动编程模型
2、轻量级的事件处理(每GB堆内存几百万Actor)
3、使用Akka可以在单机上构建高并发程序,也可以在网络中构建分布式程序。
注意:Akka是基于Actor模型的并发框架,每个Actor的实例在运行时只占用非常少的资源,大约只有300字节。这意味着在1G的内存中可以容纳接近300万个Actor,这使得Akka在处理大量并发请求时能够保持高效的内存使用。
1、ActorSystem 是管理 Actor 生命周期的组件,Actor 是负责进行通信的组件
2、每个 Actor 都有一个 MailBox,别的 Actor 发送给它的消息都首先储存在 MailBox 中,通过这种方式可以实现异步通信。
3、每个 Actor 是单线程的处理方式,不断的从 MailBox 拉取消息执行处理,所以对于 Actor 的消息处理,不适合调用会阻塞的处理方法。
4、Actor 可以改变他自身的状态,可以接收消息,也可以发送消息,还可以生成新的 Actor
5、每一个 ActorSystem 和 Actor都在启动的时候会给定一个 name,如果要从 ActorSystem 中,获取一个 Actor,则通过以下的方式来进行 Actor 的
获取:pekko.tcp://flink@localhost:6123/user/rpc/resourcemanager_* 来进行定位
6、如果一个 Actor 要和另外一个 Actor 进行通信,则必须先获取对方 Actor 的 ActorRef 对象,然后通过该对象发送消息即可。
7、通过 tell 发送异步消息,不接收响应,通过 ask 发送异步消息,得到 Future 返回,通过异步回到返回处理结果。
8.如果构建actor进行通信,Pekko版本中必须继承AbstractActor 实现createReceive()方法

3.3.2.Pekko Demo事例

3.3.2.1.PekkoData 类

1.定义了通信的类型信息也就是PekkoData
2.内部声明一个 字符串类型的info

package com.source.pekko;import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;@Data
@AllArgsConstructor
@NoArgsConstructor
public class PekkoData {private String info;
}
3.3.2.2.PekkoRpcReceiverActor类

1.PekkoRpcReceiverActor接收Actor类继承了AbstractActor
2.也就是说该类可以进行接收发送消息
3.接收消息会进入到createReceive
4.根据消息类型匹配进入到handleMessage
5.获取发送者、自身的ActorRef
6.打印信息并向发送者回复消息

package com.source.pekko;import org.apache.pekko.actor.AbstractActor;
import org.apache.pekko.actor.ActorRef;
import org.apache.pekko.japi.pf.ReceiveBuilder;/*** 继承AbstractActor定义自己的actor* Actor可以发送和接收消息*/
public class PekkoRpcReceiverActor extends AbstractActor {/*** 实现接收消息* @return*/@Overridepublic Receive createReceive() {return ReceiveBuilder.create()/**接收到PekkoData消息交给handleMessage处理* flink PekkoRpcActor 155行也是这样处理的*/.match(PekkoData.class, this::handleMessage).build();}/*** 处理具体消息* @param message*/private void handleMessage(final PekkoData message) {/** 获取发送者,发送者对应的就是actorRef */ActorRef sender = getSender();ActorRef self = getSelf();/** 打印 */System.out.println("PekkoRpcReceiverActor类收到:" +sender + ":发送者=>" + message.getInfo());/** 回复消息 向发送者sender 回复word 的消息 回复者是当前actorRef*//** 4、Actor 可以改变他自身的状态,可以接收消息,也可以发送消息,还可以生成新的 Actor  */sender.tell(new PekkoData("word"),self);}
}
3.3.2.3.PekkoRpcSenderActor 类

1.PekkoRpcSenderActor 发送Actor类继承了AbstractActor
2.也就是说该类可以进行接收发送消息
3.接收消息会进入到createReceive
4.根据消息类型匹配进入到handleMessage
5.获取发送者的ActorRef
6.打印信息

package com.source.pekko;import org.apache.pekko.actor.AbstractActor;
import org.apache.pekko.actor.ActorRef;
import org.apache.pekko.japi.pf.ReceiveBuilder;/*** 继承AbstractActor定义自己的actor* Actor可以发送和接收消息*/
public class PekkoRpcSenderActor extends AbstractActor {/*** 实现接收消息* @return*/@Overridepublic Receive createReceive() {return ReceiveBuilder.create()/**接收到PekkoData消息交给handleMessage处理* flink PekkoRpcActor 155行也是这样处理的*/.match(PekkoData.class, this::handleMessage).build();}private void handleMessage(final PekkoData message) {/** 获取发送者,发送者对应的就是actorRef */ActorRef sender = getSender();/** 打印 */System.out.println("PekkoRpcSenderActor类收到:" +sender + ":发送者=>" + message.getInfo());}
}
3.3.2.4. Demo 类

1.创建ActorSystem,名字为flink
2.获取PekkoRpcReceiverActor的ActorRef这样就可以进行发送消息了、接收消息了
3.获取PekkoRpcSenderActor的ActorRef这样就可以进行发送消息了、接收消息了
4.通过PekkoRpcSenderActor的actorRef 向PekkoRpcReceiverActor发送消息
5.PekkoRpcReceiverActor类中的createReceive接收到消息后会匹配类型转入handleMessage
6.打印信息,然后通过自身actorRef 向PekkoRpcSenderActor回复消息
7.PekkoRpcSenderActor的createReceive方法接收到后转入handleMessage
8.打印回复信息。
9结束程序。

package com.source.pekko;import org.apache.pekko.actor.ActorRef;
import org.apache.pekko.actor.ActorSystem;
import org.apache.pekko.actor.Props;public class Demo {public static void main(String[] args) {/**创建actorSystem*/ActorSystem actorSystem = ActorSystem.create("flink");/**构建PekkoRpcActor的ActorRef*/ActorRef pekkoRpcRef = actorSystem.actorOf(Props.create(PekkoRpcReceiverActor.class), "PekkoRpcReceiverActor");/**构建PekkoRpcSenderActor的ActorRef*/ActorRef pekkoRpcSenderRef = actorSystem.actorOf(Props.create(PekkoRpcSenderActor.class), "PekkoRpcSenderActor");/** pekkoRpcSenderActor作为发送者 向PekkoRpcActor发送 hello*/pekkoRpcRef.tell(new PekkoData("hello"),pekkoRpcSenderRef);}
}

运行结果
[图片]

3.4.Flink RPC通信

3.4.1Flink RPC整体架构

Flink RPC框架设计相对比较复杂,底层基于Pekko构建的通信系统,Java 动态代理构建RpcGateway接口的代理类
Flink RPC UML图
[图片]

如上图 Flink RPC UML图
1.RpcGateway接口Flink RPC底层通信用到的动态代理,动态代理中使用的目标类实现的接口最终都是RpcGateway(也就是说动态代理创建的接口最上层都是RpcGateway)
2.RpcEndpoint消息通信组件,底层都有的通信实体都要继承RpcEndpoint
3.FenceRpcEndpoint类是内部会有一个fenceToken发送消息的时候两个 token一样的时候才能发成功,FencePekkoInvocationHandler、FenceRpcGateway也一样
4.RpcEndpoint 内部使用到了RpcService、RpcService
5.RpcService就是用来服务Flink RPC通信的服务类,内部会创建RpcEndpoint的自身代理,获取远程代理。RpcService实现类是PekkoRpcService
6.RpcService 在具体通信类构建对象的时候super父类构造器也就是RpcEndpoint类的时候会初始化RpcServer代表自身代理。
7.PekkoInvocationHandler、FencePekkoInvocationHandler实现了java InvocationHandler接口,也就是说他们里面肯定有实现的invoke方法
8.Dispatcher及其子类、ResourceManager及其子类、JobMaster最终都继承了RpcEndpoint,也就是说他们都具备了通信的特质

3.4.2.RpcGateway

Rpc网关,用于远程调用的代理接接口,RPC通信的接口都继承RpcGateway,java动态代理类最终创建。
Proxy类:这个类提供了创建动态代理类和实例的静态方法。

public static Object newProxyInstance(ClassLoader loader,                                       Class<?>[] interfaces,                                       InvocationHandler h)  
interfaces=>实现了RpcGateWay的接口
如:ResourceManagerGatewayJobMasterGatewayTaskExecutorGateway

3.4.3.RpcEndpoint

1)RpcEndpoint抽象类中定义了RPC组件的基本实现,所有需要实现RPC服务的组件都会继承RpcEndpoint,
RpcEndpoint内部包含了endpointId 用来标识当前RPC节点的唯一标识,RpcEndpoint借助RpcService启动RpcServer。
2)FencedRpcEndpoint继承RpcEndpoint,内部增加了fencingToken字段,实现了FencedRpcEndpoint的节点都会有一个fencingToken,当远程RPC调用时,会比较访问者和被访问者的fencingToken是否一致,一致了才会进行后续操作。
3)FencedRpcEndpoint实现类有ResourceManager、JobMaster、TaskExecutor,RpcEndpoint的实现类有TaskExecutor

3.4.4.RpcService

创建时间ClusterEntrypoint 开始启动集群初始化的时候

private RpcService commonRpcService;
ClusterEntrypoint .runCluster ->
initializeServices ->commonRpcService =RpcUtils.createRemoteRpcService(rpcSystem,configuration,configuration.get(JobManagerOptions.ADDRESS),getRPCPortRange(configuration),configuration.get(JobManagerOptions.BIND_HOST),configuration.getOptional(JobManagerOptions.RPC_BIND_PORT));

内部提供了RpcServer的创建和启动方法,启动RpcServer(startServer)过程中,通过RpcEndpoint地址创建Akka actor实例,并基于Actor实例构建RpcServer接口的动态代理类
connect方法:连接到所提供地址下的远程rpc服务器。返回一个rpc网关(代理对象),该网关可以用于与rpc服务器通信

3.3.5.RpcServer

创建时间RpcEndpoint 构建的时候创建
RpcServer接口通过PekkoInvocationHandler动态代理类实现,所有远程获本地的执行请求,最终都会转换到PekkoInvocationHandler代理类中执行,也就是InvocationHandler的invoke方法
public ResourceManagersuper() ->
protected FencedRpcEndpoint super() ->
RpcEndpoint -> this.rpcServer = rpcService.startServer(this);
核心点:所有RpcEndpoint启动的时候调用start()方法,最终都会流转到RpcEndpoint的onStart()方法
原因如下:

ClusterEntrypoint.dispatcherResourceManagerComponentFactory.create() ->
DefaultDispatcherResourceManagerComponentFactory.create ->
resourceManagerService.start() ->
ResourceManagerServiceImpl.start() ->
StandaloneLeaderElection.startLeaderElection->
ResourceManagerServiceImpl.grantLeadership->
startNewLeaderResourceManager()->
startResourceManagerIfIsLeader->resourceManager.start();
RpcEndpoint.start ->public void start() {rpcEndpoint.tell(ControlMessages.START, ActorRef.noSender());}===========================================
PekkoRpcActor.createReceive() ->
handleControlMessage() ->
StoppedState.start() ->
RpcEndpoint.internalCallOnStart()->
onStart()

3.3.6.PekkoRpcActor

继承了AbstractActor,实现了createReceive(),也就是说Flink RPC 所有通信都会被createReceive
之后根据消息类型流转到对应的handleMessage(),消息类型有RemoteHandshakeMessage握手消息、ControlMessages 控制类消息比如start,其他消息(RpcInvocation)
暂时无法在飞书文档外展示此内容

3.3.7.场景引导方式查看源码

假设设计一个TaskExecutor进程 向ResourceManager进程注册,如何设?
1.TaskExecutor、ResourceManager要是可以进行通信的(RpcEndpoint)
1.TaskManager要能获取到ResourceManager的代理对象(TaskExecutorGateway、ResourceManagerGateway)
2.TaskManager获取到代理对象之后要知道调用ResourceManager的那个方法进行注册(ResourceManagerGateway.registerTaskExecutor)
3.要实现有能连接ResourceManager进程的通信服务(RpcService)
4.建立通信连接后要有能处理消息的公共类(PekkoRpcActor)
5.满足以上条件了,就相当于TaskManager、ResourceManager本身就是一个可以通信的进程,本地通信 自己与自己通信(RpcServer)
基于上面的设计,我们从Flink代码中可以找到对应的实现类、接口

3.5.TaskExecutor向ResourceManager注册 debug

TaskExecutor创建对象启动的时候会触发 onStart方法
Flink 内部所有的RpcEndpoint 实现(TaskExecutor,ResourceManager、JobMaster)等第一次启动都会触发onstart方法的执行,这是pekko的内部机制
1.进入到TaskExecutor的onStart方法后,调用startResourceManagerServices启动相关的服务
[图片]

startResourceManagerServices方法内部做了如下操作
2.resourceManagerLeaderRetriever监听服务中构建一个ResourceManagerLeaderListener会监听ResourceManager Leader,该类中的notifyLeaderAddress方法会在第一次启动、ResourceManager Leader的时候触发
3.启动taskSlotTable(后面章节进行分析)
4.启动jobLeaderService(后面章节进行分析)

[图片]

4.notifyLeaderAddress方法会在第一次启动、ResourceManager Leader的时候触发,直接进去方法内部
[图片]

5.获取ResourceManager地址
6.reconnectToResourceManager真正向ResouceManager注册的方法
[图片]

7.关闭历史已经连接的ResourceManager
8.启动注册超时时间
9.试图连接ResourceManager(内部会调用connectToResourceManager)
[图片]

10.方法内部调用继续下一步
[图片]

11.TaskExecutorRegistration是TaskExecutor在注册到ResourceManager时提供的信息
12.TaskExecutorToResourceManagerConnection维护TaskExecutor与ResourceManager的连接
13.start真正连接注册的方法

[图片]

14.检查状态
15.createNewRegistration()注册成功、注册失败会回调方法
16.newRegistration.startRegistration()开始注册
[图片]

17.调用RpcService.connect(内部就会用到java动态代理)
[图片]

18.调用connectInternal方法创建代理类
19.FencedPekkoInvocationHandler刚好是实现了InvocationHandler
[图片]

20.调用ask方法进行握手,保证ResourceManager能正常通信
[图片]

21.Proxy.newProxyInstance创建代理实现
[图片]

22.创建完代理类会通过异步编程调用register同时代理对象作为参数
[图片]

23.invokeRegistration真正触发调用的方法进入实现类看
[图片]

24.实现类中的invokeRegistration方法内部调用了resourceManager.registerTaskExecutor方法,此时还没有发送到ResourceManager,方法会流转到PekkoRpcActorl类中。
[图片]

25.消息会跳转到PekkoInvocationHandler类的invoke方法
26.判断本地消息还是远程消息,因为ResourceManager是远程消息,所以会调用invokeRpc
[图片]

[图片]

27.将方法参数等封装成RpcInvocation,然后调用Pekko底层的ask发送消息
28.消息会被到PekkoRpcActor类中的方法所接收
[图片]
[图片]

在这里插入图片描述

29.PekkoRpcActor类中createReceive方法接收到数据,流转到handleMessage方法中

[图片]

30.handleMessage方法中会解析出来RpcInvocation获取到方法、参数、参数类型
31.通过java反射机制调用method.invoke 传入参数最终向目标类发送消息
[图片]

32.最后进入到ResourceManager.registerTaskExecutor方法

[图片]

如愿以偿进入最终我们预想的方法。

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

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

相关文章

使用QT制作QQ登录界面

mywidget.cpp #include "mywidget.h"Mywidget::Mywidget(QWidget *parent): QWidget(parent) {/********制作一个QQ登录界面*********************/this->resize(535,415);//设置登录窗口大小this->setFixedSize(535,415);//固定窗口大小this->setWindowTi…

图片转Base64

在Python中, 可以使用内置的base64模块以及图像处理库(如PIL, 也称为Pillow)来将图片转换为Base64编码的字符串. 以下是一个简单的示例, 说明如何实现这一过程:首先, 需要安装Pillow库(如果尚未安装), 可以使用pip来安装: pip install pillow然后, 可以使用以下Python代码将图片…

【kyuubi-spark】从0-1部署kyuubi集成spark执行spark sql到k8s读取iceberg的minio数据

一、背景 团队在升级大数据架构 前端使用trino查询&#xff0c;对trino也进行了很多优化&#xff0c;目前测试来看&#xff0c;运行还算稳定&#xff0c;但是不可避免的trino的任务总会出现失败的情况。原来的架构是trino失败后去跑hive&#xff0c;而hive是跑mapreduce依赖于…

VBA语言専攻每周资料领取通知

通知20240615 各位学员∶本周MF系列VBA技术资料增加626-630讲&#xff0c;T3学员看到通知后请免费领取,领取时间6月14日晚上19:00-6月15日晚上20:00。本次增加内容&#xff1a; MF626:将列标题转换为数字 MF627:拆分日期和时间 MF628:从工作表中随机取数 MF629:从工作表中…

【MySQL】日志详解

本文使用的MySQL版本是8 日志概览 它们记录了数据库系统中的不同操作和事件&#xff0c;以便于故障排除、性能优化和数据恢复。本文将介绍MySQL中常见的几种日志&#xff0c;同时也会介绍一点常用的选项。 官方文档&#xff1a;MySQL :: MySQL 8.0 Reference Manual :: 7.4 M…

【FAS】《CN103106397B》

原文 CN103106397B-基于亮瞳效应的人脸活体检测方法-授权-2013.01.19 华南理工大学 方法 / 点评 核心方法用的是传统的形态学和模板匹配&#xff0c;亮点是双红外发射器做差分 差分&#xff1a;所述FPGA芯片控制两组红外光源&#xff08;一近一远&#xff09;交替亮灭&…

【算法专题--链表】相交链表--高频面试题(图文详解,小白一看就会!!)

目录 一、前言 二、题目描述 三、解题方法 ⭐双指针 --- 数学思维 ⭐双指针 --- 按链表长度计算 &#x1f95d; 判断相交 &#x1f347; 求出交点 &#x1f34d;实现步骤 四、总结与提炼 五、共勉 一、前言 相交链表这道题&#xff0c;可以说是--链表专题--&#xf…

【C++进阶学习】第一弹——继承(上)——探索代码复用的乐趣

前言&#xff1a; 在前面&#xff0c;我们已经将C的初阶部分全部讲完了&#xff0c;包括类与对象、STL、栈和队列等众多内容&#xff0c;今天我们就进入C进阶部分的学习&#xff0c;今天先来学习第一弹——继承 目录 一、什么是继承&#xff1f;为什么会有继承&#xff1f; 二…

C语言详解(文件操作)1

Hi~&#xff01;这里是奋斗的小羊&#xff0c;很荣幸您能阅读我的文章&#xff0c;诚请评论指点&#xff0c;欢迎欢迎 ~~ &#x1f4a5;&#x1f4a5;个人主页&#xff1a;奋斗的小羊 &#x1f4a5;&#x1f4a5;所属专栏&#xff1a;C语言 &#x1f680;本系列文章为个人学习…

【成品设计】基于物联网的停车管理系统设计与实现

《基于物联网的停车管理系统设计与实现》 整体功能&#xff1a; 本次课题中&#xff0c;主要设计的是一款基于物联网技术的校园停车的管理系统&#xff0c;该系统能更方便得让管理员对停车场进行管理&#xff0c;同时也能够满足和方便用户使用。针对此种现象&#xff0c;就需…

推流工具OBS的下载使用

一、下载安装 OBS&#xff0c;windows版本官网下载地址 二、推流步骤 安装好之后&#xff0c;打开软件 1、右下角&#xff0c;打开设置 2、输入推流地址&#xff0c;一般为rtmp格式开头的推流地址 输入完成后&#xff0c;应用并确定关闭窗口 3、“来源”里面新建媒体源、新…

[Shell编程学习路线]——探讨Shell中变量的作用范围(export)

&#x1f3e1;作者主页&#xff1a;点击&#xff01; &#x1f6e0;️Shell编程专栏&#xff1a;点击&#xff01; ⏰️创作时间&#xff1a;2024年6月14日10点14分 &#x1f004;️文章质量&#xff1a;95分 文章目录 ————前言———— 定义变量&#xff1a; 输出变…

【C语言】一篇文章带你深度理解函数

目录 1. 函数的概念 2. 库函数 2.1 标准库和头文件 2.2 库函数的使用方法 2.2.1 举例 sqrt 2.2.2 库函数文档的一般格式 3. 自定义函数 3.1 函数的语法形式 3.2 函数的举例 4. 形参和实参 4.1 实参 4.2 形参 4.3 实参和形参的关系 5. …

“三夏”农忙:EasyCVR/EasyDSS无人机技术助推现代农业走向智能化

随着科技的飞速发展&#xff0c;无人机技术已经逐渐渗透到我们生活的方方面面。其中&#xff0c;无人机在农业领域的应用尤为引人注目。它们不仅提高了农业生产的效率&#xff0c;还为农民带来了更便捷、更智能的种植方式。 无人机在农业应用场景中&#xff0c;通过搭载各种设备…

生命在于学习——Python人工智能原理(3.3)

三、深度学习 4、激活函数 激活函数的主要作用是对神经元获得的输入进行非线性变换&#xff0c;以此反映神经元的非线性特性。常见的激活函数有线性激活函数、符号激活函数、Sigmod激活函数、双曲正切激活函数、高斯激活函数、ReLU激活函数。 &#xff08;1&#xff09;线性…

Go Module详解

文章目录 基本介绍相关环境变量Go Module的使用初始化项目&#xff08;go mod init&#xff09;管理依赖项&#xff08;go mod edit&#xff09;获取依赖项&#xff08;go mod download&#xff09;整理依赖项&#xff08;go mod tidy&#xff09;导入vendor目录&#xff08;go…

Zynq学习笔记--AXI4-Stream到视频输出IP是如何工作的?

目录 1. 简介 2. 原理详解 2.1 示例工程 2.2 AXI4-Stream to Video Out 3. Master/Slave Timing Mode 3.1 Slave Timing Mode 3.2 Master Timing Mode 4. 总结 1. 简介 本文主要介绍了 AXI4-Stream 到视频输出 的内容。其中&#xff0c;示例工程展示了一个具体的设计&…

后端项目实战--瑞吉外卖项目软件说明书

瑞吉外卖项目软件说明书 一、项目概述 瑞吉外卖项目是一个外卖服务平台&#xff0c;用户可以通过该平台浏览餐厅菜单、下单、支付以及追踪订单状态。产品原型就是一款产品成型之前的一个简单的框架&#xff0c;就是将页面的排版布局展现出来&#xff0c;使产品得初步构思有一…

【 EI会议 | 西南大学主办 | 往届均已实现检索】第三届神经形态计算国际会议(ICNC 2024)

第三届神经形态计算国际会议&#xff08;ICNC 2024) 2024 3rd International Conference on Neuromorphic Computing (ICNC 2024) 一、重要信息 大会官网&#xff1a;www.ic-nc.org&#xff08;点击投稿/参会/了解会议详情&#xff09; 会议时间&#xff1a;2024年12月13-15…

OpenAI函数调用:使用Assistants API函数工具的一个示例

Are you looking to expand GPTs capabilities? Check out this tutorial for a complete example of an AI Assistant that can send emails whenever we ask it to. 您是否希望扩展GPT的功能&#xff1f;查看这个教程&#xff0c;它提供了一个完整的示例&#xff0c;展示了…