Commit 3fa1c4e1 by QIANGLU

Merge branch 'dev'

# Conflicts:
#	common-core/pom.xml
#	common-util/pom.xml
#	config-starter/pom.xml
#	dubbo-starter/pom.xml
#	elasticsearch-starter/pom.xml
#	mongodb-starter/pom.xml
#	monitor-starter/pom.xml
#	mybatis-starter/pom.xml
#	openfeign-starter/pom.xml
#	pom.xml
#	redis-starter/pom.xml
#	rocketmq-starter/pom.xml
#	web-starter/pom.xml
parents 05c1c2de f27081bd
...@@ -24,5 +24,17 @@ ...@@ -24,5 +24,17 @@
* common-\*:通用依赖 * common-\*:通用依赖
* \*-starter:组件依赖 * \*-starter:组件依赖
开发版本 1.0.3-DEV-SNAPSHOT
正式版本
1.0.3.RELEASE
1、增加monitor traceId监控模块
2、增加防灾冗余
3、优化文件结构
1.0.2.RELEASE
...@@ -11,6 +11,6 @@ public interface CommonConstant { ...@@ -11,6 +11,6 @@ public interface CommonConstant {
String DEV = "dev"; String DEV = "dev";
String TEST = "test"; String TEST = "test";
String BETA = "beta"; String BETA = "beta";
String PROD = "prod"; String PRO = "pro";
} }
} }
...@@ -11,6 +11,6 @@ public class ProdEnvCondition implements Condition { ...@@ -11,6 +11,6 @@ public class ProdEnvCondition implements Condition {
@Override @Override
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) { public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
String[] profiles = conditionContext.getEnvironment().getActiveProfiles(); String[] profiles = conditionContext.getEnvironment().getActiveProfiles();
return !ArrayUtils.contains(profiles, CommonConstant.Env.PROD); return !ArrayUtils.contains(profiles, CommonConstant.Env.PRO);
} }
} }
package com.secoo.matrix.monitor.constant;
/**
* @author qianglu
*/
public interface MonitorConstant {
String DEFAULT_PORT = "0000";
String DEFAULT_IP = "127.0.0.1";
String TRACE_ID = "traceId";
String RPC_ID = "rpcId";
String APPLICATION = "application";
String INTERFACE = "interface";
String METHOD = "method";
String GROUP = "group";
String VERSION = "version";
String CONSUMER = "consumer";
String PROVIDER = "provider";
String TIMESTAMP = "timestamp";
String SUCCESS = "success";
String FAILURE = "failure";
String ROLE = "Dubbo-RPC";
String ERROR_MSG="errorMsg";
}
package com.secoo.matrix.monitor.http.intercept;
import com.secoo.matrix.monitor.utils.TraceIDUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicInteger;
/**
* @author qianglu
* <p>
* 为了在Http请求入口生成TRACE_ID,日志ID
* </p>
*/
public class TraceInterceptor extends HandlerInterceptorAdapter {
private Logger logger = LoggerFactory.getLogger(TraceInterceptor.class);
private final ConcurrentMap<String, AtomicInteger> concurrents = new ConcurrentHashMap<>();
private ThreadLocal<Long> stime = new ThreadLocal<>();
/**
* This implementation always returns {@code true}.
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
Object handler) throws Exception {
try {
TraceIDUtils.continueTraceID();
// Long startTime = System.currentTimeMillis();
// stime.set(startTime);
// // 并发计数
// getConcurrent(request).incrementAndGet();
}catch (Exception e){//容灾
}
return true;
}
/**
* This implementation is empty.
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
}
/**
* This implementation is empty.
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
Object handler, Exception ex) throws Exception {
try {
MDC.clear();
getConcurrent(request).decrementAndGet();
}catch (Exception e){
}
}
// 获取并发计数器
private AtomicInteger getConcurrent(HttpServletRequest request) {
String key = request.getRequestURI();
AtomicInteger concurrent = concurrents.get(key);
if (concurrent == null) {
concurrents.putIfAbsent(key, new AtomicInteger());
concurrent = concurrents.get(key);
}
return concurrent;
}
}
package com.secoo.matrix.monitor.rcp.filter;
import com.secoo.matrix.monitor.constant.MonitorConstant;
import com.secoo.matrix.monitor.utils.TraceIDUtils;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.rpc.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
/**
* @author QIANGLU
*/
@Activate(group = {MonitorConstant.CONSUMER, MonitorConstant.PROVIDER}, order = -1)
public class LoggerFilter implements Filter {
private Logger LOG = LoggerFactory.getLogger(getClass());
@Override
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
RpcContext rpcContext = RpcContext.getContext();
URL url = rpcContext.getUrl();
try {
String who = url.getParameter("side");
//CONSUMER 从MDC中获取RPCID并增加其序列,传递最新序列唯一ID到下一层
if (MonitorConstant.CONSUMER.equals(who)) {
String traceId = TraceIDUtils.continueTraceID();
rpcContext.setAttachment(MonitorConstant.TRACE_ID, traceId);
} else if (MonitorConstant.PROVIDER.equals(who)) {
MDC.remove(MonitorConstant.TRACE_ID);
MDC.remove("rpcId");
String traceId = rpcContext.getAttachment(MonitorConstant.TRACE_ID);
TraceIDUtils.childTraceID(traceId);
}
} catch (Exception e) {
}
return invoker.invoke(invocation);
}
}
package com.secoo.matrix.monitor.utils;
import com.secoo.matrix.monitor.constant.MonitorConstant;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
import java.math.BigDecimal;
/**
* 服务调用追踪号工具类
* 会使用到MDC
* <p>
* 规则:首层调用开始rpcId为"0.0"(TraceID已经生成),
* 如果有跨应用调用
* 1.调用方consumer会将rpcId追加1,例如0.0->0.1,并随RPCContext传递给下一层
* 2.服务方provider收到RPCContext,取出requestId放到本地MDC中并追加本地rpcId层级
* 例如,0.1->0.1.0
* </p>
*
* @author qianglu
*/
public class TraceIDUtils {
private static Logger log = LoggerFactory.getLogger(TraceIDUtils.class);
private final static String ZERO_DOT_ZERO = "0.0";
private final static String ZERO_DOT_ONE = "0.1";
private final static String ZERO_DOT_ONE_ZERO = "0.1.0";
private final static String H_BAR = "-";//横杠
/**
* 首次创建TraceID为UUID+"0.0",RpcID的初始值为"0.0"
*/
public static String createTraceID() {
StringBuilder sb = new StringBuilder();
String requestId = sb.append(UUIDUtils.randomUUID().toString()).append(H_BAR)
.append(ZERO_DOT_ZERO).toString();
MDC.put(MonitorConstant.TRACE_ID, requestId);
MDC.put(MonitorConstant.RPC_ID, ZERO_DOT_ZERO);
log.debug("Create New TraceID is [{}],RPCID is [{}]", requestId, ZERO_DOT_ZERO);
return requestId;
}
/**
* 追踪ID子层级追加,兼顾生成追踪ID
*
* @return
*/
public static String continueTraceID() {
StringBuilder sb = new StringBuilder();
//直接从本地MDC获取requestId
String traceID = MDC.get(MonitorConstant.TRACE_ID);
/*如果为空就生成一个新的.
* 之所以没有,可能服务消费者本身没有requestId初始化入口,所以这里补足
* requestId为UUID+"0.0"从0开始并赋值到本地MDC中,因为此方法为RPC调用下一级生成ID,所以返回的是UUID+"0.1"
* rpcId从"0.1"开始
* */
if (StringUtils.isEmpty(traceID) || "null".equals(traceID)) {
traceID = UUIDUtils.randomUUID().toString();
MDC.put(MonitorConstant.TRACE_ID, traceID + H_BAR + ZERO_DOT_ZERO);
MDC.put(MonitorConstant.RPC_ID, ZERO_DOT_ONE);
log.debug("There is no [traceID], create new noe![{}] and RPCID is [{}]",
traceID + H_BAR + ZERO_DOT_ZERO, ZERO_DOT_ONE);
return traceID + H_BAR + ZERO_DOT_ONE;
}
String rpcId = MDC.get(MonitorConstant.RPC_ID);
try {
/*正常追踪ID格式应该是 " 唯一字符串(UUID)-传递层次" .*/
String[] temp = traceID.split(H_BAR);
if (temp.length > 2) {
/*其它格式*/
} else if (temp.length == 2) {
if (StringUtils.isEmpty(rpcId)) {
/*如果rpcId为空,就是用requestId后缀层级号*/
MDC.put(MonitorConstant.RPC_ID, temp[1]);
rpcId = temp[1];
}
/*将rpcId,分解,最后一位的层级数值+1*/
String[] nums = rpcId.split("\\.");
for (int i = 0; i < nums.length; i++) {
if (i == (nums.length - 1)) {
sb.append(new BigDecimal(nums[i]).add(new BigDecimal("1")));
} else {
sb.append(nums[i]).append(".");
}
}
rpcId = sb.toString();//新的+1后的rpcId
log.debug("TraceID continued ![{}],and RPCID is [{}]",
temp[0] + "-" + sb.toString(), rpcId);
MDC.put(MonitorConstant.RPC_ID, rpcId);//更新本地MDC中的rpcId
return temp[0] + H_BAR + sb.toString();
} else if (temp.length < 2) {
/*可能不包含行号*/
}
} catch (Exception e) {
log.error("ContinueTraceID is failed!!!", e);
}
return traceID;
}
/**
* RPC调用层级追加
* 子层追加使用".1"来表示
* 例如第一层为0.1,调用一次后下一个应用为0.1.1,
*
* @param traceID
* @return
*/
public static void childTraceID(String traceID) {
StringBuilder sb = new StringBuilder();
if (StringUtils.isEmpty(traceID) || "null".equals(traceID)) {
/*如果为空就生成一个新的.
* 之所以没有,是消费者没有使用相同的requestId机制,所以本地重新生成
* requestId为UUID+"0.1"并赋值到本地MDC中,因为此方法为被调用,所以一定是下一级"
* rpcId从"0.1.0"开始
* */
traceID = sb.append(UUIDUtils.randomUUID().toString()).append(H_BAR)
.append(ZERO_DOT_ONE).toString();
MDC.put(MonitorConstant.TRACE_ID, traceID);
MDC.put(MonitorConstant.RPC_ID, ZERO_DOT_ONE_ZERO);
log.debug("There is no [traceID], create new noe![{}] and RPCID is [{}]", traceID,
ZERO_DOT_ONE_ZERO);
return;
}
String rpcId = MDC.get(MonitorConstant.RPC_ID);
try {
String[] temp = traceID.split(H_BAR);
if (temp.length > 2) {
/*其它格式*/
} else if (temp.length == 2) {
if (StringUtils.isEmpty(rpcId)) {
/*如果rpcId为空,就是用requestId后缀层级号*/
MDC.put(MonitorConstant.RPC_ID, temp[1]);
rpcId = temp[1];
}
rpcId = rpcId + ".0";
//更新本地MDC值,新增TraceID
MDC.put(MonitorConstant.RPC_ID, rpcId);
MDC.put(MonitorConstant.TRACE_ID, traceID);
log.debug("RPC_ID_KEY child is ![{}]", rpcId);
} else if (temp.length < 2) {
/*可能不包含行号*/
}
} catch (Exception e) {
log.error("ChildTraceID is failed!!!", e);
}
}
// public static void main(String[] args) {
//
// Logger log = LoggerFactory.getLogger(TraceIDUtils.class);
//
// TraceIDUtils.createTraceID();
// String s = "e591a82137f04423a4933cb0cff59374-0.2";
// MDC.put("requestId", "e591a82137f04423a4933cb0cff59374-0.0");
// MDC.put("rpcId", "0.2");
// System.out.println(TraceIDUtils.continueTraceID());
// MDC.remove(RPC_ID_KEY);
// TraceIDUtils.childTraceID("");
// }
}
logFilter=com.secoo.matrix.monitor.rcp.filter.LoggerFilter
...@@ -50,7 +50,7 @@ public class MybatisConfig { ...@@ -50,7 +50,7 @@ public class MybatisConfig {
/* 乐观锁插件 */ /* 乐观锁插件 */
// configuration.addInterceptor(new OptimisticLockerInterceptor()); // configuration.addInterceptor(new OptimisticLockerInterceptor());
//非生产环境加载的插件 //非生产环境加载的插件
if (!CommonConstant.Env.PROD.equals(profile) ) { if (!CommonConstant.Env.PRO.equals(profile) ) {
configuration.addInterceptor(new PerformanceInterceptor()); configuration.addInterceptor(new PerformanceInterceptor());
} }
......
package com.secoo.mall.openfeign.config; package com.secoo.mall.openfeign.config;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.Configuration;
@Configuration
public class FeignConfig { public class FeignConfig {
// TODO: 2019/5/8 实现enable package自动加载 实现灰度发布 client请求日志拦截 // TODO: 2019/5/8 实现enable package自动加载 实现灰度发布 client请求日志拦截
......
...@@ -8,6 +8,7 @@ import com.secoo.mall.common.util.response.ResponseUtil; ...@@ -8,6 +8,7 @@ import com.secoo.mall.common.util.response.ResponseUtil;
import com.secoo.mall.web.annotation.ApiController; import com.secoo.mall.web.annotation.ApiController;
import com.secoo.mall.web.annotation.ApiIgnoreJson; import com.secoo.mall.web.annotation.ApiIgnoreJson;
import org.springframework.context.MessageSource; import org.springframework.context.MessageSource;
import org.springframework.context.i18n.LocaleContext;
import org.springframework.context.i18n.LocaleContextHolder; import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.core.MethodParameter; import org.springframework.core.MethodParameter;
import org.springframework.core.annotation.AnnotationUtils; import org.springframework.core.annotation.AnnotationUtils;
...@@ -20,6 +21,7 @@ import org.springframework.web.bind.annotation.RestControllerAdvice; ...@@ -20,6 +21,7 @@ import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice; import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.util.Locale;
@RestControllerAdvice(annotations = ApiController.class) @RestControllerAdvice(annotations = ApiController.class)
public class ControllerResponseAdvice implements ResponseBodyAdvice<Object> { public class ControllerResponseAdvice implements ResponseBodyAdvice<Object> {
...@@ -34,8 +36,9 @@ public class ControllerResponseAdvice implements ResponseBodyAdvice<Object> { ...@@ -34,8 +36,9 @@ public class ControllerResponseAdvice implements ResponseBodyAdvice<Object> {
@ExceptionHandler(ParameterException.class) @ExceptionHandler(ParameterException.class)
public Object parameterExceptionHandler(ParameterException e) { public Object parameterExceptionHandler(ParameterException e) {
LoggerUtil.info(getMsg(e)); String msg=getMsg(e);
return ResponseUtil.getFailResponse(e.getCode(), getMsg(e)); LoggerUtil.info(msg);
return ResponseUtil.getFailResponse(e.getCode(), msg);
} }
@ExceptionHandler(Exception.class) @ExceptionHandler(Exception.class)
......
package com.secoo.mall.web.config; package com.secoo.mall.web.config;
import com.secoo.mall.web.resolver.DefaultLocaleResolver;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.ResourceBundleMessageSource; import org.springframework.context.support.ResourceBundleMessageSource;
import org.springframework.web.servlet.LocaleResolver; import org.springframework.web.servlet.LocaleResolver;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver;
import org.springframework.web.servlet.i18n.CookieLocaleResolver;
import org.springframework.web.servlet.i18n.LocaleChangeInterceptor; import org.springframework.web.servlet.i18n.LocaleChangeInterceptor;
import java.util.Locale; import java.util.Locale;
@Configuration @Configuration
public class WebConfig implements WebMvcConfigurer { public class WebConfig implements WebMvcConfigurer {
/**
* 自定义国际化文件存放路径
*
* @return
*/
/*@Bean
public org.springframework.validation.Validator getValidator() {
Validator validator = Validation.byDefaultProvider()
.configure()
.messageInterpolator(new ResourceBundleMessageInterpolator(new PlatformResourceBundleLocator("i18n/message")))
.buildValidatorFactory().getValidator();
return (org.springframework.validation.Validator) validator;
}*/
@Bean @Bean
public ResourceBundleMessageSource messageSource() { public ResourceBundleMessageSource messageSource() {
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource(); ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
...@@ -34,19 +21,13 @@ public class WebConfig implements WebMvcConfigurer { ...@@ -34,19 +21,13 @@ public class WebConfig implements WebMvcConfigurer {
return messageSource; return messageSource;
} }
@Bean
public LocaleResolver localeHeaderResolver() {
AcceptHeaderLocaleResolver localeResolver = new AcceptHeaderLocaleResolver();
localeResolver.setDefaultLocale(Locale.SIMPLIFIED_CHINESE);
return localeResolver;
}
@Bean @Bean(name = "localeResolver")
public LocaleResolver localeCookieResolver() { public LocaleResolver localeCookieResolver() {
CookieLocaleResolver localeResolver = new CookieLocaleResolver(); DefaultLocaleResolver localeResolver = new DefaultLocaleResolver();
localeResolver.setCookieName("language"); localeResolver.setCookieName("language");
//设置默认区域 //设置默认区域
localeResolver.setDefaultLocale(Locale.CANADA); localeResolver.setDefaultLocale(Locale.SIMPLIFIED_CHINESE);
localeResolver.setCookieMaxAge(3600);//设置cookie有效期. localeResolver.setCookieMaxAge(3600);//设置cookie有效期.
return localeResolver; return localeResolver;
} }
......
package com.secoo.mall.web.resolver;
import com.secoo.mall.common.util.string.StringUtil;
import org.springframework.lang.Nullable;
import org.springframework.web.servlet.i18n.CookieLocaleResolver;
import javax.servlet.http.HttpServletRequest;
import java.util.Locale;
public class DefaultLocaleResolver extends CookieLocaleResolver {
@Nullable
protected Locale determineDefaultLocale(HttpServletRequest request) {
if (!StringUtil.isEmpty(request.getHeader("Accept-Language"))) {
return Locale.forLanguageTag(request.getHeader("Accept-Language"));
}
String lang = request.getParameter("lang");
if (StringUtil.isNotEmpty(lang)) {
return parseLocaleValue(lang);
}
return super.determineDefaultLocale(request);
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment