feat(商城): 使用MDC实现接口请求调用追踪,以及用户ID记录

master
wayn 2 years ago
parent 951d8eccc5
commit 81e991323f

@ -0,0 +1,30 @@
package com.wayn.common.task;
import com.wayn.common.util.ThreadMdcUtil;
import org.slf4j.MDC;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.Callable;
import java.util.concurrent.Future;
/**
* 线reqest_id
*/
public class ThreadPoolExecutorMdcWrapper extends ThreadPoolTaskExecutor {
@Override
public void execute(Runnable task) {
super.execute(ThreadMdcUtil.wrap(task, MDC.getCopyOfContextMap()));
}
@Override
public <T> Future<T> submit(Callable<T> task) {
return super.submit(ThreadMdcUtil.wrap(task, MDC.getCopyOfContextMap()));
}
@Override
public Future<?> submit(Runnable task) {
return super.submit(ThreadMdcUtil.wrap(task, MDC.getCopyOfContextMap()));
}
}

@ -0,0 +1,72 @@
package com.wayn.common.util;
import org.slf4j.MDC;
import java.util.Map;
import java.util.concurrent.Callable;
/**
* MDCreqest_id线reqest_id
*/
public class ThreadMdcUtil {
public static final String REQUEST_ID = "request_id";
/**
* ID
*/
public static void setTraceIdIfAbsent() {
if (MDC.get(REQUEST_ID) == null) {
MDC.put(REQUEST_ID, IdUtil.getUid());
}
}
/**
* userIdREQUEST_ID
* @param userId
*/
public static void setUserId(String userId) {
String s = MDC.get(REQUEST_ID);
if (s != null) {
MDC.put(REQUEST_ID, s + "_" + userId);
}
}
public static void removeTraceId() {
MDC.remove(REQUEST_ID);
}
public static <T> Callable<T> wrap(final Callable<T> callable, final Map<String, String> context) {
return () -> {
if (context == null) {
MDC.clear();
} else {
MDC.setContextMap(context);
}
setTraceIdIfAbsent();
try {
return callable.call();
} finally {
MDC.clear();
}
};
}
public static Runnable wrap(final Runnable runnable, final Map<String, String> context) {
return () -> {
if (context == null) {
MDC.clear();
} else {
MDC.setContextMap(context);
}
// 设置traceId
setTraceIdIfAbsent();
try {
runnable.run();
} finally {
MDC.clear();
}
};
}
}

@ -8,7 +8,9 @@ import com.wayn.common.util.R;
import com.wayn.mobile.api.domain.Cart;
import com.wayn.mobile.api.service.ICartService;
import com.wayn.mobile.framework.security.util.MobileSecurityUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.flyway.FlywayDataSource;
import org.springframework.web.bind.annotation.*;
import java.math.BigDecimal;
@ -22,6 +24,7 @@ import java.util.List;
* @author wayn
* @since 2020-08-03
*/
@Slf4j
@RestController
@RequestMapping("cart")
public class CartController extends BaseController {
@ -33,7 +36,9 @@ public class CartController extends BaseController {
public R list() {
Long userId = MobileSecurityUtils.getUserId();
Page<Cart> page = getPage();
return iCartService.list(page, userId);
R list = iCartService.list(page, userId);
log.info("cart list:{}", list);
return list;
}
@PostMapping

@ -53,36 +53,41 @@ public class IHomeServiceImpl implements IHomeService {
long time = (long) shopHomeIndexHash.get(SHOP_HOME_INDEX_HASH_EXPIRATION_FIELD);
if ((new Date().getTime() - time) <= Constants.ONE_DAY) {
shopHomeIndexHash.forEach(success::add);
log.info("getHomeIndexDataCompletableFuture:{}", success);
return success;
}
}
List<CompletableFuture<Void>> list = new ArrayList<>(4);
CompletableFuture<Void> f1 = CompletableFuture.supplyAsync(
() -> iBannerService.list(Wrappers.lambdaQuery(Banner.class).eq(Banner::getStatus, 0).orderByAsc(Banner::getSort)), homeThreadPoolTaskExecutor)
() -> iBannerService.list(Wrappers.lambdaQuery(Banner.class).eq(Banner::getStatus, 0).orderByAsc(Banner::getSort)), homeThreadPoolTaskExecutor)
.thenAccept(data -> {
String key = "bannerList";
redisCache.setCacheMapValue(SHOP_HOME_INDEX_HASH, key, data);
log.info("bannerList:{}", data);
success.add(key, data);
});
CompletableFuture<Void> f2 = CompletableFuture.supplyAsync(
() -> iDiamondService.list(Wrappers.lambdaQuery(Diamond.class).orderByAsc(Diamond::getSort).last("limit 10")), homeThreadPoolTaskExecutor)
() -> iDiamondService.list(Wrappers.lambdaQuery(Diamond.class).orderByAsc(Diamond::getSort).last("limit 10")), homeThreadPoolTaskExecutor)
.thenAccept(data -> {
String key = "categoryList";
redisCache.setCacheMapValue(SHOP_HOME_INDEX_HASH, key, data);
log.info("categoryList:{}", data);
success.add(key, data);
});
CompletableFuture<Void> f3 = CompletableFuture.supplyAsync(
() -> iGoodsService.selectHomeIndexGoods(Goods.builder().isNew(true).build()), homeThreadPoolTaskExecutor)
() -> iGoodsService.selectHomeIndexGoods(Goods.builder().isNew(true).build()), homeThreadPoolTaskExecutor)
.thenAccept(data -> {
String key = "newGoodsList";
redisCache.setCacheMapValue(SHOP_HOME_INDEX_HASH, key, data);
log.info("newGoodsList:{}", data);
success.add(key, data);
});
CompletableFuture<Void> f4 = CompletableFuture.supplyAsync(
() -> iGoodsService.selectHomeIndexGoods(Goods.builder().isHot(true).build()), homeThreadPoolTaskExecutor)
() -> iGoodsService.selectHomeIndexGoods(Goods.builder().isHot(true).build()), homeThreadPoolTaskExecutor)
.thenAccept(data -> {
String key = "hotGoodsList";
redisCache.setCacheMapValue(SHOP_HOME_INDEX_HASH, key, data);
log.info("hotGoodsList:{}", data);
success.add(key, data);
});
list.add(f1);

@ -1,8 +1,10 @@
package com.wayn.mobile.framework.config;
import com.wayn.common.task.ThreadPoolExecutorMdcWrapper;
import com.wayn.common.util.ThreadUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.concurrent.BasicThreadFactory;
import org.jetbrains.annotations.NotNull;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
@ -31,15 +33,7 @@ public class ThreadPoolConfig {
@Bean(name = "homeThreadPoolTaskExecutor")
public ThreadPoolTaskExecutor homeThreadPoolTaskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setMaxPoolSize(maxPoolSize);
executor.setCorePoolSize(corePoolSize);
executor.setQueueCapacity(queueCapacity);
executor.setKeepAliveSeconds(keepAliveSeconds);
// 线程池对拒绝任务(无线程可用)的处理策略
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
// 等待所有任务结束后再关闭线程池
executor.setWaitForTasksToCompleteOnShutdown(true);
ThreadPoolTaskExecutor executor = getThreadPoolTaskExecutor();
BasicThreadFactory build = new BasicThreadFactory.Builder()
.namingPattern("home-task-%d")
.uncaughtExceptionHandler((t, e) -> {
@ -51,9 +45,9 @@ public class ThreadPoolConfig {
return executor;
}
@Bean(name = "categoryThreadPoolTaskExecutor")
public ThreadPoolTaskExecutor categoryThreadPoolTaskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
@NotNull
private ThreadPoolTaskExecutor getThreadPoolTaskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolExecutorMdcWrapper();
executor.setMaxPoolSize(maxPoolSize);
executor.setCorePoolSize(corePoolSize);
executor.setQueueCapacity(queueCapacity);
@ -62,6 +56,12 @@ public class ThreadPoolConfig {
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
// 等待所有任务结束后再关闭线程池
executor.setWaitForTasksToCompleteOnShutdown(true);
return executor;
}
@Bean(name = "categoryThreadPoolTaskExecutor")
public ThreadPoolTaskExecutor categoryThreadPoolTaskExecutor() {
ThreadPoolTaskExecutor executor = getThreadPoolTaskExecutor();
BasicThreadFactory build = new BasicThreadFactory.Builder()
.namingPattern("category-task-%d")
.uncaughtExceptionHandler((t, e) -> {

@ -1,5 +1,6 @@
package com.wayn.mobile.framework.security.filter;
import com.wayn.common.util.ThreadMdcUtil;
import com.wayn.mobile.framework.security.LoginUserDetail;
import com.wayn.mobile.framework.security.service.TokenService;
import lombok.extern.slf4j.Slf4j;
@ -29,17 +30,26 @@ public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
@Autowired
private TokenService tokenService;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws ServletException, IOException {
LoginUserDetail loginUser = tokenService.getLoginUser(request);
if (Objects.nonNull(loginUser) && Objects.isNull(SecurityContextHolder.getContext().getAuthentication())) {
tokenService.verifyToken(loginUser);
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(loginUser, null, loginUser.getAuthorities());
authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
try {
// 入口传入请求ID
ThreadMdcUtil.setTraceIdIfAbsent();
LoginUserDetail loginUser = tokenService.getLoginUser(request);
if (Objects.nonNull(loginUser) && Objects.isNull(SecurityContextHolder.getContext().getAuthentication())) {
ThreadMdcUtil.setUserId(String.valueOf(loginUser.getMember().getId()));
tokenService.verifyToken(loginUser);
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(loginUser, null, loginUser.getAuthorities());
authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
}
chain.doFilter(request, response);
} finally {
// 出口移除请求ID
ThreadMdcUtil.removeTraceId();
}
chain.doFilter(request, response);
}
}

@ -43,12 +43,8 @@ token:
# 日志配置
logging:
level:
com.wayn: info
org.springframework: info
com.baomidou: info
file:
name: ${LOG_PATH_PREFIX:E:/home}/${spring.application.name}/info.log
name: ${LOG_PATH_PREFIX:E:/home/logs}/${spring.application.name}/info.log
# mybatis plus 配置
mybatis-plus:

@ -0,0 +1,57 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<springProperty scope="context" name="appName" source="spring.application.name" defaultValue="dev"/>
<property name="logPath" value="/home/logs/${appName}"/>
<property name="pattern"
value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%X{request_id}] [%thread] [%-5level] %logger{36}:%L %M - %msg%n"/>
<!-- 控制台输出 -->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<!-- encoder 默认配置为PatternLayoutEncoder -->
<encoder>
<pattern>${pattern}</pattern>
</encoder>
</appender>
<!-- 记录日志到文件 -->
<appender name="INFO" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${logPath}/info.log</file>
<encoder>
<pattern>${pattern}</pattern>
</encoder>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${logPath}/run.%d{yyyy-MM-dd}.log</fileNamePattern>
</rollingPolicy>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<!-- 过滤的级别 -->
<level>INFO</level>
<!-- 匹配时的操作:接收(记录) -->
<onMatch>ACCEPT</onMatch>
<!-- 不匹配时的操作:拒绝(不记录) -->
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<!-- 只记录SQL日志 -->
<appender name="SQL" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${logPath}/sql.log</file>
<encoder>
<pattern>${pattern}</pattern>
</encoder>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${logPath}/sql.%d{yyyy-MM-dd}.log</fileNamePattern>
</rollingPolicy>
</appender>
<logger name="org.springframework" level="INFO"/>
<logger name="io.lettuce.core" level="ERROR"/>
<!-- logger为java中的包 -->
<logger name="com.wayn.common.core.mapper" level="DEBUG" activity="false">
<appender-ref ref="SQL"/>
</logger>
<root level="INFO">
<appender-ref ref="STDOUT"/>
<appender-ref ref="INFO"/>
</root>
</configuration>
Loading…
Cancel
Save