局网站建设意见,做网站网站条件,网站字体怎么设置,领动做的企业网站怎么样优化日志对象创建以及日志对象复用
日志对象上下文实体类
traceId
请求到达时间戳
请求完成时间戳
请求总共耗费时长
get/post/put/delete请求方式
Http状态码
原始请求头中的所有键值对
请求体内容
响应体内容
失败Exception信息详细记录
是否命中缓存
package c…优化日志对象创建以及日志对象复用
日志对象上下文实体类
traceId
请求到达时间戳
请求完成时间戳
请求总共耗费时长
get/post/put/delete请求方式
Http状态码
原始请求头中的所有键值对
请求体内容
响应体内容
失败Exception信息详细记录
是否命中缓存
package com.kira.scaffoldmvc.CommonPool;import lombok.Data;
import lombok.experimental.Accessors;/*** RTA代理上下文存储请求处理全流程的关键信息* 设计特点* - 线程隔离通过ThreadLocal管理确保每个请求独立使用* - 链式调用通过Accessors(chain true)支持方法链风格* - 全量数据包含请求、响应、转发和缓存等完整生命周期信息*/
Data
Accessors(chain true)
public class RtaProxyContext {/*** 全局唯一请求IDTraceID* 用于全链路追踪关联请求与响应日志*/private String reqXid;/*** 请求到达时间戳毫秒* 用于计算请求处理耗时*/private long reqTime;/*** 请求路径*/private String url;/*** HTTP请求方法GET/POST等*/private String reqType;/*** 请求头信息* 存储原始HTTP请求头的键值对*/private Object reqHeaders;/*** 请求体内容* 通常为JSON格式的广告请求参数*/private Object reqBody;/*** 响应体内容* 最终返回给客户端的内容*/private Object respBody;/*** HTTP响应状态码* 如200、403、500等*/private Integer respCode;/*** 响应完成时间戳毫秒* 用于计算总处理耗时respTime - reqTime*/private long respTime;/*** 错误详情* 当请求处理过程中发生异常时记录*/private String errorDetails;/*** 请求体填充率* 广告请求中有效流量占比范围0.0-1.0*/private double reqBodyFillRate;/*** 是否命中缓存标识* true表示响应数据来自缓存而非实时计算*/private Boolean cached false;} RtaProxyContextHolder-对象池定义与封装
最小空闲对象数200系统启动的时候预热
最大空闲对象数600
最大对象数1000
setMaxTotal(1000)池内对象的最大数量活跃 空闲
setMaxIdle(600)最大空闲对象数超过此数量的空闲对象将被销毁
setMinIdle(200)最小空闲对象数池会自动维持此数量的空闲对象
package com.kira.scaffoldmvc.CommonPool;import lombok.Data;
import lombok.experimental.Accessors;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.pool2.BasePooledObjectFactory;
import org.apache.commons.pool2.PooledObject;
import org.apache.commons.pool2.impl.DefaultPooledObject;
import org.apache.commons.pool2.impl.GenericObjectPool;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;//ThreadLocal结合对象池,封装了对象池的相关操作
Slf4j
public class RtaProxyContextHolder {private static final ThreadLocalRtaProxyContext CONTEXT_HOLDER new ThreadLocal();public static final GenericObjectPoolRtaProxyContext RTA_PROXY_CONTENT_POOL new GenericObjectPool(new RtaProxyContextFactory(),//对象池工厂new GenericObjectPoolConfig() {{//对象池配置setMaxTotal(1000);//最大对象数setMaxIdle(600);//最大空闲对象数setMinIdle(200);//最小空闲对象数}});//自定义对象池工厂private static class RtaProxyContextFactory extends BasePooledObjectFactoryRtaProxyContext {//创建新对象Overridepublic RtaProxyContext create() {return new RtaProxyContext();}//负责将新创建或从其他地方获取的 RtaProxyContext 对象包装成 PooledObjectRtaProxyContext 类型的对象// 这里使用 DefaultPooledObject 进行包装以便对象池进行统一管理//将其他地方的对象(例如不是对象池创建的对象)转换成对象池对象实现复用Overridepublic PooledObjectRtaProxyContext wrap(RtaProxyContext context) {return new DefaultPooledObject(context);}Overridepublic void destroyObject(PooledObjectRtaProxyContext p) {// 销毁对象的逻辑}}public static RtaProxyContext getContext() {return CONTEXT_HOLDER.get();}public static RtaProxyContext borrowContext() {RtaProxyContext rtaProxyContext;try {//先从对象池获取对象rtaProxyContext RTA_PROXY_CONTENT_POOL.borrowObject();} catch (Exception e) {//对象池获取对象失败,为了保证对象池可以成功创建,所以要new一个对象rtaProxyContext new RtaProxyContext();log.warn(Failed to pull from pool);}//将详细的日志对象放到ThreadLocal里面CONTEXT_HOLDER.set(rtaProxyContext);return rtaProxyContext;}public static void returnContext(RtaProxyContext context) {//将日志对象从ThreadLocal中移除CONTEXT_HOLDER.remove();//将原本的日志对象的大部分值变成空值if (context ! null) {try {context.setReqXid(null);context.setReqTime(-1);context.setReqType(null);context.setReqHeaders(null);context.setReqBody(null);context.setRespBody(null);context.setRespCode(-1);context.setRespTime(-1);context.setErrorDetails(null);context.setReqBodyFillRate(-1);context.setCached(false);RTA_PROXY_CONTENT_POOL.returnObject(context);} catch (Exception e) {log.warn(Failed to return to pool);}}}//日志打印对象池的状态public static void printPoolStatus() {log.info(Context Pool状态, 活跃对象数 {}, 空闲对象数 {}, 等待获取对象的线程数 {}, 对象创建总次数 {}, 对象销毁总次数 {}.,RTA_PROXY_CONTENT_POOL.getNumActive(),RTA_PROXY_CONTENT_POOL.getNumIdle(),RTA_PROXY_CONTENT_POOL.getNumWaiters(),RTA_PROXY_CONTENT_POOL.getCreatedCount(),RTA_PROXY_CONTENT_POOL.getDestroyedCount());}// 动态调整对象池配置public static void adjustPoolConfig(int maxTotal, int maxIdle, int minIdle) {GenericObjectPoolConfigRtaProxyContext config new GenericObjectPoolConfig();config.setMaxTotal(maxTotal);config.setMaxIdle(maxIdle);config.setMinIdle(minIdle);// 应用新配置RTA_PROXY_CONTENT_POOL.setConfig(config);log.info(对象池配置已更新: maxTotal{}, maxIdle{}, minIdle{}, maxTotal, maxIdle, minIdle);}} RtaProxyContextHolder-请求拦截器
package com.kira.scaffoldmvc.CommonPool;import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.servlet.HandlerInterceptor;import java.util.Collections;
import java.util.stream.Collectors;/*** HTTP请求拦截器管理请求生命周期内的上下文对象(RtaProxyContext)* 主要功能* 1. 请求进入时从对象池获取上下文对象并初始化* 2. 请求处理过程中通过ThreadLocal存储上下文* 3. 请求结束后记录日志并归还对象到池** 线程安全机制* - 使用ThreadLocal确保每个请求线程拥有独立的上下文实例* - 对象池复用机制减少频繁创建/销毁对象的开销*/
Component
RequiredArgsConstructor
public class RtaProxyContextInterceptor implements HandlerInterceptor {private final LogService logService;Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {// 1. 从对象池获取可复用的上下文对象// 若池为空则创建新对象避免频繁new操作带来的GC压力RtaProxyContext rtaProxyContext RtaProxyContextHolder.borrowContext();// 2. 初始化上下文设置请求基础信息rtaProxyContext.setReqTime(System.currentTimeMillis())//请求时间戳.setReqXid(request.getHeader(traceId))// 全局唯一请求ID(traceId).setReqType(request.getMethod())// HTTP方法(GET/POST等).setReqHeaders(Collections.list(request.getHeaderNames())//请求头信息.stream().collect(Collectors.toMap(name - name, request::getHeader))).setUrl(request.getRequestURI());//请求url// 3. 放行请求继续处理链return true;}Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {// 1. 从当前线程获取上下文对象// 确保与preHandle中设置的是同一个实例RtaProxyContext rtaProxyContext RtaProxyContextHolder.getContext();// 2. 补充响应信息状态码、响应时间、异常详情rtaProxyContext.setRespCode(response.getStatus()) // HTTP响应状态码.setRespTime(System.currentTimeMillis()) // 响应完成时间.setErrorDetails(ex ! null ? ex.getMessage() : null); // 异常信息(若有)// 3. 针对特定API路径记录详细日志// 注意此处硬编码路径需根据实际业务调整if (/api/v1/aff_rta.equals(request.getRequestURI())) {//因为该日志是打印对象信息,所以封装了对应的实现类logService.logRtaAccess(RtaAccessLog.from(rtaProxyContext)); // 记录访问日志}// 4. 关键资源回收步骤// - 从ThreadLocal中移除引用防止内存泄漏// - 重置对象状态并归还到对象池供后续请求复用RtaProxyContextHolder.returnContext(rtaProxyContext);}
} 定时任务-打印对象池状态
package com.kira.scaffoldmvc.CommonPool;import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;Component
public class SysMonitorConfig {Scheduled(fixedRateString ${sys.monitor.pool.rate:300000})public void monitorContextPool() {RtaProxyContextHolder.printPoolStatus();}}
定时任务-自适应调整对象池参数
package com.kira.scaffoldmvc.CommonPool;import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;import java.util.ArrayList;
import java.util.concurrent.ConcurrentLinkedQueue;import static com.kira.scaffoldmvc.CommonPool.RtaProxyContextHolder.RTA_PROXY_CONTENT_POOL;Component
public class SysMonitorConfig {// 滑动窗口配置private final ArrayListInteger list new ArrayList();//最大修改范围是原来对象池范围的两倍或三倍private final Integer minIdle 600;private final Integer maxIdle 1800;private final Integer maxTotal 3000;//每五分钟打印一次对象池状态,并将当前活跃对象数放进List中Scheduled(cron 0 0/5 * * * *)public void monitorContextPool() {RtaProxyContextHolder.printPoolStatus();list.add(RTA_PROXY_CONTENT_POOL.getNumActive());}//每半小时统计后5个滑动窗口里的对象数,如果平均活跃对象数大于当前对象池中配置的最大空闲对象数,则更新Scheduled(cron 0 0/30 * * * *)public void adjustContextPool() {Integer averageIdle 0;Integer sum 0;for (int i list.size() - 1, j 0; j 6; i--, j) {sum list.get(i);}averageIdle sum / 6;if(averageIdleRTA_PROXY_CONTENT_POOL.getMaxIdle() RTA_PROXY_CONTENT_POOL.getMaxIdle()!maxIdle){//平均活跃数大于对象池最大空闲对象数,则进行更新,如果对象池最大空闲对象数已经更新到了可达到的最大值,那么就没必要更新int minSize 200;if(averageIdle/2RTA_PROXY_CONTENT_POOL.getMaxIdle()){//如果平均活跃数/2大于最小活跃数,则直接更新成允许范围的最大值minSizeminIdle;}int maxSize RTA_PROXY_CONTENT_POOL.getMaxIdle();while(maxSizeaverageIdle){maxSize 600;}if(maxSizemaxIdle){maxSizemaxIdle;}//更新对象池配置RtaProxyContextHolder.adjustPoolConfig(maxTotal,maxSize,minSize);}}} 如何分析响应速度
响应速度拿出之前没使用对象池时url的平均时间和使用对象池后url的平均时间进行对比 总结
因为不同的业务要用到不同的信息所以将一个请求的信息封装到对象里面这样子可以应付许多不同的日志打印场景或其他需要使用请求信息的业务
例如我们把
traceId
请求到达时间戳
请求完成时间戳
请求总共耗费时长
get/post/put/delete请求方式
Http状态吗
原始请求头中的所有键值对
请求体内容
响应体内容
失败Exception信息详细记录
是否命中缓存
封装到对象里面日志信息非常详细可以更加方便排查问题操作对象也可以使获取信息更简单 定时任务每五分钟打印对象对象池的状态
定时任务每30分钟检查之前的平均活跃对象数是否大于对象池中的最大空闲对象数如果大于则通过自适应策略对对象池容量进行一个自适应更新
通过对比不同接口使用对象池前后的用时时间计算出提高了多少的响应时间