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

自定义基座实时采集uniapp日志

自定义基座实时采集uniapp日志

打测试包给远端现场(测试/客户)实际测试时也能实时看到日志了,也有代码行数显示。
收集的日志

流程设计

重写console.log方法
Websocket
Websocket
uniapp收集日志
通过插件或Native.js传输
安卓基座接收
后台服务接收保存
前端html页面渲染

uniapp收集代码

重写console方法

通过条件编译,在app使用环境重写日志打印方法

 // #ifdef APP-PLUS=function(...args){console.log = function (...args) {try {_this.$plugins.getUtils("consoleLog", {'level': 'log', 'args': args})} catch (e) {console.info('console.log 打印失败', e);}}console.error = function (...args) {try {_this.$plugins.getUtils("consoleLog", {'level': 'error', 'args': args})} catch (e) {console.info('console.error 打印失败', e);}}console.warn = function (...args) {try {_this.$plugins.getUtils("consoleLog", {'level': 'warn', 'args': args})} catch (e) {console.info('console.warn 打印失败', e);}}// #endif

发送给安卓层

/*** 快捷调用安卓工具类方法* this.$plugins.getUtils('method',{userId:'test'})* @param {Object} method* @param {Object} jsonObject* @param {Object} successCallback* @param {Object} errorCallback* @return {String} 原始字符串,如果是json化返回的就是一个json字符串 不是对象!!!*/
getUtils: function(method, jsonObject, successCallback, errorCallback) {try {var success = typeof successCallback !== 'function' ? null : function(args) {successCallback(args);},fail = typeof errorCallback !== 'function' ? null : function(code) {errorCallback(code);};var callbackID = plus.bridge.callbackId(success, fail);return plus.bridge.exec(_BARCODE, "getUtils", [callbackID, method, jsonObject]);} catch (e) {console.error(e)errorCallback(e)}},//初始化方法,一般是登录后调用
_this.$plugins.getUtils("initConsoleLog", {'userId': _this.GLOBAL.$USER_INFO.user_iidd})

安卓自定义基座收集日志

跳转方法

/*** 工具类获取** @param pWebview* @param array* @return*/public void getUtils(IWebview pWebview, JSONArray array) {Log.i("getUtils", "工具类获取" + array.toString());String result = null;String CallBackID = array.optString(0);try {//方法String method = array.optString(1);JSONObject json = new JSONObject(array.optString(2));result = this.utilMethood(method, json, pWebview);} catch (Exception e) {e.printStackTrace();JSUtil.execCallback(pWebview, CallBackID, e.getMessage(), JSUtil.ERROR, false);}Log.i("getUtils", "工具类返回信息:\n" + result);JSUtil.execCallback(pWebview, CallBackID, result, JSUtil.OK, true);}

初始化日志信息方法

/*** WebSocket调试信息推送客户端*/
private PushConsoleWebSocketClient pushConsoleWebSocketClient = null;/*** 初始化推送*/
public static boolean pushLogInit = false;/*** 调试日志地址*/
public static String LOG_WS_URL = "ws://127.0.0.1:5080/weblog/uniapplogv2/";/*** 调试id*/
public static String LOG_WS_USERID = null;/*** 初始化日志信息** @param params* @param pWebview* @return*/
private String initConsoleLog(JSONObject params, IWebview pWebview) {LOG_WS_USERID = params.optString("userId");Log.i(TAG, "uniapp层初始化日志信息: " + LOG_WS_USERID);if (null != LOG_WS_USERID && !"".equals(LOG_WS_USERID)) {try {new Thread(new Runnable() {@Overridepublic void run() {try {pushConsoleWebSocketClient = PushConsoleWebSocketClient.builder(LOG_WS_URL, "系统名称", LOG_WS_USERID);pushConsoleWebSocketClient.connect();pushLogInit = true;} catch (Exception e) {e.printStackTrace();}}}).start();} catch (Exception e) {Log.e(TAG, "initConsoleLog: 初始化调试信息推送服务异常", e);}}return ResultUtil.ok("日志初始化完毕");
}

推送日志调试信息方法

/*** 推送日志信息到调试页面** @param log   日志内容* @param level 日志等级*/
private void pushLogToCache(String level, JSONArray log) {Thread thread = new Thread(new Runnable() {@Overridepublic void run() {try {com.alibaba.fastjson.JSONObject params = new com.alibaba.fastjson.JSONObject();params.put("code", "push");params.put("sys", pushConsoleWebSocketClient.getSys());params.put("userId", pushConsoleWebSocketClient.getUserId());params.put("level", level);params.put("timestamp", System.currentTimeMillis());params.put("time", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date()));try {params.put("log", parseUniappConsoleLog(log));} catch (Exception e) {params.put("log", log);}pushConsoleWebSocketClient.send(params.toString());} catch (Exception e) {e.printStackTrace();}}});thread.start();
//        executorService.submit(thread);
}

安装自定义基座

安卓WebSocket客户端

安卓WebSocket客户端推送负责将调试日志推送给后端

gradle依赖

//WebSocket连接
implementation 'org.java-websocket:Java-WebSocket:1.5.3'

import android.util.Log;import com.inspur.mobilefsp.plugins.WfmPlugin;import org.java_websocket.client.WebSocketClient;
import org.java_websocket.handshake.ServerHandshake;
import org.json.JSONObject;import java.net.URI;
import java.net.URISyntaxException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.nio.charset.Charset;/*** WebSocket客户端类,用于与服务器建立WebSocket连接并处理消息。* 该类实现了WebSocketClient接口,并提供了连接、消息处理和错误处理的功能。** @author 淡梦如烟* @date 20250211*/
public class PushConsoleWebSocketClient extends WebSocketClient {/*** 日志标签,用于标识日志输出的来源*/public final static String TAG = "PushLogClient";/*** WebSocket服务器的URL*/private String url;/*** 系统名称*/private String sys;/*** 用户ID*/private String userId;/*** 构造函数,初始化WebSocket客户端** @param serverUrl WebSocket服务器的URL* @param sys       系统名称* @param userId    用户ID* @throws URISyntaxException 如果提供的URL格式不正确*/public PushConsoleWebSocketClient(String serverUrl, String urlParams, String sys, String userId) throws URISyntaxException {super(new URI(serverUrl + urlParams));this.url = serverUrl;this.sys = sys;this.userId = userId;}/*** 建造者生成客户端** @param serverUrl* @param sys* @param userId* @return*/public static PushConsoleWebSocketClient builder(String serverUrl, String sys, String userId) {try {//自定义参数,自行实现JSONObject json = new JSONObject();json.put("code", "pushStart");json.put("userId", userId);json.put("sys", sys);JSONObject password = new JSONObject();password.put("userId", userId);password.put("timestamp", System.currentTimeMillis());//aes加密 ,自行实现或者用第三方包String encode = QEncodeUtil.aesEncrypt(json.toString(), "aes秘钥2");encode = URLEncoder.encode(encode, "UTF-8");//百分号不能作为参数encode = encode.replaceAll("%", "BaiFenHao");String url = serverUrl + encode;Log.e(TAG, "builder: websocket地址:" + url);PushConsoleWebSocketClient pushConsoleWebSocketClient = new PushConsoleWebSocketClient(serverUrl, encode, sys, userId);return pushConsoleWebSocketClient;} catch (Exception e) {throw new RuntimeException(e);}}/*** 获取WebSocket服务器的URL** @return WebSocket服务器的URL*/public String getUrl() {return url;}/*** 获取系统名称** @return 系统名称*/public String getSys() {return sys;}/*** 获取用户ID** @return 用户ID*/public String getUserId() {return userId;}/*** 当WebSocket连接成功建立时调用** @param handshake 握手信息*/@Overridepublic void onOpen(ServerHandshake handshake) {// WebSocket连接已成功建立// 在此执行任何必要的操作Log.i(TAG, "onOpen: " + handshake.getHttpStatus());WfmPlugin.pushLogInit = true;}/*** 当接收到来自服务器的消息时调用** @param message 收到的消息内容*/@Overridepublic void onMessage(String message) {// 处理来自服务器的传入消息Log.i(TAG, "onMessage: " + message);}/*** 当WebSocket连接关闭时调用** @param code   关闭状态码* @param reason 关闭原因* @param remote 是否由远程服务器关闭*/@Overridepublic void onClose(int code, String reason, boolean remote) {Log.e(TAG, "onClose: code[" + code + "];remote[" + remote + "];url[" + this.url + "];reason:" + reason);// WebSocket连接已关闭// 在此执行任何必要的清理操作
//        this.reconnectAfterMillis(100L);}/*** 重连锁*/private static boolean reConnectLock = false;/*** 延迟重连** @param millis*/public void reconnectAfterMillis(Long millis) {try {if (reConnectLock) {return;}reConnectLock = true;new Thread(new Runnable() {@Overridepublic void run() {try {// 尝试在5秒后重新连接Thread.sleep(millis);reconnect();} catch (Exception e) {e.printStackTrace();} finally {reConnectLock = false;}}}).start();} catch (Exception e) {e.printStackTrace();reConnectLock = false;} finally {}}/*** 当WebSocket连接期间发生错误时调用** @param ex 发生的异常*/@Overridepublic void onError(Exception ex) {Log.e(TAG, "onError: ", ex);// 处理WebSocket连接期间发生的任何错误
//        this.reconnectAfterMillis(5000L);}
}

后台代码

springboot接受日志和推送日志

package com.faker.weblog.websocket;import cn.hutool.core.net.URLDecoder;
import com.alibaba.fastjson2.JSONObject;
import com.faker.weblog.model.dto.PushUniappLogDto;
import com.faker.weblog.util.Toolkit;
import com.faker.weblog.wrapper.WrapMapper;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiModelProperty;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;@Lazy
@Component
@Slf4j
@ServerEndpoint("/uniapplogv2/{id}")
@Api(value = "websocket日志接受和推送uniapp日志工具")
public class UniappLogWebHandleV2 {@ApiModelProperty(value = "客户端id")private String id;@ApiModelProperty(value = "是否初始化", example = "true")private boolean initialized = false;@ApiModelProperty(value = "是否接受日志消息", example = "true")private boolean isPullLogs = false;@ApiModelProperty(value = "系统名称", example = "fakerSys")private String sys;@ApiModelProperty(value = "用户id", example = "test")private String userId;/*** 日志列表*/private static ConcurrentHashMap<String, ConcurrentHashMap<String, List<String>>> logListMap = new ConcurrentHashMap<>();/*** 获取日志列表** @return*/public ConcurrentHashMap<String, ConcurrentHashMap<String, List<String>>> getlogListMap() {return logListMap;}/*** 清理日志列表*/public static void cleanLogListMap() {logListMap.clear();}/*** concurrent包的线程安全Map,用来存放每个客户端对应的MyWebSocket对象。*/private static ConcurrentHashMap<String, UniappLogWebHandleV2> webSocketMap = new ConcurrentHashMap<String, UniappLogWebHandleV2>();/*** websocket的session*/private Session session;/*** 获取session** @return*/public Session getSession() {return this.session;}/*** 新的WebSocket请求开启*/@OnOpenpublic void onOpen(Session session, @PathParam("id") String id) {log.info("新的WebSocket请求开启:" + id);try {String decode = id.replaceAll("BaiFenHao", "%");decode = URLDecoder.decode(decode, Charset.forName("UTF-8"));String aesJson = com.faker.dba.util.QEncodeUtil.aesDecrypt(decode, "aes秘钥2");JSONObject jsonObject = JSONObject.parseObject(aesJson);String userId = jsonObject.getString("userId");String password = jsonObject.getString("password");String sign = jsonObject.getString("sign");if (jsonObject.get("isPullLogs") != null) {this.isPullLogs = jsonObject.getBoolean("isPullLogs");}this.sys = jsonObject.getString("sys");this.userId = userId;this.session = session;//鉴权方法,自行实现this.validate(userId, sign, password);this.id = id;webSocketMap.put(id, this);String code = jsonObject.getString("code");if ("pushStart".equalsIgnoreCase(code)) {//app推送方法if (thisLististMap == null) {thisLististMap = new ConcurrentHashMap<>();logListMap.put(this.sys, thisLististMap);}List<String> logList = thisLististMap.get(this.userId);if (logList == null) {logList = new ArrayList<>();thisLististMap.put(this.userId, logList);}} else if ("webStart".equalsIgnoreCase(code)) {//pc端查看日志方法this.isPullLogs = true;this.sys = jsonObject.getString("watchSys");this.userId = jsonObject.getString("watchUserId");ConcurrentHashMap<String, List<String>> thisLististMap = logListMap.get(this.sys);if (thisLististMap != null) {List<String> logList = thisLististMap.get(this.userId);if (logList != null) {for (String log : logList) {try {session.getBasicRemote().sendText(log);} catch (IOException e) {e.printStackTrace();}}}}}} catch (Exception e) {log.error("鉴权错误:" + id, e);}}/*** WebSocket 请求关闭*/@OnClosepublic void onClose() {// 从set中删除log.info("WebSocket请求关闭:" + id);webSocketMap.remove(id);}/*** 发生异常*/@OnErrorpublic void onErro(Throwable throwable) {throwable.printStackTrace();}/*** 收到客户端消息后调用的方法** @param message 客户端发送过来的消息*/@OnMessagepublic void onMessage(String message, Session session) throws IOException {log.debug("websocket来自客户端的消息:{}", message);JSONObject jsonObject = JSONObject.parseObject(message);String code = jsonObject.getString("code");if (this.initialized) {if ("push".equalsIgnoreCase(code)) {PushUniappLogDto params = JSONObject.parseObject(message, PushUniappLogDto.class);if (Toolkit.isNullOrEmpty(params.getSys())) {log.warn("系统名称不能为空");return;}if (Toolkit.isNullOrEmpty(params.getUserId())) {log.warn("用户id不能为空");return;}if (Toolkit.isNullOrEmpty(params.getLevel())) {log.warn("日志等级不能为空");return;}if (Toolkit.isNullOrEmpty(params.getLog()) || "[]".equals(params.getLog())) {log.warn("日志信息不能为空");return;}this.sendLogs(JSONObject.toJSONString(params));}} else {log.warn("[" + this.sys + "][" + this.userId + "]未初始化" + this.initialized);}}/*** token鉴权** @param userId* @param sign* @param password* @throws IOException*/public void validate(String userId, String sign, String password) throws IOException {if (Toolkit.isNotNull(userId) && Toolkit.isNotNull(sign)) {//校验userId和密码 这里简化为校验userId和时间戳的aes加密信息,校验通过初始化连接try {String aesJson = com.faker.dba.util.QEncodeUtil.aesDecrypt(sign, "aes秘钥1");JSONObject aesJsonObject = JSONObject.parseObject(aesJson);if (aesJsonObject.get("userId") == null || aesJsonObject.get("timestamp") == null) {session.getBasicRemote().sendText("加密信息校验错误,已记录!" + "<br>" + aesJson + "<br>");session.close();}if (userId.equals(aesJsonObject.getString("userId"))) {if (aesJsonObject.getLong("timestamp") > System.currentTimeMillis() - 1000 * 60 * 5|| aesJsonObject.getLong("timestamp") < System.currentTimeMillis() + 1000 * 60 * 5) {this.initialized = true;session.getBasicRemote().sendText(JSONObject.toJSONString(WrapMapper.ok("签名[" + sign + "]正确,已记录!")));} else {session.getBasicRemote().sendText(JSONObject.toJSONString(WrapMapper.error("签名[" + sign + "]已过期,已记录!")));session.close();}} else {session.getBasicRemote().sendText(JSONObject.toJSONString(WrapMapper.error("签名[" + sign + "]错误,已记录!")));session.close();}} catch (Exception e) {log.error("加密信息[" + password + "]校验错误", e);session.getBasicRemote().sendText(JSONObject.toJSONString(WrapMapper.error("加密信息校验错误,已记录!" + "<br>" + e.getMessage())));session.close();}} else if (Toolkit.isNotNull(userId) && Toolkit.isNotNull(password)) {//todo 校验登录密码} else {log.error("登录信息错误[" + userId + "][" + password + "][" + sign + "]");session.getBasicRemote().sendText(JSONObject.toJSONString(WrapMapper.error("登录信息错误,已记录!")));session.close();}}/*** 向客户端发送消息** @param message*/public void sendLogs(String message) {ConcurrentHashMap<String, List<String>> thisLististMap = logListMap.get(this.sys);if (thisLististMap == null) {thisLististMap = new ConcurrentHashMap<>();}List<String> logList = thisLististMap.get(this.userId);if (logList == null) {logList = new ArrayList<>();}logList.add(message);//日志暂存最新的100条if (logList.size() > 100) {logList.remove(0);}this.sendToUser(message);}/*** 向指定客户端发送消息** @param message*/private void sendToUser(String message) {for (UniappLogWebHandleV2 webSocket : webSocketMap.values()) {if (webSocket.isInitialized() && webSocket.isPullLogs() && webSocket.getSys().equals(this.sys) && webSocket.getUserId().equals(this.userId)) {log.debug("【websocket消息】广播消息, message={}", message);try {Session session = webSocket.getSession();session.getBasicRemote().sendText(message);} catch (Exception e) {log.error("【websocket消息】广播消息, message={}", message);}}}}/*** 向所有客户端发送消息** @param message*/public void sendToAll(String message) {for (UniappLogWebHandleV2 webSocket : webSocketMap.values()) {if (!webSocket.isInitialized()) {continue;}log.debug("【websocket消息】广播消息, message={}", message);try {Session session = webSocket.getSession();session.getBasicRemote().sendText(message);} catch (Exception e) {log.error("【websocket消息】广播消息, message={}", message);}}}public String getId() {return id;}public String getSys() {return sys;}public String getUserId() {return userId;}public boolean isInitialized() {return initialized;}public boolean isPullLogs() {return isPullLogs;}
}

html页面渲染日志

const id = JSON.stringify({code: 'webStart',userId: userToken.userId,sign: userToken.token,isPullLogs: true,watchSys: $('#sys').val(),watchUserId: $('#userId').val()
})
//aes加密
const aes = aesEncrypt(id)
//替换百分号
const url = encodeURIComponent(aes).replaceAll('%', 'BaiFenHao')
console.log('[信息]传输协议秘钥', id, aes, url)
// 指定websocket路径
var wsUrl = 'ws://' + location.host + '/weblog/uniapplogv2/' + url;try {if (null != websocket && undefined != websocket) {websocket.close();}
} catch (e) {console.warn(e)
}
websocket = new WebSocket(wsUrl);
websocket.onmessage = function (event) {// 接收服务端的实时日志并添加到HTML页面中$("#log-container div").append(showColorLog(event.data));$("#log-container div").append('<br/>')if (localStorage.autoJump == '是') {// 滚动条滚动到最低部$("#log-container").scrollTop($("#log-container div").height() - $("#log-container").height());}
};
websocket.onopen = function (event) {reloadLock = false;
}websocket.onerror = function (error) {console.log('onerror', error)// $("#log-container div").append('<br/><br/>连接已断开...    5秒后尝试重新连接........ <br/><br/>');// setTimeout(reloadWebSocket(), 5000)
}
websocket.onclose = function (event) {console.log('onclose', event)$("#log-container div").append('<br/><br/>连接已关闭...    5秒后尝试重新连接........ <br/><br/>');setTimeout(reloadWebSocket, 5000)
}

总结

给远程调试提供方便,websocket推送消耗较少,也是有序推送,完善好重连机制比post提交更方便查看。

相关文章:

自定义基座实时采集uniapp日志

自定义基座实时采集uniapp日志 打测试包给远端现场(测试/客户)实际测试时也能实时看到日志了&#xff0c;也有代码行数显示。 流程设计 #mermaid-svg-1I5W9r1DU4xUsaTF {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid…...

基于YALMIP和cplex工具箱的微电网最优调度算法matlab仿真

目录 1.课题概述 2.系统仿真结果 3.核心程序与模型 4.系统原理简介 4.1 系统建模 4.2 YALMIP工具箱 4.3 CPLEX工具箱 5.完整工程文件 1.课题概述 基于YALMIP和cplex工具箱的微电网最优调度算法matlab仿真。通过YALMIP和cplex这两个工具箱&#xff0c;完成微电网的最优调…...

Effective Objective-C 2.0 读书笔记——内存管理(上)

Effective Objective-C 2.0 读书笔记——内存管理&#xff08;上&#xff09; 文章目录 Effective Objective-C 2.0 读书笔记——内存管理&#xff08;上&#xff09;引用计数属性存取方法中的内存管理autorelease保留环 ARCARC必须遵循的方法命名原则ARC 的自动优化&#xff1…...

蓝桥杯-洛谷刷题-day5(C++)(为未完成)

1.P1328 [NOIP2014 提高组] 生活大爆炸版石头剪刀布 i.题目 ii.代码 #include <iostream> #include <string> using namespace std;int N, Na, Nb; //0-"剪刀", 1-"石头", 2-"布", 3-"蜥", 4-"斯"&#xff1…...

conda 修复 libstdc++.so.6: version `GLIBCXX_3.4.30‘ not found 简便方法

ImportError: /data/home/hum/anaconda3/envs/ipc/bin/../lib/libstdc.so.6: version GLIBCXX_3.4.30 not found (required by /home/hum/anaconda3/envs/ipc/lib/python3.11/site-packages/paddle/base/libpaddle.so) 1. 检查版本 strings /data/home/hum/anaconda3/envs/ipc/…...

数据结构之队列,哈希表

一 队列(先进先出) 1.定义&#xff1a;从一端进行数据插入&#xff0c;另一端进行删除的线性存储结构 队列类型 常见操作 - 入队&#xff08;Enqueue&#xff09;&#xff1a;将新元素添加到队列的尾部。若队列有空间&#xff0c;新元素会成为队列的新尾部元素&#xff1b;若…...

讯方·智汇云校华为授权培训机构的介绍

官方授权 华为授权培训服务伙伴&#xff08;Huawei Authorized Learning Partner&#xff0c;简称HALP&#xff09;是获得华为授权&#xff0c;面向公众&#xff08;主要为华为企业业务的伙伴/客户&#xff09;提供与华为产品和技术相关的培训服务&#xff0c;培养华为产业链所…...

【16届蓝桥杯寒假刷题营】第1期DAY4

1.披萨和西蓝花 - 蓝桥云课 1. 披萨和西蓝花 问题描述 在接下来的 N 天里&#xff08;编号从 1 到 N&#xff09;&#xff0c;坤坤计划烹饪披萨或西兰花。他写下一个长度为 N 的字符串 A&#xff0c;对于每个有效的 i&#xff0c;如果字符 Ai 是 1&#xff0c;那么他将在第 i…...

【Linux】cron计划任务定时执行命令

在Linux系统中&#xff0c;crontab 是一种用于设置周期性执行任务的工具&#xff0c;通过编辑 crontab 文件&#xff0c;用户可以指定在特定时间自动运行命令或脚本。以下是关于 crontab 的详细介绍&#xff1a; 1. crontab 基本结构 每个 crontab 任务由一行配置组成&#xf…...

rdian是一个结构体,pdian=^Rdian,list泛型做什么用?

不明白不让编译的原因&#xff0c;记录下之遇到注意原油。 var mylist:TList<string>; mylist1:TList<Pdian>; mydian:Pdian; i:Integer; mylist2:TList<Rdian>; mydian2:rdian; arr:array of Rdian; begin mylist:TList…...

【05】RUST错误处理

文章目录 错误处理panic代码运行ResutResult中的一些方法介绍传播错误`?`运算符错误处理 建议是尽量用Result由调用者自行决定是否恢复,不恢复也可直接在Err中调用panic。代码分支不可能走的分支可panic。 需要panic的情况: 有害状态:当一些假设、保证、协议或不可变性被打…...

WinForm 防破解、反编译设计文档

一、引言 1.1 文档目的 本设计文档旨在阐述 WinForm 应用程序防破解、反编译的设计方案&#xff0c;为开发团队提供详细的技术指导&#xff0c;确保软件的知识产权和商业利益得到有效保护。 1.2 背景 随着软件行业的发展&#xff0c;软件破解和反编译现象日益严重。WinForm…...

1 推荐系统概述

推荐系统概述 1 推荐系统的意义平台方信息生产者&#xff08;物品&#xff09;信息消费者&#xff08;用户&#xff09;推荐和搜索的区别 2 推荐系统架构系统架构算法架构 3 推荐系统技术栈算法画像层召回/粗排精排重排序 工程 1 推荐系统的意义 信息生产者&#xff08;平台方…...

Redis初阶笔记

1. 认识Redis Redis是一个基于内存运行的缓存中间件&#xff0c;有着多种的数据类型可供使用。Redis的使用主要是为关系性数据库&#xff08;MySQL等&#xff09;分担压力&#xff0c;在高并发环境下MySQL执行命令的压力是很大的&#xff0c;容易宕机&#xff0c;所以需要中间件…...

electron.vite 项目创建以及better-sqlite3数据库使用

1.安装electron.vite npm create quick-start/electronlatest中文官网&#xff1a;https://cn.electron-vite.org/ 2. 安装项目依赖 npm i3.修改 electron-builder 配置文件 appId: com.electron.app productName: text33 directories:buildResources: build files:- !**/.v…...

【新品解读】AI 应用场景全覆盖!解码超高端 VU+ FPGA 开发平台 AXVU13F

「AXVU13F」Virtex UltraScale XCVU13P Jetson Orin NX 继发布 AMD Virtex UltraScale FPGA PCIE3.0 开发平台 AXVU13P 后&#xff0c;ALINX 进一步研究尖端应用市场&#xff0c;面向 AI 场景进行优化设计&#xff0c;推出 AXVU13F。 AXVU13F 和 AXVU13P 采用相同的 AMD Vir…...

Proxmox VE 8.3 qm 方式导入ESXi Linux OVA UEFI模式虚拟机

前言 实现esxi ova uefi 虚拟机导入到pve,Linux UEFI 都支持 创建一个105虚拟机 qm 参数使用参考,以下可以根据自己的实际情况执行调整 esxi 导出虚拟机参考 #vmid (100 - 999999999) vmid=105# qm vm name...

OpenAI 放王炸,将发布整合多项技术的 GPT-5,并免费无限使用,该模型有哪些技术亮点

对于 ChatGPT 的免费用户&#xff0c;将可以无限制地访问 GPT-5&#xff0c;但仅限于标准的智能级别。该级别会设定滥用限制&#xff0c;以防止不当使用(意思就是你得付费嘛)。 OpenAI CEO Sam Altman 今天在 X 上透露了 GPT-4.5 和 GPT-5 的最新发展计划。 OpenAI 将发布代…...

【前端框架与库】「深入理解 Vue 插槽」:类型、用法与实际场景解析,增强组件复用性的利器

深入理解 Vue 插槽 [TOC](深入理解 Vue 插槽) 前言一、插槽的几种类型1. 默认插槽&#xff08;Default Slot&#xff09;2. 具名插槽&#xff08;Named Slot&#xff09;3. 作用域插槽&#xff08;Scoped Slot&#xff09; 二、插槽的作用与实际使用场景三、延伸知识总结 前言 …...

对比 LVS 负载均衡群集的 NAT 模式和 DR 模式,比较其各自的优势 与基于 openEuler 构建 LVS-DR 群集

一、 对比 LVS 负载均衡群集的 NAT 模式和 DR 模式&#xff0c;比较其各自的优势 NAT 模式 部署简单&#xff1a;NAT 模式下&#xff0c;所有的服务器节点只需要连接到同一个局域网内&#xff0c;通过负载均衡器进行网络地址转换&#xff0c;就可以实现负载均衡功能。不需要对…...

在 Nginx Stream 层“改写”MQTT ngx_stream_mqtt_filter_module

1、为什么要修改 CONNECT 报文&#xff1f; 多租户隔离&#xff1a;自动为接入设备追加租户前缀&#xff0c;后端按 ClientID 拆分队列。零代码鉴权&#xff1a;将入站用户名替换为 OAuth Access-Token&#xff0c;后端 Broker 统一校验。灰度发布&#xff1a;根据 IP/地理位写…...

【论文笔记】若干矿井粉尘检测算法概述

总的来说&#xff0c;传统机器学习、传统机器学习与深度学习的结合、LSTM等算法所需要的数据集来源于矿井传感器测量的粉尘浓度&#xff0c;通过建立回归模型来预测未来矿井的粉尘浓度。传统机器学习算法性能易受数据中极端值的影响。YOLO等计算机视觉算法所需要的数据集来源于…...

WordPress插件:AI多语言写作与智能配图、免费AI模型、SEO文章生成

厌倦手动写WordPress文章&#xff1f;AI自动生成&#xff0c;效率提升10倍&#xff01; 支持多语言、自动配图、定时发布&#xff0c;让内容创作更轻松&#xff01; AI内容生成 → 不想每天写文章&#xff1f;AI一键生成高质量内容&#xff01;多语言支持 → 跨境电商必备&am…...

三体问题详解

从物理学角度&#xff0c;三体问题之所以不稳定&#xff0c;是因为三个天体在万有引力作用下相互作用&#xff0c;形成一个非线性耦合系统。我们可以从牛顿经典力学出发&#xff0c;列出具体的运动方程&#xff0c;并说明为何这个系统本质上是混沌的&#xff0c;无法得到一般解…...

【OSG学习笔记】Day 16: 骨骼动画与蒙皮(osgAnimation)

骨骼动画基础 骨骼动画是 3D 计算机图形中常用的技术&#xff0c;它通过以下两个主要组件实现角色动画。 骨骼系统 (Skeleton)&#xff1a;由层级结构的骨头组成&#xff0c;类似于人体骨骼蒙皮 (Mesh Skinning)&#xff1a;将模型网格顶点绑定到骨骼上&#xff0c;使骨骼移动…...

Spring数据访问模块设计

前面我们已经完成了IoC和web模块的设计&#xff0c;聪明的码友立马就知道了&#xff0c;该到数据访问模块了&#xff0c;要不就这俩玩个6啊&#xff0c;查库势在必行&#xff0c;至此&#xff0c;它来了。 一、核心设计理念 1、痛点在哪 应用离不开数据&#xff08;数据库、No…...

基于TurtleBot3在Gazebo地图实现机器人远程控制

1. TurtleBot3环境配置 # 下载TurtleBot3核心包 mkdir -p ~/catkin_ws/src cd ~/catkin_ws/src git clone -b noetic-devel https://github.com/ROBOTIS-GIT/turtlebot3.git git clone -b noetic https://github.com/ROBOTIS-GIT/turtlebot3_msgs.git git clone -b noetic-dev…...

作为测试我们应该关注redis哪些方面

1、功能测试 数据结构操作&#xff1a;验证字符串、列表、哈希、集合和有序的基本操作是否正确 持久化&#xff1a;测试aof和aof持久化机制&#xff0c;确保数据在开启后正确恢复。 事务&#xff1a;检查事务的原子性和回滚机制。 发布订阅&#xff1a;确保消息正确传递。 2、性…...

深入理解Optional:处理空指针异常

1. 使用Optional处理可能为空的集合 在Java开发中&#xff0c;集合判空是一个常见但容易出错的场景。传统方式虽然可行&#xff0c;但存在一些潜在问题&#xff1a; // 传统判空方式 if (!CollectionUtils.isEmpty(userInfoList)) {for (UserInfo userInfo : userInfoList) {…...

从面试角度回答Android中ContentProvider启动原理

Android中ContentProvider原理的面试角度解析&#xff0c;分为​​已启动​​和​​未启动​​两种场景&#xff1a; 一、ContentProvider已启动的情况 1. ​​核心流程​​ ​​触发条件​​&#xff1a;当其他组件&#xff08;如Activity、Service&#xff09;通过ContentR…...