当前位置: 首页 > news >正文

odoo16前端框架源码阅读——rpc_service.js

odoo16前端框架源码阅读——rpc_service.js

先介绍点背景知识,这样方便阅读代码。

一、 JSONRPC的规范

https://www.jsonrpc.org/specification

中文翻译版本:https://wiki.geekdream.com/Specification/json-rpc_2.0.html

JSON-RPC是一个无状态且轻量级的远程过程调用(RPC)协议。 本规范主要定义了一些数据结构及其相关的处理规则。它允许运行在基于socket,http等诸多不同消息传输环境的同一进程中。其使用JSON(RFC 4627)作为数据格式。

它为简单而生!

由于JSON-RPC使用JSON,它具有与其相同的类型系统(见http://www.json.org或RFC 4627)。JSON可以表示四个基本类型(String、Numbers、Booleans和Null)和两个结构化类型(Objects和Arrays)。 规范中,术语“Primitive”标记那4种原始类型,“Structured”标记两种结构化类型。任何时候文档涉及JSON数据类型,第一个字母都必须大写:Object,Array,String,Number,Boolean,Null。包括True和False也要大写。

1、请求对象

发送一个请求对象至服务端代表一个rpc调用, 一个请求对象包含下列成员:

jsonrpc

指定JSON-RPC协议版本的字符串,必须准确写为“2.0”

method

包含所要调用方法名称的字符串,以rpc开头的方法名,用英文句号(U+002E or ASCII 46)连接的为预留给rpc内部的方法名及扩展名,且不能在其他地方使用。

params

调用方法所需要的结构化参数值,该成员参数可以被省略。

id

已建立客户端的唯一标识id,值必须包含一个字符串、数值或NULL空值。如果不包含该成员则被认定为是一个通知。该值一般不为NULL[1],若为数值则不应该包含小数[2]。

服务端必须回答相同的值如果包含在响应对象。 这个成员用来两个对象之间的关联上下文。

没有包含“id”成员的请求对象为通知, 作为通知的请求对象表明客户端对相应的响应对象并不感兴趣

    const data = {id: rpcId,jsonrpc: "2.0",method: "call",params: params,};

2、响应对象

当发起一个rpc调用时,除通知之外,服务端都必须回复响应。响应表示为一个JSON对象,使用以下成员:

jsonrpc

指定JSON-RPC协议版本的字符串,必须准确写为“2.0”

result

该成员在成功时必须包含。

当调用方法引起错误时必须不包含该成员。

服务端中的被调用方法决定了该成员的值。

error

该成员在失败是必须包含。

当没有引起错误的时必须不包含该成员。

该成员参数值必须为5.1中定义的对象。

id

该成员必须包含。

该成员值必须于请求对象中的id成员值一致。

若在检查请求对象id时错误(例如参数错误或无效请求),则该值必须为空值。

响应对象必须包含result或error成员,但两个成员必须不能同时包含。

3、错误对象

当一个rpc调用遇到错误时,返回的响应对象必须包含错误成员参数,并且为带有下列成员参数的对象:

code

使用数值表示该异常的错误类型。 必须为整数。

message

对该错误的简单描述字符串。 该描述应尽量限定在简短的一句话。

data

包含关于错误附加信息的基本类型或结构化类型。该成员可忽略。 该成员值由服务端定义(例如详细的错误信息,嵌套的错误等)。

code	message	meaning
-32700	Parse error语法解析错误	服务端接收到无效的json。该错误发送于服务器尝试解析json文本
-32600	Invalid Request无效请求	发送的json不是一个有效的请求对象。
-32601	Method not found找不到方法	该方法不存在或无效
-32602	Invalid params无效的参数	无效的方法参数。
-32603	Internal error内部错误	JSON-RPC内部错误。
-32000 to -32099	Server error服务端错误	预留用于自定义的服务器错误。

二、rpc_service.js

路径:addons\web\static\src\core\network\rpc_service.js

1、引入相关模块, 新建了相关的Error类

导入了两个js模块, browser估计是跟浏览器相关

registry 是前端的注册表

然后是定义了四个Error继承自己标准类Error

  • RPCError
  • ConnectionLostError
  • ConnectionAbortedError
  • HTTPError
/** @odoo-module **/import { browser } from "../browser/browser";
import { registry } from "../registry";// -----------------------------------------------------------------------------
// Errors
// -----------------------------------------------------------------------------
export class RPCError extends Error {constructor() {super(...arguments);this.name = "RPC_ERROR";this.type = "server";this.code = null;this.data = null;this.exceptionName = null;this.subType = null;}
}export class ConnectionLostError extends Error {}export class ConnectionAbortedError extends Error {}export class HTTPError extends Error {}

2、Error对象

根据响应值来返回一个RPCError,这句结构赋值挺有意思

const { code, data: errorData, message, type: subType } = reponse;

我猜是吧reponse中的4个属性 code,data,message,type 分别赋值给了code,errorData,message,subType,有两个变量名称做了替换。有啥必要吗?

// -----------------------------------------------------------------------------
// Main RPC method
// -----------------------------------------------------------------------------
export function makeErrorFromResponse(reponse) {// Odoo returns error like this, in a error field instead of properly// using http error codes...const error = new RPCError();error.exceptionName = errorData.name;error.subType = subType;error.data = errorData;error.message = message;error.code = code;return error;
}

3、定义相关变量

5个输入参数

  1. env 前端环境,似乎在前端js代码中,直接可以使用这个对象。
  2. rpcId id, 为了跟响应对应起来,所以需要这个id
  3. url 地址
  4. params 参数值
  5. setting 默认是一个空对象
export function jsonrpc(env, rpcId, url, params, settings = {}) {const bus = env.bus;const XHR = browser.XMLHttpRequest;const data = {id: rpcId,jsonrpc: "2.0",method: "call",params: params,};const request = settings.xhr || new XHR();let rejectFn;

参数看着有点多,其实在后面封装成service的时候, 前两个参数是不用传的,只需要传递后面三个就行, 其实params和setting都可以不用传,必须要传的只有url。

bus: 总线, 这里要通过总线发送一些信号,rpc毕竟是远程调用,难免有意外情况发生,所以需要bus进行通信。

XHR: 浏览器发起request请求

data: 标准的jsonrpc数据格式

rejectFn, 这里值得一提,在这里声明这个变量,但是并没有赋值,是为了后面使用。

4、复习promise

promise是ES6引入的异步编程的新解决方案,语法上Promise是一个构造函数,用来封装异步操作并可以获取成功或失败的结果。

// new Promise 生成一个异步任务,参数是具体执行任务的函数,接收两个参数
// 一般叫resolve和reject都是函数,异步任务执行成功调用前者,否则调用后者
// 这两个方法将改变p对象的状态,同时给下一步处理传递数据
// 然后调用p.then  ,接收两个函数型参数,分别对应异步任务成功的回调和失败的回调
const p = new Promise(function(resolve,reject){setTimeout(function () {// let data="数据库中的用户数据";// resolve(data);let err="数据读取失败";reject(err);},1000);
})p.then(function(value){console.log(value);
},function(reason){console.error(reason);
})// 其实promise解决的也是回调地狱,嵌套过多的问题,将异步任务封装成对象了

其实,promise分了两步来完成,对象本身只是执行了一个异步任务,并没有处理异步返回的结果。 异步任务返回的结果是在p.then函数中处理的。
异步任务的返回值会影响promise对象本身的状态,会决定p.then中执行哪个回调函数。

另外,就是promise可以链式调用,执行串行的异步任务。

5、promise 中的Bus

在promise里面往总线里发送了很多消息

        if (!settings.silent) {bus.trigger("RPC:REQUEST", data.id);}

这里既然发了,那么就一定有地方接收,在odoo中搜索一下RPC:REQUEST

addons\web\static\src\webclient\loading_indicator\loading_indicator.js

这个文件代码不长,直接贴过来好了。注释中大概的意思是:

加载指示器:

当用户执行一个动作,最好是给他一些反馈说明当前有些事情正在发生。 这个指示器的作用就是在屏幕的右下角显示一个小的矩形框,里面有Loading字样,并且还有rpc的id。 3秒之后,如果rpc依然没有完成,我们将阻塞整个UI。 回头测试一下。

/** @odoo-module **/import { browser } from "@web/core/browser/browser";
import { registry } from "@web/core/registry";
import { useService } from "@web/core/utils/hooks";
import { Transition } from "@web/core/transition";import { Component, onWillDestroy, useState } from "@odoo/owl";/*** Loading Indicator** When the user performs an action, it is good to give him some feedback that* something is currently happening.  The purpose of the Loading Indicator is to* display a small rectangle on the bottom right of the screen with just the* text 'Loading' and the number of currently running rpcs.** After a delay of 3s, if a rpc is still not completed, we also block the UI.*/
export class LoadingIndicator extends Component {setup() {this.uiService = useService("ui");this.state = useState({count: 0,show: false,});this.rpcIds = new Set();this.shouldUnblock = false;this.startShowTimer = null;this.blockUITimer = null;this.env.bus.addEventListener("RPC:REQUEST", this.requestCall.bind(this));this.env.bus.addEventListener("RPC:RESPONSE", this.responseCall.bind(this));onWillDestroy(() => {this.env.bus.removeEventListener("RPC:REQUEST", this.requestCall.bind(this));this.env.bus.removeEventListener("RPC:RESPONSE", this.responseCall.bind(this));});}requestCall({ detail: rpcId }) {if (this.state.count === 0) {browser.clearTimeout(this.startShowTimer);this.startShowTimer = browser.setTimeout(() => {if (this.state.count) {this.state.show = true;this.blockUITimer = browser.setTimeout(() => {this.shouldUnblock = true;this.uiService.block();}, 3000);}}, 250);}this.rpcIds.add(rpcId);this.state.count++;}responseCall({ detail: rpcId }) {this.rpcIds.delete(rpcId);this.state.count = this.rpcIds.size;if (this.state.count === 0) {browser.clearTimeout(this.startShowTimer);browser.clearTimeout(this.blockUITimer);this.state.show = false;if (this.shouldUnblock) {this.uiService.unblock();this.shouldUnblock = false;}}}
}LoadingIndicator.template = "web.LoadingIndicator";
LoadingIndicator.components = { Transition };registry.category("main_components").add("LoadingIndicator", {Component: LoadingIndicator,
});

6、request 绑定load事件

request绑定了一个load事件,也就是请求返回的时候触发的操作

        request.addEventListener("load", () => {if (request.status === 502) {// If Odoo is behind another server (eg.: nginx)if (!settings.silent) {bus.trigger("RPC:RESPONSE", data.id);}reject(new ConnectionLostError());return;}let params;try {params = JSON.parse(request.response);} catch (_) {// the response isn't json parsable, which probably means that the rpc request could// not be handled by the server, e.g. PoolError('The Connection Pool Is Full')if (!settings.silent) {bus.trigger("RPC:RESPONSE", data.id);}return reject(new ConnectionLostError());const { error: responseError, result: responseResult } = params;if (!settings.silent) {bus.trigger("RPC:RESPONSE", data.id);}if (!responseError) {return resolve(responseResult);}const error = makeErrorFromResponse(responseError);reject(error);});

6.1、status = 502 是个什么鬼?

502 Bad Gateway错误是指代理或网关从上一个服务器接收到的响应无效或不完整。通常,这种情况发生在文件太大或处理速度太慢的高流量网站上。例如,当您访问一个具有高流量的网站时,您的请求将被发送到它的代理服务器。如果代理服务器在尝试访问网站时无法从上游服务器获取完整的响应,则会生成502错误代码。

502错误代码通常是由代理服务器、网关或负载均衡器等设备导致的,而不是由您的计算机或网络连接引起的。这意味着您只能为自己的网络连接做些有限的调整,但无法修复网关响应错误。

注释中也写的明白,502可能是因为使用了nginx反向代理,而 502错误是nginx和odoo通讯不佳造成的,这种情况rpc执行失败,

执行这一句

 reject(new ConnectionLostError());

6.2 解析返回值

如果返回的不是json格式的数据,也会触发错误

 try {params = JSON.parse(request.response);} catch (_) {return reject(new ConnectionLostError());}

catch 后面的这个下划线是什么鬼? 可能是并不关心发生了什么错误,只要解析错误,就调用reject

6.3 解构赋值

const { error: responseError, result: responseResult } = params;

根据jsonrpc规范, error和result 必须并且只能返回一个。

后面也做了判断

        if (!responseError) {return resolve(responseResult);}

如果没有错误,那就调用resolve,并返回。否则说明有错误发生,先生成一个error,然后调用reject

            const error = makeErrorFromResponse(responseError);reject(error);

7、request绑定 error事件

        request.addEventListener("error", () => {if (!settings.silent) {bus.trigger("RPC:RESPONSE", data.id);}reject(new ConnectionLostError());});

8、 该干正事了

        request.open("POST", url);request.setRequestHeader("Content-Type", "application/json");request.send(JSON.stringify(data));

三行代码:

1、用post方法请求的url,为什么不用get? 因为post更安全

2、指定了Content-Type为json, 这个很重要,如果不指定,服务器端不知道怎么解析数据

3、将data转成字符串并发送出去。(忙活半天,就是为了这句)

9、定义了promise.abort

Promise只有三种状态:pending、resolve、reject,一个异步的承诺一旦发出,经历等待(pending)后,最终只能为成功或者失败,中途无法取消(abort)。

这里定义promise.abort,注释也讲的明白,允许用户取消被忽略的rpc请求来接触对UI的阻塞并且不要显示错误。

        /*** @param {Boolean} rejectError Returns an error if true. Allows you to cancel*                  ignored rpc's in order to unblock the ui and not display an error.*/promise.abort = function (rejectError = true) {if (request.abort) {request.abort();}if (!settings.silent) {bus.trigger("RPC:RESPONSE", data.id);}if (rejectError) {rejectFn(new ConnectionAbortedError("XmlHttpRequestError abort"));}};

10、jsonrpc

这个函数就干了一件事,定义了一个promise对象来发送rpc请求,并把它返回。

export function jsonrpc(env, rpcId, url, params, settings = {}) {const bus = env.bus;const XHR = browser.XMLHttpRequest;const data = {id: rpcId,jsonrpc: "2.0",method: "call",params: params,};const request = settings.xhr || new XHR();let rejectFn;const promise = new Promise((resolve, reject) => {rejectFn = reject;// handle successrequest.addEventListener("load", () => {});// handle failurerequest.addEventListener("error", () => {});request.open("POST", url);request.setRequestHeader("Content-Type", "application/json");request.send(JSON.stringify(data));});promise.abort = function (rejectError = true) {};return promise;
}

11、定义RPC服务

这里对jsonrpc做了进一步封装,并且注册为服务,看来每个服务都有个start函数,而且将env作为参数传进去。

// -----------------------------------------------------------------------------
// RPC service
// -----------------------------------------------------------------------------
export const rpcService = {async: true,start(env) {let rpcId = 0;return function rpc(route, params = {}, settings) {return jsonrpc(env, rpcId++, route, params, settings);};},
};registry.category("services").add("rpc", rpcService);

附录: odoo16 rpc_service.js

/** @odoo-module **/import { browser } from "../browser/browser";
import { registry } from "../registry";// -----------------------------------------------------------------------------
// Errors
// -----------------------------------------------------------------------------
export class RPCError extends Error {constructor() {super(...arguments);this.name = "RPC_ERROR";this.type = "server";this.code = null;this.data = null;this.exceptionName = null;this.subType = null;}
}export class ConnectionLostError extends Error {}export class ConnectionAbortedError extends Error {}export class HTTPError extends Error {}// -----------------------------------------------------------------------------
// Main RPC method
// -----------------------------------------------------------------------------
export function makeErrorFromResponse(reponse) {// Odoo returns error like this, in a error field instead of properly// using http error codes...const { code, data: errorData, message, type: subType } = reponse;const error = new RPCError();error.exceptionName = errorData.name;error.subType = subType;error.data = errorData;error.message = message;error.code = code;return error;
}export function jsonrpc(env, rpcId, url, params, settings = {}) {const bus = env.bus;const XHR = browser.XMLHttpRequest;const data = {id: rpcId,jsonrpc: "2.0",method: "call",params: params,};const request = settings.xhr || new XHR();let rejectFn;const promise = new Promise((resolve, reject) => {rejectFn = reject;if (!settings.silent) {bus.trigger("RPC:REQUEST", data.id);}// handle successrequest.addEventListener("load", () => {if (request.status === 502) {// If Odoo is behind another server (eg.: nginx)if (!settings.silent) {bus.trigger("RPC:RESPONSE", data.id);}reject(new ConnectionLostError());return;}let params;try {params = JSON.parse(request.response);} catch (_) {// the response isn't json parsable, which probably means that the rpc request could// not be handled by the server, e.g. PoolError('The Connection Pool Is Full')if (!settings.silent) {bus.trigger("RPC:RESPONSE", data.id);}return reject(new ConnectionLostError());}const { error: responseError, result: responseResult } = params;if (!settings.silent) {bus.trigger("RPC:RESPONSE", data.id);}if (!responseError) {return resolve(responseResult);}const error = makeErrorFromResponse(responseError);reject(error);});// handle failurerequest.addEventListener("error", () => {if (!settings.silent) {bus.trigger("RPC:RESPONSE", data.id);}reject(new ConnectionLostError());});// configure and send requestrequest.open("POST", url);request.setRequestHeader("Content-Type", "application/json");request.send(JSON.stringify(data));});/*** @param {Boolean} rejectError Returns an error if true. Allows you to cancel*                  ignored rpc's in order to unblock the ui and not display an error.*/promise.abort = function (rejectError = true) {if (request.abort) {request.abort();}if (!settings.silent) {bus.trigger("RPC:RESPONSE", data.id);}if (rejectError) {rejectFn(new ConnectionAbortedError("XmlHttpRequestError abort"));}};return promise;
}// -----------------------------------------------------------------------------
// RPC service
// -----------------------------------------------------------------------------
export const rpcService = {async: true,start(env) {let rpcId = 0;return function rpc(route, params = {}, settings) {return jsonrpc(env, rpcId++, route, params, settings);};},
};registry.category("services").add("rpc", rpcService);

相关文章:

odoo16前端框架源码阅读——rpc_service.js

odoo16前端框架源码阅读——rpc_service.js 先介绍点背景知识,这样方便阅读代码。 一、 JSONRPC的规范 https://www.jsonrpc.org/specification 中文翻译版本:https://wiki.geekdream.com/Specification/json-rpc_2.0.html JSON-RPC是一个无状态且轻…...

Nat. Med. | 成年人的城市生活环境对心理健康的影响

今天为大家介绍的是来自Jiayuan Xu和Gunter Schumann团队的一篇论文。城市居民暴露于许多可能相互结合和相互作用的环境因素,这些因素可能影响心理健康。目前尚未有工作尝试建模城市生活的复杂实际暴露与大脑和心理健康之间的关系,以及这如何受遗传因素调…...

stm32 WIFI模块_8266使用

使用以上配置可以正常回应,其中无论勾选或者不勾选DTR/RTS都可以得到正常回应 ATCWMODE?表示查询当前WiFi状态是处于热点模式(AP模式)或者是连接其他WiFi的那个模式。通过图片看出这个符号不能省略。 设置AP热点命令格式:ATCWSAP…...

【C/C++】malloc 或者 new 动态分配内存

1. malloc 是一个在 C 语言中用于动态分配内存的函数。 通过 malloc 函数,我们可以在程序运行时请求一定大小的内存块,然后将该内存块用于存储数据。 malloc 函数的声明如下: void* malloc(size_t size);它接受一个参数 size,表…...

如果让你重新开始学 C/C++,你的学习路线会是怎么选择?

1. 第一阶段 学好 C 语言和 Linux 1.1 学好 C 语言 无论你是科班还是非科班,建议你一定要学好 C 语言,它应该作为你必须掌握好的语言。你要熟悉 C 语言的基本语法,包括: 顺序、条件、循环三大控制语句 C 中几大基元数据类型的用…...

PCL安装与使用

1 apt安装 ubuntu20.04及以上版本下可以直接通过apt方式安装pcl编译好的二进制文件,二进制安装的版本为1.10。 sudo apt update sudo apt install libpcl-dev 2 源码安装 在pcl的github上下载对应的版本进行安装: https://github.com/PointCloudLibrary/pcl/rel…...

力扣刷题-二叉树-对称二叉树

101 对称二叉树 给你一个二叉树的根节点 root , 检查它是否轴对称。 示例 1: 输入:root [1,2,2,3,4,4,3] 输出:true 示例 2: 输入:root [1,2,2,null,3,null,3] 输出:false 思路 我的思路…...

常见面试题-计算机网络相关

1.OSI 七层模型? OSI 七层模型:应用层、表示层、会话层、传输层、网络层、数据链路层、物理层 TCP/IP 五层模型:应用层、传输层、网络层、链路层、物理层 应用层 应用层是由网络应用程序使用的,是离用户最近的一层 应用层通过…...

leetcode做题笔记231. 2 的幂

给你一个整数 n,请你判断该整数是否是 2 的幂次方。如果是,返回 true ;否则,返回 false 。 如果存在一个整数 x 使得 n 2x ,则认为 n 是 2 的幂次方。 示例 1: 输入:n 1 输出:tr…...

AI主播“败走”双11,想用AI省成本的商家醒醒吧,程序员不必担心失业,发展空间依旧很大

目录 1 2 3 “AI人”并不算是新鲜事,随着AI的发展,AI主播也开始悄悄进入到直播间中。 持续无间断的直播、比人工费便宜等优势,让很多商家选择了AI主播。 AI主播到底好不好用?终于在今年“双11”现出了原形。 1 AI主播没火过半年…...

◢Django 自写分页与使用

目录 1、设置分页样式,并展示到浏览器 2、模拟页码 3、生成分页 4、数据显示 5、上一页下一页 6、数据库的数据分页 7、封装分页 8、使用封装好的分页 建立好app后,设置路径path(in2/,views.in2),视图def in2(request): ,HTML: in2.html…...

某城高速综合管控大数据大屏可视化【可视化项目案例-04】

🎉🎊🎉 你的技术旅程将在这里启航! 🚀🚀 本文选自专栏:可视化技术专栏100例 可视化技术专栏100例,包括但不限于大屏可视化、图表可视化等等。订阅专栏用户在文章底部可下载对应案例源码以供大家深入的学习研究。 🎓 每一个案例都会提供完整代码和详细的讲解,不…...

如何在Linux下进行文件查看

cat 文本内容显示到终端 head 查看文件开头 tail 查看文件结尾 常用参数 -f 文件内容更新后,显示信息同步更新 wc 统计文件内容信息...

OSG练习:模仿Ventsim制作三维矿井智能通风系统

1、效果 2、计划内容 1) 三维场景的加载显示;已实现 2)矿井巷道建模及纹理;已实现 3)矿井基础数据采集及修正;已实现 4)通风网络解算算法;已实现 5)通风设备及设施模型制作;未实现 6)风流模拟效果 ;进行中 7)火灾模拟效果;未实现 8)巷道属性查看栏;未实现 9)…...

【数据结构】非递归实现二叉树的前 + 中 + 后 + 层序遍历(听说面试会考?)

👦个人主页:Weraphael ✍🏻作者简介:目前学习C和算法 ✈️专栏:数据结构 🐋 希望大家多多支持,咱一起进步!😁 如果文章对你有帮助的话 欢迎 评论💬 点赞&…...

32 Feign性能优化

2.3.Feign使用优化 Feign底层发起http请求,依赖于其它的框架。其底层客户端实现包括: •URLConnection:默认实现,不支持连接池 •Apache HttpClient :支持连接池 •OKHttp:支持连接池 因此提高Feign的…...

星岛专栏|从Web3发展看金融与科技的融合之道

11月起,欧科云链与香港主流媒体星岛集团开设Web3.0安全技术专栏,该专栏主要面向香港从业者、交易机构、监管机构输出专业性的安全合规建议,旨在促进香港Web3.0行业向安全与合规发展。 出品|欧科云链研究院 自2016年首届香港金融…...

什么是网络爬虫?

网络爬虫是一种自动化程序,可以自动地浏览网站并从网站上抽取数据。APP数据抓取实际上也是运用了网络爬虫的技术,只不过抓取的对象不是网站上的信息,而是手机APP上的数据。下面详细介绍APP数据抓取的过程。 1、确定数据需求 首先需要明确要抓…...

酷柚易汛ERP - 商品库存余额表操作指南

1、应用场景 商品库存余额表用于查询商品在各仓库的实际结存量、单位成本以及成本等明细。 2、主要操作 打开【仓库】-【商品库存余额表】,可筛选仓库、商品、商品类别,导出/打印等操作见【销货单】不再赘述。 3、分享操作 库存余额分享,…...

第27期 | GPTSecurity周报

GPTSecurity是一个涵盖了前沿学术研究和实践经验分享的社区,集成了生成预训练Transformer(GPT)、人工智能生成内容(AIGC)以及大型语言模型(LLM)等安全领域应用的知识。在这里,您可以…...

基于算法竞赛的c++编程(28)结构体的进阶应用

结构体的嵌套与复杂数据组织 在C中,结构体可以嵌套使用,形成更复杂的数据结构。例如,可以通过嵌套结构体描述多层级数据关系: struct Address {string city;string street;int zipCode; };struct Employee {string name;int id;…...

Android Wi-Fi 连接失败日志分析

1. Android wifi 关键日志总结 (1) Wi-Fi 断开 (CTRL-EVENT-DISCONNECTED reason3) 日志相关部分: 06-05 10:48:40.987 943 943 I wpa_supplicant: wlan0: CTRL-EVENT-DISCONNECTED bssid44:9b:c1:57:a8:90 reason3 locally_generated1解析: CTR…...

springboot 百货中心供应链管理系统小程序

一、前言 随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱,百货中心供应链管理系统被用户普遍使用,为方…...

K8S认证|CKS题库+答案| 11. AppArmor

目录 11. AppArmor 免费获取并激活 CKA_v1.31_模拟系统 题目 开始操作: 1)、切换集群 2)、切换节点 3)、切换到 apparmor 的目录 4)、执行 apparmor 策略模块 5)、修改 pod 文件 6)、…...

Admin.Net中的消息通信SignalR解释

定义集线器接口 IOnlineUserHub public interface IOnlineUserHub {/// 在线用户列表Task OnlineUserList(OnlineUserList context);/// 强制下线Task ForceOffline(object context);/// 发布站内消息Task PublicNotice(SysNotice context);/// 接收消息Task ReceiveMessage(…...

基于uniapp+WebSocket实现聊天对话、消息监听、消息推送、聊天室等功能,多端兼容

基于 ​UniApp + WebSocket​实现多端兼容的实时通讯系统,涵盖WebSocket连接建立、消息收发机制、多端兼容性配置、消息实时监听等功能,适配​微信小程序、H5、Android、iOS等终端 目录 技术选型分析WebSocket协议优势UniApp跨平台特性WebSocket 基础实现连接管理消息收发连接…...

1688商品列表API与其他数据源的对接思路

将1688商品列表API与其他数据源对接时,需结合业务场景设计数据流转链路,重点关注数据格式兼容性、接口调用频率控制及数据一致性维护。以下是具体对接思路及关键技术点: 一、核心对接场景与目标 商品数据同步 场景:将1688商品信息…...

postgresql|数据库|只读用户的创建和删除(备忘)

CREATE USER read_only WITH PASSWORD 密码 -- 连接到xxx数据库 \c xxx -- 授予对xxx数据库的只读权限 GRANT CONNECT ON DATABASE xxx TO read_only; GRANT USAGE ON SCHEMA public TO read_only; GRANT SELECT ON ALL TABLES IN SCHEMA public TO read_only; GRANT EXECUTE O…...

高危文件识别的常用算法:原理、应用与企业场景

高危文件识别的常用算法:原理、应用与企业场景 高危文件识别旨在检测可能导致安全威胁的文件,如包含恶意代码、敏感数据或欺诈内容的文档,在企业协同办公环境中(如Teams、Google Workspace)尤为重要。结合大模型技术&…...

解决本地部署 SmolVLM2 大语言模型运行 flash-attn 报错

出现的问题 安装 flash-attn 会一直卡在 build 那一步或者运行报错 解决办法 是因为你安装的 flash-attn 版本没有对应上,所以报错,到 https://github.com/Dao-AILab/flash-attention/releases 下载对应版本,cu、torch、cp 的版本一定要对…...