WebRTC 服务器之Janus视频会议插件信令交互

 1.基础知识回顾

WebRTC 服务器之Janus概述和环境搭建-CSDN博客

WebRTC 服务器之Janus架构分析-CSDN博客

2.插件使用流程

我们要使⽤janus的功能时,通常要执⾏以下操作:

1. 在你的⽹⻚引入 Janus.js 库,即是包含janus.js;

<!-- 网页头部或 body 底部引入 -->
<script src="https://your-cdn-path/janus.js"></script>

2. 初始化 Janus 库,并传递其依赖项(依赖项可选);

Janus.init({debug: true,    // 开启调试日志dependencies: {// 可选依赖(如 adapter.js 用于跨浏览器兼容)adapter: adapter},callback: function() {console.log("Janus 库初始化完成");// 初始化完成后连接服务器connectToJanusServer();}
});

3. 连接服务器 & 创建会话 连接到janus server并创建create⼀个会话session;

let janusSession;function connectToJanusServer() {const serverUrl = "wss://your-janus-server:8989/janus";new Janus({server: serverUrl,success: function(session) {janusSession = session;console.log("会话创建成功:", session.getId());// 创建插件句柄(例如加入视频房间)attachToVideoRoomPlugin();},error: function(error) {console.error("连接失败:", error);}});
}

4. 创建插件句柄 (Handle) 创建⼀个或多个handle 以attach到插件(plugin)(例如videoroom、videocall等插件);

let pluginHandle;function attachToVideoRoomPlugin() {janusSession.attach({plugin: "janus.plugin.videoroom",  // 插件名称success: function(handle) {pluginHandle = handle;console.log("成功附加到插件,句柄 ID:", handle.getId());// 发送加入房间请求joinVideoRoom();},error: function(error) {console.error("附加插件失败:", error);}});
}

5. 信令交互示例 - 加入视频房间 与创建交互(发送/接收消息,协商PeerConnection);

function joinVideoRoom() {const joinMsg = {request: "join",room: 1234,       // 房间 IDptype: "publisher",display: "用户1"};pluginHandle.send({ message: joinMsg });
}// 处理服务器事件(如房间通知)
pluginHandle.on("message", (msg, jsep) => {console.log("收到服务器消息:", msg);if (msg["videoroom"] === "joined") {console.log("成功加入房间!");// 处理媒体协商(如创建 Offer)startWebRTCNegotiation();}
});

6.  WebRTC 协商流程 

function startWebRTCNegotiation() {// 创建媒体配置const mediaConfig = {audio: true,video: true,data: true};// 准备 PeerConnectionpluginHandle.createOffer({media: mediaConfig,success: function(sdp) {// 发送 SDP OfferpluginHandle.sendSDP(sdp);},error: function(error) {console.error("创建 Offer 失败:", error);}});
}// 处理 ICE Candidate
pluginHandle.on("ice", (candidate) => {// 发送候选到服务器pluginHandle.trickle(candidate);
});

7. 最后 关闭所有的handles并关闭相关的相应的PeerConnections; 消耗destroy会话session。

function cleanup() {// 1. 销毁插件句柄if (pluginHandle) {pluginHandle.detach();pluginHandle = null;}// 2. 销毁会话if (janusSession) {janusSession.destroy();janusSession = null;}
}// 页面关闭时触发清理
window.onbeforeunload = cleanup;

 完整流程总结

  1. 初始化

    Janus.init({ debug: true, callback: createJanusSession });
  2. 会话与插件

    创建会话 (createSession) → 创建插件句柄 (createHandle) → 注册事件监听 (eventHandler). 
  3. 信令交互

    发送消息 (sendMessage) 加入房间 → 接收服务器事件 (handleEvent).
  4. WebRTC 协商

    创建 Offer (createOffer) → 发送 SDP (sendSDP) → 接收 Answer → 创建 Answer (createAnswer).
  5. 收发av数据

  6. 资源释放

    销毁句柄 (destroyHandle) → 销毁会话 (destroySession).

2.1 协议基本类型

"janus"后⾯的字符串代表消息的类型,主要有:

  1. create:创建⼀个Janus会话命令;
  2. attach:attach⼀个插件到Janus会话命令;
  3. success:⼀个命令的成功结果;
  4. error:⼀个命令的失败结果;
  5. ack:⼀个命令的ack,因为不能直接返回结果,先回ack,后续的结果通过event返回;
  6. event:推送给客户端的异步事件,主要由插件发出,这些事件需要插件来⾃⼰定义;
  7. message:客户端发给插件的消息,message和event就构成了应⽤协议;
  8. trickle:客户端发送的candidate,会传递给ICE句柄;
  9. keepalive:客户端发送的⼼跳;
  10. webrtcup:ICE成功建⽴通道的通知;
  11. media:⾳频、视频媒体的接收通知;
  12. slowlink:链路恶化通知;
  13. hangup:挂断通知;
  14. detached:插件从Janus会话detach的通知,释放了⼀个插件句柄。

3.videoroom插件信令交互分析

Janus中所有插件都遵循以下基本数据流程:

  1. 客户端发送create创建一个Janus会话;
  2. Janus回复success返回Janus会话句柄
  3. 客户端发送attach命令在Janus会话上attach指定插件;
  4. Janus回复success返回插件的句柄
  5. 客户端给指定的插件发送message进行信令控制;
  6. Janus上的插件发送event通知事件给客户端;
  7. 客户端收集candidate并通过trickle消息发送给插件绑定的ICE通道;
  8. Janus发送webrtcup通知ICE通道建立;
  9. 客户端发送媒体数据;
  10. Janus发送media消息通知媒体数据的第一次到达;
  11. Janus进行媒体数据转发。

    3.总体逻辑

    1. Janus客户端->Janus网关: Janus客户端发送create请求创建会话
      • JBJ-WEBSOCKET RECV: {"janus":"create","transaction":"aAMxRfsTsVVQ"}
        
    2. Janus网关–>>Janus客户端: Janus网关创建会话,并返回会话IDJ
      1. JBJ-WEBSOCKET responses:Sending Janus API response(
        {"janus": "success","transaction": "aAMxRfsTsVVQ","data": {"id": 5109637625847547}
        }
        ) to janus.transport.websockets (0x7f5cd400d3c0)
    3. Janus客户端->Janus网关: 客户端发送attach命令在Janus会话上attach指定插件
      1. JBJ-WEBSOCKET RECV: {"janus": "attach","plugin": "janus.plugin.videoroom","opaque_id": "videoroomtest-jIAzP2ZkPUrn","transaction": "2kuM6pzfX4Zh","session_id": 5109637625847547
        }

    4. Janus网关–>>Janus客户端:  Janus回复success返回插件的句柄
      1. 
        JBJ-WEBSOCKET responses:Sending Janus API response({"janus": "success","session_id": 5109637625847547,"transaction": "2kuM6pzfX4Zh","data": {"id": 6769547295474368}
        }) to janus.transport.websockets (0x7f5cd400d3c0)

    5. Janus客户端->Janus网关: Janus客户端给指定插件发送message(Join)
      1. JBJ-WEBSOCKET RECV: {"janus": "message","body": {"request": "join","room": 1234,"ptype": "publisher","display": "321"},"transaction": "11JDzIbsbK91","session_id": 5109637625847547,"handle_id": 6769547295474368
        }
        
        JBJ-WEBSOCKET responses:Sending Janus API response({"janus": "ack","session_id": 5109637625847547,"transaction": "11JDzIbsbK91"
        }) to janus.transport.websockets (0x7f5cd400d3c0)

    6. Janus网关–>>Janus客户端: Janus网关处理message,并将结果通过event事件发送给客户端
      1. Preparing JSON event as a reply
        JBJ-video.room.event: [6769547295474368] Sending event is: {"janus": "event","session_id": 5109637625847547,"transaction": "11JDzIbsbK91","sender": 6769547295474368,"plugindata": {"plugin": "janus.plugin.videoroom","data": {"videoroom": "joined","room": 1234,"description": "Demo Room","id": 6538458198168085,"private_id": 1041514801,"publishers": []}}} to transport...
        Sending event to janus.transport.websockets (0x7f5cd400d3c0)>> 0 (Success)

    7. Janus客户端->Janus网关: Janus客户端给指定插件发送message(Configure & offer)
      1. ####################Configure & offer
        JBJ-WEBSOCKETRECV: {"janus": "message","body": {"request": "configure","audio": true,"video": true},"transaction": "MHTliRCHqY5y","jsep": {"type": "offer","sdp": "v=0\r\no=- 6239954181818903803 2 IN IP4 127.0.0.1\r\ns=-\r\nt=0 0\r\na=group:BUNDLE 0 1\r\na=extmap-allow-mixed\r\na=msid-semantic: WMS 900722df-7e7a-4951-b1bf-1902cb7fe6e0\r\nm=audio 9 UDP/TLS/RTP/SAVPF 111 63 9 0 8 13 110 126\r\nc=IN IP4 0.0.0.0\r\na=rtcp:9 IN IP4 0.0.0.0\r\na=ice-ufrag:eHT/\r\na=ice-pwd:aAxznBSHiqTXMkZAzJs5xv3B\r\na=ice-options:trickle\r\na=fingerprint:sha-256 75:80:26:F9:EB:47:14:51:61:D3:CD:8B:60:C1:5E:F5:67:53:1E:88:9C:45:53:3B:32:D0:D2:5F:CD:45:63:7F\r\na=setup:actpass\r\na=mid:0\r\na=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level\r\na=extmap:2 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time\r\na=extmap:3 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01\r\na=extmap:4 urn:ietf:params:rtp-hdrext:sdes:mid\r\na=sendonly\r\na=msid:900722df-7e7a-4951-b1bf-1902cb7fe6e0 9718362e-2152-4c32-9e79-da62a8a609c5\r\na=rtcp-mux\r\na=rtcp-rsize\r\na=rtpmap:111 opus/48000/2\r\na=rtcp-fb:111 transport-cc\r\na=fmtp:111 minptime=10;useinbandfec=1\r\na=rtpmap:63 red/48000/2\r\na=fmtp:63 111/111\r\na=rtpmap:9 G722/8000\r\na=rtpmap:0 PCMU/8000\r\na=rtpmap:8 PCMA/8000\r\na=rtpmap:13 CN/8000\r\na=rtpmap:110 telephone-event/48000\r\na=rtpmap:126 telephone-event/8000\r\na=ssrc:2665350871 cname:+3Rknr5a6Axa5msx\r\na=ssrc:2665350871 msid:900722df-7e7a-4951-b1bf-1902cb7fe6e0 9718362e-2152-4c32-9e79-da62a8a609c5\r\nm=video 9 UDP/TLS/RTP/SAVPF 96 97 103 104 107 108 109 114 115 116 117 118 39 40 45 46 98 99 100 101 119 120 123 124 125\r\nc=IN IP4 0.0.0.0\r\na=rtcp:9 IN IP4 0.0.0.0\r\na=ice-ufrag:eHT/\r\na=ice-pwd:aAxznBSHiqTXMkZAzJs5xv3B\r\na=ice-options:trickle\r\na=fingerprint:sha-256 75:80:26:F9:EB:47:14:51:61:D3:CD:8B:60:C1:5E:F5:67:53:1E:88:9C:45:53:3B:32:D0:D2:5F:CD:45:63:7F\r\na=setup:actpass\r\na=mid:1\r\na=extmap:14 urn:ietf:params:rtp-hdrext:toffset\r\na=extmap:2 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time\r\na=extmap:13 urn:3gpp:video-orientation\r\na=extmap:3 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01\r\na=extmap:5 http://www.webrtc.org/experiments/rtp-hdrext/playout-delay\r\na=extmap:6 http://www.webrtc.org/experiments/rtp-hdrext/video-content-type\r\na=extmap:7 http://www.webrtc.org/experiments/rtp-hdrext/video-timing\r\na=extmap:8 http://www.webrtc.org/experiments/rtp-hdrext/color-space\r\na=extmap:4 urn:ietf:params:rtp-hdrext:sdes:mid\r\na=extmap:10 urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id\r\na=extmap:11 urn:ietf:params:rtp-hdrext:sdes:repaired-rtp-stream-id\r\na=sendonly\r\na=msid:900722df-7e7a-4951-b1bf-1902cb7fe6e0 d97d2702-4a0f-4693-8499-71c66f8c03fe\r\na=rtcp-mux\r\na=rtcp-rsize\r\na=rtpmap:96 VP8/90000\r\na=rtcp-fb:96 goog-remb\r\na=rtcp-fb:96 transport-cc\r\na=rtcp-fb:96 ccm fir\r\na=rtcp-fb:96 nack\r\na=rtcp-fb:96 nack pli\r\na=rtpmap:97 rtx/90000\r\na=fmtp:97 apt=96\r\na=rtpmap:103 H264/90000\r\na=rtcp-fb:103 goog-remb\r\na=rtcp-fb:103 transport-cc\r\na=rtcp-fb:103 ccm fir\r\na=rtcp-fb:103 nack\r\na=rtcp-fb:103 nack pli\r\na=fmtp:103 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42001f\r\na=rtpmap:104 rtx/90000\r\na=fmtp:104 apt=103\r\na=rtpmap:107 H264/90000\r\na=rtcp-fb:107 goog-remb\r\na=rtcp-fb:107 transport-cc\r\na=rtcp-fb:107 ccm fir\r\na=rtcp-fb:107 nack\r\na=rtcp-fb:107 nack pli\r\na=fmtp:107 level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=42001f\r\na=rtpmap:108 rtx/90000\r\na=fmtp:108 apt=107\r\na=rtpmap:109 H264/90000\r\na=rtcp-fb:109 goog-remb\r\na=rtcp-fb:109 transport-cc\r\na=rtcp-fb:109 ccm fir\r\na=rtcp-fb:109 nack\r\na=rtcp-fb:109 nack pli\r\na=fmtp:109 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f\r\na=rtpmap:114 rtx/90000\r\na=fmtp:114 apt=109\r\na=rtpmap:115 H264/90000\r\na=rtcp-fb:115 goog-remb\r\na=rtcp-fb:115 transport-cc\r\na=rtcp-fb:115 ccm fir\r\na=rtcp-fb:115 nack\r\na=rtcp-fb:115 nack pli\r\na=fmtp:115 level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=42e01f\r\na=rtpmap:116 rt
        }JBJ-video.room.event: [6769547295474368] Sending event is: {"janus": "event","session_id": 5109637625847547,"transaction": "MHTliRCHqY5y","sender": 6769547295474368,"plugindata": {"plugin": "janus.plugin.videoroom","data": {"videoroom": "event","room": 1234,"configured": "ok","audio_codec": "opus","video_codec": "vp8"}},"jsep": {"type": "answer","sdp": "v=0\r\no=- 6239954181818903803 2 IN IP4 117.72.13.81\r\ns=VideoRoom 1234\r\nt=0 0\r\na=group:BUNDLE 0 1\r\na=msid-semantic: WMS janus\r\nm=audio 9 UDP/TLS/RTP/SAVPF 111\r\nc=IN IP4 117.72.13.81\r\na=recvonly\r\na=mid:0\r\na=rtcp-mux\r\na=ice-ufrag:0pFN\r\na=ice-pwd:wGcEE+9WQLoHULcXGP8KFn\r\na=ice-options:trickle\r\na=fingerprint:sha-256 C7:C1:43:EE:67:07:A0:6D:42:33:6A:CA:4E:EA:95:E0:55:17:03:CB:88:FD:B6:37:AB:1C:7B:85:65:9A:95:C6\r\na=setup:active\r\na=rtpmap:111 opus/48000/2\r\na=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level\r\na=extmap:3 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01\r\na=extmap:4 urn:ietf:params:rtp-hdrext:sdes:mid\r\na=msid:janus janusa0\r\na=ssrc:3338353236 cname:janus\r\na=ssrc:3338353236 msid:janus janusa0\r\na=ssrc:3338353236 mslabel:janus\r\na=ssrc:3338353236 label:janusa0\r\na=candidate:1 1 udp 2013266431 117.72.13.81 53576 typ host\r\na=candidate:2 1 udp 1677722111 117.72.13.81 53576 typ srflx raddr 172.16.0.4 rport 53576\r\na=candidate:3 1 udp 503316991 172.16.0.4 54497 typ relay raddr 172.16.0.4 rport 53576\r\na=end-of-candidates\r\nm=video 9 UDP/TLS/RTP/SAVPF 96 97\r\nc=IN IP4 117.72.13.81\r\na=recvonly\r\na=mid:1\r\na=rtcp-mux\r\na=ice-ufrag:0pFN\r\na=ice-pwd:wGcEE+9WQLoHULcXGP8KFn\r\na=ice-options:trickle\r\na=fingerprint:sha-256 C7:C1:43:EE:67:07:A0:6D:42:33:6A:CA:4E:EA:95:E0:55:17:03:CB:88:FD:B6:37:AB:1C:7B:85:65:9A:95:C6\r\na=setup:active\r\na=rtpmap:96 VP8/90000\r\na=rtcp-fb:96 ccm fir\r\na=rtcp-fb:96 nack\r\na=rtcp-fb:96 nack pli\r\na=rtcp-fb:96 goog-remb\r\na=rtcp-fb:96 transport-cc\r\na=extmap:13 urn:3gpp:video-orientation\r\na=extmap:3 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01\r\na=extmap:5 http://www.webrtc.org/experiments/rtp-hdrext/playout-delay\r\na=extmap:4 urn:ietf:params:rtp-hdrext:sdes:mid\r\na=extmap:10 urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id\r\na=extmap:11 urn:ietf:params:rtp-hdrext:sdes:repaired-rtp-stream-id\r\na=rtpmap:97 rtx/90000\r\na=fmtp:97 apt=96\r\na=msid:janus janusv0\r\na=ssrc:3467980833 cname:janus\r\na=ssrc:3467980833 msid:janus janusv0\r\na=ssrc:3467980833 mslabel:janus\r\na=ssrc:3467980833 label:janusv0\r\na=ssrc:3074101642 cname:janus\r\na=ssrc:3074101642 msid:janus janusv0\r\na=ssrc:3074101642 mslabel:janus\r\na=ssrc:3074101642 label:janusv0\r\na=candidate:1 1 udp 2013266431 117.72.13.81 53576 typ host\r\na=candidate:2 1 udp 1677722111 117.72.13.81 53576 typ srflx raddr 172.16.0.4 rport 53576\r\na=candidate:3 1 udp 503316991 172.16.0.4 54497 typ relay raddr 172.16.0.4 rport 53576\r\na=end-of-candidates\r\n"}
        }#######################trickle
        JBJ-WEBSOCKET RECV: {"janus": "trickle","candidate": {"candidate": "candidate:980779705 1 udp 2122260223 192.168.126.1 60475 typ host generation 0 ufrag eHT/ network-id 2","sdpMid": "0","sdpMLineIndex": 0},"transaction": "0WWSoZXbCxhA","session_id": 5109637625847547,"handle_id": 6769547295474368
        }JBJ-WEBSOCKET RECV: {"janus": "trickle","candidate": {"candidate": "candidate:3993315412 1 udp 2122194687 192.168.248.1 60476 typ host generation 0 ufrag eHT/ network-id 3","sdpMid": "0","sdpMLineIndex": 0},"transaction": "DddvxlDlWuZN","session_id": 5109637625847547,"handle_id": 6769547295474368
        }
        JBJ-WEBSOCKET RECV: {"janus": "trickle","candidate": {"candidate": "candidate:2923601174 1 udp 2122129151 172.25.0.107 60477 typ host generation 0 ufrag eHT/ network-id 1 network-cost 10","sdpMid": "0","sdpMLineIndex": 0},"transaction": "HCdvw7ihHaaZ","session_id": 5109637625847547,"handle_id": 6769547295474368
        }
    8. Janus网关–>>Janus客户端: Janus网关返回ack消息
      1. JBJ-WEBSOCKET responses:Sending Janus API response({"janus": "ack","session_id": 5109637625847547,"transaction": "MHTliRCHqY5y"
        }) to janus.transport.websockets (0x7f5cd400d3c0)JBJ-WEBSOCKET responses:Sending Janus API response({"janus": "ack","session_id": 5109637625847547,"transaction": "0WWSoZXbCxhA"
        }) to janus.transport.websockets (0x7f5cd400d3c0)JBJ-WEBSOCKET responses:Sending Janus API response({"janus": "ack","session_id": 5109637625847547,"transaction": "DddvxlDlWuZN"
        }) to janus.transport.websockets (0x7f5cd400d3c0)

    学习资料分享

    0voice · GitHub

     

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

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

    相关文章

    Go语言中的无锁数据结构与并发效率优化

    1. 引言 在高并发系统开发中&#xff0c;性能瓶颈往往出现在并发控制上。作为一个有着10年Go开发经验的后端工程师&#xff0c;我见证了无数因锁竞争导致的性能问题&#xff0c;也亲历了无锁编程为系统带来的巨大提升。 传统的锁机制就像是十字路口的红绿灯——虽然能确保安全…

    STM32部分:2、环境搭建

    飞书文档https://x509p6c8to.feishu.cn/wiki/DQsBw76bCiWaO4kS8TXcWDs0nAh Keil MDK用于编写代码&#xff0c;编译代码芯片支持包&#xff0c;用于支持某类芯片编程支持STM32CubeMX用于自动生成工程&#xff0c;减少手动重复工作 STM32F1系列芯片支持包 软件下载 直接下载&am…

    U3D工程师简历模板

    模板信息 简历范文名称&#xff1a;U3D工程师简历模板&#xff0c;所属行业&#xff1a;其他 | 职位&#xff0c;模板编号&#xff1a;B29EPQ 专业的个人简历模板&#xff0c;逻辑清晰&#xff0c;排版简洁美观&#xff0c;让你的个人简历显得更专业&#xff0c;找到好工作。…

    Java设计模式: 实战案例解析

    Java设计模式: 实战案例解析 在软件开发中&#xff0c;设计模式是一种用来解决特定问题的可复用解决方案。它们是经过实践验证的最佳实践&#xff0c;能够帮助开发人员设计出高质量、易于维护的代码。本文将介绍一些常见的Java设计模式&#xff0c;并通过实战案例解析它们在实际…

    Vue3源码学习5-不使用 `const enum` 的原因

    文章目录 前言✅ 什么是 const enum❌ 为什么 Vue 3 不使用 const enum1. &#x1f4e6; **影响构建工具兼容性**2. &#x1f501; **难以做模块间 tree-shaking**3. &#x1f9ea; **调试困难**4. &#x1f4e6; **Vue 是库&#xff0c;不掌控用户配置** ✅ 官方推荐做法&…

    C++学习:六个月从基础到就业——C++11/14:lambda表达式

    C学习&#xff1a;六个月从基础到就业——C11/14&#xff1a;lambda表达式 本文是我C学习之旅系列的第四十篇技术文章&#xff0c;也是第三阶段"现代C特性"的第二篇&#xff0c;主要介绍C11/14中引入的lambda表达式。查看完整系列目录了解更多内容。 引言 Lambda表达…

    AIDC智算中心建设:计算力核心技术解析

    目录 一、智算中心发展概览 二、计算力核心技术解析 一、智算中心发展概览 智算中心是人工智能发展的关键基础设施&#xff0c;基于人工智能计算架构&#xff0c;提供人工智能应用所需算力服务、数据服务和算法服务的算力基础设施&#xff0c;融合高性能计算设备、高速网络以…

    IoTDB时序数据库V2.0.2大版本更新的一些梳理

    一些小知识&#xff1a; 关于事务&#xff1a;时序数据库是没有事务的&#xff0c;它和关系数据库的应用场景不同&#xff0c;通常情况下不需要多点同时操作同一条数据&#xff0c;而且要保证极高的吐出量&#xff0c;事务太消耗资源&#xff0c;并且时序数据库提供了覆写的功能…

    CSS定位详解

    在前端开发中&#xff0c;CSS 定位&#xff08;positioning&#xff09;是一个核心概念&#xff0c;它决定了元素在页面上的位置和布局方式。无论是构建复杂的交互界面&#xff0c;还是实现简单的页面排版&#xff0c;CSS 定位都是不可或缺的工具。本文将全面介绍 CSS 中的五种…

    React 语法扩展

    useReducer钩子函数 不同action类型返回不同处理行为 useState()函数返回解构为两个值 state当前状态 dispatch修改状态函数 dispatch()函数参数为一个actuon对象 如 &#xff1a; 样例&#xff1a; import { useReducer } from react; import ./App.css;// 定义一个Reduce…

    MCP协议与Dify集成教程

    一、MCP协议概述 MCP&#xff08;Model Control Protocol&#xff09;是一种新兴的开放协议&#xff0c;为大型语言模型&#xff08;LLM&#xff09;与外部应用之间构建了双向通信通道。它就像是AI的"USB-C"接口&#xff0c;帮助模型发现、理解并安全调用各种外部工…

    学习springboot-条件化配置@Conditional(条件注解)

    前言 在Spring Boot中&#xff0c;Conditional 注解及其相关注解是用于条件化配置的重要工具。它们允许开发者根据特定条件决定是否加载某个Bean或配置类。 注意&#xff1a;Conditional 相关注解&#xff0c;通常和Bean搭配使用 学习springboot-Bean管理&#xff08;Bean 注…

    2025年- H18-Lc126-54.螺旋矩阵(矩阵)---java版

    1.题目描述 2.思路* 思路1&#xff1a; 补充2&#xff1a; directions[1][0] // 表示“下”这个方向的行增量&#xff08;1&#xff09; directions[1][1] // 表示“下”这个方向的列增量&#xff08;0&#xff09; int[][] directions {{0, 1}, {1, 0}, {0, -1}, {-…

    微信小程序连续多个特殊字符自动换行解决方法

    效果图 .wxml <view class"container"><text>没转换{{text}}</text><view style"height: 60rpx;" /><text>转换后{{convert}}</text> </view>.js Page({data: {text:&#xff01;&#xff01;&#xff01;&am…

    编程速递-RAD Studio 12.3 Athens四月补丁:关注软件性能的开发者,安装此补丁十分必要

    2025年4月22日&#xff0c;Embarcadero发布了针对RAD Studio 12.3、Delphi 12.3以及CBuilder 12.3的四月补丁。此更新旨在提升这些产品的质量&#xff0c;特别关注于Delphi编译器、C 64位现代工具链、RAD Studio 64位IDE及其调试器、VCL库和其他RAD Studio特性。强烈建议所有使…

    Linux 进程基础(二):操作系统

    目录 一、什么是操作系统&#xff1a;用户和电脑之间的「翻译官」&#x1f310; OS 的层状结构&#x1f9e9; 案例解析&#xff1a;双击鼠标的「跨层之旅」 二、操作系统的必要性探究&#xff1a;缺乏操作系统的环境面临的挑战剖析&#x1f511; OS 的「管理者」属性&#xff1…

    第 11 届蓝桥杯 C++ 青少组中 / 高级组省赛 2020 年真题,选择题详细解释

    一、选择题 第 2 题 在二维数组按行优先存储的情况下&#xff0c;元素 a[i][j] 前的元素个数计算如下&#xff1a; 1. **前面的完整行**&#xff1a;共有 i 行&#xff0c;每行 n 个元素&#xff0c;总计 i * n 个元素。 2. **当前行的前面元素**&#xff1a;在行内&#x…

    1️⃣7️⃣three.js_OrbitControls相机控制器

    17、相机轨道控制器 3D虚拟工厂在线体验相机轨道控制器OrbitControls 它是 Three.js 中最常用的交互控制器之一,专门用于通过鼠标/触摸控制相机围绕一个目标点(target)旋转、缩放和平移。import {OrbitControls } from three/addons/controls/OrbitControls.js; const cont…

    以下是在 Ubuntu 上的几款PDF 阅读器,涵盖轻量级、功能丰富和特色工具:

    默认工具&#xff1a;Evince&#xff08;GNOME 文档查看器&#xff09; 特点&#xff1a;Ubuntu 预装&#xff0c;轻量快速&#xff0c;支持基本标注和书签。 安装&#xff1a;已预装&#xff0c;或手动安装&#xff1a; sudo apt install evince功能全面&#xff1a;Okular&…

    基于用户场景的汽车行驶工况构建:数据驱动下的能耗优化革命

    行业现状&#xff1a;标准工况与用户场景的割裂 全球汽车行业普遍采用WLTC工况进行能耗测试&#xff0c;但其与真实道路场景差异显著。据研究&#xff0c;WLTC工况下车辆能耗数据比实际道路低10%-30%&#xff0c;导致用户对续航虚标投诉激增&#xff08;数据来源&#xff1a;东…