《WebKit 技术内幕》学习之十一(4):多媒体
4 WebRTC
4.1 历史
相信读者都有过使用Tencent QQ或者FaceTime进行视频通话的经历,这样的应用场景相当典型和流行,但是基本上来说它们都是每个公司推出的私有产品,而且通信等协议也都是保密的,这使得一种产品的用户基本上不可能同其他产品的用户进行视频通信。还有一些更大的应用场景,那就是众多用户一起召开视频会议,这比简单的点对点更为复杂,很多公司已投身其中,因为这一市场非常广大。
几年前,笔者是很难想象这么复杂的需求和应用场景能够在Web领域中被实现,但是现在Chromium和Firefox浏览器都支持Web视频通信,而且神奇的是它们之间也可以相互通信,听起来非常不可思议——只是需要Web开发者使用JavaScript和HTML5技术就可以完成,还是免费的,而且还不需要额外安装应用软件,不需要额外安装插件。现在真是互联网和HTML5技术的好时代,这也是HTML5的多媒体领域的一个重大进展。
WebRTC(Web Real Time Communication)技术,中文全称为Web实时通信技术,它是一种提供实时视频通信的规范,目前是W3C推荐的规范。它是一个开放的规范,任何人都可以免费使用,目前Chromium/Chrome和Firefox浏览器都支持了该规范。WebRTC是HTML5对多媒体支持的一个重大进展,因为该技术不仅使用了音视频的输入和输出,而且还涉及连接等网络连接,是一个非常复杂但非常有用的技术。图11-19是一个简单的示意图,说明两个支持WebRTC规范的浏览器之间是如何进行视频通信的。事实上,WebRTC既允许使用服务器来进行通信,也支持点对点(Peer-to-Peer)通信,当然需要服务器的辅助。

图11-19 Chromium/Chrome和Firefox浏览器的视频通信示例图
不过,很多事情并非是一蹴而就的,何况这么复杂的WebRTC技术,WebRTC发展至今也经历了很长的过程。首先得从一个音视频的捕获需求开始。最初,HTML5希望能够提供一种捕获用户音频和视频的技术,这就是getUserMedia,当然刚开始也不是它,而是使用下面的语句来完成音频和视频的捕获。
<input type="file" accept="video/*;capture=camcorder"><input type="file" accept="audio/*;capture=microphone">
但是它们太简单了,只能将捕获的信息保存为一个文件或者一个快照,这显然不能满足很多实际的需求,之后一个新的元素定义诞生,就是使用“device”元素,这一元素很快被抛弃。这就是getUserMedia的前身,但标准化组织很快从“device”元素转向getUserMedia,它的基本使用方式是:
navigator.getUserMedia({video: true, audio: true}, function (videostream) { … });
思想很简单,就是在navigator这个全局对象下加入一个新接口,该接口使用两个参数,第一表示它需要捕获视频或者音频或者两者都需要,第二个是一个回调函数,当捕获成功后,将捕获的视频流可以输出到一个视频元素,这就像是一个从服务器加载的视频文件,当然它是一个视频流,所以某些查找(Seek)操作不能工作。这里,不再使用新元素,而是利用原有的“video”元素,实在是一个好的设计。
在这之后,一个更为大胆和激进的想法诞生了,就是将RTC技术引入到HTML5中来,这就是WebRTC技术,因为getUserMedia是入口,所以自然而然地被使用到WebRTC技术的规范中来。
11.4.2 原理和规范
大家可以在脑海中想象一下如何要构建一个网络视频通信、需要哪些部分的参与及共同工作才能完成整个过程。总体上,这一过程中需要三种类型的技术,其一是视频,其二是音频,其三是网络传输。在这三种技术上,具体需要以下一些部分。
- 音视频输入和输出设备 :同音视频播放不同,因为它们只是需要输出设备,这里需要输入和输出设备(麦克风和摄像头)。同时,输入使用getUserMedia技术,而输出,基本上可以采用音视频播放的基本框架,当然,需要一些额外的支持。
- 网络连接的建立 :因为视频通信需要不停地传送大量数据,所以需要建立一种可靠的网络连接来让各个参与方传输数据。
- 数据捕获、编码和发送 :当用户打开设备之后,需要捕获这些数据并对它们进行编码,因为原始数据的数据量太大,然后需要将编码后的数据通过连接传输出去。
- 数据接收、解码和显示 :接收来自其他方的数据流并进行解码,然后显示出来,这个需求跟播放媒体文件的需求比较类似。
根据上面的解释,不难理解图11-20所描述的过程,结合这些主要组成部分,构成了一个比较完整的音视频通信过程。

图11-20 使用WebRTC技术的视频通信详细过程
下面了解一下规范中如何针对上面的描述来定义相应的JavaScript接口的。根据目前W3C推荐的规范草案,主要包括以下几个部分。
- Media Capture and Streams规范和WebRTC对它的扩展,这个主要是依赖摄像头和麦克风来捕获多媒体流,WebRTC对它进行扩展,使得多媒体流可以满足网络传输用途,也就是“video”元素可以来源于多媒体流而不仅仅是资源文件。
- 点到点的连接,也就是规范中的RTCPeerConnection接口,它能够建立端到端的连接,两者直接通过某种方式传输控制信息,至于方式并没有进行规定。
- RTCDataChannel接口,通过该接口,通信双方可以发送任何类型的消息,例如文本或者二进制数据,这个不是必须的。不过这一功能极大地方便了开发者,其主要思想来源于WebSocket。
11.4.3 实践——一个WebRTC例子
在介绍内部原理之前,笔者希望通过剖析W3C规范中的一个例子来进一步加深对它的理解。示例代码11-5来源于W3C WebRTC规范中的示例代码,笔者稍微作了一些修改以简化理解,并加入注释作进一步说明。
这里主要是点对点的直接通信,当然需要借助于网络提供的NAT服务,其中包括三个文件,第一个是双方共享的JavaScript代码,第二个是发起端的HTML代码,第三个是接收端的HTML代码。通常第二个和第三个可以是一样的,这里为了方便理解作了少许区别。因为代码中已经作了较为详细的讲解,所以后面不再赘述其中的原理。
示例代码11-5 使用WebRTC技术的P2P网络视频通信
JavaScript文件:common.js// 创建消息通道,例如使用XMLHttpRequest或者WebSocket。根据WebRTC规范的说明// 本身WebRTC不提供双方进行控制信息传输的通道,由开发者自行选择合适的方法// 这里,简单使用一个函数表示创建了一个通道,该通道包含一个能够发送消息的“send”var signalChannel = createSignalChannel();// 将ICE的Candidate发送给对方,这个是ICE定义的,主要是ICE协议用来建立连接需// 要的信息。双方需要交互这个信息function sendCandidate(candidate) {if (candidate) signalChannel.send(JSON.stringify({ "candidate": candidate }));}function sendDescription() {signalingChannel.send(JSON.stringify({ "sdp": conn.localDescription }));}signalingChannel.onmessage = function (event) {// 如果没有建立连接,需要创建,在这里表明这是接受者端if (conn == null) start();// 从控制信息中获取信息内容var message = JSON.parse(event.data);// 如果信息类型是设置Description相关的,就调用setRemoteDescriptionif (message.sdp) {conn.setRemoteDescription(new RTCSessionDescription(message.sdp), function () {// 如果受到一个请求(offer),需要答复它,这里应该是接收方处理的if (conn.remoteDescription.type == "offer") {conn.createAnswer(function(desc) {conn.setLocalDescription(desc, sendDescription);});} else if (message.candidate) {conn.addIceCandidate(new RTCIceCandidate(message.candidate));}};// 用来存放RTCPeerConnection对象var conn = null;// 用来显示从自身设备捕获的多媒体流var selfView = document.getElementById("selfView");// 用来显示从对方传送过来的多媒体流var remoteView = document.getElementById("remoteView");// 开始创建连接等function start() {// 创建连接conn = new RTCPeerConnection({ "iceServers": [{ "url": "stun:stun. example.org" }] });// 保存从自身捕获的多媒体流var capturedStream = null;// 捕获音视频navigator.getUserMedia({ "audio": true, "video": true }, function (stream) {// 将捕获的多媒体流使用“video”元素播放出来selfView.src = URL.createObjectURL(stream);capturedStream = stream;}// 将多媒体流加入连接conn.addStream(capturedStream);// 接收到ICE Candidate事件,需要将candidate信息传送给对方conn.onicecandidate = function (event) {sendCandidate(event.candidate);};// 这个是由发起者调用,因为接收者不会发送该事件conn.onnegotiationneeded = function() {// 创建一个Offer,然后发送给接收者conn.createOffer(function (desc) {conn.setLocalDescription(desc, sendDescription););});// 将远端多媒体流使用“video”元素显示出来conn.onaddstream = function (evt) {remoteView.src = URL.createObjectURL(evt.stream);};}发起者HTML文件节选:<video id="selfView" autoplay ></video><video id="remoteView" autoplay></video><script src='common.js'></script><script>// 上面的两个"video"元素分别用来显示自己捕获的多媒体流和对方的多媒体流// 实际情况中,可能是某个用户作为发起者单击了“开始”按钮,启动音视频通信start();</script>接受者HTML文件节选:<video id="selfView" autoplay ></video><video id="remoteView" autoplay></video><script src='common.js' type='javascript'></script>
相信通过上面的代码介绍,读者应该理解使用WebRTC构建一个P2P视频通信的基本过程,这其中网络连接的部分主要基于ICE协议(NAT)和SDP协议,以及一些支持NAT的辅助设施(STUN和URN),有兴趣的读者可以自行查阅相关技术文档。
4.4 WebKit和Chromium的实现
下面来看一看WebKit和Chromium是如何支持WebRTC规范的。笔者首先需要澄清一下关于WebRTC的两种解释,本节中会有两种WebRTC用法:一种是指WebRTC这项技术和规范;另一种是WebRTC这个项目,它是支持WebRTC规范的一个开源项目。默认情况下是指前者,如果是指WebRTC这个开源项目,笔者会明确指出。
下面来了解一下从webrtc.org上介绍的关于支持WebRTC技术的内部框架和功能模块,图11-21来源于“http://www.webrtc.org/reference/architecture”的架构图,但是缩减了其中一些部分。这里所示的是实现了WebRTC功能的开源项目架构图。
图11-21中主要包括三大方面,即语音、视频和传输,它们三个构成了WebRTC的主要组成部分。其中iSAC(internet Speech Audio Codec)和iLBC(internet Low Bitrate Codec)是两种不同的音频编码格式,是为了适应互联网的语音传输要求而存在的,前者是针对带宽比较大的情况,后者针对带宽较小的情况,目前都是可以免费使用的。其中VP8同样是Google提供免费视频格式,前面介绍过了。传输部分主要是加入了对前面协议的支持模块。在会话管理中,主要使用一个开源项目libjingle来进行管理。下面灰色部分主要是WebRTC工作时依赖的下层功能的接口,在Chromium浏览器中,它会提供相应接口和功能给WebRTC使用。

图11-21 WebRTC项目的架构图
上面是WebRTC开源项目的架构图,在Chromium中,通常使用WebRTC项目来完成WebRTC规范的功能,并使用libjingle项目来建立点到点的连接。所以,Chromium主要的目的是将WebRTC和libjingle的能力桥接到浏览器中来,先看WebRTC规范中建立连接所需要的相关基础设施,图11-22是WebKit、Chromium及Chromium中使用libjingle的类的层次图。

图11-22 WebKit和Chromium建立连接的基础设施
基础设施主要分成三个层次,首先是WebKit,也就是最上面的部分。该部分最上面的类是RTCPeerConnection,从名字就可以猜出,该类是对WebRTC连接的接口类,实际上它就是从规范中定义的RTCPeerConnection接口文件生成的基本框架,当然真正和JavaScript引擎打交道还需要一个桥接类。该桥接类包含一个实际实现的连接类句柄m_peerHandler,它是这个连接所包含的本地多媒体流和远端对方的多媒体流。读者可以想象一下视频会议的场景,首先Webkit需要将本地的多媒体流收集起来,通过连接传输给对方,本地可以选择是否通过“video”播放。同时需要接收从对方传输过来的多媒体流,这也是WebRTC的主要部分。当然还包括DataChannel相关对象,这里没有标出。
至于接下来的部分就是WebKit的实现类,该类能够满足RTCPeerConnection的功能要求,但是它需要通过不同移植的实现才能完成,因为本身WebKit的WebCore并没有这样的能力。在WebKit的Chromium中同样定义了两个类WebRTCPeerConnectionHandler和WebRTCPeerConnectionHandlerClient,根据WebKit的类名定义方式,前者是需要Chromium来实现,而后者则是由Chromium调用,并由WebKit来实现的,这里主要是应用连接事件的监听函数,所以WebKit能够将它们传递给JavaScript引擎。
之后是Chromium的实现类。RTCPeerConnectionHandler类继承自WebKit的Chromium移植的接口类,并做了具体的实现,这就是content::RTCPeerConnectionHandler,它同时集成自PeerConnectionHandleBase类,而该类拥有了支持建立连接所需的能力,当然它是依赖于libjingle项目提供的连接能力。
libjingle提供了建立和管理连接的能力,支持透过NAT和防火墙设备、代理等建立连接。libjingle不仅支持点到点的连接,也支持多用户连接。同时还包含了连接所使用的MediaStream接口,这是因为Chromium本身不直接使用WebRTC项目提供的接口,而是调用libjingle来建立连接,并使用libjingle提供的MediaStream接口,而libjingle本身则会使用WebRTC项目的音视频处理引擎。
接下来要介绍的是多媒体流,它需要一个非常复杂的框架,首先来看WebKit是如何支持getUserMedia这个接口的。图11-23描述了WebKit,以及WebKit的Chromium移植中所定义的接口,图中虚线上半部分是WebKit中的类,下半部分是Chromium中的类。
图11-23 WebKit支持多媒体流的基础设施
最上层是WebKit支持多媒体流编程接口提供的具体实现类,如NavigatorMediaStream类,而直接同V8 JavaScript引擎桥接的类是V8NavigatorUser-MediaSuccessCallback,它是一个绑定类。因为getUserMedia接口主要是返回一个MediaStream对象,而MediaStream类可以提供众多访问数据流的接口,而连接的目的就是需要将MediaStream对应的多媒体流传输出去。图中UserMediaRequest类负责请求创建一个MediaStream对象。在WebKit的Chromium移植中,定义WebUserMediaClient为一个接口类,Chromium需要新建子类来实现这一功能,这就是Chromium中的MediaStreamImpl类,它在后面的介绍中还会出现。
WebKit中使用MediaStreamRegistry类来注册和管理对应的类,管理类根据ID信息来识别各个多媒体数据流。在接口层中,Chromium移植使用WebMediaStream类来表示多媒体流,使用WebMediaStreamRegistry类来表示注册管理类。
下面的问题是MediaStream接口需要提供各种事件给网页,因为很多实际的工作是在Chromium中来完成的,所以MediaStreamImpl会将这些事件从Chromium传递给WebKit。同时因为Chromium的多进程和沙箱模型,一些工作需要在Browser进程中完成,所以可以见到如图11-24所描述的跨进程的基础设施。

图11-24 Chromium支持MediaStream接口基础设施
IPC左侧部分是Browser进程中的两个主要类,分别是消息处理类和MediaStream的管理类,该管理类知道MediaStream对应的网页是什么,并将事件(如创建和销毁等)传回Renderer进程。右侧是消息派发类,主要帮助MediaStreamImpl类来完成与Browser进程相关的MediaStream消息的传递。
实际上,MediaStream可以表示本地的多媒体流,也可以表示远端的多媒体流。对于本地的多媒体流,需要音频和视频的捕获机制,同时使用上面建立的连接传输给远端。对于远端的多媒体流,需要使用连接来接收数据,并使用到音频和视频的解码能力。下面分成四个部分来分别介绍。
首先是音频的捕获机制,图11-25描述了该机制使用的主要类。当网页需要创建多媒体流的时候,MediaStreamImpl会创建音频捕获类,也就是WebRtcAudioCapturer类,如图中上半部分。因为捕获音频需要音频输入设备,所以使用AudioDeviceFactory工厂类创建一个逻辑上的AudioInputDevice对象。另外一个重要的类是WebRtcAudioDeviceImpl,用来表示音频的设备,该类继承自WebRtcAudioDeviceNotImpl类。这其实是继承自libjingle和WebRTC项目中的抽线接口的一个桥接类,用来表示它们需要的音频设备,当然包括输入设备。同样因为Renderer进程不能访问音频输入设备,所以需要IPC来完成这一功能,Browser进程的AudioInputController会控制和访问设备,而AudioInputDeviceManager可以管理和控制所有输入设备。

图11-25 Chromium本地捕获音频的基础设施
其次是处理远端多媒体流中的音频解码和播放功能。图11-26是Chromium处理远端音频流所需要的一些主要类及关系图。这里不涉及连接如何接收传输的数据,因为Chromium是使用libjingle和WebRTC项目来完成连接的功能。Chromium使用WebRtcAudioRender类来完成音频渲染,该桥接类会被WebMediaPlayer作为渲染音频的实现类,其作用主要是将MediaStream的数据同实际的音频渲染类结合起来。

图11-26 Chromium处理远端音频基础设施
再次是从视频输入设备请求捕获本地视频流,图11-27是该功能依赖的一些主要类。

图11-27 Chromium本地捕获视频的基础设施
首先看虚线右侧Renderer进程中的设施。同样是MediaStreamImpl类发起,由辅助工厂类MeidaStreamDependencyFactory帮助创建一个RtcVideoCapaturer,用来获取视频。该类有两个作用,其一是实现libjingle和WebRTC项目中的接口类,因为需要视频输入的实现,这个同音频部分非常类似。另外就是将调用请求交给一个代理类来完成,这就是RtcVideoCaptureDelegate类。下面的就比较好理解了,分别是管理类VideoCaptureImplManager和视频捕获类VideoCaptureImpl,并包括一个发送消息到Browser进程的辅助类。在Browser进程使用控制类VideoCaptureController来获取VideoCaptureDevice,该类会使用摄像头等视频输入设备,效果都相对比较简单直观。
最后是处理远端多媒体流中的视频解码和播放功能。当MediaStreamImpl对象接收到远端的多媒体流之后,它会使用WebRTC来对视频数据进行解码,因为可以使用硬件来解码,所以提高了处理的性能。

图11-28 Chromium处理远端视频基础设施
把WebRTC整个过程综合起来分析,可以有一种更为整体和直观的感受,如图11-29所示。读者可以结合这个图来回味一下之前所描述的众多细节。图中没有本地捕获的音视频的播放过程,因为它们不是必须的,而且同播放远端多媒体流类似,这里便不再赘述。

图11-29 WebKit、Chromium、libjingle和WebRTC等项目支持WebRTC规范的框架
目前,在Chrome浏览器中,基于WebRTC的网页已经可以在移动操作系统(如Android)上获得支持,移动领域的进展必将推动该技术的进一步发展。回顾本章介绍的多媒体各个方面的技术,读者可以看出,HTML5技术不仅引入了多媒体的支持,而且加入了之前插件也不能支持的众多更新更复杂的技术,但是却极大提升了HTML5的应用范围。
相关文章:
《WebKit 技术内幕》学习之十一(4):多媒体
4 WebRTC 4.1 历史 相信读者都有过使用Tencent QQ或者FaceTime进行视频通话的经历,这样的应用场景相当典型和流行,但是基本上来说它们都是每个公司推出的私有产品,而且通信等协议也都是保密的,这使得一种产品的用户基本上不可能…...
k8s基础知识
理解docker [二] - namespace - 知乎 Kubernetes Controller 机制详解(一)-赵化冰的博客 | Zhaohuabing Blog K8S之自定义Controller - 知乎 Controller - K8S - 知乎 https://coolshell.cn/articles/17010.html/comment-page-2#comment-2133157 ht…...
Docker容器引擎(3)
目录 一.Docker 镜像的创建 1.基于现有镜像创建 2.基于本地模板创建 3.基于Dockerfile创建: Dockerfile 操作常用的指令: ADD 和 COPY 的区别? CMD 和 ENTRYPOINT 的区别? 容器启动命令的优先级 如…...
【Android12】Android Framework系列---Adb和PMS安装apk源码流程
Adb和PMS安装apk源码流程 adb install命令 通过adb install命令可以将apk安装到Android系统(注意:特定类型的apk,比如persist类型是无法通过adb安装的) 下述命令中adb解析install命令,并调用Android PackageManagerS…...
web漏洞总结大全(基础)
前言 本文章是和cike_y师傅一起写的,cike_y博客:https://blog.csdn.net/weixin_53912233?typeblog 也欢迎大家对本文章进行补充和指正,共同维护这个项目,本文的github项目地址: https://github.com/baimao-box/Sum…...
获取双异步返回值时,如何保证主线程不阻塞?
目录 一、前情提要二、JDK8的CompletableFuture1、ForkJoinPool2、从ForkJoinPool和ThreadPoolExecutor探索CompletableFuture和Future的区别 三、通过CompletableFuture优化 “通过Future获取异步返回值”1、通过Future获取异步返回值关键代码(1)将异步…...
hosts文件修改后无法保存的解决办法
目录 第一步 右键点击C盘里的hosts文件,选择重命名。 第二步 在桌面新建一个txt文件,命名为hosts。并把自己需要的内容写入保存。 第三步 把hosts.txt文件复制到原本hosts文件的路径下。右键选中hosts.txt文件,选择重命名,去掉…...
源码篇--Redis 五种数据类型
文章目录 前言一、 字符串类型:1.1 字符串的编码格式:1.1.1 raw 编码格式:1.1.2 empstr编码格式:1.1.3 int 编码格式:1.1.4 字符串存储结构展示: 二、 list类型:2.1 List 底层数据支持:2.2 List 源码实现:2.3 List 结构…...
Sulfo Cy2 Biotin,水溶性 Cy2 生物素,能够与各种氨基基团特异性结合
您好,欢迎来到新研之家 文章关键词:Sulfo Cyanine2 Biotin,Sulfo Cy2 Biotin,水溶性 Cy2 生物素,Sulfo-Cy2-Biotin,水溶性-Cy2-生物素 一、基本信息 产品简介:Sulfo Cyanine2 Biotin, also k…...
NineData支持制定安全、可靠的SQL开发规范
在和数据库打交道中,不管是数据库管理员(DBA)还是开发人员,经常会做一些CURD操作。因为每个人对数据库的了解程度不一样,所以在项目上线时,往往还需要专职人员对数据库的CURD操作进行审核,确保C…...
LSTM时间序列预测
本文借鉴了数学建模清风老师的课件与思路,可以点击查看链接查看清风老师视频讲解:【1】演示:基于LSTM深度学习网络预测时间序列(MATLAB工具箱)_哔哩哔哩_bilibili % Forecast of time series based on LSTM deep learn…...
Rocky8 顺利安装 Airflow 并解决数据库报错问题
rocky是替代centos的服务器系统,稳定可靠。rocky8会比centos7新,可以支持更多服务软件的安装,免去升级各种库的麻烦,本文运行airflow服务就用rocky8系统。airflow是一个定时任务管理系统,功能强大,目前是ap…...
[足式机器人]Part2 Dr. CAN学习笔记- 最优控制Optimal Control Ch07-3 线性二次型调节器(LQR)
本文仅供学习使用 本文参考: B站:DR_CAN Dr. CAN学习笔记 - 最优控制Optimal Control Ch07-3 线性二次型调节器(LQR) 1. 数学推导2. 案例反洗与代码详解 1. 数学推导 2. 案例反洗与代码详解...
Eyes Wide Shut? Exploring the Visual Shortcomings of Multimodal LLMs
大开眼界?探索多模态模型种视觉编码器的缺陷。 论文中指出,上面这些VQA问题,人类可以瞬间给出正确的答案,但是多模态给出的结果却是错误的。是哪个环节出了问题呢?视觉编码器的问题?大语言模型出现了幻觉&…...
汤姆·齐格弗里德《纳什均衡与博弈论》笔记(4)博弈论与人性
第五章 弗洛伊德的梦——博弈和大脑 大脑和经济学 曾经有一段时间——就像在弗洛伊德的年代——心理学家们无法准确地回答人类行为背后的大脑机制。但随着现代神经科学的兴起,情形改变了。比如,人类的情绪不再像过去一样是个谜。科学家们可以观察当人们…...
MacOS平台翻译OCR软件,双管齐下,还可自定义插件,为其添砖加瓦!
小编昨天为大家分享了Windows系统下的一款功能强大且免费的 OCR 开源工具 Umi-OCR。 今天则为大家推荐一款 MacOS系统下的一款 翻译 OCR 多功能双管齐下的桌面应用软件 Bob。这款软件虽然也上线了GitHub,但它不是一款开源软件,仓库只是作者为了用户反馈…...
使用docker配置semantic slam
一.Docker环境配置 1.拉取Docker镜像 sudo docker pull ubuntu:16.04拉取的为ununtu16版本镜像,环境十分干净,可以通过以下命令查看容器列表 sudo docker images 如果想删除多余的docker image,可以使用指令 sudo docker rmi -f <id&g…...
面试常问的Spring AOP底层原理
AOP底层原理可以划分成四个阶段:创建代理对象阶段、拦截目标对象阶段、调用代理对象阶段、调用目标对象阶段 第一阶段:创建代理对象阶段 通过getBean()方法创建Bean实例根据AOP的配置匹配目标类的类名,判断是否满足切…...
C++拾遗(四)引用与指针
引用和指针是两种不同的概念,尽管它们在某些方面有一些相似之处,但它们在功能和用途上是有所区别 声明与定义 引用:引用是别名,是对已存在变量的另一个称呼,一旦一个变量被引用,就不能再被引用其他变 量…...
k8s架构、工作流程、集群组件详解
目录 k8s概述 特性 作用(为什么使用) k8s架构 k8s工作流程 k8s集群架构与组件 核心组件详解 Master节点 Kube-apiserver Kube-controller-manager Kube-scheduler 存储中心 etcd Node Kubelet Kube-Proxy 网络通信模型 容器引擎 k8s核…...
k8s从入门到放弃之Ingress七层负载
k8s从入门到放弃之Ingress七层负载 在Kubernetes(简称K8s)中,Ingress是一个API对象,它允许你定义如何从集群外部访问集群内部的服务。Ingress可以提供负载均衡、SSL终结和基于名称的虚拟主机等功能。通过Ingress,你可…...
Admin.Net中的消息通信SignalR解释
定义集线器接口 IOnlineUserHub public interface IOnlineUserHub {/// 在线用户列表Task OnlineUserList(OnlineUserList context);/// 强制下线Task ForceOffline(object context);/// 发布站内消息Task PublicNotice(SysNotice context);/// 接收消息Task ReceiveMessage(…...
oracle与MySQL数据库之间数据同步的技术要点
Oracle与MySQL数据库之间的数据同步是一个涉及多个技术要点的复杂任务。由于Oracle和MySQL的架构差异,它们的数据同步要求既要保持数据的准确性和一致性,又要处理好性能问题。以下是一些主要的技术要点: 数据结构差异 数据类型差异ÿ…...
Maven 概述、安装、配置、仓库、私服详解
目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...
Hive 存储格式深度解析:从 TextFile 到 ORC,如何选对数据存储方案?
在大数据处理领域,Hive 作为 Hadoop 生态中重要的数据仓库工具,其存储格式的选择直接影响数据存储成本、查询效率和计算资源消耗。面对 TextFile、SequenceFile、Parquet、RCFile、ORC 等多种存储格式,很多开发者常常陷入选择困境。本文将从底…...
九天毕昇深度学习平台 | 如何安装库?
pip install 库名 -i https://pypi.tuna.tsinghua.edu.cn/simple --user 举个例子: 报错 ModuleNotFoundError: No module named torch 那么我需要安装 torch pip install torch -i https://pypi.tuna.tsinghua.edu.cn/simple --user pip install 库名&#x…...
面向无人机海岸带生态系统监测的语义分割基准数据集
描述:海岸带生态系统的监测是维护生态平衡和可持续发展的重要任务。语义分割技术在遥感影像中的应用为海岸带生态系统的精准监测提供了有效手段。然而,目前该领域仍面临一个挑战,即缺乏公开的专门面向海岸带生态系统的语义分割基准数据集。受…...
使用Spring AI和MCP协议构建图片搜索服务
目录 使用Spring AI和MCP协议构建图片搜索服务 引言 技术栈概览 项目架构设计 架构图 服务端开发 1. 创建Spring Boot项目 2. 实现图片搜索工具 3. 配置传输模式 Stdio模式(本地调用) SSE模式(远程调用) 4. 注册工具提…...
【p2p、分布式,区块链笔记 MESH】Bluetooth蓝牙通信 BLE Mesh协议的拓扑结构 定向转发机制
目录 节点的功能承载层(GATT/Adv)局限性: 拓扑关系定向转发机制定向转发意义 CG 节点的功能 节点的功能由节点支持的特性和功能决定。所有节点都能够发送和接收网格消息。节点还可以选择支持一个或多个附加功能,如 Configuration …...
【LeetCode】算法详解#6 ---除自身以外数组的乘积
1.题目介绍 给定一个整数数组 nums,返回 数组 answer ,其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。 题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。 请 不要使用除法,且在 O…...
