测试环境路由,需要建立在全链路调用追踪系统的基础之上,使环境标识可以一直不中断的传递下去。
在现有全链路调用追踪系统的基础上,打造测试环境路由,会起到事半功倍的效果。
全链路调用追踪的权威知识可以参见:
Dapper:http://bigbully.github.io/Dapper-translation/
让我们先比较几个开源的全链路调用追踪系统吧!
技术选型
pinpoint | zipkin | jaeger | apache skywalking | |
OpenTracing兼容 | 否 | 是 | 是 | 是 |
客户端支持语言 | java、php、python | java,c#,go,php等 | java,c#,go,php等 | Java, .NET Core, NodeJS and PHP、go、 |
存储 | hbase | ES,mysql,Cassandra,内存 | ES,kafka,Cassandra,内存 | ES,H2,mysql,TIDB,sharding sphere等 |
传输协议支持 | thrift | http,MQ | udp/http | gRPC |
UI丰富程度 | 高 | 低 | 中 | 中 |
实现方式-代码侵入性 | asm、javaagent,无侵入 | 拦截请求,侵入 | 拦截请求,侵入 | bytebuddy,javaagent,无侵入 |
扩展性 | 低 | 高 | 高 | 高 |
trace查询 | 不支持 | 支持 | 支持 | 支持 |
告警支持 | 支持 | 不支持 | 不支持 | 支持 |
jvm监控 | 支持 | 不支持 | 不支持 | 支持 |
性能损失 | 高 | 中 | 中 | 低 |
厂商 | 韩国团队 | | Uber | 国人/Apache顶级项目 |
活跃度 | 13.4k | 16.9k | 20.2k | 23.7k |
依据业务侵入性、性能损失、扩展性、运维成本、社区活跃度等对比,建议选择skywalking作为基础。
SkyWalking官方介绍
SkyWalking is an open-source APM system that provides monitoring, tracing and diagnosing capabilities for distributed systems in Cloud Native architectures.
-
Distributed Tracing
-
End-to-end distributed tracing. Service topology analysis, service-centric observability and APIs dashboards.
-
-
Agents for your stack
-
Java, .Net Core, PHP, NodeJS, Golang, LUA, Rust, C++, Client JavaScript and Python agents with active development and maintenance.
-
-
eBPF early adoption
-
Rover agent works as a monitor and profiler powered by eBPF to monitor Kubernetes deployments and diagnose CPU and network performance.
-
-
Scaling
-
100+ billion telemetry data could be collected and analyzed from one SkyWalking cluster.
-
-
Mature Telemetry Ecosystems Supported
-
Metrics, Traces, and Logs from mature ecosystems are supported, e.g. Zipkin, OpenTelemetry, Prometheus, Zabbix, Fluentd
-
-
Native APM Database
-
BanyanDB, an observability database, created in 2022, aims to ingest, analyze and store telemetry/observability data.
-
-
Consistent Metrics Aggregation
-
SkyWalking native meter format and widely known metrics format(OpenTelemetry, Telegraf, Zabbix, e.g.) are processed through the same script pipeline.
-
-
Log Management Pipeline
-
Support log formatting, extract metrics, various sampling policies through script pipeline in high performance.
-
-
Alerting and Telemetry Pipelines
-
Support service-centric, deployment-centric, API-centric alarm rule setting. Support forwarding alarms and all telemetry data to 3rd party.
-
部署
#运行skywalking-oap容器
docker run --name skywalking-oap -e TZ=Asia/Shanghai -p 12800:12800 -p 11800:11800 --restart always -d apache/skywalking-oap-server:9.2.0
#运行skywalking ui容器
docker run -d --name skywalking-ui --restart=always -e TZ=Asia/Shanghai -p 8080:8080 --link skywalking-oap:oap -e SW_OAP_ADDRESS=http://oap:12800 apache/skywalking-ui:9.2.0
下载并配置java agent:https://skywalking.apache.org/downloads/
#配置jvm参数
DSW_AGENT_NAME=<application name> -DSW_AGENT_COLLECTOR_BACKEND_SERVICES=<oap地址> -javaagent:<agent路径>
#如:
-DSW_AGENT_NAME=devops -DSW_AGENT_COLLECTOR_BACKEND_SERVICES=127.0.0.1:11800 -javaagent:D:\skywalking-agent\skywalking-agent.jar
访问 Skywalking UI,即可看到详细的APM信息:http://localhost:8080/
给日志添加追踪Id
<!--skywalking traceId 记录到logback日志-->
<dependency>
<groupId>org.apache.skywalking</groupId>
<artifactId>apm-toolkit-logback-1.x</artifactId>
<version>9.2.0</version>
</dependency>
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<!--<include resource="org/springframework/boot/logging/logback/base.xml"/>-->
<include resource="org/springframework/boot/logging/logback/defaults.xml"/>
<!--<include resource="org/springframework/boot/logging/logback/console-appender.xml"/>-->
<!--<include resource="org/springframework/boot/logging/logback/file-appender.xml" />-->
<springProperty scope="context" name="appName" source="spring.application.name"/>
<!-- 链路日志打印 -->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
<layout class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.TraceIdPatternLogbackLayout">
<pattern>[%tid] ${CONSOLE_LOG_PATTERN:-%clr(%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd HH:mm:ss.SSS}}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---) {faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}</pattern>
</layout>
</encoder>
</appender>
<!-- skyWalking日志采集 -->
<appender name="APM_LOG" class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.log.GRPCLogClientAppender">
<encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
<layout class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.mdc.TraceIdMDCPatternLogbackLayout">
<pattern>[%tid] ${CONSOLE_LOG_PATTERN:-%clr(%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd HH:mm:ss.SSS}}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---) {faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}</pattern>
</layout>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="STDOUT"/>
<appender-ref ref="APM_LOG"/>
</root>
</configuration>
改造 -- 重点来了,请细看
1、环境标识注册
当服务启动时,需要注册自己的身份
#启动参数中添加
-Dskywalking.agent.branch=xxx
当使用注册中心时,如dubbo,需要向注册中心注册自己的环境标识身份,用于环境路由判断。
以后只需要从Config.Agent.BRANCH获取当前服务的身份了
具体做法请参见下一篇文章,敬请期待!!
2、链路信息携带环境标识
skywalking的traceId由多个部分组成,比较长:
// GlobalIdGenerator
private static final String PROCESS_ID = UUID.randomUUID().toString().replaceAll("-", "");
private static final ThreadLocal<IDContext> THREAD_ID_SEQUENCE = ThreadLocal.withInitial(
() -> new IDContext(System.currentTimeMillis(), (short) 0));
public static String generate() {
return StringUtil.join(
'.',
PROCESS_ID,
String.valueOf(Thread.currentThread().getId()),
String.valueOf(THREAD_ID_SEQUENCE.get().nextSeq())
);
}
因为uuid重复的可能性非常小,因此我们简化了生成规则,直接使用uuid+环境标识,并增加了从traceId中解析环境标识的方法。
public static String generate() {
String uuid = UUID.randomUUID().toString().replaceAll("-", "");
if (Config.Agent.OPEN_ROUTER) {
String branch = Optional.ofNullable(HEADER_BRANCH.get()).orElseGet(() -> Config.Agent.BRANCH);
return StringUtil.join('.', uuid, branch);
}
return uuid;
}
/**
* 获取分支名称
*
* @param tranceId notnull 此值的解析需要跟上面生成规则形成对应
* @return
*/
public static String getBranch(String tranceId) {
if (tranceId == null) {
return null;
}
String[] strings = tranceId.split("\\.");
if (strings.length != 2) {
return null;
}
return strings[1];
}
这样附加了几个优点:traceid中携带了环境标识,不用做特殊处理,直接可以透传,同时在ui界面中也可以直接看出是哪个环境的调用链路。
3、服务名称、实例名称生成方式优化
在k8s中,服务名称和实例名称是可以根据固定的规则生成的,而不需要人为指定,这样可以降低配置错误的概率。
public class CustomerConfigInit {
private static ILog LOGGER = LogManager.getLogger(CustomerConfigInit.class);
private static String ENV_KEY = "agent.host_env";
private static String SERVICE_NAME_KEY = "agent.service_name";
private static String INSTANCE_NAME_KEY = "agent.instance_name";
public static void setHostNameConfig(Properties config) {
try {
LOGGER.info("配置文件内容:{}", config);
String hostname = System.getenv("HOSTNAME");
if (StringUtil.isBlank(hostname)) {
hostname= InetAddress.getLocalHost().getHostName();
setInstanceName(hostname, config);
return;
}
if (StringUtil.isBlank(hostname)) {
return;
}
// 设置instanceName
setInstanceName(hostname, config);
// 设置serviceName
String hostEnvString = ((String) config.get(ENV_KEY)).trim();
if (StringUtil.isBlank(hostEnvString)) {
return;
}
LOGGER.info("hostName:{} hostenv:{}", hostname, hostEnvString);
String[] hostEnvList = hostEnvString.split(",");
for (String s : hostEnvList) {
if (StringUtil.isBlank(s)) {
continue;
}
int i = hostname.indexOf(s);
if (i == -1) {
continue;
}
String serviceName = hostname.substring(0, i + s.length() - 1);
config.put(SERVICE_NAME_KEY, serviceName);
LOGGER.info("自动设置 serviceName 是:{}", serviceName);
break;
}
} catch (Throwable e) {
LOGGER.error("自定义设置servicerName异常", e);
}
}
private static void setInstanceName(String hostname, Properties config) {
config.put(INSTANCE_NAME_KEY, hostname);
}
}
总结
本文简单介绍了skywalking的使用,并对skywalking做了简单的增强,以支持环境标识的注入。
后面我会提供一个最基础的demo出来,方便大家演示。
如果你觉得这篇文章对你有帮助,欢迎点赞+在看+转发,并关注我的公众号“架构师吕师傅”,会有更多精彩内容持续分享给大家。你的支持永远是我前进的动力~~~