您当前的位置:首页 > 电脑百科 > 程序开发 > 框架

请求与响应参数日志记录框架

时间:2022-09-22 11:23:06  来源:今日头条  作者:是啊超ya

前言

实际开发中,如果项目权限管控比较严格,自己又上不去服务器查看日志文件,怎么办?而且日志文件查看也比较繁琐。就随便搞一个数据库记录请求参数与响应数据的日志框架。方便自己排查问题排查问题。

设计

  1. 使用AOP切面技术,将controller层的入参与出参,还有错误信息输出到数据库表logger_info中。
  2. 配合日志级别,使得如果不需要,则不开启,或者只输出特定级别的操作。
  3. 启动时,创建日志表。不需要手动创建。
  4. 定时备份日志表,减少单表数据量过大。

 

根据以上内容工程分成设计如下:


 

注:由于整个项目使用的是MyBatis-plus框架,所以添加了service和mApper层,可以使用SQL语句替换

正文1. 日志实体(日志表)

既然是记录,当然是有记录表了,入参,出参,请求,类,方法,IP,执行时间,都是基本记录。所以就有如下的实体设计。

package com.cah.project.module.logger.domAIn.entity;import com.baomidou.mybatisplus.annotation.FieldFill;import com.baomidou.mybatisplus.annotation.IdType;import com.baomidou.mybatisplus.annotation.TableField;import com.baomidou.mybatisplus.annotation.TableId;import com.baomidou.mybatisplus.annotation.TableName;import io.swagger.annotations.ApiModel;import io.swagger.annotations.ApiModelProperty;import lombok.Data;import JAVA.time.LocalDate;@Data@TableName(LoggerInfoEntity.TABLE_NAME)@ApiModel("日志信息")public class LoggerInfoEntity {public static final String TABLE_NAME = "logger_info";@ApiModelProperty("主键ID")@TableId(value = "id", type = IdType.AUTO)private Long id;@ApiModelProperty("访问的url")@TableField("url")private String url;@ApiModelProperty("类名")@TableField("class_name")private String className;@ApiModelProperty("方法名")@TableField("method_name")private String methodName;@ApiModelProperty("请求的ip地址")@TableField("req_ip_adr")private String reqIpAdr;@ApiModelProperty("响应的ip地址(集群提供)")@TableField("rsp_ip_adr")private String rspIpAdr;@ApiModelProperty("成功标志")@TableField("success_ind")private Boolean successInd;@ApiModelProperty("请求报文头")@TableField("req_header")private String reqHeader;@ApiModelProperty("请求报文体")@TableField("req_body")private String reqBody;@ApiModelProperty("响应报文体")@TableField("rsp_body")private String rspBody;@ApiModelProperty("错误信息")@TableField("error_msg")private String errorMsg;@ApiModelProperty("总耗时")@TableField("total_time")private Long totalTime;@ApiModelProperty("创建时间")@TableField(value = "create_time", fill = FieldFill.INSERT)private LocalDate createTime;2. 日志打印级别

这里自定义日志的打印级别,分别为:不打印,打印正常,打印错误,全部打印。根据自身需要,自行修改就好了。

package com.cah.project.module.logger.conf;* 功能描述: 日志级别枚举
public enum LoggerLevelEnum {/** 不打印 */NONE,/** 打印正常 */PRINT,/** 打印异常 */ERROR,/** 全部打印 */ALL,
3. 日志级别配置

将日志级别放到配置文件中,方便修改调整。

package com.cah.project.module.logger.conf;import lombok.Data;import org.springframework.boot.context.properties.ConfigurationProperties;import org.springframework.stereotype.Component;@Data@Component@ConfigurationProperties(prefix = "logger.project")public class LoggerConfig {/** 定义日志级别 */private LoggerLevelEnum level = LoggerLevelEnum.NONE;复制代码

默认级别为:不打印。如果需要调整级别,则在application.yml配置文件中,添加如下配置即可生效。

# 日志打印级别:不打印-NONE;打印正常-PRINT;打印异常-ERROR;全部打印-ALLlogger:project:level: PRINT4. 日志保存服务

在保存日志时,使用@Async注解,达到异步效果,不影响主流程。在接口直接使用default关键字,省事。

package com.cah.project.module.logger.service;import com.baomidou.mybatisplus.extension.service.IService;import com.cah.project.module.logger.domain.entity.LoggerInfoEntity;import org.springframework.scheduling.annotation.Async;* 功能描述: 日志服务接口
public interface ILoggerInfoService extends IService {* 功能描述: 异步保存
* @param info 日志信息@Asyncdefault void saveAsync(LoggerInfoEntity info) {save(info);

ServiceImpl 和 Mapper 略,直接继承基类就好了。

5. 请求工具类

这个东西,网上找个就好了,主要是为了获取HttpServletRequest的请求头和 IP地址的作用。如果不需要记录,都可以删除了。

package com.cah.project.module.logger.util;import org.springframework.web.context.Request.RequestContextHolder;import org.springframework.web.context.request.ServletRequestAttributes;import javax.servlet.http.HttpServletRequest;import java.NET.InetAddress;import java.net.UnknownHostException;import java.util.Enumeration;import java.util.HashMap;import java.util.Map;* 功能描述: 请求工具类
public class HttpRequestUtil {private static final String UNKNOWN = "unknown";private static final String LOCALHOST_IP = "127.0.0.1";// 客户端与服务器同为一台机器,获取的 ip 有时候是 ipv6 格式private static final String LOCALHOST_IPV6 = "0:0:0:0:0:0:0:1";private static final String SEPARATOR = ",";public static HttpServletRequest getHttpServletRequest() {ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();if(servletRequestAttributes == null) {return null;return servletRequestAttributes.getRequest();public static Map getHeader() {HttpServletRequest request = getHttpServletRequest();if(request == null) {return new HashMap<>();Map headerMap = new HashMap<>();Enumeration headerNames = request.getHeaderNames();while (headerNames.hasMoreElements()) {String headerName = headerNames.nextElement();headerMap.put(headerName, request.getHeader(headerName));return headerMap;public static String getRealIpAddress() {HttpServletRequest request = getHttpServletRequest();if (request == null) {return "";String ip = request.getHeader("x-forwarded-for");if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {ip = request.getHeader("Proxy-Client-IP");if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {ip = request.getHeader("X-Forwarded-For");if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {ip = request.getHeader("WL-Proxy-Client-IP");if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {ip = request.getHeader("X-Real-IP");if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {ip = request.getRemoteAddr();if (LOCALHOST_IP.equalsIgnoreCase(ip) || LOCALHOST_IPV6.equalsIgnoreCase(ip)) {// 根据网卡取本机配置的 IPInetAddress iNet = null;try {iNet = InetAddress.getLocalHost();} catch (UnknownHostException e) {e.printStackTrace();if (iNet != null)ip = iNet.getHostAddress();// 对于通过多个代理的情况,分割出第一个 IPif (ip != null && ip.length() > 15) {if (ip.indexOf(SEPARATOR) > 0) {ip = ip.substring(0, ip.indexOf(SEPARATOR));return LOCALHOST_IPV6.equals(ip) ? LOCALHOST_IP : ip;
6. 请求日志切面(核心)

前面弄的那么多,都是为了给这个切面类服务的。

package com.cah.project.module.logger.aspect;import cn.hutool.core.net.NetUtil;import cn.hutool.json.JSONUtil;import com.cah.project.module.logger.conf.LoggerConfig;import com.cah.project.module.logger.conf.LoggerLevelEnum;import com.cah.project.module.logger.domain.entity.LoggerInfoEntity;import com.cah.project.module.logger.service.ILoggerInfoService;import com.cah.project.module.logger.util.HttpRequestUtil;import org.aspectj.lang.ProceedingJoinPoint;import org.aspectj.lang.annotation.Around;import org.aspectj.lang.annotation.Aspect;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.core.annotation.Order;import org.springframework.stereotype.Component;import org.springframework.web.context.request.RequestContextHolder;import org.springframework.web.context.request.ServletRequestAttributes;import javax.servlet.http.HttpServletRequest;import java.util.Optional;* 功能描述: 日志切面
@Order(99)@Aspect@Componentpublic class RequestLogAspect {@Autowiredprivate LoggerConfig loggerConfig;@Autowiredprivate ILoggerInfoService loggerInfoService;@Around("execution(* com.cah.project..*.controller..*.*(..))")public Object doAround(ProceedingJoinPoint point) throws Throwable {// 如果没有开启,则直接返回if(LoggerLevelEnum.NONE.equals(loggerConfig.getLevel())) {return point.proceed();long startTime = System.currentTimeMillis();LoggerInfoEntity info = new LoggerInfoEntity();// 设置urlinfo.setUrl(Optional.ofNullable((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).map(ServletRequestAttributes::getRequest).map(HttpServletRequest::getRequestURI).orElse(""));// 设置类名info.setClassName(point.getTarget().getClass().getName());// 设置方法名info.setMethodName(point.getSignature().getName());// 设置请求IP地址info.setReqIpAdr(HttpRequestUtil.getRealIpAddress());// 设置响应IP地址info.setRspIpAdr(NetUtil.getLocalhostStr());// 设置请求头info.setReqHeader(JSONUtil.toJsonStr(HttpRequestUtil.getHeader()));// 设置请求体info.setReqBody(JSONUtil.toJsonStr(point.getArgs()));// 设置请求成功info.setSuccessInd(Boolean.TRUE);// 定义返回值Object obj;try {Object result = point.proceed();info.setRspBody(JSONUtil.toJsonStr(result));obj = result;} catch (Exception e) {// 设置请求异常info.setSuccessInd(Boolean.FALSE);// 设置异常信息info.setErrorMsg(e.getLocalizedMessage());throw e;} finally {// 计算处理时间info.setTotalTime(System.currentTimeMillis() - startTime);// 如果为全部打印或正常打印,并且为正常标志,记录if(LoggerLevelEnum.ALL.equals(loggerConfig.getLevel()) || (LoggerLevelEnum.PRINT.equals(loggerConfig.getLevel()) && info.getSuccessInd())) {loggerInfoService.saveAsync(info);// 如果为全部打印或者异常打印,并且为异常标志,记录if(LoggerLevelEnum.ALL.equals(loggerConfig.getLevel()) || (LoggerLevelEnum.ERROR.equals(loggerConfig.getLevel())) && !info.getSuccessInd()) {loggerInfoService.saveAsync(info);return obj;
7. 启动监听

在项目启动后,需要判断是否需要创建日志表,如果已经存在,则跳过,不存在,则创建日志表。

package com.cah.project.module.logger;import com.baomidou.mybatisplus.annotation.DbType;import com.baomidou.mybatisplus.extension.toolkit.JdbcUtils;import com.cah.project.module.logger.conf.LoggerConfig;import com.cah.project.module.logger.sql.DdlSqlFactory;import com.cah.project.module.logger.sql.IDdlSql;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.CommandLineRunner;import org.springframework.stereotype.Component;import javax.sql.DataSource;import java.sql.Connection;import java.sql.ResultSet;import java.sql.SQLException;import java.sql.Statement;* 功能描述: 日志信息启动执行
* 日志备份,创建日志表等操作@Componentpublic class LoggerInfoApplicationListener implements CommandLineRunner {@Autowiredprivate DataSource dataSource;@Autowiredprivate LoggerConfig loggerConfig;private IDdlSql ddl;@Overridepublic void run(String... args) throws Exception {// 判断数据库类型Connection conn = dataSource.getConnection();try (Statement statement = conn.createStatement()) {DbType dbType = JdbcUtils.getDbType(conn.getMetaData().getURL());ddl = DdlSqlFactory.valueOf(dbType.name()).getDdl();// 查询表有没有存在if(!existTable(statement)) {createTable(statement);} catch (Exception e) {e.printStackTrace();* 功能描述: 建表
private void createTable(Statement statement) throws SQLException {statement.execute(ddl.createTable());* 功能描述: 是否存在表
private boolean existTable(Statement statement) throws SQLException {ResultSet resultSet = statement.executeQuery(ddl.queryTable(""));resultSet.next();return resultSet.getInt(1) == 1;

介绍一下 SQL 语句的设计思路。因为可能会扩展到不同的数据库(正常也没那么多屁事)使用枚举,实现单利单利工厂模式,如果真的有需要扩展,则只要修改DdlSqlFactory类和添加一个扩展的IDdlSql实现类即可。

7.1 SQL 接口

queryTable和backTable为什么会有入参呢,是因为备份表的命名规则为:原表名+"_"+日期。如果不想集成Mybaties-plus,则可以将insert语句放在这个接口里。

package com.cah.project.module.logger.sql;* 功能描述: 数据库语句
public interface IDdlSql {/** 查询表是否存在 */String queryTable(String date);/** 建表语句 */String createTable();/** 备份表 */String backTable(String date);/** 删除表 */String dropTable();
7.2 SQL 枚举工厂package com.cah.project.module.logger.sql;import com.cah.project.module.logger.sql.impl.MySQLDdlSql;import lombok.AllArgsConstructor;import lombok.Getter;* 功能描述: SQL执行ddl语句工厂
@Getter@AllArgsConstructorpublic enum DdlSqlFactory {MYSQL(new MySQLDdlSql()),private final IDdlSql ddl;
7.3 SQL 接口实现package com.cah.project.module.logger.sql.impl;import cn.hutool.core.util.StrUtil;import com.cah.project.module.logger.domain.entity.LoggerInfoEntity;import com.cah.project.module.logger.sql.IDdlSql;public class MySQLDdlSql implements IDdlSql {@Overridepublic String queryTable(String date) {return "select count(1) from information_schema.tables where table_name ='" + LoggerInfoEntity.TABLE_NAME + (StrUtil.isNotBlank(date) ? "_" + date : "") + "';";@Overridepublic String createTable() {return "create table " + LoggerInfoEntity.TABLE_NAME + "(" +" `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',n" +" `url` varchar(500) COMMENT '访问的url',n" +" `class_name` varchar(500) COMMENT '类名',n" +" `method_name` varchar(100) COMMENT '方法名',n" +" `req_ip_adr` varchar(20) COMMENT '请求的ip地址',n" +" `rsp_ip_adr` varchar(20) COMMENT '响应的ip地址',n" +" `success_ind` tinyint COMMENT '成功标志',n" +" `req_header` text COMMENT '请求报文头',n" +" `req_body` text COMMENT '请求报文体',n" +" `rsp_body` text COMMENT '响应报文体',n" +" `error_msg` text COMMENT '错误信息',n" +" `total_time` Long COMMENT '总耗时',n" +" `create_time` datetime DEFAULT NULL COMMENT '创建时间',n" +" PRIMARY KEY (`id`) USING BTREE,n" +" KEY `idx_name` (`url`) USING BTREEn" +") ENGINE=InnoDB COMMENT='日志信息';";@Overridepublic String backTable(String date) {return "rename table " + LoggerInfoEntity.TABLE_NAME + " to " + LoggerInfoEntity.TABLE_NAME + "_" + date + ";";@Overridepublic String dropTable() {return "drop table " + LoggerInfoEntity.TABLE_NAME + ";";8. 日志备份

备份日志选择了通过@Scheduled定时器来处理。使用时,需要在启动类上面添加@EnableScheduling来开启。

这里定时是每天的 00:00:01 秒开始备份。为啥是1秒呢?没有为啥。

package com.cah.project.module.logger;import cn.hutool.core.date.DatePattern;import cn.hutool.core.date.DateUtil;import com.baomidou.mybatisplus.annotation.DbType;import com.baomidou.mybatisplus.extension.toolkit.JdbcUtils;import com.cah.project.module.logger.conf.LoggerConfig;import com.cah.project.module.logger.conf.LoggerLevelEnum;import com.cah.project.module.logger.sql.DdlSqlFactory;import com.cah.project.module.logger.sql.IDdlSql;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.scheduling.annotation.Scheduled;import org.springframework.stereotype.Component;import javax.sql.DataSource;import java.sql.Connection;import java.sql.ResultSet;import java.sql.SQLException;import java.sql.Statement;* 功能描述: 日志备份任务
@Componentpublic class LoggerInfoBackTask {@Autowiredprivate DataSource dataSource;@Autowiredprivate LoggerConfig loggerConfig;private IDdlSql ddl;* 功能描述: 每天0点1秒开始执行备份表(确定日期)
@Scheduled(cron = "1 0 0 * * ?")public void backTask() throws Throwable {if(LoggerLevelEnum.NONE.equals(loggerConfig.getLevel())) {return;Connection conn = dataSource.getConnection();try (Statement statement = conn.createStatement()) {DbType dbType = JdbcUtils.getDbType(conn.getMetaData().getURL());ddl = DdlSqlFactory.valueOf(dbType.name()).getDdl();String yesterday = DateUtil.format(DateUtil.yesterday(), DatePattern.PURE_DATE_FORMAT);// 查询表有没有存在if(!existTable(statement, yesterday)) {backTable(statement, yesterday);} catch (Exception e) {e.printStackTrace();* 功能描述: 每日备份操作
public void backTable(Statement statement, String yesterday) throws SQLException {// 先备份statement.execute(ddl.backTable(yesterday));// 再创建表statement.execute(ddl.createTable());* 功能描述: 是否存在表
private boolean existTable(Statement statement, String yesterday) throws SQLException {ResultSet resultSet = statement.executeQuery(ddl.queryTable(yesterday));resultSet.next();return resultSet.getInt(1) == 1;
测试

启动项目后,随便访问访问,看看日志表有没有记录成功就好了。


 

调整日期,看看有没有进行日志备份

代码

project-logger代码地址

总结

自己项目的日子记录,自己查看起来方便,区别于整体项目框架的日志。方便自己在没有权限的时候排查问题,开个小后门。有条件的话,自己写一个前端,然后做一下权限控制。



Tags:框架   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,不构成投资建议。投资者据此操作,风险自担。如有任何标注错误或版权侵犯请与我们联系,我们将及时更正、删除。
▌相关推荐
Qt与Flutter:在跨平台UI框架中哪个更受欢迎?
在跨平台UI框架领域,Qt和Flutter是两个备受瞩目的选择。它们各自具有独特的优势,也各自有着广泛的应用场景。本文将对Qt和Flutter进行详细的比较,以探讨在跨平台UI框架中哪个更...【详细内容】
2024-04-12  Search: 框架  点击:(1)  评论:(0)  加入收藏
Web Components实践:如何搭建一个框架无关的AI组件库
一、让人又爱又恨的Web ComponentsWeb Components是一种用于构建可重用的Web元素的技术。它允许开发者创建自定义的HTML元素,这些元素可以在不同的Web应用程序中重复使用,并且...【详细内容】
2024-04-03  Search: 框架  点击:(8)  评论:(0)  加入收藏
Htmx,它到底是框架还是库?
在最近的前端开发技术的探讨中,htmx经常成为热议的话题。一些人批评它,认为尽管htmx批评现代前端框架过于复杂,但它自己却似乎也是一个复杂的框架。这种看法值得我们深入思考。...【详细内容】
2024-03-28  Search: 框架  点击:(17)  评论:(0)  加入收藏
五大跨平台桌面应用开发框架:Electron、Tauri、Flutter等
一、什么是跨平台桌面应用开发框架跨平台桌面应用开发框架是一种工具或框架,它允许开发者使用一种统一的代码库或语言来创建能够在多个操作系统上运行的桌面应用程序。传统上...【详细内容】
2024-02-26  Search: 框架  点击:(47)  评论:(0)  加入收藏
Spring Security权限控制框架使用指南
在常用的后台管理系统中,通常都会有访问权限控制的需求,用于限制不同人员对于接口的访问能力,如果用户不具备指定的权限,则不能访问某些接口。本文将用 waynboot-mall 项目举例...【详细内容】
2024-02-19  Search: 框架  点击:(39)  评论:(0)  加入收藏
Go Gin框架实现优雅地重启和停止
在Web应用程序中,有时候我们需要重启或停止服务器,无论是因为更新代码还是进行例行维护。在这种情景下,我们需要保证应用程序的可用性和数据的一致性。这就需要优雅地关闭和重...【详细内容】
2024-01-30  Search: 框架  点击:(68)  评论:(0)  加入收藏
链世界:一种简单而有效的人类行为Agent模型强化学习框架
强化学习是一种机器学习的方法,它通过让智能体(Agent)与环境交互,从而学习如何选择最优的行动来最大化累积的奖励。强化学习在许多领域都有广泛的应用,例如游戏、机器人、自动驾...【详细内容】
2024-01-30  Search: 框架  点击:(68)  评论:(0)  加入收藏
OpenHarmony - 基于ArkUI框架实现日历应用
前言对于刚刚接触OpenHarmony应用开发的开发者,最快的入门方式就是开发一个简单的应用,下面记录了一个日历应用的开发过程,通过日历应用的开发,来熟悉基本图形的绘制,ArkUI的组件...【详细内容】
2024-01-16  Search: 框架  点击:(54)  评论:(0)  加入收藏
阿里“AI替换万物”框架火爆社区,网友:偶像不需要真人了?
白交 发自 凹非寺量子位 | 公众号 QbitAIReplace Anything as you want。现在只需框住你需要保留的区域,AI就可以替换万物了!比如让霉霉穿上中国旗袍,结果发饰、服装、背景等各...【详细内容】
2024-01-15  Search: 框架  点击:(66)  评论:(0)  加入收藏
分布式事务框架选择与实践
分布式事务是处理跨多个服务的原子操作的关键概念,而选择适合应用场景的框架对于确保事务一致性至关重要。以下是几个常见的分布式事务框架,并讨论它们的使用和实践。1. XA协...【详细内容】
2024-01-05  Search: 框架  点击:(96)  评论:(0)  加入收藏
▌简易百科推荐
Qt与Flutter:在跨平台UI框架中哪个更受欢迎?
在跨平台UI框架领域,Qt和Flutter是两个备受瞩目的选择。它们各自具有独特的优势,也各自有着广泛的应用场景。本文将对Qt和Flutter进行详细的比较,以探讨在跨平台UI框架中哪个更...【详细内容】
2024-04-12  刘长伟    Tags:UI框架   点击:(1)  评论:(0)  加入收藏
Web Components实践:如何搭建一个框架无关的AI组件库
一、让人又爱又恨的Web ComponentsWeb Components是一种用于构建可重用的Web元素的技术。它允许开发者创建自定义的HTML元素,这些元素可以在不同的Web应用程序中重复使用,并且...【详细内容】
2024-04-03  京东云开发者    Tags:Web Components   点击:(8)  评论:(0)  加入收藏
Kubernetes 集群 CPU 使用率只有 13% :这下大家该知道如何省钱了
作者 | THE STACK译者 | 刘雅梦策划 | Tina根据 CAST AI 对 4000 个 Kubernetes 集群的分析,Kubernetes 集群通常只使用 13% 的 CPU 和平均 20% 的内存,这表明存在严重的过度...【详细内容】
2024-03-08  InfoQ    Tags:Kubernetes   点击:(18)  评论:(0)  加入收藏
Spring Security:保障应用安全的利器
SpringSecurity作为一个功能强大的安全框架,为Java应用程序提供了全面的安全保障,包括认证、授权、防护和集成等方面。本文将介绍SpringSecurity在这些方面的特性和优势,以及它...【详细内容】
2024-02-27  风舞凋零叶    Tags:Spring Security   点击:(54)  评论:(0)  加入收藏
五大跨平台桌面应用开发框架:Electron、Tauri、Flutter等
一、什么是跨平台桌面应用开发框架跨平台桌面应用开发框架是一种工具或框架,它允许开发者使用一种统一的代码库或语言来创建能够在多个操作系统上运行的桌面应用程序。传统上...【详细内容】
2024-02-26  贝格前端工场    Tags:框架   点击:(47)  评论:(0)  加入收藏
Spring Security权限控制框架使用指南
在常用的后台管理系统中,通常都会有访问权限控制的需求,用于限制不同人员对于接口的访问能力,如果用户不具备指定的权限,则不能访问某些接口。本文将用 waynboot-mall 项目举例...【详细内容】
2024-02-19  程序员wayn  微信公众号  Tags:Spring   点击:(39)  评论:(0)  加入收藏
开发者的Kubernetes懒人指南
你可以将本文作为开发者快速了解 Kubernetes 的指南。从基础知识到更高级的主题,如 Helm Chart,以及所有这些如何影响你作为开发者。译自Kubernetes for Lazy Developers。作...【详细内容】
2024-02-01  云云众生s  微信公众号  Tags:Kubernetes   点击:(51)  评论:(0)  加入收藏
链世界:一种简单而有效的人类行为Agent模型强化学习框架
强化学习是一种机器学习的方法,它通过让智能体(Agent)与环境交互,从而学习如何选择最优的行动来最大化累积的奖励。强化学习在许多领域都有广泛的应用,例如游戏、机器人、自动驾...【详细内容】
2024-01-30  大噬元兽  微信公众号  Tags:框架   点击:(68)  评论:(0)  加入收藏
Spring实现Kafka重试Topic,真的太香了
概述Kafka的强大功能之一是每个分区都有一个Consumer的偏移值。该偏移值是消费者将读取的下一条消息的值。可以自动或手动增加该值。如果我们由于错误而无法处理消息并想重...【详细内容】
2024-01-26  HELLO程序员  微信公众号  Tags:Spring   点击:(88)  评论:(0)  加入收藏
SpringBoot如何实现缓存预热?
缓存预热是指在 Spring Boot 项目启动时,预先将数据加载到缓存系统(如 Redis)中的一种机制。那么问题来了,在 Spring Boot 项目启动之后,在什么时候?在哪里可以将数据加载到缓存系...【详细内容】
2024-01-19   Java中文社群  微信公众号  Tags:SpringBoot   点击:(86)  评论:(0)  加入收藏
站内最新
站内热门
站内头条