调用链相关理解和说明

调用链定义:

在系统完成一次业务调用的过程中,把服务之间的调用信息(时间、接口、层次、结果)打点到日志中,然后将所有的打点数据连接为一个树状链条就产生了一个调用链。跟踪系统把过程中产生的日志信息进行分析处理,将业务端到端的执行完整的调用过程进行还原,根据不同维度进行统计分析;从而标识出有异常的服务调用,能够快速分析定界到出异常的服务;同时可根据数据统计分析系统性能瓶颈。

核心概念:

1、Trace:
Trace是指一次请求调用的链路过程,trace id 是指这次请求调用的ID。在一次请求中,会在网络的最开始生成一个全局唯一的用于标识此次请求的trace id,这个trace id在这次请求调用过程中无论经过多少个节点都会保持不变,并且在随着每一层的调用不停的传递。最终,可以通过trace id将这一次用户请求在系统中的路径全部串起来。

2、Span:
Span是指一个模块的调用过程,一般用span id来标识。在一次请求的过程中会调用不同的节点/模块/服务,每一次调用都会生成一个新的span id来记录。这样,就可以通过span id来定位当前请求在整个系统调用链中所处的位置,以及它的上下游节点分别是什么。

3、Annotation:
是指附属信息,可以用于附属在每一个Span上自定义的数据。

在这里插入图片描述
从图中可见,一次请求只有一个唯一的trace id=12345,在请求过程中的任何环节都不会改变。在这个请求的调用链中,SpanA调用了SpanB,然后SpanB又调用了SpanC和SpanD,每一次Span调用都会生成一个自己的span id,并且还会记录自己的上级span id是谁。通过这些id,整个链路基本上就都能标识出来了。

系统整个调用链(核心思路):

整个调用过程的追踪,主要有下面几个步骤:

  • 所有请求到来即生成一个全局唯一编码的TraceID,通过TraceID可以串联起整个调用链,一个TraceID只代表一次请求。

  • 在调用过程中,需要SpanID用于记录调用父子关系。每个服务会记录下Parent ID和Span
    ID。这样通过TraceID和TraceID可以组织一次完整调用链的父子关系。

  • 一个没有Parent Id的Span成为Root Span,可以看成调用链入口。
    所有这些ID可用全局唯一的64位整数表示。

  • 整个调用过程中每个请求都要透传TraceID和SpanID。

  • 每个服务将该次请求附带的TraceID和附带的SpanID作为Parent ID记录下,并且将自己生成的SpanID也记录下。

  • 要查看某次完整的调用,只要根据TraceID查出所有调用记录,然后通过Parent ID和Span Id组织起整个调用父子关系即可。

  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
好的,这是一个比较复杂的JNI项目,需要注意的地方比较多,我会尽量详细地给出代码和说明。 首先,我们需要在VS中创建一个空的C++项目,命名为"JNIproject",然后在项目中添加两个cpp文件,分别命名为"JNItest.cpp"和"JNIfunction.cpp"。 在"JNIfunction.cpp"中,我们写一些C++函数,这些函数最终会被Java层调用。以下是一个简单的例子: ```cpp #include <stdio.h> #include "JNIfunction.h" JNIEXPORT void JNICALL Java_com_example_JNItest_print(JNIEnv *env, jobject obj, jstring str) { const char *s = env->GetStringUTFChars(str, NULL); printf("%s\n", s); env->ReleaseStringUTFChars(str, s); } JNIEXPORT jstring JNICALL Java_com_example_JNItest_reverse(JNIEnv *env, jobject obj, jstring str) { const char *s = env->GetStringUTFChars(str, NULL); char *r = new char[strlen(s) + 1]; for (int i = 0; i < strlen(s); i++) { r[i] = s[strlen(s) - i - 1]; } r[strlen(s)] = '\0'; jstring result = env->NewStringUTF(r); delete[] r; env->ReleaseStringUTFChars(str, s); return result; } ``` 这里我们定义了两个函数,一个是"print"函数,它接收一个字符串并打印出来;另一个是"reverse"函数,它接收一个字符串并返回其反转后的结果。 然后我们需要在"JNIfunction.h"中声明这些函数: ```cpp #ifndef _Included_JNIfunction #define _Included_JNIfunction #include <jni.h> #ifdef __cplusplus extern "C" { #endif JNIEXPORT void JNICALL Java_com_example_JNItest_print(JNIEnv *, jobject, jstring); JNIEXPORT jstring JNICALL Java_com_example_JNItest_reverse(JNIEnv *, jobject, jstring); #ifdef __cplusplus } #endif #endif ``` 接下来我们需要在"JNItest.cpp"中实现Java层调用C++层的接口: ```cpp #include <jni.h> #include "JNIfunction.h" JNIEXPORT void JNICALL Java_com_example_JNItest_print(JNIEnv *env, jobject obj, jstring str) { jclass cls = env->GetObjectClass(obj); jmethodID mid = env->GetMethodID(cls, "printString", "(Ljava/lang/String;)V"); if (mid == NULL) { return; } env->CallVoidMethod(obj, mid, str); } JNIEXPORT jstring JNICALL Java_com_example_JNItest_reverse(JNIEnv *env, jobject obj, jstring str) { jclass cls = env->GetObjectClass(obj); jmethodID mid = env->GetMethodID(cls, "reverseString", "(Ljava/lang/String;)Ljava/lang/String;"); if (mid == NULL) { return NULL; } return (jstring) env->CallObjectMethod(obj, mid, str); } JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) { JNIEnv *env; if (vm->GetEnv((void **) &env, JNI_VERSION_1_6) != JNI_OK) { return -1; } jclass cls = env->FindClass("com/example/JNItest"); if (cls == NULL) { return -1; } static JNINativeMethod methods[] = { {"print", "(Ljava/lang/String;)V", (void *) Java_com_example_JNItest_print}, {"reverse", "(Ljava/lang/String;)Ljava/lang/String;", (void *) Java_com_example_JNItest_reverse} }; jint ret = env->RegisterNatives(cls, methods, sizeof(methods) / sizeof(methods[0])); if (ret != JNI_OK) { return -1; } return JNI_VERSION_1_6; } ``` 这里我们实现了Java层调用C++层的"print"和"reverse"函数,并在"JNI_OnLoad"函数中注册这些函数。 最后,我们需要在VS中设置项目的属性,以便将其编译成.so库。具体步骤如下: 1. 右键点击"JNIproject"项目,选择"属性"。 2. 在"常规"选项卡中,将"配置类型"设置为"动态库(.dll)",将"目标扩展名"设置为".so"。 3. 在"C/C++"选项卡中,将"附加包含目录"设置为JDK的include目录,例如:"C:\Program Files\Java\jdk1.8.0_271\include"。 4. 在"C/C++"选项卡中,将"附加库目录"设置为JDK的lib目录,例如:"C:\Program Files\Java\jdk1.8.0_271\lib"。 5. 在"C/C++"选项卡中,将"代码生成"选项卡中的"运行库"设置为"多线程DLL(/MD)"。 6. 在"接器"选项卡中,将"附加依赖项"设置为"jvm.lib"。 7. 在"接器"选项卡中,将"输出文件"设置为"JNItest.so"。 现在我们可以编译项目了。编译完成后,我们可以在"JNIproject\Debug"目录下找到生成的JNItest.so库。 最后,我们需要在Linux下编写Java代码,调用这个.so库。以下是一个简单的例子: ```java package com.example; public class JNItest { static { System.loadLibrary("JNItest"); } private native void print(String str); private native String reverse(String str); public static void main(String[] args) { JNItest jni = new JNItest(); jni.print("Hello JNI"); String rev = jni.reverse("Hello JNI"); System.out.println(rev); } } ``` 这里我们通过静态代码块加载JNItest.so库,并实现了"print"和"reverse"函数的Java层接口。 希望这个例子能够帮助你理解如何在VS中创建含有多个cpp文件组成的复杂JNI项目,并编译成.so库,最终由Linux下的Java native调用

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值