当前位置: 首页 > news >正文

【SkyWalking】SkyWalking是如何实现跨进程传播链路数据?

文章目录

  • 一、简介
    • 1 为什么写这篇文章
    • 2 跨进程传播协议-简介
  • 二、协议
    • 1 Standard Header项
    • 2 Extension Header项
    • 3 Correlation Header项
  • 三、跨进程传播协议的源码分析
    • 1 OpenTracing规范
    • 2 通过dubbo插件分析跨进程数据传播
    • 3 分析跨进程传播协议的核心源码
  • 四、小结
  • 参考

一、简介

1 为什么写这篇文章

写这篇文章是为了让自己和大家梳理这些内容:

  1. SkyWalking的链路串联依赖跨进程数据传播,他的跨进程传播协议是怎样的?
  2. 如果我想借助SkyWalking的跨进程传播协议实现传递全链路业务数据(如全局userId等),该如何实现?

2 跨进程传播协议-简介

SkyWalking 跨进程传播协议是用于上下文的传播,之前经历过sw3协议、sw6协议,本文介绍是当前(2023年)最新的sw8协议。
该协议适用于不同语言、系统的探针之间传递上下文。

二、协议

Header项分为三类:

  • Standard Header项,Header名称:sw8
  • Extension Header项,Header名称:sw8-x
  • Correlation Header项,Header名称:sw8-correlation

协议的整体设计:
在这里插入图片描述

下面详细讲解协议的Header项:

1 Standard Header项

该Header项是上下文传播必须包含的。

  • Header名称:sw8.
  • Header值:由-分隔的8个字段组成。Header值的长度应该小于2KB。

Header值中具体包含以下8个字段:

  • 采样(Sample),0 或 1,0 表示上下文存在,但是可以(也很可能)被忽略而不做采样;1 表示这个trace需要采样并发送到后端。
  • 追踪ID(Trace Id),是 Base64 编码的字符串,其内容是由 . 分割的三个 long 类型值, 表示此trace的唯一标识。
  • 父追踪片段ID(Parent trace segment Id),是 Base64 编码的字符串,其内容是字符串且全局唯一。
  • 父跨度ID(Parent span Id),是一个从 0 开始的整数,这个跨度ID指向父追踪片段(segment)中的父跨度(span)。
  • 父服务名称(Parent service),是 Base64 编码的字符串,其内容是一个长度小于或等于50个UTF-8编码的字符串。
  • 父服务实例标识(Parent service instance),是 Base64 编码的字符串,其内容是一个长度小于或等于50个UTF-8编码的字符串。
  • 父服务的端点(Parent endpoint),是 Base64 编码的字符串,其内容是父追踪片段(segment)中第一个入口跨度(span)的操作名,由长度小于或等于50个UTF-8编码的字符组成。
  • 本请求的目标地址(Peer),是 Base64 编码的字符串,其内容是客户端用于访问目标服务的网络地址(不一定是 IP + 端口)。

示例值: 1-TRACEID-SEGMENTID-3-PARENT_SERVICE-PARENT_INSTANCE-PARENT_ENDPOINT-IPPORT

2 Extension Header项

该Header项是可选的。扩展Header项是为高级特性设计的,它提供了部署在上游和下游服务中的探针之间的交互功能。

Header名称:sw8-x

Header值:由-分割,字段可扩展。

扩展Header值
当前值包括的字段:

追踪模式(Tracing Mode),空、0或1,默认为空或0。表示在这个上下文中生成的所有跨度(span)应该跳过分析。在默认情况下,这个应该在上下文中传播到服务端,除非它在跟踪过程中被更改。
客户端发送的时间戳:用于异步RPC,如MQ。一旦设置,消费端将计算发送和接收之间的延迟,并使用key transmission.latency自动在span中标记延迟。

示例值:1-1621344125000

3 Correlation Header项

该Header项是是可选的。并非所有语言的探针都支持,已知的是Java的探针是支持该协议。
该Header项用于跨进程传递用户自定义数据,例如userId、orgId。
这个协议跟OpenTracing 的 Baggage很类似,但是Correlation Header项相比,在默认设置下会更有更严格的限制,例如,只能存放3个字段,且有字段长度限制,这个是为了安全、性能等考虑。
数据格式:

Header名称:sw8-correlation

Header值:由,分割一对对key、value,每对key、value逗号分割,key、value的由Base64编码。

示例值:a2V5MQ==:dmFsdWUx,a2V5LTI=:dmFsdWUy

三、跨进程传播协议的源码分析

1 OpenTracing规范

SkyWalking是基于OpenTracing标准的追踪系统,参考吴晟老师翻译的OpenTracing规范的文章opentracing之Inject和Extract,OpenTracing定义了跨进程传播的几个要素:

SpanContext:SpanContext代表跨越进程边界,传递到下级span的状态。在SkyWalking中的实现类是org.apache.skywalking.apm.agent.core.context.TracingContext
Carrier:传递跨进程数据的搬运工,负责将追踪状态从一个进程"carries"(携带,传递)到另一个进程
Inject 和 Extract:SpanContexts可以通过Inject(注入)操作向Carrier增加,或者通过Extract(提取)从Carrier中获取,跨进程通讯数据(例如:HTTP头)。通过这种方式,SpanContexts可以跨越进程边界,并提供足够的信息来建立跨进程的span间关系(因此可以实现跨进程连续追踪)

2 通过dubbo插件分析跨进程数据传播

我们以SkyWalking java agent的dubbo-2.7.x-plugin插件为例,其中跨进程传播数据的核心代码在org.apache.skywalking.apm.plugin.asf.dubbo.DubboInterceptor,下面是该类跨进程传播的核心代码:

public class DubboInterceptor implements InstanceMethodsAroundInterceptor {/*** Consumer: The serialized trace context data will* inject to the {@link RpcContext#attachments} for transport to provider side.* <p>* Provider: The serialized trace context data will extract from* {@link RpcContext#attachments}. current trace segment will ref if the serialization context data is not null.*/@Overridepublic void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class<?>[] argumentsTypes,MethodInterceptResult result) throws Throwable {......if (isConsumer) { // 1、consumer端// ContextCarrierfinal ContextCarrier contextCarrier = new ContextCarrier();// 1.1 createExitSpan()内部会调用TracerContext.inject(carrier),将TracerContext中的context数据inject(注入)到ContextCarrier的context中span = ContextManager.createExitSpan(generateOperationName(requestURL, invocation), contextCarrier, host + ":" + port);CarrierItem next = contextCarrier.items();// 1.2 遍历ContextCarrier,从ContextCarrier的context获取数据,注入到dubbo的attachment,从consumer端传递到provider端while (next.hasNext()) {next = next.next();rpcContext.setAttachment(next.getHeadKey(), next.getHeadValue());if (invocation.getAttachments().containsKey(next.getHeadKey())) {invocation.getAttachments().remove(next.getHeadKey());}}} else { // 2 provider端// 2.1 从consumer端传递到provider端的attachment中获取跨进程协议数据,然后设置到contextContextCarrier contextCarrier = new ContextCarrier();CarrierItem next = contextCarrier.items();while (next.hasNext()) {next = next.next();next.setHeadValue(rpcContext.getAttachment(next.getHeadKey()));}// 2.2 createEntrySpan()内部会调用TracerContext.extract(carrier),将ContextCarrier的context数据extract(提取)到将TracerContext中的context中span = ContextManager.createEntrySpan(generateOperationName(requestURL, invocation), contextCarrier);span.setPeer(rpcContext.getRemoteAddressString());}}
}

从上面的源码可以看出在服务调用方和被调用方,都会用到ContextCarrier,他是临时搬运工,负责两个进程的TracerContext数据的传递。
下面分析ContextCarrier等类的核心源码。

3 分析跨进程传播协议的核心源码

TracingContext
org.apache.skywalking.apm.agent.core.context.TracingContext是OpenTracing的SpanContext的一种实现,里面包含了span的上下文,包含在segment、correlationContext、extensionContext,而inject()、extract()负责跨进程上下文透传。

public class TracingContext implements AbstractTracerContext {/*** The final {@link TraceSegment}, which includes all finished spans.*/private TraceSegment segment;@Getter(AccessLevel.PACKAGE)private final CorrelationContext correlationContext;@Getter(AccessLevel.PACKAGE)private final ExtensionContext extensionContext;/*** Prepare for the cross-process propagation. How to initialize the carrier, depends on the implementation.** @param carrier to carry the context for crossing process.*/void inject(ContextCarrier carrier);/*** Build the reference between this segment and a cross-process segment. How to build, depends on the* implementation.** @param carrier carried the context from a cross-process segment.*/void extract(ContextCarrier carrier);
}

ContextCarrier
ContextCarrier作为传递跨进程数据的搬运工,负责将追踪状态从一个进程"carries"(携带,传递)到另一个进程,其中包含了sw8协议里的Standard Header项、Extension Header项、Correlation Header项相关的上下文数据,具体参考下面的代码:

public class ContextCarrier implements Serializable {/*** extensionContext包含了在某些特定场景中用于增强分析的可选上下文,对应sw8的Extension Header项*/private ExtensionContext extensionContext = new ExtensionContext();/*** 用户的自定义上下文容器。此上下文与主追踪上下文一同传播。对应sw8的Correlation Header项*/private CorrelationContext correlationContext = new CorrelationContext();/*** @return 存在于当前tracing上下文中的item清单*/public CarrierItem items() {SW8ExtensionCarrierItem sw8ExtensionCarrierItem = new SW8ExtensionCarrierItem(extensionContext, null);SW8CorrelationCarrierItem sw8CorrelationCarrierItem = new SW8CorrelationCarrierItem(correlationContext, sw8ExtensionCarrierItem);SW8CarrierItem sw8CarrierItem = new SW8CarrierItem(this, sw8CorrelationCarrierItem);return new CarrierItemHead(sw8CarrierItem);}/*** Extract the extension context to tracing context*/void extractExtensionTo(TracingContext tracingContext) {tracingContext.getExtensionContext().extract(this);// The extension context could have field not to propagate further, so, must use the this.* to process.this.extensionContext.handle(tracingContext.activeSpan());}/*** Extract the correlation context to tracing context*/void extractCorrelationTo(TracingContext tracingContext) {tracingContext.getCorrelationContext().extract(this);// The correlation context could have field not to propagate further, so, must use the this.* to process.this.correlationContext.handle(tracingContext.activeSpan());}/*** 序列化sw8的Standard Header项,使用 '-' 分割各个字段* Serialize this {@link ContextCarrier} to a {@link String}, with '|' split.* @return the serialization string.*/String serialize(HeaderVersion version) {if (this.isValid(version)) {return StringUtil.join('-',"1",Base64.encode(this.getTraceId()),Base64.encode(this.getTraceSegmentId()),this.getSpanId() + "",Base64.encode(this.getParentService()),Base64.encode(this.getParentServiceInstance()),Base64.encode(this.getParentEndpoint()),Base64.encode(this.getAddressUsedAtClient()));}return "";}/*** 反序列化sw8的Standard Header项* Initialize fields with the given text.* @param text carries {@link #traceSegmentId} and {@link #spanId}, with '|' split.*/ContextCarrier deserialize(String text, HeaderVersion version) {if (text == null) {return this;}if (HeaderVersion.v3.equals(version)) {String[] parts = text.split("-", 8);if (parts.length == 8) {try {// parts[0] is sample flag, always trace if header exists.this.traceId = Base64.decode2UTFString(parts[1]);this.traceSegmentId = Base64.decode2UTFString(parts[2]);this.spanId = Integer.parseInt(parts[3]);this.parentService = Base64.decode2UTFString(parts[4]);this.parentServiceInstance = Base64.decode2UTFString(parts[5]);this.parentEndpoint = Base64.decode2UTFString(parts[6]);this.addressUsedAtClient = Base64.decode2UTFString(parts[7]);} catch (IllegalArgumentException ignored) {}}}return this;}
}

CorrelationContext
ContextCarrier里包含里sw8的Correlation Header项存放于CorrelationContext,这个类非常有用,适合我们去在全链路跨进程传递自定义的数据。
sw8协议里的Standard Header项、Extension Header项是比较固定的协议格式,我们可以扩展这些协议,例如Standard Header项,当前固定是8位的,对应8个字段,我们可以扩展为9位,第九位可以定义为userId。但是如果要这样改造,就得修改ContextCarrier类序列化、反序列的逻辑,要重新发布agent,并考虑好新旧版本兼容性问题、以及不同语言的agent是否兼容。
而sw8的Correlation Header项使用起来就非常方便。先看下对应实现了CorrelationContext的源码:

/*** Correlation context, use to propagation user custom data.* Correlation上下文,用于传播用户自定义数据*/
public class CorrelationContext {private final Map<String, String> data;/*** Add or override the context. 添加或覆盖上下文数据** @param key   to add or locate the existing context* @param value as new value* @return old one if exist.*/public Optional<String> put(String key, String value) {// 可以存放于span的tag中if (AUTO_TAG_KEYS.contains(key) && ContextManager.isActive()) {ContextManager.activeSpan().tag(new StringTag(key), value);}// settingdata.put(key, value);return Optional.empty();}/*** @param key to find the context 获取上下文数据* @return value if exist.*/public Optional<String> get(String key) {return Optional.ofNullable(data.get(key));}/*** Serialize this {@link CorrelationContext} to a {@link String} 序列化** @return the serialization string.*/String serialize() {if (data.isEmpty()) {return "";}return data.entrySet().stream().map(entry -> Base64.encode(entry.getKey()) + ":" + Base64.encode(entry.getValue())).collect(Collectors.joining(","));}/*** Deserialize data from {@link String} 反序列化*/void deserialize(String value) {if (StringUtil.isEmpty(value)) {return;}for (String perData : value.split(",")) {// Only data with limited count of elements can be addedif (data.size() >= Config.Correlation.ELEMENT_MAX_NUMBER) {break;}final String[] parts = perData.split(":");if (parts.length != 2) {continue;}data.put(Base64.decode2UTFString(parts[0]), Base64.decode2UTFString(parts[1]));}}/*** Prepare for the cross-process propagation. Inject the {@link #data} into {@link* ContextCarrier#getCorrelationContext()}*/void inject(ContextCarrier carrier) {carrier.getCorrelationContext().data.putAll(this.data);}/*** Extra the {@link ContextCarrier#getCorrelationContext()} into this context.*/void extract(ContextCarrier carrier) {......}/*** Clone the context data, work for capture to cross-thread. 克隆数据,用于跨线程传递*/@Overridepublic CorrelationContext clone() {final CorrelationContext context = new CorrelationContext();context.data.putAll(this.data);return context;}/*** Continue the correlation context in another thread.传递到另外的线程** @param snapshot holds the context.*/void continued(ContextSnapshot snapshot) {this.data.putAll(snapshot.getCorrelationContext().data);}
}

通过源码可知,CorrelationContext通过Map<String, String>来存放数据,CorrelationContext数据支持跨线程、跨进程透传。

四、小结

分析Dubbo插件的跨进程核心代码,了解了跨进程传播协议的核心实现逻辑。

其实在其他分布式追踪系统(如Zipkin、Jager)、全链路灰度系统等涉及到跨进程数据传播的系统中,也是使用了类似于上面SkyWalking协议的思路。

参考

SkyWalking Cross Process Propagation Headers Protocol
SkyWalking Cross Process Correlation Headers Protocol
详解 Apache SkyWalking 的跨进程传播协议

相关文章:

【SkyWalking】SkyWalking是如何实现跨进程传播链路数据?

文章目录 一、简介1 为什么写这篇文章2 跨进程传播协议-简介 二、协议1 Standard Header项2 Extension Header项3 Correlation Header项 三、跨进程传播协议的源码分析1 OpenTracing规范2 通过dubbo插件分析跨进程数据传播3 分析跨进程传播协议的核心源码 四、小结参考 一、简介…...

px4仿真实现无人机自主飞行

一,确定消息类型 无人机通过即在电脑是现自主飞行:思路如下。 通过Mavros功能包,将ROS消息转换为Mavlink消息。实现对无人机的控制。 几种消息之间的关系如下: 对于ROS数据,就是我们机载电脑执行ROS系统的数据。 对于Mavros消息,就是Mavros功能包内部的消息。查询网站…...

详解Linux的系统调用fork()函数

在Linux系统中&#xff0c;fork()是一个非常重要的系统调用&#xff0c;它的作用是创建一个新的进程。具体来说&#xff0c;fork()函数会在当前进程的地址空间中复制一份子进程&#xff0c;并且这个子进程几乎完全与父进程相同&#xff0c;包括进程代码、数据、堆栈以及打开的文…...

构建捡垃圾机器人的 ROS 2 项目

一、说明 本系列是关于学习如何使用 ROS2、Docker 和 Github 设计、设置和维护机器人项目。 先决条件 — ROS2 软件包的基本知识、实现发布者、订阅者、操作并连接它们。 我们之前在 ROS2 中了解了不同的部分。但是&#xff0c;在我们转向实际的基于硬件的项目之前&#xff0c;…...

Spring常用注解(2)

6、切面&#xff08;AOP&#xff09;相关注解 Spring AOP详细介绍 Spring支持AspectJ的注解式切面编程。 Aspect 声明一个切面 After 在方法执行之后执行&#xff08;方法上&#xff09; Before 在方法执行之前执行&#xff08;方法上&#xff09; Around 在方法执行之前与之后…...

upload-labs靶场通关

文章目录 Pass-01 前端检测&#xff08;JS检测&#xff09;1.1 原理分析1.2 实验 Pass-02 后端检测&#xff08;MIME检测&#xff09;2.1 原理分析2.2 实验 Pass-03 后端检测&#xff08;黑名单绕过&#xff0c;特殊后缀名&#xff09;3.1 原理分析3.2 实验 Pass-04 后端检测&a…...

git拉取代码过程

第一步&#xff1a;先在本地创建文件夹 &#xff0c;比如我这里的文件夹名称是 fengkgong_zntjfx 第二步&#xff1a;执行命令&#xff1a;git init 第三步&#xff1a;git clone 第四步&#xff1a;git fetch 第五步&#xff1a;git branch -a 第六步&#xff1a;cd 项目 【…...

Swift | 属性包装器

Swift | 属性包装器 1. 什么是 Swift Property Wrapper&#xff1f; Swift Property Wrapper 是一种特性&#xff0c;它允许我们在声明属性时添加自定义的包装逻辑。通过使用属性包装器&#xff0c;我们可以在不修改类或结构体定义的情况下&#xff0c;定制属性的访问和存储方…...

Android改造CardView为圆形View,Kotlin

Android改造CardView为圆形View&#xff0c;Kotlin 可以利用androidx.cardview.widget.CardView的cardCornerRadius特性&#xff0c;将CardView改造成一个圆形的View&#xff0c;技术实现的关键首先设定CardView为一个宽高相等的View&#xff08;正方形&#xff09;&#xff0c…...

Idea下面git的使用:变基、合并、优选、还原提交、重置、回滚、补丁

多分支和分支切换 变基和合并 变基是把本项目的所有提交都列出来按顺序一个个提交到目标分支上去 而合并是把两个分支合并起来&#xff0c;但是旧的分支还是可以启动其他分支&#xff0c;在旧的分支上继续开发 master: A -- B -- C -- M/ feature: D -- Emaster: A -…...

【数据结构】什么是算法

&#x1f984;个人主页:修修修也 &#x1f38f;所属专栏:数据结构 ⚙️操作环境:Visual Studio 2022 目录 一.算法的定义 1.算法的概念 2.数据结构与算法的关系 二.算法的特性 输入 输出 有穷性 确定性 可行性 三.算法的设计要求 1.正确性 2.可读性 3.健壮性 4.效…...

复旦大学EMBA:揭秘科创企业,领略未来战略!

智能制造&#xff0c;国之重器。作为制造强国建设的主攻方向&#xff0c;智能制造的发展水平关系到我国未来制造业在全球的地位与影响力。发展智能制造&#xff0c;是加快建设现代化产业体系的重要手段&#xff0c;提升供给体系适配性的有力抓手&#xff0c;也是建设数字中国的…...

根据您的数据量定制的ChatGPT,改变客户服务的方式

在当今竞争激烈的商业环境中&#xff0c;提供优质的客户服务对于保持忠诚的客户群和推动业务增长至关重要。客户满意度已成为各行各企业的首要任务&#xff0c;因为它直接影响客户留存和品牌声誉。随着技术的进步&#xff0c;公司不断探索创新解决方案&#xff0c;以增强客户服…...

《Unity Shader 入门精要》笔记03

UnityShader的内置变量&#xff08;数学篇&#xff09; Unity内置的变换矩阵摄像机和屏幕参数float3 _WorldSpaceCameraPosfloat4 _ProjectionParamsfloat4 _ZBufferParamsfloat4 unity_OrthoParamsfloat4x4 unity_CameraProjectionfloat4x4 unity_CameraInvProjectionfloat4 u…...

LINUX系统使用软件异地同步数据(灾备)

rsync是linux系统下的数据镜像备份工具。使用快速增量备份工具Remote Sync可以远程同步&#xff0c;支持本地复制&#xff0c;或者与其他SSH、rsync主机同步 一、宝塔环境: 有宝塔软件商城支持&#xff0c;参考&#xff1a;https://www.bt.cn/bbs/thread-98022-1-1.html 二、…...

IDEA Rogstry中找不到compiler.automake.allow.when.app.running问题解决

网上大部分人教我们 先 File > Settings 然后 勾选 Build 下的 Compiler中的 Build project automatically 这些步骤都不会有问题 然后就会让我们 ctrl shift alt / 点 Rogstry 打开后 我人就麻了 根本没有什么 compiler.automake.allow.when.app.running 也不用慌 我们…...

c#设计模式-行为型模式 之 状态模式

&#x1f680;简介 状态模式是一种行为设计模式&#xff0c;它允许对象在其内部状态改变时改变其行为&#xff0c;我们可以通过创建一个状态接口和一些实现了该接口的状态类来实现状态模式。然后&#xff0c;我们可以创建一个上下文类&#xff0c;它会根据其当前的状态对象来改…...

使用Docker安装JupyterHub

安装JupyterHub 拉取Jupyter镜像并运行容器 docker run -d -p 8000:8000 --name jupyterhub jupyterhub/jupyterhub jupyterhub # -d&#xff1a;后台运行 # -p 8000:8000&#xff1a;宿主机的8000端口映射容器中的8000端口 # --name jupyterhub&#xff1a;给运行的容器起名…...

SpringCloudGateway网关整合swagger3+Knife4j3,basePath丢失请求404问题

在集成 Spring Cloud Gateway 网关的时候&#xff0c;会出现没有 basePath 的情况&#xff0c;例如定义的 /jeeplus-auth、/jeeplus-system 等微服务前缀导致访问接口404&#xff1a; maven依赖&#xff1a; swagger2于17年停止维护&#xff0c;现在最新的版本为 Swagger3&am…...

html通过使用图像源的协议(protocol)相对 URL 来防止安全/不安全错误

有人知道使用 protocol relative URLs 是否有问题吗&#xff1f;用于图像源以防止混合内容安全警告。 例如链接一张图片: <img src"//domain.com/img.jpg" /> 代替: <img src"http://domain.com/img.jpg" /> or <img src"https…...

【Python】 -- 趣味代码 - 小恐龙游戏

文章目录 文章目录 00 小恐龙游戏程序设计框架代码结构和功能游戏流程总结01 小恐龙游戏程序设计02 百度网盘地址00 小恐龙游戏程序设计框架 这段代码是一个基于 Pygame 的简易跑酷游戏的完整实现,玩家控制一个角色(龙)躲避障碍物(仙人掌和乌鸦)。以下是代码的详细介绍:…...

深入剖析AI大模型:大模型时代的 Prompt 工程全解析

今天聊的内容&#xff0c;我认为是AI开发里面非常重要的内容。它在AI开发里无处不在&#xff0c;当你对 AI 助手说 "用李白的风格写一首关于人工智能的诗"&#xff0c;或者让翻译模型 "将这段合同翻译成商务日语" 时&#xff0c;输入的这句话就是 Prompt。…...

AI Agent与Agentic AI:原理、应用、挑战与未来展望

文章目录 一、引言二、AI Agent与Agentic AI的兴起2.1 技术契机与生态成熟2.2 Agent的定义与特征2.3 Agent的发展历程 三、AI Agent的核心技术栈解密3.1 感知模块代码示例&#xff1a;使用Python和OpenCV进行图像识别 3.2 认知与决策模块代码示例&#xff1a;使用OpenAI GPT-3进…...

云启出海,智联未来|阿里云网络「企业出海」系列客户沙龙上海站圆满落地

借阿里云中企出海大会的东风&#xff0c;以**「云启出海&#xff0c;智联未来&#xff5c;打造安全可靠的出海云网络引擎」为主题的阿里云企业出海客户沙龙云网络&安全专场于5.28日下午在上海顺利举办&#xff0c;现场吸引了来自携程、小红书、米哈游、哔哩哔哩、波克城市、…...

23-Oracle 23 ai 区块链表(Blockchain Table)

小伙伴有没有在金融强合规的领域中遇见&#xff0c;必须要保持数据不可变&#xff0c;管理员都无法修改和留痕的要求。比如医疗的电子病历中&#xff0c;影像检查检验结果不可篡改行的&#xff0c;药品追溯过程中数据只可插入无法删除的特性需求&#xff1b;登录日志、修改日志…...

IGP(Interior Gateway Protocol,内部网关协议)

IGP&#xff08;Interior Gateway Protocol&#xff0c;内部网关协议&#xff09; 是一种用于在一个自治系统&#xff08;AS&#xff09;内部传递路由信息的路由协议&#xff0c;主要用于在一个组织或机构的内部网络中决定数据包的最佳路径。与用于自治系统之间通信的 EGP&…...

《Playwright:微软的自动化测试工具详解》

Playwright 简介:声明内容来自网络&#xff0c;将内容拼接整理出来的文档 Playwright 是微软开发的自动化测试工具&#xff0c;支持 Chrome、Firefox、Safari 等主流浏览器&#xff0c;提供多语言 API&#xff08;Python、JavaScript、Java、.NET&#xff09;。它的特点包括&a…...

相机从app启动流程

一、流程框架图 二、具体流程分析 1、得到cameralist和对应的静态信息 目录如下: 重点代码分析: 启动相机前,先要通过getCameraIdList获取camera的个数以及id,然后可以通过getCameraCharacteristics获取对应id camera的capabilities(静态信息)进行一些openCamera前的…...

【决胜公务员考试】求职OMG——见面课测验1

2025最新版&#xff01;&#xff01;&#xff01;6.8截至答题&#xff0c;大家注意呀&#xff01; 博主码字不易点个关注吧,祝期末顺利~~ 1.单选题(2分) 下列说法错误的是:&#xff08; B &#xff09; A.选调生属于公务员系统 B.公务员属于事业编 C.选调生有基层锻炼的要求 D…...

鱼香ros docker配置镜像报错:https://registry-1.docker.io/v2/

使用鱼香ros一件安装docker时的https://registry-1.docker.io/v2/问题 一键安装指令 wget http://fishros.com/install -O fishros && . fishros出现问题&#xff1a;docker pull 失败 网络不同&#xff0c;需要使用镜像源 按照如下步骤操作 sudo vi /etc/docker/dae…...