测试环境路由可以这么做(三):特性环境标识注入

测试环境路由,需要建立在全链路调用追踪系统的基础之上,使环境标识可以一直不中断的传递下去。

在现有全链路调用追踪系统的基础上,打造测试环境路由,会起到事半功倍的效果。

全链路调用追踪的权威知识可以参见:

    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监控

支持

不支持

不支持

支持

性能损失

厂商

韩国团队

Twitter

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出来,方便大家演示。

如果你觉得这篇文章对你有帮助,欢迎点赞+在看+转发,并关注我的公众号“架构师吕师傅”,会有更多精彩内容持续分享给大家。你的支持永远是我前进的动力~~~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值