对于 大型 几十个、几百个微服务构成的微服务架构系统 出现问题无法快速定位、各个微服务之间的依赖关系理不清、各个微服务的接口性能很难分析、业务调用流程处理顺序理不清

skywalking是一个国产开源框架,2015年由吴晟开源 , 2017年加入Apache孵化器。skywalking是分布式系统的应用程序性能监视工具,专为微服务、云原生架构和基于容器(Docker、K8s、Mesos)架构而设计。SkyWalking 是观察性分析平台和应用性能管理系统,提供分布式追踪、服务网格遥测分析、度量聚合和可视化一体化解决方案。
官网:http://skywalking.apache.org/
下载:http://skywalking.apache.org/downloads/
Github:https://github.com/apache/skywalking
文档: https://skywalking.apache.org/docs/main/v8.4.0/readme/
中文文档: https://skyapm.github.io/document-cn-translation-of-skywalking/

Skywalking架构的四个部分

上部分使用java的Agent机制从应用中收集链路信息 发送给SkyWalking OAP服务器

下部分SkyWalking OAP 当收到上部分发来的链路信息之后进行分析 分析结果存到外部存储器 以提供查询

右部分Storage:Tracing数据存储,目前支持ES、MySQL、Sharding Sphere、TiDB、H2多种存储器,目前采用较多的是ES,主要考虑是SkyWalking开发团队自己的生产环境采用ES为主

左部分SkyWalking UI:负责提供控制台,查看链路等等

Agent机制 是jdk1.5提供的可以获取jvm中的字节码 然后对它进行增强

在idea中使用Agent机制(探针机制)

给jvm传参 javaagent: 传入一个实现premain方法的jar 看下面代码

Agent类 加强类

package bat.ke.qq.com;

import java.lang.instrument.Instrumentation;

public class AmAgent {

public static void premain(String args, Instrumentation instrumentation){
    System.out.println("premain:" + args);
}
}

加强类的pom

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>trace-agent</artifactId>
        <groupId>bat.ke.qq.com</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>agent-demo</artifactId>
    

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-jar-plugin</artifactId>
                <version>2.2</version>
                <configuration>
                    <archive>
                        <manifestEntries>
                            <Project-name>${project.name}</Project-name>
                            <Project-version>${project.version}</Project-version>
                            <Premain-Class>bat.ke.qq.com.AmAgent</Premain-Class> // 这里指定<Premain-Class> 这个preamin会比main先运行
                        </manifestEntries>
                    </archive>
                    <skip>true</skip>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

被加强类

package com.am;


public class AgentTest {

public static void main(String[] args) {
    //  TODO   启动服务 获取进程 memory  jvm
    //  Instrument  定位到UserService 统计方法执行时间
    // 微服务  引入nacos  pom jar yml
    System.out.println("hello world");
    
    // TODO业务逻辑    UserService方法  统计方法执行时间
}
}

被加强类pom

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>trace-agent</artifactId>
        <groupId>bat.ke.qq.com</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    
    <artifactId>agent-demo2</artifactId>

    <dependencies>
    
    </dependencies>
</project>

然后在被加强类启动之前传入jvm参数-javaagent:D:\2021java代码存放地\Skywalking\agent-demo\target\agent-demo-1.0-SNAPSHOT.jar=name=123

D:\2021java代码存放地\Skywalking\agent-demo\target\agent-demo-1.0-SNAPSHOT.jar 所指的文件就是加强类的jar 然后运行被加强类可以看到效果

以上是简单使用 下面加入了修改字节码

加强类

package com.am;

import net.bytebuddy.agent.builder.AgentBuilder;
import net.bytebuddy.description.method.MethodDescription;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.implementation.MethodDelegation;
import net.bytebuddy.matcher.ElementMatchers;
import net.bytebuddy.utility.JavaModule;

import java.lang.instrument.Instrumentation;


public class FoxAgent {

public static void premain(String agentArgs, Instrumentation inst) {
    System.out.println("premain:获取方法调用时间");
    
    AgentBuilder.Transformer transformer = new AgentBuilder.Transformer() {
        @Override
        public DynamicType.Builder<?> transform(DynamicType.Builder<?> builder,
                TypeDescription typeDescription,
                ClassLoader classLoader) {
            return builder
                    // 拦截任意方法
                    .method(ElementMatchers.<MethodDescription>any())
                    // 指定方法拦截器,此拦截器中做具体的操作
                    .intercept(MethodDelegation.to(TimeInterceptor.class));
        }
    };
    
    AgentBuilder.Listener listener = new AgentBuilder.Listener() {
        @Override
        public void onTransformation(TypeDescription typeDescription, ClassLoader classLoader, JavaModule module, DynamicType dynamicType) {}
        
        @Override
        public void onIgnored(TypeDescription typeDescription, ClassLoader classLoader, JavaModule module) { }
        
        @Override
        public void onError(String typeName, ClassLoader classLoader, JavaModule module, Throwable throwable) { }
        
        @Override
        public void onComplete(String typeName, ClassLoader classLoader, JavaModule module) { }
    };
    
    new AgentBuilder
            .Default()
            // 指定需要拦截的类
            .type(ElementMatchers.nameStartsWith("com.tuling"))
            .transform(transformer)
            .with(listener)
            .installOn(inst);
}

}

加强类所用的

package com.am;

import net.bytebuddy.implementation.bind.annotation.Origin;
import net.bytebuddy.implementation.bind.annotation.RuntimeType;
import net.bytebuddy.implementation.bind.annotation.SuperCall;

import java.lang.reflect.Method;
import java.util.concurrent.Callable;


public class TimeInterceptor {

@RuntimeType
public static Object intercept(@Origin Method method,
        @SuperCall Callable<?> callable) throws Exception {
    long start = System.currentTimeMillis();
    try {
        // 原方法执行
        return callable.call();
    } finally {
        System.out.println(method + ": cost " + (System.currentTimeMillis() - start) + "ms");
    }
}

}

加强类pom

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>trace-agent</artifactId>
        <groupId>bat.ke.qq.com</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    
    <artifactId>bytebuddy-demo</artifactId>
    
    <dependencies>
        <dependency>
            <groupId>javassist</groupId>
            <artifactId>javassist</artifactId>
            <version>3.12.1.GA</version>
            <type>jar</type>
        </dependency>
    
        <dependency>
            <groupId>net.bytebuddy</groupId>
            <artifactId>byte-buddy</artifactId>
            <version>1.5.13</version>
        </dependency>
    
        <dependency>
            <groupId>net.bytebuddy</groupId>
            <artifactId>byte-buddy-agent</artifactId>
            <version>1.5.13</version>
        </dependency>
    </dependencies>
    
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-jar-plugin</artifactId>
                <version>2.2</version>
                <configuration>
                    <archive>
                        <manifestEntries>
                            <Project-name>${project.name}</Project-name>
                            <Project-version>${project.version}</Project-version>
                            <Premain-Class>com.tuling.FoxAgent</Premain-Class>
                        </manifestEntries>
                    </archive>
                    <skip>true</skip>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-shade-plugin</artifactId>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>shade</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
                    <artifactSet>
                        <includes>
                            <include>javassist:javassist:jar:</include>
                            <include>net.bytebuddy:byte-buddy:jar:</include>
                            <include>net.bytebuddy:byte-buddy-agent:jar:</include>
                        </includes>
                    </artifactSet>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

被加强类

package com.am;


public class AgentBytebuddyTest {

public static void main(String[] args) {

    HelloService helloService = new HelloService();
    helloService.say();
    helloService.say2();

   
}
}

被加强类所用的

package com.am;


public class HelloService {


public String say(){
    System.out.println("===hello world====");
    return "hello world";
}


public String say2(){
    try {
        Thread.sleep(1000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    return "hello world";
}

}

被加强类pom

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>trace-agent</artifactId>
        <groupId>bat.ke.qq.com</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    
    <artifactId>bytebuddy-demo2</artifactId>


</project>

利用agent机制 实现业务零侵入 打印jvm使用情况

加强类pom

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>trace-agent</artifactId>
        <groupId>bat.ke.qq.com</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

<artifactId>Metric-tool</artifactId>

<dependencies>
    <dependency>
        <groupId>javassist</groupId>
        <artifactId>javassist</artifactId>
        <version>3.12.1.GA</version>
        <type>jar</type>
    </dependency>
</dependencies>


<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-jar-plugin</artifactId>
            <version>2.2</version>
            <configuration>
                <archive>
                    <manifestEntries>
                        <Project-name>${project.name}</Project-name>
                        <Project-version>${project.version}</Project-version>
                        <Premain-Class>com.tuling.FoxAgent</Premain-Class>
                    </manifestEntries>
                </archive>
                <skip>true</skip>
            </configuration>
        </plugin>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-shade-plugin</artifactId>
            <executions>
                <execution>
                    <phase>package</phase>
                    <goals>
                        <goal>shade</goal>
                    </goals>
                </execution>
            </executions>
            <configuration>
                <artifactSet>
                    <includes>
                        <include>javassist:javassist:jar:</include>
                    </includes>
                </artifactSet>
            </configuration>
        </plugin>
    </plugins>
</build>
</project>

加强类

package com.am;

import java.lang.instrument.Instrumentation;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;


public class AmAgent {

public static void premain(String agentArgs, Instrumentation inst) {
    
    //每隔5秒打印JVM内存和GC信息
    Executors.newScheduledThreadPool(1).scheduleAtFixedRate(new Runnable() {
        @Override
        public void run() {
            Metric.printMemoryInfo();
            Metric.printGCInfo();
        }
    }, 0, 5000, TimeUnit.MILLISECONDS);
}
}

加强类所用的 打印jvm使用情况实现

package com.am;

import java.lang.management.GarbageCollectorMXBean;
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryMXBean;
import java.lang.management.MemoryUsage;
import java.util.Arrays;
import java.util.List;

/**
 *
 * 获取JVM 内存以及GC信息
 *
 */
public class Metric {
private static final long MB = 1048576L;

public static void printMemoryInfo() {
    MemoryMXBean memory = ManagementFactory.getMemoryMXBean();
    MemoryUsage headMemory = memory.getHeapMemoryUsage();

    String info = String.format("\nHeapMemory init: %s\t max: %s\t used: %s\t committed: %s\t use rate: %s\n",
            headMemory.getInit() / MB + "MB",
            headMemory.getMax() / MB + "MB", headMemory.getUsed() / MB + "MB",
            headMemory.getCommitted() / MB + "MB",
            headMemory.getUsed() * 100 / headMemory.getCommitted() + "%"

    );

    System.out.print(info);

    MemoryUsage nonheadMemory = memory.getNonHeapMemoryUsage();

    info = String.format("NonHeapMemory init: %s\t max: %s\t used: %s\t committed: %s\t use rate: %s\n",
            nonheadMemory.getInit() / MB + "MB",
            nonheadMemory.getMax() / MB + "MB", nonheadMemory.getUsed() / MB + "MB",
            nonheadMemory.getCommitted() / MB + "MB",
            nonheadMemory.getUsed() * 100 / nonheadMemory.getCommitted() + "%"

    );
    System.out.println(info);
}

public static void printGCInfo() {
    List<GarbageCollectorMXBean> garbages = ManagementFactory.getGarbageCollectorMXBeans();
    for (GarbageCollectorMXBean garbage : garbages) {
        String info = String.format("name: %s\t count:%s\t took:%s\t pool name:%s",
                garbage.getName(),
                garbage.getCollectionCount(),
                garbage.getCollectionTime(),
                Arrays.deepToString(garbage.getMemoryPoolNames()));
        System.out.println(info);
    }
}
}

被加强类pom

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>trace-agent</artifactId>
        <groupId>bat.ke.qq.com</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    
    <artifactId>Metric-tool-demo</artifactId>


</project>

被加强类

package com.am;

import java.util.ArrayList;
import java.util.List;


public class MetricToolAgentTest {

public static void main(String[] args) {

    boolean flag = true;
    while (flag) {
        List<Object> list = new ArrayList<Object>();
        list.add("Hello World");
    }
    
}

}

SkyWalking环境搭建

下载:http://skywalking.apache.org/downloads/

目录结构:

在这里插入图片描述

搭建SkyWalking OAP 服务
先使用默认的H2数据库存储,不用修改配置

config/application.yml

使用vim指令进入config/application.yml搜索storage 可以看到它的下一行是selector: ${SW_STORAGE:h2}

启动脚本 bin/startup.sh

日志信息存储在logs目录

启动成功后会启动两个服务,一个是skywalking-oap-server,一个是skywalking-web-ui
skywalking-oap-server服务启动后会暴露11800 和 12800 两个端口,分别为收集监控数据的端口11800和接受前端请求的端口12800,修改端口可以修改config/applicaiton.yml

skywalking-web-ui服务会占用 8080 端口, 修改端口可以修改webapp/webapp.yml

Skywalking到这就搭建好了

然后使用

准备一个springboot程序,打成可执行jar包,写一个shell脚本,在启动项目的Shell脚本上,通过 -javaagent 参数进行配置SkyWalking Agent来跟踪微服务;
startup.sh脚本:

#!/bin/sh
# SkyWalking Agent配置
export SW_AGENT_NAME=springboot-skywalking-demo #Agent名字,一般使用`spring.application.name`
export SW_AGENT_COLLECTOR_BACKEND_SERVICES=127.0.0.1:11800 #配置 Collector 地址。
export SW_AGENT_SPAN_LIMIT=2000 #配置链路的最大Span数量,默认为 300。
export JAVA_AGENT=-javaagent:/usr/local/soft/apache-skywalking-apm-bin-es7/agent/skywalking-agent.jar
java $JAVA_AGENT -jar springboot-skywalking-demo-0.0.1-SNAPSHOT.jar #jar启动

这样启动之后 访问请求 就可以在Skywalking UI界面中查看到 多个微服务 只需要添加javaagent参数指定skywalking-agent.jar这个jar文件即可

在idea中使用 也是给jvm传参

# skywalking-agent.jar的本地磁盘的路径
-javaagent:D:\apache\apache-skywalking-apm-es7-8.4.0\apache-skywalking-apm-bin-es7\agent\skywalking-agent.jar
# 在skywalking上显示的服务名
-DSW_AGENT_NAME=springboot-skywalking-demo
# skywalking的collector服务的IP及端口
-DSW_AGENT_COLLECTOR_BACKEND_SERVICES=192.168.3.100:11800 

自定义链路 就是可以让Skywalking可以检测到我们的业务代码

导入依赖

<!-- SkyWalking 工具类 -->
<dependency>
    <groupId>org.apache.skywalking</groupId>
    <artifactId>apm-toolkit-trace</artifactId>
    <version>8.4.0</version>
</dependency>

在业务方法中可以TraceContext获取到traceId

@RequestMapping("/list")
public List<User> list(){

    //TraceContext可以得到Skywalking的上下文 下面的代码时绑定一个key-value
    TraceContext.putCorrelation("name", "fox");
    Optional<String> op = TraceContext.getCorrelation("name");
    log.info("name = {} ", op.get());
    //获取跟踪的traceId
    String traceId = TraceContext.traceId();
    log.info("traceId = {} ", traceId);

    return userService.list();
}

userService.list();会调用到下面的list 然后list方法加了@Trace注解 这样Skywalking就可以把list方法加入到链路中 @Tag注解是用来把方法返回值写到SKywalking中

@Trace
@Tag(key = "list", value = "returnedObj")
public List<User> list(){
    return userMapper.list();
}

@Trace
@Tags({@Tag(key = "param", value = "arg[0]"),
        @Tag(key = "user", value = "returnedObj")})
public User getById(Integer id){
    return userMapper.getById(id);
}

日志集成

logback官方配置 https://github.com/apache/skywalking/blob/master/docs/en/setup/service-agent/java-agent/Application-toolkit-logback-1.x.md

log4j官方配置 https://skywalking.apache.org/docs/main/v8.4.0/en/setup/service-agent/java-agent/application-toolkit-log4j-1.x/

log4j2j官方配置 https://skywalking.apache.org/docs/main/v8.4.0/en/setup/service-agent/java-agent/application-toolkit-log4j-2.x

Skywalking集群部署

Skywalking集群是将skywalking oap作为一个服务注册到nacos上,只要skywalking oap服务没有全部宕机,保证有一个skywalking oap在运行,就能进行跟踪。

搭建一个skywalking oap集群需要:
(1)至少一个Nacos(也可以是nacos集群)
(2)至少一个ElasticSearch(也可以是es集群)
(3)至少2个skywalking oap服务;
(4)至少1个UI(UI也可以集群多个,用Nginx代理统一入口)
1.修改config/application.yml文件
使用nacos作为注册中心

在这里插入图片描述

修改nacos配置
在这里插入图片描述

修改存储策略,使用elasticsearch7作为storage

在这里插入图片描述
修改elasticsearch7配置
在这里插入图片描述

配置ui服务webapp.yml文件的listOfServers,写两个地址

在这里插入图片描述
listOfServers是OAP的地址

然后springboot应用传入的jvm参数的更改

-DSW_AGENT_COLLECTOR_BACKEND_SERVICES=192.168.3.10:11800,192.168.3.12:11800

Logo

K8S/Kubernetes社区为您提供最前沿的新闻资讯和知识内容

更多推荐