This commit is contained in:
luke 2025-05-19 23:52:15 +08:00
commit 821bec35b5
21 changed files with 986 additions and 0 deletions

37
.gitignore vendored Normal file
View File

@ -0,0 +1,37 @@
target/
!.mvn/wrapper/maven-wrapper.jar
!**/src/main/**/target/
!**/src/test/**/target/
### STS ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
### IntelliJ IDEA ###
.idea
*.iws
*.iml
*.ipr
### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
build/
!**/src/main/**/build/
!**/src/test/**/build/
### VS Code ###
.vscode/
/arthas-output/
/doc-parser-server-log/
### Mac OS ###
.DS_Store

3
Readme.md Normal file
View File

@ -0,0 +1,3 @@
#TODO

295
pom.xml Normal file
View File

@ -0,0 +1,295 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>luke.micro.saas</groupId>
<artifactId>doc-parser-server</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>doc-parser-server</name>
<description>文档资源解析服务</description>
<properties>
<java.version>11</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<spring-boot.version>2.7.6</spring-boot.version>
<arthas-spring-boot-starter.version>3.6.7</arthas-spring-boot-starter.version>
<!-- 日志相关 -->
<slf4j.version>1.7.36</slf4j.version>
<log4j.core.version>2.17.1</log4j.core.version>
<aspectj.version>1.9.9.1</aspectj.version>
<!-- nacos相关 -->
<spring-cloud-starter-loadbalancer.version>3.1.5</spring-cloud-starter-loadbalancer.version>
<spring-cloud-starter-alibaba-nacos.version>2021.0.5.0</spring-cloud-starter-alibaba-nacos.version>
<spring-cloud-starter-openfeign.version>3.1.5</spring-cloud-starter-openfeign.version>
<jackson-databind.version>2.13.4.2</jackson-databind.version>
<jackson-core.version>2.13.4</jackson-core.version>
<lombok.version>1.18.24</lombok.version>
<commons-lang3.version>3.12.0</commons-lang3.version>
<hutool-all.version>5.7.20</hutool-all.version>
<guava.version>31.0.1-jre</guava.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.taobao.arthas</groupId>
<artifactId>arthas-spring-boot-starter</artifactId>
<version>${arthas-spring-boot-starter.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>${spring-boot.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>${spring-boot.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<version>${spring-boot.version}</version>
</dependency>
<!-- #日志依赖,参考 https://cdn.jsdelivr.net/gh/lukeyewu/pictures@main/uPic/202411/YhMx1M.png -->
<!-- 日志_抽象API层 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
</dependency>
<!-- 日志_适配层 -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>${log4j.core.version}</version>
</dependency>
<!-- 日志_非原生实现层 -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>${log4j.core.version}</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>${log4j.core.version}</version>
</dependency>
<!--切面-->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>${aspectj.version}</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>${aspectj.version}</version>
</dependency>
<!-- nacos相关 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<version>${spring-cloud-starter-openfeign.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
<version>${spring-cloud-starter-alibaba-nacos.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<version>${spring-cloud-starter-alibaba-nacos.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
<version>${spring-cloud-starter-loadbalancer.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson-databind.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>${jackson-core.version}</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
<version>${lombok.version}</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>${commons-lang3.version}</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>${hutool-all.version}</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>${guava.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
</dependency>
<dependency>
<groupId>com.taobao.arthas</groupId>
<artifactId>arthas-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<exclusions>
<exclusion>
<artifactId>logback-classic</artifactId>
<groupId>ch.qos.logback</groupId>
</exclusion>
<exclusion>
<artifactId>spring-boot-starter-logging</artifactId>
<groupId>org.springframework.boot</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- <dependency>-->
<!-- <groupId>org.springframework.cloud</groupId>-->
<!-- <artifactId>spring-cloud-starter-openfeign</artifactId>-->
<!-- </dependency>-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<!-- <dependency>-->
<!-- <groupId>com.alibaba.cloud</groupId>-->
<!-- <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>-->
<!-- </dependency>-->
<!-- <dependency>-->
<!-- <groupId>org.springframework.cloud</groupId>-->
<!-- <artifactId>spring-cloud-starter-loadbalancer</artifactId>-->
<!-- </dependency>-->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
<encoding>UTF-8</encoding>
<annotationProcessorPaths>
<path>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.17.1</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring-boot.version}</version>
<configuration>
<mainClass>com.doc.parser.DocParserApplication</mainClass>
</configuration>
<executions>
<execution>
<id>repackage</id>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,16 @@
package com.doc.parser;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* @author Luke.ye
*/
@SpringBootApplication
public class DocParserApplication {
public static void main(String[] args) {
SpringApplication.run(DocParserApplication.class, args);
}
}

View File

@ -0,0 +1,14 @@
package com.doc.parser.application.anno;
import java.lang.annotation.*;
/**
* @author Luke.ye
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
@Inherited
@Documented
public @interface OperationLog {
}

View File

@ -0,0 +1,42 @@
package com.doc.parser.application.aspect;
import cn.hutool.json.JSONUtil;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
/**
* @author Luke.ye
* @date 2025/5/7 15:01
*/
@Component
@Aspect
public class OperationLogAspect {
private static final Logger LOGGER = LoggerFactory.getLogger(OperationLogAspect.class);
@Around("@annotation(com.doc.parser.application.anno.OperationLog)")
public Object logOperation(ProceedingJoinPoint joinPoint) throws Throwable {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
Object result = null;
try {
result = joinPoint.proceed();
LOGGER.info("方法名 [{}] , 输入参数: {}, 返回结果: {}", method.getName()
, JSONUtil.toJsonStr(joinPoint.getArgs()), JSONUtil.toJsonStr(result));
} catch (Throwable ex) {
LOGGER.error("方法名 [{}] 抛出异常: {}", method.getName(), ex.getMessage(), ex);
throw ex;
}
return result;
}
}

View File

@ -0,0 +1,23 @@
package com.doc.parser.application.enums;
/**
* @Author: luke ye
* @Date:05/06/2025 15:19
* @Description:
*/
public enum ErrorCodeEnum {
INVALID_PARAMS(1, "参数错误"),
INNER_SERVER_ERROR(99, "服务端处理异常")
;
public int code;
public String desc;
ErrorCodeEnum(int code, String desc) {
this.code = code;
this.desc = desc;
}
}

View File

@ -0,0 +1,20 @@
package com.doc.parser.application.enums;
/**
* @author Luke.ye
* @date 2025/5/6 08:49
*/
public enum ExceptionTypeEnum {
INVALID_PARAMS(1, "请求参数校验失败!"),
;
public int code;
public String desc;
ExceptionTypeEnum(int code, String desc) {
this.code = code;
this.desc = desc;
}
}

View File

@ -0,0 +1,20 @@
package com.doc.parser.application.enums;
/**
* @author Luke.ye
* @date 2024/11/6 21:21
*/
public enum MDCKeyEnum {
TRACE_ID("traceId", "日志跟踪号"),
HOST_NAME("hostName", "主机名")
;
public String name;
public String desc;
MDCKeyEnum(String name, String desc) {
this.name = name;
this.desc = desc;
}
}

View File

@ -0,0 +1,24 @@
package com.doc.parser.application.exceptions;
/**
* @author Luke.ye
* @date 2025/5/6 08:47
*/
public class BizException extends RuntimeException {
private int code;
public BizException(int code, String message) {
super(message);
this.code = code;
}
public BizException(int code, String message, Throwable cause) {
super(message, cause);
this.code = code;
}
public int getCode() {
return code;
}
}

View File

@ -0,0 +1,98 @@
package com.doc.parser.application.filter;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.doc.parser.application.enums.MDCKeyEnum;
import com.doc.parser.infrastructure.config.DynamicConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.StopWatch;
import org.springframework.web.filter.OncePerRequestFilter;
import org.springframework.web.util.ContentCachingRequestWrapper;
import org.springframework.web.util.ContentCachingResponseWrapper;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.InetAddress;
import java.util.Optional;
import java.util.UUID;
/**
* Description: 交易日志记录
*/
@Component
public class LogFilter extends OncePerRequestFilter {
private final static Logger LOGGER = LoggerFactory.getLogger(LogFilter.class);
@Autowired
private DynamicConfig dynamicConfig;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
ContentCachingRequestWrapper contentCachingRequestWrapper = new ContentCachingRequestWrapper(request);
ContentCachingResponseWrapper contentCachingResponseWrapper = new ContentCachingResponseWrapper(response);
if (!"Y".equals(dynamicConfig.getRecordMsgBody())) {
// 不记录请求和响应信息
filterChain.doFilter(contentCachingRequestWrapper, contentCachingResponseWrapper);
contentCachingResponseWrapper.copyBodyToResponse();
return;
}
StopWatch stopWatch = new StopWatch();
try {
stopWatch.start();
//获取http header 中的traceid,没有默认生成一个,放入到mdc中,进行日志打印
String traceId = Optional.ofNullable(request.getHeader("traceId"))
.orElse(UUID.randomUUID().toString().replace("-", ""));
InetAddress addr = InetAddress.getLocalHost();
MDC.put(MDCKeyEnum.HOST_NAME.name, addr.getHostName());
MDC.put(MDCKeyEnum.TRACE_ID.name, traceId);
filterChain.doFilter(contentCachingRequestWrapper, contentCachingResponseWrapper);
} catch (Exception e) {
LOGGER.warn("交易跟踪号[{}]记录交易报文信息异常:{}", MDC.get(MDCKeyEnum.TRACE_ID.name), e.getMessage());
} finally {
byte[] requestBody = contentCachingRequestWrapper.getContentAsByteArray();
byte[] responseBody = contentCachingResponseWrapper.getContentAsByteArray();
try {
//获取请求路径
String url = contentCachingRequestWrapper.getRequestURL().toString();
//获取报文头交易码
String tranCode = Optional.ofNullable(contentCachingRequestWrapper.getHeader("TRANCODE")).orElse("UNKOWN");
//获取IP地址
String remoteIp = getRemortIP(contentCachingRequestWrapper);
//获取IP端口
int remotePort = contentCachingRequestWrapper.getRemotePort();
stopWatch.stop();
LOGGER.info("交易跟踪号[{}]\n请求路径[{}]\n交易码[{}]\n远程地址[{}]\n远程端口[{}]\n请求内容:[{}]\n响应内容:[{}]\n响应时间:[{}]毫秒",
MDC.get(MDCKeyEnum.TRACE_ID.name),
url,
tranCode,
remoteIp,
remotePort,
JSONUtil.toJsonStr(new String(requestBody)),
JSONUtil.toJsonStr(new String(responseBody)),
stopWatch.getTotalTimeMillis());
} catch (Exception e) {
LOGGER.warn("登记交易信息异常:" + e.getMessage());
}
MDC.remove(MDCKeyEnum.HOST_NAME.name);
MDC.remove(MDCKeyEnum.TRACE_ID.name);
contentCachingResponseWrapper.copyBodyToResponse();
}
}
private String getRemortIP(HttpServletRequest request) {
return StrUtil.isBlank(request.getHeader("x-forwarded-for")) ?
request.getRemoteAddr() :
request.getHeader("x-forwarded-for");
}
}

View File

@ -0,0 +1,47 @@
package com.doc.parser.application.filter;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.IOException;
import cn.hutool.core.util.IdUtil;
import com.doc.parser.application.enums.MDCKeyEnum;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.ThreadContext;
/**
* #TODO 使用log4j的traceId无法解决异步记录日志传递traceId的问题暂时先使用servlet的filter来实现
*
* @author Luke.ye
* @date 2025/5/6 14:21
*/
//@Component
public class ServletTraceIdFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
if (StringUtils.isBlank(ThreadContext.get(MDCKeyEnum.TRACE_ID.name))) {
// 如果没有 traceId则生成并添加一个新的 traceId
String traceId = IdUtil.randomUUID();
ThreadContext.put(MDCKeyEnum.TRACE_ID.name, traceId);
}
try {
chain.doFilter(request, response); // 执行业务逻辑
} finally {
ThreadContext.clearMap(); // 清理上下文
}
}
@Override
public void destroy() {}
}

View File

@ -0,0 +1,9 @@
package com.doc.parser.infrastructure.config;
/**
* @author Luke.ye
* @date 2025/5/19 11:18
*/
public class ConstantConfig {
}

View File

@ -0,0 +1,21 @@
package com.doc.parser.infrastructure.config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.stereotype.Component;
/**
* @author Luke.ye
* @date 2025/5/8 09:59
*/
@Component
@RefreshScope
public class DynamicConfig {
//是否记录请求和响应信息 Y-记录 N-不记录 默认记录
@Value("${micro.saas.doc.parser.recordMsgBody:Y}")
private String recordMsgBody;
public String getRecordMsgBody() {
return recordMsgBody;
}
}

View File

@ -0,0 +1,111 @@
package com.doc.parser.infrastructure.north.dto;
import cn.hutool.core.bean.BeanUtil;
import com.doc.parser.application.enums.ErrorCodeEnum;
import java.io.Serializable;
import java.util.Map;
/**
* @Author: luke ye
* @Date:05/06/2025 16:54
* @Description:
*/
public class ApiResponse<T> implements Serializable {
private static final long serialVersionUID = -7006468489245806612L;
private boolean status;
/**
* {@link ErrorCodeEnum}
*/
private int errorCode;
private String errorMessage;
private T data;
public ApiResponse() {
}
public ApiResponse(boolean status) {
this.status = status;
}
public static <T> ApiResponse<T> toSuccess(T result) {
ApiResponse<T> response = new ApiResponse<>();
response.setStatus(true);
response.setData(result);
return response;
}
public static <T> ApiResponse<T> toFalse() {
ApiResponse<T> apiResponse = new ApiResponse<>();
apiResponse.setStatus(false);
return apiResponse;
}
public static <T> ApiResponse<T> toFailure(String errorMessage) {
ApiResponse<T> response = new ApiResponse<>();
response.setStatus(false);
response.setErrorMessage(errorMessage);
return response;
}
public static <T> ApiResponse<T> toFailure(int errorCode) {
ApiResponse<T> response = new ApiResponse<>();
response.setStatus(false);
response.setErrorCode(errorCode);
return response;
}
public static <T> ApiResponse<T> toFailure(int errorCode, String errorMessage) {
return toFailure(errorCode, errorMessage, null);
}
public static <T> ApiResponse<T> toFailure(int errorCode, String errorMessage, T result) {
ApiResponse<T> response = new ApiResponse<>();
response.setStatus(false);
response.setErrorCode(errorCode);
response.setErrorMessage(errorMessage);
if (result != null) {
response.setData(result);
}
return response;
}
public Map toMap() {
return BeanUtil.beanToMap(this);
}
public boolean getStatus() {
return status;
}
public void setStatus(boolean status) {
this.status = status;
}
public int getErrorCode() {
return errorCode;
}
public void setErrorCode(int errorCode) {
this.errorCode = errorCode;
}
public String getErrorMessage() {
return errorMessage;
}
public void setErrorMessage(String errorMessage) {
this.errorMessage = errorMessage;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
}

View File

@ -0,0 +1,103 @@
package com.doc.parser.infrastructure.util;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.TimeUnit;
/**
* 使用 Guava 实现的本地缓存工具类
*
* @author Luke.ye
* @date 2025/5/7 08:46
*/
public class LocalCacheUtil {
private static final Logger LOGGER = LoggerFactory.getLogger(LocalCacheUtil.class);
// 默认缓存配置
private static final int DEFAULT_MAX_CACHE_SIZE = 1000; // 最大缓存条目数
private static final int DEFAULT_INITIAL_CAPACITY = 400; // 缓存初始容量
private static final int DEFAULT_EXPIRATION_DAYS = 7; // 缓存过期时间单位
private static Cache<String, Object> cache;
static {
// 初始化缓存使用默认配置
initializeCache(DEFAULT_MAX_CACHE_SIZE, DEFAULT_INITIAL_CAPACITY, DEFAULT_EXPIRATION_DAYS);
}
/**
* 初始化缓存
*
* @param maxSize 缓存的最大容量
* @param initialCapacity 缓存的初始容量
* @param expirationDays 缓存条目的过期时间单位
*/
private static void initializeCache(int maxSize, int initialCapacity, int expirationDays) {
cache = CacheBuilder.newBuilder()
.expireAfterAccess(expirationDays, TimeUnit.DAYS) // 设置访问过期时间
.initialCapacity(initialCapacity) // 设置初始容量
.maximumSize(maxSize) // 设置最大缓存条目数
.build();
}
/**
* 根据 key 获取缓存中的值
*
* @param key 缓存的键
* @return 返回对应的值如果键不存在则返回 null
*/
public static Object get(String key) {
return cache.getIfPresent(key);
}
/**
* 向缓存中存储一个键值对
* 如果缓存容量接近上限记录警告日志
*
* @param key 缓存的键
* @param value 缓存的值
*/
public static void put(String key, Object value) {
long size = cache.size();
if (size >= cache.stats().loadCount() && size >= DEFAULT_MAX_CACHE_SIZE >> 1) {
LOGGER.warn("缓存接近最大容量 {},当前大小为 {},建议增加缓存容量。", DEFAULT_MAX_CACHE_SIZE, size);
}
cache.put(key, value);
}
/**
* 根据 key 移除缓存中的条目
*
* @param key 要移除的键
*/
public static void remove(String key) {
cache.invalidate(key);
}
/**
* 清空缓存中的所有条目
*/
public static void clearAll() {
cache.invalidateAll();
}
/**
* 查询缓存中的所有键值对
*
* @return 返回包含缓存内容的并发映射
*/
public static ConcurrentMap<String, Object> getAll() {
return cache.asMap();
}
/**
* 打印当前缓存统计信息如命中率加载次数等
*/
public static void logCacheStats() {
LOGGER.info("缓存最大容量 {},当前大小为 {}, 缓存统计信息: {}", DEFAULT_MAX_CACHE_SIZE, cache.size(), cache.stats().toString());
}
}

View File

@ -0,0 +1,22 @@
# 日志
logging.config=classpath:log/log4j.xml
# nacos相关
## nacos公用
spring.cloud.nacos.config.data-id-without-suffix=nacos
spring.cloud.nacos.config.file-extension=properties
spring.config.import=nacos:${spring.cloud.nacos.config.data-id-without-suffix}.${spring.cloud.nacos.config.file-extension}?refresh=true
## Nacos配置中心
spring.cloud.nacos.config.username=nacos
spring.cloud.nacos.config.password=lukeye
spring.cloud.nacos.config.contextPath=/nacos
spring.cloud.nacos.config.server-addr=http://101.132.255.39:8848
spring.cloud.nacos.config.namespace=lukeye
spring.cloud.nacos.config.refreshEnabled=true
# Arthas配置
arthas.telnetPort=-1
arthas.httpPort=-1
arthas.ip=127.0.0.1
arthas.appName=${spring.application.name}
arthas.tunnel-server=ws://101.132.255.39:7777/ws

View File

@ -0,0 +1,2 @@
# TODO

View File

@ -0,0 +1,12 @@
spring.application.name=doc-parser-server
application.author=Luke.Ye
server.port=18080
# 日志相关
logging.config=classpath:log/log4j.xml
log4j2.enable.threadlocals=true
logging.level.com.doc.parser=info
logging.level.root=${logging.level.com.doc.parser}
logging.org.springframework.web=${logging.level.com.doc.parser}
logging.org.springframework.context=${logging.level.com.doc.parser}

View File

@ -0,0 +1,67 @@
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="INFO">
<!-- 使用相对路径指定日志目录 -->
<Properties>
<Property name="logDir">doc-parser-server-log</Property>
<Property name="appName">doc-parser-server</Property>
</Properties>
<!-- 文件日志输出(滚动策略)-->
<Appenders>
<!-- INFO 日志输出(滚动策略)-->
<RollingFile name="InfoFileAppender" fileName="${logDir}/${appName}-info.log"
filePattern="${logDir}/${appName}-info-%d{yyyy-MM-dd}-%i.log.gz">
<PatternLayout pattern="[%d{yyyy-MM-dd HH:mm:ss.SSS}] [Thread-ID: %threadId, Thread-Name: %t] [%-5level] [traceId={%X{traceId}}] [%c{1}:%line] - %msg%n"/>
<Policies>
<SizeBasedTriggeringPolicy size="50MB"/>
<TimeBasedTriggeringPolicy interval="1" modulate="true"/>
</Policies>
<DefaultRolloverStrategy max="7"/>
<Filters>
<ThresholdFilter level="INFO" onMatch="ACCEPT" onMismatch="DENY"/>
</Filters>
</RollingFile>
<!-- ERROR 日志输出(滚动策略)-->
<RollingFile name="ErrorFileAppender" fileName="${logDir}/${appName}-error.log"
filePattern="${logDir}/${appName}-error-%d{yyyy-MM-dd}-%i.log.gz">
<PatternLayout pattern="[%d{yyyy-MM-dd HH:mm:ss.SSS}] [Thread-ID: %threadId, Thread-Name: %t] [%-5level] [traceId={%X{traceId}}] [%c{1}:%line] - %msg%n"/>
<Policies>
<SizeBasedTriggeringPolicy size="50MB"/>
<TimeBasedTriggeringPolicy interval="1" modulate="true"/>
</Policies>
<DefaultRolloverStrategy max="7"/>
<Filters>
<ThresholdFilter level="ERROR" onMatch="ACCEPT" onMismatch="DENY"/>
</Filters>
</RollingFile>
<!-- 控制台输出 -->
<Console name="ConsoleAppender" target="SYSTEM_OUT">
<PatternLayout pattern="[%d{yyyy-MM-dd HH:mm:ss.SSS}] [Thread-ID: %threadId, Thread-Name: %t] [%-5level] [traceId={%X{traceId}}] [%c{1}:%line] - %msg%n"/>
</Console>
<!-- 异步文件日志输出 -->
<Async name="AsyncInfoFileAppender" includeLocation="true" blocking="false" bufferSize="256">
<AppenderRef ref="InfoFileAppender"/>
</Async>
<Async name="AsyncErrorFileAppender" includeLocation="true" blocking="false" bufferSize="256">
<AppenderRef ref="ErrorFileAppender"/>
</Async>
<!-- 异步控制台输出 -->
<Async name="AsyncConsoleAppender" includeLocation="true" blocking="false" bufferSize="256">
<AppenderRef ref="ConsoleAppender"/>
</Async>
</Appenders>
<Loggers>
<!-- 根日志配置,控制台和文件异步输出 -->
<Root level="INFO">
<AppenderRef ref="AsyncInfoFileAppender" />
<AppenderRef ref="AsyncErrorFileAppender" />
<!-- 仅供本地调试使用,打包时需注释掉 -->
<AppenderRef ref="AsyncConsoleAppender"/>
</Root>
</Loggers>
</Configuration>