1. SQL 同步最新

2. 优化 trace 相关的逻辑
This commit is contained in:
YunaiV 2021-04-27 00:32:57 +08:00
parent e65488265d
commit 6f6e8777e7
9 changed files with 668 additions and 151 deletions

View File

@ -56,7 +56,8 @@
| | MySQL 监控 | 监视当前系统数据库连接池状态可进行分析SQL找出系统性能瓶颈 | | | MySQL 监控 | 监视当前系统数据库连接池状态可进行分析SQL找出系统性能瓶颈 |
| | Redis 监控 |监控 Redis 数据库的使用情况,使用的 Redis Key 管理 | | | Redis 监控 |监控 Redis 数据库的使用情况,使用的 Redis Key 管理 |
| 🚀 |Java 监控 | 基于 Spring Boot Admin 实现 Java 应用的监控 | | 🚀 |Java 监控 | 基于 Spring Boot Admin 实现 Java 应用的监控 |
| 🚀 | 链路追踪 | 基于 SkyWalking 实现性能监控,特别是链路的追踪 | | 🚀 | 链路追踪 | 接入 SkyWalking 组件,实现链路追踪 |
| 🚀 | 日志中心 | 接入 SkyWalking 组件,实现日志中心 |
| 🚀 | 分布式锁 | 基于 Redis 实现分布式锁,满足并发场景 | | 🚀 | 分布式锁 | 基于 Redis 实现分布式锁,满足并发场景 |
| 🚀 | 幂等组件 | 基于 Redis 实现幂等组件,解决重复请求问题 | | 🚀 | 幂等组件 | 基于 Redis 实现幂等组件,解决重复请求问题 |
| 🚀 | 服务保障 | 基于 Resilience4j 实现服务的稳定性,包括限流、熔断等功能 | | 🚀 | 服务保障 | 基于 Resilience4j 实现服务的稳定性,包括限流、熔断等功能 |

File diff suppressed because one or more lines are too long

View File

@ -1,8 +1,9 @@
package cn.iocoder.dashboard.framework.tracer.core.aop; package cn.iocoder.dashboard.framework.tracer.core.aop;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import cn.iocoder.dashboard.framework.tracer.core.annotation.BizTrace; import cn.iocoder.dashboard.framework.tracer.core.annotation.BizTrace;
import cn.iocoder.dashboard.util.sping.SpElUtil; import cn.iocoder.dashboard.util.sping.SpringExpressionUtils;
import io.opentracing.Span; import io.opentracing.Span;
import io.opentracing.Tracer; import io.opentracing.Tracer;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@ -11,6 +12,9 @@ import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Aspect;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.util.Map;
import static java.util.Arrays.asList;
/** /**
* {@link BizTrace} 切面记录业务链路 * {@link BizTrace} 切面记录业务链路
@ -55,8 +59,9 @@ public class BizTraceAspect {
private void setBizTag(Span span, ProceedingJoinPoint joinPoint, BizTrace trace) { private void setBizTag(Span span, ProceedingJoinPoint joinPoint, BizTrace trace) {
try { try {
span.setTag(BizTrace.TYPE_TAG, StrUtil.toString(SpElUtil.analysisSpEl(trace.type(), joinPoint))); Map<String, Object> result = SpringExpressionUtils.parseExpressions(joinPoint, asList(trace.type(), trace.id()));
span.setTag(BizTrace.ID_TAG, StrUtil.toString(SpElUtil.analysisSpEl(trace.id(), joinPoint))); span.setTag(BizTrace.TYPE_TAG, MapUtil.getStr(result, trace.type()));
span.setTag(BizTrace.ID_TAG, MapUtil.getStr(result, trace.id()));
} catch (Exception ex) { } catch (Exception ex) {
log.error("[setBizTag][解析 bizType 与 bizId 发生异常]", ex); log.error("[setBizTag][解析 bizType 与 bizId 发生异常]", ex);
} }

View File

@ -79,7 +79,7 @@ public class ToolTestDemoController {
@ApiImplicitParam(name = "ids", value = "编号列表", required = true, dataTypeClass = List.class) @ApiImplicitParam(name = "ids", value = "编号列表", required = true, dataTypeClass = List.class)
@PreAuthorize("@ss.hasPermission('tool:test-demo:query')") @PreAuthorize("@ss.hasPermission('tool:test-demo:query')")
// @RateLimiter(name = "backendA") // @RateLimiter(name = "backendA")
@BizTrace(id = "1", type = "'user'") @BizTrace(id = "#ids", type = "'user'")
public CommonResult<List<ToolTestDemoRespVO>> getTestDemoList(@RequestParam("ids") Collection<Long> ids) { public CommonResult<List<ToolTestDemoRespVO>> getTestDemoList(@RequestParam("ids") Collection<Long> ids) {
List<ToolTestDemoDO> list = testDemoService.getTestDemoList(ids); List<ToolTestDemoDO> list = testDemoService.getTestDemoList(ids);
return success(ToolTestDemoConvert.INSTANCE.convertList(list)); return success(ToolTestDemoConvert.INSTANCE.convertList(list));

View File

@ -1,70 +0,0 @@
package cn.iocoder.dashboard.util.sping;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* SpEl解析类
*
* @author mashu
*/
public class SpElUtil {
private static SpelExpressionParser parser = new SpelExpressionParser();
private static DefaultParameterNameDiscoverer nameDiscoverer = new DefaultParameterNameDiscoverer();
private SpElUtil() {
}
/**
* 解析切面SpEL
*
* @param spElString 表达式
* @param joinPoint 切面点
* @return 执行界面
*/
public static Object analysisSpEl(String spElString, ProceedingJoinPoint joinPoint) {
// 通过joinPoint获取被注解方法
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
Method method = methodSignature.getMethod();
// 使用spring的DefaultParameterNameDiscoverer获取方法形参名数组
String[] paramNames = nameDiscoverer.getParameterNames(method);
// 解析过后的Spring表达式对象
Expression expression = parser.parseExpression(spElString);
// spring的表达式上下文对象
EvaluationContext context = new StandardEvaluationContext();
// 通过joinPoint获取被注解方法的形参
Object[] args = joinPoint.getArgs();
// 给上下文赋值
for (int i = 0; i < args.length; i++) {
context.setVariable(paramNames[i], args[i]);
}
return expression.getValue(context);
}
/**
* 批量解析切面SpEL
*
* @param spElStrings 表达式
* @param joinPoint 切面点
* @return 执行界面
*/
public static Map<String, Object> analysisSpEls(List<String> spElStrings, ProceedingJoinPoint joinPoint) {
if (null == spElStrings) {
return null;
}
Map<String, Object> resultMap = new HashMap<>(spElStrings.size());
spElStrings.forEach(expression -> resultMap.put(expression, analysisSpEl(expression, joinPoint)));
return resultMap;
}
}

View File

@ -1,46 +1,46 @@
package cn.iocoder.dashboard.util; package cn.iocoder.dashboard.util.sping;
import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.bean.BeanUtil;
import org.springframework.aop.framework.AdvisedSupport; import org.springframework.aop.framework.AdvisedSupport;
import org.springframework.aop.framework.AopProxy; import org.springframework.aop.framework.AopProxy;
import org.springframework.aop.support.AopUtils; import org.springframework.aop.support.AopUtils;
/** /**
* Spring AOP 工具类 * Spring AOP 工具类
* *
* 参考波克尔 http://www.bubuko.com/infodetail-3471885.html 实现 * 参考波克尔 http://www.bubuko.com/infodetail-3471885.html 实现
*/ */
public class AopTargetUtils { public class SpringAopUtils {
/** /**
* 获取代理的目标对象 * 获取代理的目标对象
* *
* @param proxy 代理对象 * @param proxy 代理对象
* @return 目标对象 * @return 目标对象
*/ */
public static Object getTarget(Object proxy) throws Exception { public static Object getTarget(Object proxy) throws Exception {
// 不是代理对象 // 不是代理对象
if (!AopUtils.isAopProxy(proxy)) { if (!AopUtils.isAopProxy(proxy)) {
return proxy; return proxy;
} }
// Jdk 代理 // Jdk 代理
if (AopUtils.isJdkDynamicProxy(proxy)) { if (AopUtils.isJdkDynamicProxy(proxy)) {
return getJdkDynamicProxyTargetObject(proxy); return getJdkDynamicProxyTargetObject(proxy);
} }
// Cglib 代理 // Cglib 代理
return getCglibProxyTargetObject(proxy); return getCglibProxyTargetObject(proxy);
} }
private static Object getCglibProxyTargetObject(Object proxy) throws Exception { private static Object getCglibProxyTargetObject(Object proxy) throws Exception {
Object dynamicAdvisedInterceptor = BeanUtil.getFieldValue(proxy, "CGLIB$CALLBACK_0"); Object dynamicAdvisedInterceptor = BeanUtil.getFieldValue(proxy, "CGLIB$CALLBACK_0");
AdvisedSupport advisedSupport = (AdvisedSupport) BeanUtil.getFieldValue(dynamicAdvisedInterceptor, "advised"); AdvisedSupport advisedSupport = (AdvisedSupport) BeanUtil.getFieldValue(dynamicAdvisedInterceptor, "advised");
return advisedSupport.getTargetSource().getTarget(); return advisedSupport.getTargetSource().getTarget();
} }
private static Object getJdkDynamicProxyTargetObject(Object proxy) throws Exception { private static Object getJdkDynamicProxyTargetObject(Object proxy) throws Exception {
AopProxy aopProxy = (AopProxy) BeanUtil.getFieldValue(proxy, "h"); AopProxy aopProxy = (AopProxy) BeanUtil.getFieldValue(proxy, "h");
AdvisedSupport advisedSupport = (AdvisedSupport) BeanUtil.getFieldValue(aopProxy, "advised"); AdvisedSupport advisedSupport = (AdvisedSupport) BeanUtil.getFieldValue(aopProxy, "advised");
return advisedSupport.getTargetSource().getTarget(); return advisedSupport.getTargetSource().getTarget();
} }
} }

View File

@ -0,0 +1,82 @@
package cn.iocoder.dashboard.util.sping;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ArrayUtil;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.List;
import java.util.Map;
/**
* Spring EL 表达式的工具类
*
* @author mashu
*/
public class SpringExpressionUtils {
private static final ExpressionParser EXPRESSION_PARSER = new SpelExpressionParser();
private static final ParameterNameDiscoverer PARAMETER_NAME_DISCOVERER = new DefaultParameterNameDiscoverer();
private SpringExpressionUtils() {
}
/**
* 从切面中单个解析 EL 表达式的结果
*
* @param joinPoint 切面点
* @param expressionString EL 表达式数组
* @return 执行界面
*/
public static Object parseExpression(ProceedingJoinPoint joinPoint, String expressionString) {
Map<String, Object> result = parseExpressions(joinPoint, Collections.singletonList(expressionString));
return result.get(expressionString);
}
/**
* 从切面中批量解析 EL 表达式的结果
*
* @param joinPoint 切面点
* @param expressionStrings EL 表达式数组
* @return 结果key 为表达式value 为对应值
*/
public static Map<String, Object> parseExpressions(ProceedingJoinPoint joinPoint, List<String> expressionStrings) {
// 如果为空则不进行解析
if (CollUtil.isEmpty(expressionStrings)) {
return MapUtil.newHashMap();
}
// 第一步构建解析的上下文 EvaluationContext
// 通过 joinPoint 获取被注解方法
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
Method method = methodSignature.getMethod();
// 使用 spring ParameterNameDiscoverer 获取方法形参名数组
String[] paramNames = PARAMETER_NAME_DISCOVERER.getParameterNames(method);
// Spring 的表达式上下文对象
EvaluationContext context = new StandardEvaluationContext();
// 给上下文赋值
if (ArrayUtil.isNotEmpty(paramNames)) {
Object[] args = joinPoint.getArgs();
for (int i = 0; i < paramNames.length; i++) {
context.setVariable(paramNames[i], args[i]);
}
}
// 第二步逐个参数解析
Map<String, Object> result = MapUtil.newHashMap(expressionStrings.size(), true);
expressionStrings.forEach(key -> {
Object value = EXPRESSION_PARSER.parseExpression(key).getValue(context);
result.put(key, value);
});
return result;
}
}

View File

@ -12,7 +12,7 @@ import cn.iocoder.dashboard.modules.system.dal.mysql.permission.SysMenuMapper;
import cn.iocoder.dashboard.modules.system.enums.permission.MenuTypeEnum; import cn.iocoder.dashboard.modules.system.enums.permission.MenuTypeEnum;
import cn.iocoder.dashboard.modules.system.mq.producer.permission.SysMenuProducer; import cn.iocoder.dashboard.modules.system.mq.producer.permission.SysMenuProducer;
import cn.iocoder.dashboard.modules.system.service.permission.impl.SysMenuServiceImpl; import cn.iocoder.dashboard.modules.system.service.permission.impl.SysMenuServiceImpl;
import cn.iocoder.dashboard.util.AopTargetUtils; import cn.iocoder.dashboard.util.sping.SpringAopUtils;
import cn.iocoder.dashboard.util.RandomUtils; import cn.iocoder.dashboard.util.RandomUtils;
import cn.iocoder.dashboard.util.object.ObjectUtils; import cn.iocoder.dashboard.util.object.ObjectUtils;
import com.google.common.collect.Multimap; import com.google.common.collect.Multimap;
@ -57,7 +57,7 @@ public class SysMenuServiceTest extends BaseDbUnitTest {
sysMenuService.initLocalCache(); sysMenuService.initLocalCache();
// 获取代理对象 // 获取代理对象
SysMenuServiceImpl target = (SysMenuServiceImpl) AopTargetUtils.getTarget(sysMenuService); SysMenuServiceImpl target = (SysMenuServiceImpl) SpringAopUtils.getTarget(sysMenuService);
Map<Long, SysMenuDO> menuCache = Map<Long, SysMenuDO> menuCache =
(Map<Long, SysMenuDO>) BeanUtil.getFieldValue(target, "menuCache"); (Map<Long, SysMenuDO>) BeanUtil.getFieldValue(target, "menuCache");
@ -227,7 +227,7 @@ public class SysMenuServiceTest extends BaseDbUnitTest {
public void testListMenusFromCache_success() throws Exception { public void testListMenusFromCache_success() throws Exception {
Map<Long, SysMenuDO> mockCacheMap = new HashMap<>(); Map<Long, SysMenuDO> mockCacheMap = new HashMap<>();
//获取代理对象 //获取代理对象
SysMenuServiceImpl target = (SysMenuServiceImpl) AopTargetUtils.getTarget(sysMenuService); SysMenuServiceImpl target = (SysMenuServiceImpl) SpringAopUtils.getTarget(sysMenuService);
BeanUtil.setFieldValue(target, "menuCache", mockCacheMap); BeanUtil.setFieldValue(target, "menuCache", mockCacheMap);
Map<Long, SysMenuDO> idMenuMap = new HashMap<>(); Map<Long, SysMenuDO> idMenuMap = new HashMap<>();
@ -256,7 +256,7 @@ public class SysMenuServiceTest extends BaseDbUnitTest {
public void testListMenusFromCache2_success() throws Exception { public void testListMenusFromCache2_success() throws Exception {
Map<Long, SysMenuDO> mockCacheMap = new HashMap<>(); Map<Long, SysMenuDO> mockCacheMap = new HashMap<>();
//获取代理对象 //获取代理对象
SysMenuServiceImpl target = (SysMenuServiceImpl) AopTargetUtils.getTarget(sysMenuService); SysMenuServiceImpl target = (SysMenuServiceImpl) SpringAopUtils.getTarget(sysMenuService);
BeanUtil.setFieldValue(target, "menuCache", mockCacheMap); BeanUtil.setFieldValue(target, "menuCache", mockCacheMap);
Map<Long, SysMenuDO> idMenuMap = new HashMap<>(); Map<Long, SysMenuDO> idMenuMap = new HashMap<>();

View File

@ -13,14 +13,8 @@ import cn.iocoder.dashboard.modules.system.dal.mysql.permission.SysRoleMapper;
import cn.iocoder.dashboard.modules.system.enums.permission.SysRoleTypeEnum; import cn.iocoder.dashboard.modules.system.enums.permission.SysRoleTypeEnum;
import cn.iocoder.dashboard.modules.system.mq.producer.permission.SysRoleProducer; import cn.iocoder.dashboard.modules.system.mq.producer.permission.SysRoleProducer;
import cn.iocoder.dashboard.modules.system.service.permission.impl.SysRoleServiceImpl; import cn.iocoder.dashboard.modules.system.service.permission.impl.SysRoleServiceImpl;
import cn.iocoder.dashboard.util.AopTargetUtils; import cn.iocoder.dashboard.util.sping.SpringAopUtils;
import cn.iocoder.dashboard.util.AssertUtils;
import cn.iocoder.dashboard.util.RandomUtils;
import cn.iocoder.dashboard.util.object.ObjectUtils;
import com.google.common.collect.Sets;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.annotation.Import; import org.springframework.context.annotation.Import;
@ -63,7 +57,7 @@ public class SysRoleServiceTest extends BaseDbUnitTest {
//断言 //断言
//获取代理对象 //获取代理对象
SysRoleServiceImpl target = (SysRoleServiceImpl) AopTargetUtils.getTarget(sysRoleService); SysRoleServiceImpl target = (SysRoleServiceImpl) SpringAopUtils.getTarget(sysRoleService);
Map<Long, SysRoleDO> roleCache = (Map<Long, SysRoleDO>) BeanUtil.getFieldValue(target, "roleCache"); Map<Long, SysRoleDO> roleCache = (Map<Long, SysRoleDO>) BeanUtil.getFieldValue(target, "roleCache");
assertPojoEquals(roleDO1, roleCache.get(roleDO1.getId())); assertPojoEquals(roleDO1, roleCache.get(roleDO1.getId()));