SpringBoot最佳实践之 - 项目中统一记录正常和异常日志
1. 前言
此篇博客是本人在实际项目开发工作中的一些总结和感悟。是在特定需求背景下,针对项目中统一记录日志(包括正常和错误日志)需求的实现方式之一,并不是普适的记录日志的解决方案。所以阅读本篇博客的朋友,可以参考此篇博客中记录日志的方式,可能会对你有些许帮助和启发。
2. 需求描述
项目的大背景是:前后端分离项目,后端框架使用SpringBoot+MyBatis,前端使用Vue。前端调用后端API接口时,需要在请求头中传递一个唯一身份标识(为了让服务端知道此次是谁在调用我的接口)。
在一次完整的调用中,可能会有两种情况:
(a)调用过程正常,后端给前端返回想要的数据(通过统一返回对象返回数据);
(b)调用过程异常,后端抛出异常,并通过全局异常处理类进行异常处理,然后通过统一返回对象返回异常码和异常消息。
现在的需求是:在情况(a),即一次请求正常完成,需要在日志中记录本次的请求和响应的相关信息。包括:
1)请求身份唯一标识(我需要知道是哪个人访问了我);
2)请求的接口名(我需要知道你请求的是哪个接口);
3)请求的参数(我需要知道你此次请求这个接口传递了哪些参数);
4)请求的时间(我需要知道你此次请求的开始时间);
5)响应时间(我需要知道此次请求服务端给客户端响应的时间);
6)请求处理时间(我需要知道此次请求耗费了多少时间);
7)响应的结果信息(我需要知道此次请求的响应结果是什么);
8)请求的客户端IP地址(我需要知道此次请求的客户端IP地址);
9)此次请求是否成功的标识码(如果是成功日志,则标识位是0;失败日志,标识位是1)。
在情况(b),即此次请求出现异常,也需要在日志中记录本次的请求和响应信息。包括:
1)请求身份唯一标识(我需要知道是哪个人访问了我);
2)请求的接口名(我需要知道你请求的是哪个接口);
3)请求的参数(我需要知道你此次请求这个接口传递了哪些参数);
4)请求的时间(我需要知道你此次请求的开始时间);
5)响应时间(我需要知道此次请求服务端给客户端响应的时间);
6)请求处理时间(我需要知道此次请求耗费了多少时间);
7)响应的异常信息(我需要知道此次请求的响应的异常信息是什么]);
8)请求的客户端IP地址(我需要知道此次请求的客户端IP地址);
9)此次请求是否成功的标识码(如果是成功日志,则标识位是0;失败日志,标识位是1)。
总结需求就一句话:正常和异常请求我都需要记录日志,只不过正常请求后记录的是正常返回的结果信息,而异常请求后记录的是出现异常的原因。
3. 代码实现
3.1 logback日志配置
因为要记录日志,在SpringBoot项目中通常是使用SLF4J作为日志门面,Logback作为底层日志框架实现配合进行日志记录。关于logback的配置信息,可以参考我之前写的一篇博客,你也可以直接把这里的配置信息放到SpringBoot项目的resources目录即可。logback-spring.xml文件的一些记录
3.2 需要的组件
实现此需求,需要如下一些功能组件:过滤器、拦截器、存放日志对象的ThreadLocal、统一返回结果对象、统一异常处理、统一响应结果处理器。此处先简单介绍下各个组件在需求中的作用是什么,主要还是靠理解代码实现逻辑。
1)过滤器:因为需要在请求到达Controller之前获取请求体中的请求参数,而 HttpServletReqeust 获取输入流时仅允许读取一次,如果你直接在拦截器里面获取输入流,拿到里面的请求参数,后续处理过程中就会报java.io.IOException: Stream closed。具体可以参考这篇博文:SpringBoot如何在拦截器中获取@RequestBody参数;
2)拦截器:在请求进来的时候,可以在拦截器里面获取一些请求信息,如此次请求的身份唯一标识、接口名、请求参数、请求时间、客户端ip地址。然后创建日志记录对象并把这些信息设置到日志记录对象中;
3)存放日志对象的ThreadLocal:为了把第 2)步中拦截器里面的日志对象放到ThreadLocal里面,便于后续使用;
4)统一返回结果:SpringBoot的最佳实践之一就是定义全局的统一返回对象,便于和前端联调;
5)统一异常处理:SpringBoot的最佳实践之一就是在业务处理过程中,如果某些条件校验没通过,就直接抛出异常,然后由统一异常处理类进行统一处理,如打印异常堆栈信息,记录异常日志、通过统一返回结果对象给前端封装错误信息并返回;
6)统一响应结果处理器:在给前端响应数据之前,可以对要响应的结果进行拦截并做一些事情,此处主要是为了记录一次正常请求过程中的日志信息。
3.2.1 过滤器 (HttpServletRequestFilter)
package com.shg.component;import cn.hutool.extra.servlet.ServletUtil;
import org.springframework.stereotype.Component;import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ReadListener;
import javax.servlet.ServletException;
import javax.servlet.ServletInputStream;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;/**** HttpServletRequest 过滤器* 解决: request.getInputStream()只能读取一次的问题* 目标: 流可重复读**/
@Component
public class HttpServletRequestFilter implements Filter {@Overridepublic void init(FilterConfig filterConfig) throws ServletException {}@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {ServletRequest requestWrapper = null;if (servletRequest instanceof HttpServletRequest) {requestWrapper = new RequestWrapper((HttpServletRequest) servletRequest);}//获取请求中的流,将取出来的字符串,再次转换成流,然后把它放入到新 request对象中.在chain.doFiler方法中传递新的request对象if (null == requestWrapper) {filterChain.doFilter(servletRequest, servletResponse);} else {filterChain.doFilter(requestWrapper, servletResponse);}}@Overridepublic void destroy() {}/**** HttpServletRequest 包装器* 解决: request.getInputStream()只能读取一次的问题* 目标: 流可重复读*/public class RequestWrapper extends HttpServletRequestWrapper {/*** 请求体*/private String mBody;public RequestWrapper(HttpServletRequest request) {super(request);// 将body数据存储起来mBody = getBody(request);}/*** 获取请求体** @param request 请求* @return 请求体*/private String getBody(HttpServletRequest request) {return ServletUtil.getBody(request);}/*** 获取请求体** @return 请求体*/public String getBody() {return mBody;}@Overridepublic BufferedReader getReader() throws IOException {return new BufferedReader(new InputStreamReader(getInputStream()));}@Overridepublic ServletInputStream getInputStream() throws IOException {// 创建字节数组输入流final ByteArrayInputStream bais = new ByteArrayInputStream(mBody.getBytes(StandardCharsets.UTF_8));return new ServletInputStream() {@Overridepublic boolean isFinished() {return false;}@Overridepublic boolean isReady() {return false;}@Overridepublic void setReadListener(ReadListener readListener) {}@Overridepublic int read() throws IOException {return bais.read();}};}}
}
3.2.2 拦截器 (RequestGlobalInterceptor)
package com.shg.component;import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.shg.common.ResponseCodeEnum;
import com.shg.exception.BizException;
import com.shg.model.pojo.RecordLog;
import com.shg.utils.LogUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Date;
import java.util.Objects;@Slf4j
@Component
public class RequestGlobalInterceptor implements HandlerInterceptor {private final StringRedisTemplate stringRedisTemplate;public RequestGlobalInterceptor(StringRedisTemplate stringRedisTemplate) {this.stringRedisTemplate = stringRedisTemplate;}@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {if ("OPTIONS".equals(request.getMethod())) {return true;}if (request.getRequestURI().contains("/swagger-ui.html") ||request.getRequestURI().contains("/webjars/springfox-swagger-ui") ||request.getRequestURI().contains("swagger") ||request.getRequestURI().contains("webjars") ||request.getRequestURI().contains("images") ||request.getRequestURI().contains("api-docs") ||request.getRequestURI().contains("configuration/ui") ||request.getRequestURI().contains("configuration/security")) {return true;}String appId = null;try {appId = request.getHeader("appId");String appSecret = request.getHeader("appSecret");// 校验appId和appSecret是否合法 TODOif(Objects.isNull(appId)){throw new BizException(ResponseCodeEnum.APP_ID_NOT_PASSED);}} finally {// --- 日志相关 ---// 1. 接口名称String requestURI = request.getRequestURI();String interfaceName;if (requestURI.contains("/api/crypto")) {int startIndex = requestURI.indexOf("/api/crypto");interfaceName = requestURI.substring(startIndex + "/api/crypto/".length());} else {interfaceName = requestURI;}// 2. 请求参数String requestParameter = "";if (request instanceof HttpServletRequestFilter.RequestWrapper) {HttpServletRequestFilter.RequestWrapper repeatedlyRequest = ((HttpServletRequestFilter.RequestWrapper) request);requestParameter = StrUtil.removeAny(StrUtil.removeAllLineBreaks(repeatedlyRequest.getBody()), " ");//log.info("body参数: " + requestParameter);}if (StrUtil.isEmpty(requestParameter)) {requestParameter = JSONUtil.toJsonStr(request.getParameterMap());//log.info("查询字符串参数: " + requestParameter);}// 创建日志对象RecordLog recordLog = new RecordLog();recordLog.setAppId(appId);recordLog.setInterfaceName(interfaceName);recordLog.setArguments(requestParameter);recordLog.setRequestTime(DateUtil.format(new Date(), DatePattern.NORM_DATETIME_MS_PATTERN));recordLog.setServerIp(request.getRemoteAddr());// 把日志对象放到ThreadLocal里面,便于后续使用LogUtil.set(recordLog);}return true;}@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {LogUtil.remove();}
}
3.2.3 存放日志对象的ThreadLocal (LogUtil)
package com.shg.utils;import com.shg.model.pojo.RecordLog;public class LogUtil {private static final ThreadLocal<RecordLog> appCacheDtoThreadLocal = new ThreadLocal<>();public static void set(RecordLog log) {appCacheDtoThreadLocal.set(log);}public static RecordLog get() {return appCacheDtoThreadLocal.get();}public static void remove() {appCacheDtoThreadLocal.remove();}
}
3.2.4 控制器Controller
package com.shg.controller;import com.shg.common.ResultMessage;
import com.shg.model.vo.RandomRequest;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;@RestController
public class TestController {@GetMapping("/test1")public ResultMessage<String> test1(@RequestBody RandomRequest randomRequest) {int i = 1/0;// TODO 调用Service层的业务逻辑return ResultMessage.success("这是测试接口..." +randomRequest.getLength());}
}
3.2.5 统一返回结果对象 (ResultMessage)
package com.shg.common;import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.Data;@Data
@JsonInclude(JsonInclude.Include.NON_NULL)
public class ResultMessage<T> {private Integer code;private String message;private T data;private long timestamp = System.currentTimeMillis();public ResultMessage() {this(ResponseCodeEnum.SUCCESS.getCode(), ResponseCodeEnum.SUCCESS.getMessage(), null);}public ResultMessage(Integer code, String message, T data) {this.code = code;this.message = message;this.data = data;}public static <T> ResultMessage<T> success() {return new ResultMessage<>();}public static <T> ResultMessage<T> success(T data) {ResultMessage<T> resultMessage = new ResultMessage<>();resultMessage.setMessage(ResponseCodeEnum.SUCCESS.getMessage());resultMessage.setData(data);return resultMessage;}public static <T> ResultMessage<T> success(String message, T data) {ResultMessage<T> resultMessage = new ResultMessage<>();resultMessage.setMessage(message);resultMessage.setData(data);return resultMessage;}public static <T> ResultMessage<T> success(String message, boolean success) {ResultMessage<T> resultMessage = new ResultMessage<>();resultMessage.setMessage(message);return resultMessage;}public static <T> ResultMessage<T> errorResult(ResponseCodeEnum responseCodeEnum) {return new ResultMessage<>(responseCodeEnum.getCode(), responseCodeEnum.getMessage(), null);}public static <T> ResultMessage<T> errorResult(Integer code, String message) {return new ResultMessage<>(code, message, null);}public static <T> ResultMessage<T> errorResult(Integer code, String message, T data) {return new ResultMessage<>(code, message, data);}
}
3.2.6 统一异常处理 (GlobalExceptionHandler)
package com.shg.exception;import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSON;
import com.shg.common.ResponseCodeEnum;
import com.shg.common.ResultMessage;
import com.shg.constant.CommonConstant;
import com.shg.model.pojo.RecordLog;
import com.shg.utils.LogUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;import java.util.Date;
import java.util.Objects;@Slf4j
@ControllerAdvice
public class GlobalExceptionHandler {@ExceptionHandler(Throwable.class)@ResponseBodypublic ResultMessage error(Throwable e) {e.printStackTrace();recordLog(e, e.getMessage());return ResultMessage.errorResult(ResponseCodeEnum.SYSTEM_EXCEPTION.getCode(), e.getMessage());}@ExceptionHandler(BizException.class)@ResponseBodypublic ResultMessage error(BizException e) {recordLog(e, e.getMessage());e.printStackTrace();return ResultMessage.errorResult(e.getCode(), e.getMessage());}private void recordLog(Throwable e, String message) {RecordLog recordLog = LogUtil.get();if (!Objects.isNull(recordLog)) { // 有一种情况是,当请求方法不支持时,经过过滤器进行放行之后,会直接抛出异常,然后就会在这里进行处理了,此时RecordLog还没有对象,所以这里要判断recordLog是否为空String responseTime = DateUtil.format(new Date(), DatePattern.NORM_DATETIME_MS_PATTERN);String requestTime = recordLog.getRequestTime();long costTime = getCostTime(responseTime, requestTime);recordLog.setResponseTime(responseTime);recordLog.setResponseInterval(costTime);recordLog.setMessage(message);recordLog.setResult(CommonConstant.ONE);String myLogJson = JSON.toJSONString(recordLog);log.error("[EX]:" + System.currentTimeMillis() + " " + myLogJson, e);} else {log.error(e.getMessage(), e);}}private long getCostTime(String responseTime, String requestTime) {Date endTime = DateUtil.parse(responseTime);Date startTime = DateUtil.parse(requestTime);return DateUtil.between(endTime, startTime, DateUnit.MS);}
}
package com.shg.common;public enum ResponseCodeEnum {SUCCESS(0, "success"),SYSTEM_EXCEPTION(500, "System internal exception"),APP_ID_NOT_PASSED(1001, "The appId is not passed");private final int code;private final String message;ResponseCodeEnum(int code, String message) {this.code = code;this.message = message;}public int getCode() {return code;}public String getMessage() {return message;}}
3.2.7 统一响应结果处理器 (ResponseBodyAdviceAdapter)
package com.shg.component;import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.shg.common.ResponseCodeEnum;
import com.shg.common.ResultMessage;
import com.shg.constant.CommonConstant;
import com.shg.model.pojo.RecordLog;
import com.shg.utils.LogUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;import java.util.Date;
import java.util.Objects;/*** @DESCRIPTION: 在返回结果前进行日志记录* @USER: shg* @DATE: 2024/10/24 22:55*/
@Slf4j
@ControllerAdvice
public class ResponseBodyAdviceAdapter implements ResponseBodyAdvice<Object> {@Overridepublic boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {return true;}@Overridepublic Object beforeBodyWrite(Object body,MethodParameter returnType,MediaType selectedContentType,Class<? extends HttpMessageConverter<?>> selectedConverterType,ServerHttpRequest request,ServerHttpResponse response) {if (body instanceof ResultMessage) {ResultMessage resultMessage = ((ResultMessage<?>) body);if (resultMessage.getCode() == ResponseCodeEnum.SUCCESS.getCode()) {recordLog(JSONUtil.toJsonStr(resultMessage));}}return body;}private void recordLog(String message) {RecordLog recordLog = LogUtil.get();String responseTime = DateUtil.format(new Date(), DatePattern.NORM_DATETIME_MS_PATTERN);String requestTime = recordLog.getRequestTime();long costTime = getCostTime(responseTime, requestTime);recordLog.setResponseTime(responseTime);recordLog.setResponseInterval(costTime);recordLog.setMessage(message);recordLog.setResult(CommonConstant.ZERO);String myLogJson = JSON.toJSONString(recordLog);if (!Objects.isNull(recordLog.getAppId())) {log.info("[SUCCESS RESULT:]" + myLogJson);}}private long getCostTime(String responseTime, String requestTime) {Date endTime = DateUtil.parse(responseTime);Date startTime = DateUtil.parse(requestTime);return DateUtil.between(endTime, startTime, DateUnit.MS);}}
4. 效果演示
4.1. 请求正常执行,记录日志
前端收到的响应结果:
记录的正常执行日志信息如下:
4.2. 请求异常,记录错误日志
4.21. 异常示例一
前端收到的响应结果如下:
记录的异常日志如下:
4.2.2 异常示例二
模拟业务执行过程中出现异常。前端收到的响应结果如下:
记录的异常日志如下:
5. 其他
记录项目运行过程中的正常和异常日志,还有很多其他方法。比如使用AOP等。这里只是我个人在项目开发过程中为了实现需要而使用的一种方式。
具体代码示例请参考码云:springboot-best-practice: 初次提交
如果此篇文章对你有帮助,感谢点个赞~~
相关文章:
SpringBoot最佳实践之 - 项目中统一记录正常和异常日志
1. 前言 此篇博客是本人在实际项目开发工作中的一些总结和感悟。是在特定需求背景下,针对项目中统一记录日志(包括正常和错误日志)需求的实现方式之一,并不是普适的记录日志的解决方案。所以阅读本篇博客的朋友,可以参考此篇博客中记录日志的…...
【Flutter】状态管理:高级状态管理 (Riverpod, BLoC)
当项目变得更加复杂时,简单的状态管理方式(如 setState() 或 Provider)可能不足以有效地处理应用中状态的变化和业务逻辑的管理。在这种情况下,高级状态管理框架,如 Riverpod 和 BLoC,可以提供更强大的工具…...
OAK相机的RGB-D彩色相机去畸变做对齐
▌低畸变标准镜头的OAK相机RGB-D对齐的方法 OAK相机内置的RGB-D管道会自动将深度图和RGB图对齐。其思想是将深度图像中的每个像素与彩色图像中对应的相应像素对齐。产生的RGB-D图像可以用于OAK内置的图像识别模型将识别到的2D物体自动映射到三维空间中去,或者产生的…...
smartctl硬盘检查工具
一、smartctl工具简介 Smartmontools是一种硬盘检测工具,通过控制和管理硬盘的SMART(Self Monitoring Analysis and Reporting Technology),自动检测分析及报告技术)技术来实现的,SMART技术可以对硬盘的磁头单元、盘片电机驱动系统、硬盘…...
清空MySQL数据表
要清空 MySQL 数据表,您可以使用 TRUNCATE 或 DELETE 命令 使用 TRUNCATE 命令 TRUNCATE 命令用于删除表中的所有数据,并重置自增 ID(如果存在): TRUNCATE TABLE table_name;将 table_name 替换为您要清空的表的名称…...
2024年妈杯MathorCup大数据竞赛A题超详细解题思路
2024年妈杯大数据竞赛初赛整体难度约为0.6个国赛。A题为台风中心路径相关问题,为评价预测问题;B题为库存和销量的预测优化问题。B题难度稍大于A题,可以根据自己队伍情况进行选择。26日早六点之前发布AB两题相关解题代码论文。 下面为大家带来…...
Kafka系列之:Kafka集群磁盘条带划分和Kafka集群磁盘扩容详细方案
Kafka系列之:Kafka集群磁盘条带划分和Kafka集群磁盘扩容详细方案 一、lsblk命令二、Kafka节点磁盘条带化方案一三、Kafka节点磁盘条带化方案二四、理解逻辑区块LE五、查看kafka节点磁盘条带划分情况六、Kafka节点磁盘扩容一、lsblk命令 lsblk命令用于列出块设备的信息,包括磁…...
【LeetCode】修炼之路-0007- Reverse Integer (整数反转)【python】
题目 Reverse Integer Given a signed 32-bit integer x, return x with its digits reversed. If reversing x causes the value to go outside the signed 32-bit integer range [-231, 231 - 1], then return 0. Assume the environment does not allow you to store 64-b…...
【Flutter】页面布局:线性布局(Row 和 Column)
在 Flutter 中,布局(Layout)是应用开发的核心之一。通过布局组件,开发者可以定义应用中的控件如何在屏幕上排列。Row 和 Column 是 Flutter 中最常用的两种线性布局方式,用于水平和垂直排列子组件。在本教程中…...
C语言巨难题:执行操作可获得的最大总奖励 I(C语言版)
1.题目: 给你一个整数数组 rewardValues,长度为 n,代表奖励的值。 最初,你的总奖励 x 为 0,所有下标都是 未标记 的。你可以执行以下操作 任意次 : 从区间 [0, n - 1] 中选择一个 未标记 的下标 i。如果…...
【力扣】GO解决子序列相关问题
文章目录 一、引言二、动态规划方法论深度提炼子序列问题的通用解法模式 三、通用方法论应用示例:最长递增子序列(LeetCode题目300)Go 语言代码实现 四、最长连续递增序列(LeetCode题目674)Go 语言代码实现 五、最长重…...
Ubuntu20.04安装VM tools并实现主机和虚拟机之间文件夹共享
1、Ubuntu20.04安装VM tools 参考这个,很详细:Ubuntu 20.04 安装 VMwareTools 教程 2、实现主机与VMware虚拟机共享文件夹 设置共享文件夹参考:windows和虚拟机互传文件的三种方式 挂载操作参考:主机与VMware虚拟机共享文件夹&…...
Linux 学习笔记(十七)—— 文件系统
终极目标:理解 inode 和 软硬连接; 文件系统:Ext2; 文件 文件内容 文件属性; ——> 磁盘上存储的文件 存储的文件内容 存储的文件属性; Linux系统中:文件内容使用数据块存储,文件属性使用inode(固定…...
【计算机网络 - 基础问题】每日 3 题(五十八)
✍个人博客:https://blog.csdn.net/Newin2020?typeblog 📣专栏地址:http://t.csdnimg.cn/fYaBd 📚专栏简介:在这个专栏中,我将会分享 C 面试中常见的面试题给大家~ ❤️如果有收获的话,欢迎点赞…...
Netty入门基础:IO模型中BIO\NIO概念及区别【附演示代码】
文章目录 😀BIO💢实战demo 🌈NIO🏍Buffer核心属性核心方法 🎗Channel🎈Selector核心方法 🧨实战demo 🎨粘包与半包 😀BIO 传统IO模型,同步阻塞,每…...
vue2 使用环境变量
一. 在根目录下创建.env.xxx文件 .env 基础系统变量,无论何种环境,都可使用其中配置的值,其他环境中的变量会覆盖.env中的同名变量。 .env.development 开发环境 .env.production 生产环境 .env.staging 测试环境 二. 内容格式 vue2 使用是以…...
数据预处理
继续提取代码片段: 12. **导入iris数据集并查看前5行数据**: python from sklearn.datasets import load_iris iris load_iris() X iris.data print(iris数据集的维度为:, X.shape) print(iris数据集的前5行数据为:\n, X[:5]) …...
django宠物领养管理系统-计算机毕业设计源码26858
目录 1 绪论 1.1 选题背景与意义 1.2国内外研究现状 1.3论文结构与章节安排 2系统分析 2.1 可行性分析 2.2 系统流程分析 2.2.1 数据流程 3.3.2 业务流程 2.3 系统功能分析 2.3.1 功能性分析 2.3.2 非功能性分析 2.4 系统用例分析 2.5本章小结 3 系统总体设计 3…...
使用TeamViewer远程局域网内的两台电脑
有个场景,有人还不知道TV可以局域网操作,记录一下。 主要就是修改设置,将取消激活改为接受 然后输入受控端的ip即可...
GUI简介、Swing的常用组件、java程序的运行过程、class文件、JAR、runable_jar、双括号初始化
GUI简介 GUI:图形用户界面,在计算机中采用图形的方式显示用户界面 java的GUI开发 AWT:java最早推出的GUI编程开发包,界面风格跟随操作系统SWT:eclipse就是java使用SWT开发的Swing:在AWT的基础上扩充了功能…...
@Autowired和@Resource和getBean()区别
今天遇到一个对我来说很奇葩的错误,我想在Service中注入bean,我这里使用了Autowired和Resource都不能注入,导致初始化失败,使用了getBean()方法就可以注入。从来没有遇到过这个问题。后来我查询了一下,才明白了原理。我…...
Merlion笔记(四):添加一个新的预测模型
文章目录 1 模型配置类2 模型类3 运行模型:一个简单的例子4 可视化5 定量评估6 定义一个基于预测器的异常检测器 本文提供了一个示例,展示如何向 Merlion 添加一个新的预测模型,遵循 CONTRIBUTING.md 中的说明。建议在阅读本篇文章之前,先查…...
【论文阅读】ESRGAN
学习资料 论文题目:增强型超分辨率生成对抗网络(ESRGAN: Enhanced Super-Resolution Generative Adversarial Networks)论文地址:[1809.00219] ESRGAN:增强型超分辨率生成对抗网络代码:xinntao / ESRGAN&am…...
电脑异常情况总结
文章目录 笔记本无症状息屏黑屏 笔记本无症状息屏黑屏 🍎 问题描述: 息屏导致黑屏;依次操作计算机--》右键--》管理--》事件查看器--》Windows日志--》系统;从息屏到异常黑屏之间出现了很多错误,如下:事件…...
[项目详解][boost搜索引擎#1] 概述 | 去标签 | 数据清洗 | scp
目录 一、前言 二、项目的相关背景 三、搜索引擎的宏观原理 四、搜索引擎技术栈和项目环境 五、正排索引 VS 倒排索引--原理 正排索引 分词 倒排索引 六、编写数据去除标签和数据清洗模块 Parser 1.数据准备 parser 编码 1.枚举文件 EnumFile 2.去标签ParseHtml(…...
PL/I语言的起源?有C语言,有B语言和A语言吗?为什么shell脚本最开始可能有#!/bin/bash字样?为什么不支持嵌套注释?
PL/I语言的起源 在20世纪50~60年代,当时主流的编程语言是COBOL/FORTRAN/ALGOL等,IBM想要设计一门通用的编程语言,已有的编程语言无法实现此要求,故想要设计一门新语言,即是PL/I. PL/I是Programming Language/One的缩写…...
gin入门教程(3):创建第一个 HTTP 服务器
首先设置golang github代理,可解决拉取git包的时候,无法拉取的问题: export GOPROXYhttps://goproxy.io再查看自己的go版本: go version我这里的版本是:go1.23.2 linux/arm64 准备工作做好之后就可以进行开发了 3.…...
Vue+ECharts+iView实现大数据可视化大屏模板
Vue数据可视化 三个大屏模板 样式还是比较全的 包括世界地图、中国地图、canvas转盘等 项目演示: 视频: vue大数据可视化大屏模板...
el-table 表格设置必填项
el-table 表格设置必填项 要在 el-table 中集成 el-form 来设置必填项,并进行表单验证,可以使用 Element UI 提供的表单验证功能。下面是一个详细的示例,展示了如何在 el-table 中使用 el-form 来设置必填项,并进行验证。 示例代…...
vivo 轩辕文件系统:AI 计算平台存储性能优化实践
在早期阶段,vivo AI 计算平台使用 GlusterFS 作为底层存储基座。随着数据规模的扩大和多种业务场景的接入,开始出现性能、维护等问题。为此,vivo 转而采用了自研的轩辕文件系统,该系统是基于 JuiceFS 开源版本开发的一款分布式文件…...
网站的推广方法/牛推网络
asp javascript值的互相传递方法如果你能很好的理解我下面的一句话,那这些都不是问题了。asp是服务器端语言,它的作用是动态生成客户端浏览器所能识别的html css javascript 等...那么asp 怎么传值给js呢?动态的去生成js就好啦~~ 我们是怎么生…...
环保网页设计制作流程/seo推广优化外包公司
我要实现的就是,当接口返回数据时,我在任何组件中都能感知到到该数据的变化,然后根据业务逻辑进行处理、展示。 实现这个效果的方式很多,比如当接口返回数据后,就emit这数据,在另外组件中on接收渲染即可&am…...
扬州企业网站建设/重庆百度快速优化
sessionStorage作为HTML5的Web Storage的两种存储方式之一。 用于本地存储一个会话(session)中的数据,这些数据只有在同一个会话中的页面才能访问并且当会话结束后数据会被销毁。 不是一种持久化的本地存储。 会话级的存储。1、接口…...
现在还用dw做网站设计么/站长网站统计
进程间通信之管道篇 一:管道实现原理 在 Linux 中, 管道是一种使用非常频繁的通信机制。 从本质上说,管道也是一种文件,但它又和一般的文件有所不同,管道可以克服使用文件进行通信的两个问题,具体表现为&a…...
怎样做网站推广啊/关键词优化哪家强
概念 算法:任何一个良定义的计算过程,该过程取某个值或者值的集合作为输入并产生某个值或值的集合作为输出。这是比较概念化的定义,我们来分析一下,算法到底是什么,比如有一个问题:计算1到10所有整数的和&…...
做公司网站的目的是什么/站长工具平台
2012.8,结束欧洲意法两国游之后,经过漫长的国际航线,终于飞临香港上空。由于喜欢拍照,总是尽量找人调换靠窗的位置,无奈这次没有换成功,所以没有赶上更好的视角,都是匆忙间拍下来的,…...