目的
- 在log4j2的基礎(chǔ)上集成sleuth名斟,進(jìn)行日志調(diào)用鏈跟蹤,進(jìn)行日志分析
- 改造項(xiàng)目接口砰盐,將原有/task/id,改成通過gateway-->client-->server的鏈子岩梳,方便測試,并同時(shí)改造原有g(shù)ateway(參考上篇文章zuul實(shí)戰(zhàn))的驗(yàn)簽方式
簡介
在Peter Deutsch的《The Eight Fallacies of Distributed Computing》中指出八個(gè)分布式計(jì)算的誤區(qū):
- 網(wǎng)絡(luò)可靠
- 延遲為零
- 帶寬無限
- 網(wǎng)絡(luò)絕對安全
- 網(wǎng)絡(luò)拓?fù)洳粫?huì)變
- 必須有一個(gè)管理員
- 傳輸成本為零
- 網(wǎng)絡(luò)同質(zhì)化
總結(jié)下上述問題淘捡,重點(diǎn)出在網(wǎng)路問題藕各。網(wǎng)絡(luò)常常十分脆弱,而我們部署了微服務(wù)作彤,系統(tǒng)變多,網(wǎng)絡(luò)傳輸增多竭讳,對我們排查問題提出了挑戰(zhàn)浙踢。sleuth的作用就是解決這個(gè)問題,進(jìn)行調(diào)用跟蹤洛波,形成調(diào)用鏈,方便快速找出問題所在蹬挤。
sleuth有幾個(gè)專業(yè)術(shù)語:
- span(跨度):一次調(diào)用鏈每個(gè)應(yīng)用的唯一id
- trace (跟蹤):一次調(diào)用鏈所有應(yīng)用共享的唯一id
- annotation (標(biāo)注):記錄事件,定義請求為止:
- CS 客戶端發(fā)送請求
- SR 服務(wù)端收到請求
- SS 服務(wù)處理完請求倦零,發(fā)送返回信息
- CR 客戶端收到請求
開工
- 添加sleuth依賴
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>
- log4j2配置文件中添加sleuth配置,暫時(shí)還用的STDOUT模式吨悍,打印到控制臺,方便測試育瓜,通過自定義sleuth參數(shù)
[%X{X-B3-TraceId},%X{X-B3-SpanId},%X{X-B3-ParentSpanId},%X{X-Span-Export}]
進(jìn)行打印,其中TraceId為此次調(diào)用鏈共享id爆雹,SpanId本應(yīng)用唯一id,ParentSpanId為上級應(yīng)用唯一id慧起,X-Span-Export是否是發(fā)送給Zipkin(后邊我們將會(huì)用到,本章不討論)
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
<Appenders>
<File name="FileAppender" fileName="./log/logFile.log" append="true">
<PatternLayout pattern="%d %-5p %c:%L [%t] - %m%n" />
</File>
<Console name="STDOUT" target="SYSTEM_OUT">
//重點(diǎn)----自定義sleuth參數(shù)
<PatternLayout pattern="%d [%X{X-B3-TraceId},%X{X-B3-SpanId},%X{X-B3-ParentSpanId},%X{X-Span-Export}] %-5p %c:%L [%t] - %m%n" />
</Console>
<RollingFile name="RollingFileInfo" fileName="./log/bee-client.log"
filePattern="./log/bee-client-gz/$${date:yyyy-MM}/bee-client-%d{yyyy-MM-dd}-%i.log.gz">
<Filters>
<ThresholdFilter level="INFO" onMatch="ACCEPT" onMismatch="DENY"/>
</Filters>
<PatternLayout pattern="%d %-5p %c:%L [%t] - %m%n"/>
<Policies>
<TimeBasedTriggeringPolicy/>
<SizeBasedTriggeringPolicy size="200 M"/>
</Policies>
</RollingFile>
</Appenders>
<Loggers>
<Logger name="com.opensymphony.xwork2.ognl.OgnlValueStack" level="ERROR" />
<Logger name="open.template" level="DEBUG" />
<Logger name="org.springframework.cloud.netflix" level="DEBUG" />
<Logger name="com.alisoft.xplatform.asf" level="WARN" />
<Logger name="com.mbi" level="ERROR" />
<Logger name="net.mlw" level="INFO" />
<Logger name="java.sql" level="INFO" />
<Logger name="org.hibernate.type" level="ERROR" />
<Logger name="com.opensymphony.webwork" level="ERROR" />
<Logger name="org.apache" level="INFO" />
<Logger name="org.jgroups" level="WARN" />
<Logger name="org.jboss.axis" level="INFO" />
<Logger name="org.jboss.management" level="INFO" />
<Logger name="org.apache.commons.httpclient" level="ERROR" />
<Logger name="com.alibaba.dubbo" level="WARN" />
<Logger name="com.mchange.v2.resourcepool" level="ERROR" />
<Logger name="org.mybatis.spring" level="ERROR" />
<Logger name="org.apache.ibatis" level="ERROR" />
<Root level="INFO"><!-- 缺省日志級別,如果package有定制級別,則按package的定制級別走灿意,即使package級別更低 -->
<AppenderRef ref="STDOUT" />
<!--<AppenderRef ref="FileAppender" />-->
<!--<AppenderRef ref="RollingFileInfo" />-->
</Root>
</Loggers>
</Configuration>
- 改造gateway、udm-client缤剧、udm-server服務(wù),同樣的方法荒辕,集成sleuth,改造日志配置,在gateway路由規(guī)則中添加udm-client配置抵窒,進(jìn)行鏈?zhǔn)綔y試
zuul:
#配置zuul統(tǒng)一前綴
prefix: /api
#禁止所有eureka服務(wù)通過服務(wù)名直接訪問
ignored-services:
"*"
routes:
#將微服務(wù)需要暴露服務(wù)進(jìn)行路由映射
udm-server: /udm/**
udm-client: /udc/**
- 改造gateway驗(yàn)簽,暫時(shí)替換為md5方式削茁,首先在commons中添加md5工具類
public class Md5Sign {
public static String sign(String s) {
try {
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] bytes = md.digest(s.getBytes("utf-8"));
return toHex(bytes);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private static String toHex(byte[] bytes) {
final char[] HEX_DIGITS = "0123456789ABCDEF".toCharArray();
StringBuilder ret = new StringBuilder(bytes.length * 2);
for (int i = 0; i < bytes.length; i++) {
ret.append(HEX_DIGITS[(bytes[i] >> 4) & 0x0f]);
ret.append(HEX_DIGITS[bytes[i] & 0x0f]);
}
return ret.toString();
}
public static void main(String[] args) {
StringBuffer stringBuffer=new StringBuffer();
stringBuffer.append("ddd");
stringBuffer.append("sdfadflakjsfd;aljfda;dkfja;dfk");
String sign = sign(stringBuffer.toString());
System.out.println(sign);
}
}
然后修改gateway項(xiàng)目中添加feign依賴,用于調(diào)用獲取用戶信息方法茧跋,添加對應(yīng)service
//添加pom依賴
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
</dependency>
......
//啟動(dòng)類添加feign掃包
@SpringBootApplication
@EnableZuulProxy
@EnableFeignClients(basePackages = "open.template.work.gateway.service")
public class GatewayApplication {
public static void main(String[] args) {
SpringApplication.run(GatewayApplication.class, args);
}
}
......
//添加接口service
@FeignClient(name = CloudServerDirectory.UDM_SERVER)
public interface AuthorizationService {
/**
* 調(diào)用獲取用戶密鑰
* @param appKey
* @return
*/
@RequestMapping("/user/key")
String getUserKey(String appKey);
}
最后在AuthorizationFilter中注入此AuthorizationService圃阳,并修改run方法
@Override
public Object run() {
RequestContext currentContext = RequestContext.getCurrentContext(); // 獲取當(dāng)前請求的上下文
Map<String, Object> params = null;
try {
//1.獲取請求參數(shù),支持json格式數(shù)據(jù)捍岳,通過inputstream解析數(shù)據(jù)
try {
params = HttpParser.requestJsonParser(currentContext.getRequest().getInputStream());
} catch (IOException e) {
throw new BaseTemplateException(ExceptionCodeEnum.Z88888);
}
LOGGER.info("獲取授權(quán)參數(shù)data={}", params);
//2.通過md5驗(yàn)簽,生產(chǎn)環(huán)境可以根據(jù)需求調(diào)整更復(fù)雜的算法锣夹,為了測試簡單暫時(shí)通過客戶端的treemap進(jìn)行排序,將所有參數(shù)生成字符串银萍,然后拼接用戶密鑰
//生成md5作為簽名
if (params.get("appKey") != null) {
String key = authorizationService.getUserKey(params.get("appKey").toString());
if (StringUtils.isEmpty(key)) {
throw new BaseTemplateException(ExceptionCodeEnum.Z88887);
}
Object sign = params.get("sign");
if (sign == null) {
throw new BaseTemplateException(ExceptionCodeEnum.Z88888);
}
StringBuffer stringBuffer = new StringBuffer();
Set<String> paramsSet = params.keySet();
for (String param : paramsSet) {
if (!param.equals("sign")) {
stringBuffer.append(params.get(param));
}
}
//本地加簽
String localSign = Md5Sign.sign(stringBuffer.append(key).toString());
if (!localSign.equals(sign)) {
throw new BaseTemplateException(ExceptionCodeEnum.Z88888);
}
}
} catch (BaseTemplateException e) {
currentContext.setSendZuulResponse(false);
currentContext.setResponseStatusCode(200);
currentContext.setResponseBody("{\"result \":\""+e.getMessage()+"\"}");
currentContext.getResponse().setContentType("text/html;charset=UTF-8");
}
return null;
}
- udm-server添加模擬獲取用戶密鑰信息接口
@RestController
public class UserApi {
/**
* 根據(jù)appKey獲取用戶密鑰
* @param appKey
* @return
*/
@RequestMapping("/user/key")
public String getUserKey(String appKey){
return "sdfadflakjsfd;aljfda;dkfja;dfk";
}
}
- 重啟相應(yīng)服務(wù)贴唇,通過curl模擬json格式post請求(因?yàn)閰?shù)中有對象參數(shù),索所以需要requestBody注解戳气,必須post的請求,其中sign參數(shù)通過md5多工具類生成),查看日志
curl -H "Content-Type:application/json" -X POST --data '{"appKey":"ddd","sign":"6CBC57C961FCA12D89FB724400E87326"}' http://localhost:8061/api/udm/task/get
1.gateway日志
2. udm-server獲取用戶信息日志
3.udm-client調(diào)用日志
4. udm-server接受udm-client請求日志
完工麻捻!