在引入了注册中心后,微服务项目的架构会变成下图:

image-20230514175352625

但是,如果此时有一个请求的响应速度比较慢,而这个请求调用了多个RPC服务,该如何去排查呢?

一体化架构中的慢请求排查如何做

最简单的办法,就是像下面这样:

1
2
3
4
5
6
7
8
9
10
11
12
long start = System.currentTimeMillis();
processA();
// 打印A步骤的耗时
Logs.info("process A cost " + (System.currentTimeMillis() - start));
start = System.currentTimeMillis();
processB();
// 打印B步骤的耗时
Logs.info("process B cost " + (System.currentTimeMillis() - start));
start = System.currentTimeMillis();
processC();
// 打印C步骤的耗时
Logs.info("process C cost " + (System.currentTimeMillis() - start));

但是这样会有一个问题,如果多个用户同时下单,那么日志的打印是穿插进行的,无法判断。

一个简单的解决办法是,给每个请求每一行日志加一个id,用于区分。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
String requestId = UUID.randomUUID().toString();
ThreadLocal<String> tl = new ThreadLocal<String>(){
@Override
protected String initialValue() {
return requestId;
}
}; //requestId存储在线程上下文中
long start = System.currentTimeMillis();
processA();
Logs.info("rid : " + tl.get() + ", process A cost " + (System.currentTimeMillis() - start)); // 日志中增加requestId
start = System.currentTimeMillis();
processB();
Logs.info("rid : " + tl.get() + ", process B cost " + (System.currentTimeMillis() - start));
start = System.currentTimeMillis();
processC();
Logs.info("rid : " + tl.get() + ", process C cost " + (System.currentTimeMillis() - start));

利用切面编程

一般来说,切面分为两类:

  • 一类是静态代理,典型的代表是 AspectJ,它的特点是在编译期做切面代码注入;

  • 另一类是动态代理,典型的代表是 Spring AOP,它的特点是在运行期做切面代码注入

区别:

静态代理是在编译期插入代码,增加了编译的时间,给你的直观感觉就是启动的时间变长了,但是一旦在编译期插入代码完毕之后在运行期就基本对于性能没有影响。

而动态代理不会去修改生成的 Class 文件,而是会在运行期生成一个代理对象,这个代理对象对源对象做了字节码增强,来完成切面所要执行的操作。由于在运行期需要生成代理对象,所以动态代理的性能要比静态代理要差。

其他问题

为了避免打印过多的日志,我们可以根据id进行采样,比如说只打印id % 10 == 0的,这样就只统计局部的 也可以实现需求。

但是还有一个问题,如果这样的话,拿到一个id我们并不知道它是属于哪个服务器的,我们还需要去服务器查询。解决办法就是将日志通过消息队列插入Elasticsearch,这样搜索也比较简单。如下图:

image-20230514183317870

如何来做分布式 Trace

采用traceId + spanId

image-20230514184244939