Skywalking流程分析_7(构造/实例方法的增强流程)

前言

this.enhance

protected DynamicType.Builder<?> enhance(TypeDescription typeDescription, DynamicType.Builder<?> newClassBuilder,ClassLoader classLoader, EnhanceContext context) throws PluginException {//静态方法的增强newClassBuilder = this.enhanceClass(typeDescription, newClassBuilder, classLoader);//构造、实例方法的增强newClassBuilder = this.enhanceInstance(typeDescription, newClassBuilder, classLoader, context);return newClassBuilder;
}

在上文中我们分析的静态方法的增强是如何做的,接下来我们来分析构造和实例方法

ClassEnhancePluginDefine.enhanceInstance(typeDescription, newClassBuilder, classLoader, context)

protected DynamicType.Builder<?> enhanceInstance(TypeDescription typeDescription,DynamicType.Builder<?> newClassBuilder, ClassLoader classLoader,EnhanceContext context) throws PluginException {//查找构造方法的切点ConstructorInterceptPoint[] constructorInterceptPoints = getConstructorsInterceptPoints();//查找实例方法的切点InstanceMethodsInterceptPoint[] instanceMethodsInterceptPoints = getInstanceMethodsInterceptPoints();String enhanceOriginClassName = typeDescription.getTypeName();boolean existedConstructorInterceptPoint = false;if (constructorInterceptPoints != null && constructorInterceptPoints.length > 0) {existedConstructorInterceptPoint = true;}boolean existedMethodsInterceptPoints = false;if (instanceMethodsInterceptPoints != null && instanceMethodsInterceptPoints.length > 0) {existedMethodsInterceptPoints = true;}/*** nothing need to be enhanced in class instance, maybe need enhance static methods.* 没有要增强的那么直接返回*/if (!existedConstructorInterceptPoint && !existedMethodsInterceptPoints) {return newClassBuilder;}/*** Manipulate class source code.<br/>** new class need:<br/>* 1.Add field, name {@link #CONTEXT_ATTR_NAME}.* 2.Add a field accessor for this field.** And make sure the source codes manipulation only occurs once.* */// 如果被拦截的类没有实现EnhancedInstance接口if (!typeDescription.isAssignableTo(EnhancedInstance.class)) {if (!context.isObjectExtended()) {//给要增强的类添加_$EnhancedClassField_ws字段,//并实现了EnhancedInstance接口的set/get方法,来获取这个字段//新增这个字段的作用是可以放置自己的数据,然后通过这两个set/get方法来存放和拿取newClassBuilder = newClassBuilder.defineField(CONTEXT_ATTR_NAME, Object.class, ACC_PRIVATE | ACC_VOLATILE).implement(EnhancedInstance.class).intercept(FieldAccessor.ofField(CONTEXT_ATTR_NAME));// 将记录状态EnhanceContext标识符设置为已新增新的字段或者实现新的接口                                 context.extendObjectCompleted();}}/*** 2. enhance constructors* 构造器增强逻辑*/if (existedConstructorInterceptPoint) {//获取所有构造方法的切点for (ConstructorInterceptPoint constructorInterceptPoint : constructorInterceptPoints) {//要增加的类是Bootstrap类加载器加载的if (isBootstrapInstrumentation()) {newClassBuilder = newClassBuilder.constructor(constructorInterceptPoint.getConstructorMatcher()).intercept(SuperMethodCall.INSTANCE.andThen(MethodDelegation.withDefaultConfiguration().to(BootstrapInstrumentBoost.forInternalDelegateClass(constructorInterceptPoint.getConstructorInterceptor()))));} else {newClassBuilder = newClassBuilder.constructor(constructorInterceptPoint.getConstructorMatcher()).intercept(SuperMethodCall.INSTANCE.andThen(MethodDelegation.withDefaultConfiguration().to(new ConstructorInter(constructorInterceptPoint.getConstructorInterceptor(), classLoader))));}}}/*** 3. enhance instance methods* 实例方法增强逻辑*/if (existedMethodsInterceptPoints) {//获取所有实例方法的切点for (InstanceMethodsInterceptPoint instanceMethodsInterceptPoint : instanceMethodsInterceptPoints) {String interceptor = instanceMethodsInterceptPoint.getMethodsInterceptor();if (StringUtil.isEmpty(interceptor)) {throw new EnhanceException("no InstanceMethodsAroundInterceptor define to enhance class " + enhanceOriginClassName);}ElementMatcher.Junction<MethodDescription> junction = not(isStatic()).and(instanceMethodsInterceptPoint.getMethodsMatcher());if (instanceMethodsInterceptPoint instanceof DeclaredInstanceMethodsInterceptPoint) {junction = junction.and(ElementMatchers.<MethodDescription>isDeclaredBy(typeDescription));}if (instanceMethodsInterceptPoint.isOverrideArgs()) {//要增加的类是Bootstrap类加载器加载的if (isBootstrapInstrumentation()) {newClassBuilder = newClassBuilder.method(junction).intercept(MethodDelegation.withDefaultConfiguration().withBinders(Morph.Binder.install(OverrideCallable.class)).to(BootstrapInstrumentBoost.forInternalDelegateClass(interceptor)));} else {newClassBuilder = newClassBuilder.method(junction).intercept(MethodDelegation.withDefaultConfiguration().withBinders(Morph.Binder.install(OverrideCallable.class)).to(new InstMethodsInterWithOverrideArgs(interceptor, classLoader)));}} else {//要增加的类是Bootstrap类加载器加载的if (isBootstrapInstrumentation()) {newClassBuilder = newClassBuilder.method(junction).intercept(MethodDelegation.withDefaultConfiguration().to(BootstrapInstrumentBoost.forInternalDelegateClass(interceptor)));} else {newClassBuilder = newClassBuilder.method(junction).intercept(MethodDelegation.withDefaultConfiguration().to(new InstMethodsInter(interceptor, classLoader)));}}}}return newClassBuilder;
}

总结

  • 给要增强的类添加_$EnhancedClassField_ws字段,并实现了EnhancedInstance接口的set/get方法,来获取这个字段。新增这个字段的作用是可以放置自己的数据,然后通过这两个set/get方法来存放和拿取
  • 执行构造器增强逻辑
  • 执行实例方法增强逻辑

下面我们来逐步分析构造器增强和实例方法增强

构造器增强

非JDK的类库是被ConstructorInter所增强的

newClassBuilder = newClassBuilder.constructor(constructorInterceptPoint.getConstructorMatcher()).intercept(SuperMethodCall.INSTANCE.andThen(MethodDelegation.withDefaultConfiguration().to(new ConstructorInter(constructorInterceptPoint.getConstructorInterceptor(), classLoader))));
ConstructorInter
public class ConstructorInter {private static final ILog LOGGER = LogManager.getLogger(ConstructorInter.class);private InstanceConstructorInterceptor interceptor;/*** @param constructorInterceptorClassName class full name.*/public ConstructorInter(String constructorInterceptorClassName, ClassLoader classLoader) throws PluginException {try {// 实例化自定义的拦截器//对于同一份字节码,如果由不同的类加载器进行加载,那么加载出来的两个实例不相同interceptor = InterceptorInstanceLoader.load(constructorInterceptorClassName, classLoader);} catch (Throwable t) {throw new PluginException("Can't create InstanceConstructorInterceptorV2.", t);}}/*** Intercept the target constructor.** @param obj          target class instance.* @param allArguments all constructor arguments*/@RuntimeTypepublic void intercept(@This Object obj, @AllArguments Object[] allArguments) {try {//这里向上转型为EnhancedInstance的原因是为了使用之前新添加的_$EnhancedClassField_ws字段//使用setSkyWalkingDynamicField/getSkyWalkingDynamicField方法来存放和获取自己的数据EnhancedInstance targetObject = (EnhancedInstance) obj;//在原来的构造方法执行完后,再执行interceptor.onConstruct(targetObject, allArguments);} catch (Throwable t) {LOGGER.error("ConstructorInter failure.", t);}}
}

ConstructorInter逻辑总结

  • 构造方法中实例化自定义的拦截器
  • 在原先的构造方法执行完,再执行interceptor.onConstruct(targetObject, allArguments)

kafka为例

public class KafkaProducerInstrumentation extends AbstractKafkaInstrumentation {public static final String ENHANCE_CLASS = "org.apache.kafka.clients.producer.KafkaProducer";public static final String CONSTRUCTOR_INTERCEPTOR_CLASS = "org.apache.skywalking.apm.plugin.kafka.ProducerConstructorInterceptor";public static final String CONSTRUCTOR_INTERCEPTOR_FLAG = "org.apache.kafka.clients.producer.ProducerConfig";@Overridepublic ConstructorInterceptPoint[] getConstructorsInterceptPoints() {return new ConstructorInterceptPoint[] {new ConstructorInterceptPoint() {@Overridepublic ElementMatcher<MethodDescription> getConstructorMatcher() {return takesArgumentWithType(0, CONSTRUCTOR_INTERCEPTOR_FLAG);}@Overridepublic String getConstructorInterceptor() {return CONSTRUCTOR_INTERCEPTOR_CLASS;}}};}@Overrideprotected ClassMatch enhanceClass() {return byName(ENHANCE_CLASS);}
}
  • 这里拦截的是org.apache.kafka.clients.producer.KafkaProducer类的构造方法
  • 拦截具体的构造方法是第一个参数类型为org.apache.kafka.clients.producer.ProducerConfig
  • 拦截的执行类是org.apache.skywalking.apm.plugin.kafka.ProducerConstructorInterceptor
public class ProducerConstructorInterceptor implements InstanceConstructorInterceptor {@Overridepublic void onConstruct(EnhancedInstance objInst, Object[] allArguments) {ProducerConfig config = (ProducerConfig) allArguments[0];objInst.setSkyWalkingDynamicField(StringUtil.join(';', config.getList("bootstrap.servers").toArray(new String[0])));}
}

注意

能看到这里使用_$EnhancedClassField_ws字段来保存一些数据方便后续使用,这也是为什么要执行这段逻辑,设置此字段的原因

if (!typeDescription.isAssignableTo(EnhancedInstance.class)) {if (!context.isObjectExtended()) {//给要增强的类添加_$EnhancedClassField_ws字段,//并实现了EnhancedInstance接口的set/get方法,来获取这个字段//新增这个字段的作用是可以放置自己的数据,然后通过这两个set/get方法来存放和拿取newClassBuilder = newClassBuilder.defineField(CONTEXT_ATTR_NAME, Object.class, ACC_PRIVATE | ACC_VOLATILE).implement(EnhancedInstance.class).intercept(FieldAccessor.ofField(CONTEXT_ATTR_NAME));context.extendObjectCompleted();}
}
实例方法增强

首先看这段逻辑

ElementMatcher.Junction<MethodDescription> junction = not(isStatic()).and(instanceMethodsInterceptPoint.getMethodsMatcher());
if (instanceMethodsInterceptPoint instanceof DeclaredInstanceMethodsInterceptPoint) {//拦截到的方法必须是当前类上的 注解匹配到的方法有可能不是当前类上的junction = junction.and(ElementMatchers.<MethodDescription>isDeclaredBy(typeDescription));
}
  • 比如说,要对Student.add()方法增强,那么直接指定类名即可,但如果想对所有Controller的SpringMVC控制层的方法进行增强,那么就要使用注解。
  • 但有可能通过注解匹配到的方法不是当前类上的,所以判断如果拦截点为DeclaredInstanceMethodsInterceptPoint会新增匹配条件:获取到的方法必须是当前类上的
    非JDK的类库是被ConstructorInter所增强的

我们依旧先分析不是JDK类库的类

不修改原方法的参数
newClassBuilder = newClassBuilder.method(junction).intercept(MethodDelegation.withDefaultConfiguration().to(new InstMethodsInter(interceptor, classLoader)));

具体逻辑在InstMethodsInter

InstMethodsInter

public class InstMethodsInter {private static final ILog LOGGER = LogManager.getLogger(InstMethodsInter.class);/*** An {@link InstanceMethodsAroundInterceptor} This name should only stay in {@link String}, the real {@link Class}* type will trigger classloader failure. If you want to know more, please check on books about Classloader or* Classloader appointment mechanism.*/private InstanceMethodsAroundInterceptor interceptor;/*** @param instanceMethodsAroundInterceptorClassName class full name.*/public InstMethodsInter(String instanceMethodsAroundInterceptorClassName, ClassLoader classLoader) {try {//使用自定义加载器加载拦截器interceptor = InterceptorInstanceLoader.load(instanceMethodsAroundInterceptorClassName, classLoader);} catch (Throwable t) {throw new PluginException("Can't create InstanceMethodsAroundInterceptor.", t);}}/*** Intercept the target instance method.** @param obj          target class instance.* @param allArguments all method arguments* @param method       method description.* @param zuper        the origin call ref.* @return the return value of target instance method.* @throws Exception only throw exception because of zuper.call() or unexpected exception in sky-walking ( This is a*                   bug, if anything triggers this condition ).*/@RuntimeTypepublic Object intercept(@This Object obj, @AllArguments Object[] allArguments, @SuperCall Callable<?> zuper,@Origin Method method) throws Throwable {EnhancedInstance targetObject = (EnhancedInstance) obj;MethodInterceptResult result = new MethodInterceptResult();try {//前置增强interceptor.beforeMethod(targetObject, method, allArguments, method.getParameterTypes(), result);} catch (Throwable t) {LOGGER.error(t, "class[{}] before method[{}] intercept failure", obj.getClass(), method.getName());}Object ret = null;try {if (!result.isContinue()) {ret = result._ret();} else {//执行原有方法ret = zuper.call();}} catch (Throwable t) {try {//异常增强interceptor.handleMethodException(targetObject, method, allArguments, method.getParameterTypes(), t);} catch (Throwable t2) {LOGGER.error(t2, "class[{}] handle method[{}] exception failure", obj.getClass(), method.getName());}throw t;} finally {try {//后置增强ret = interceptor.afterMethod(targetObject, method, allArguments, method.getParameterTypes(), ret);} catch (Throwable t) {LOGGER.error(t, "class[{}] after method[{}] intercept failure", obj.getClass(), method.getName());}}return ret;}
}
修改原方法的参数
newClassBuilder = newClassBuilder.method(junction).intercept(MethodDelegation.withDefaultConfiguration().withBinders(Morph.Binder.install(OverrideCallable.class)).to(new InstMethodsInterWithOverrideArgs(interceptor, classLoader)));

具体逻辑在InstMethodsInterWithOverrideArgs

InstMethodsInterWithOverrideArgs

public class InstMethodsInterWithOverrideArgs {private static final ILog LOGGER = LogManager.getLogger(InstMethodsInterWithOverrideArgs.class);/*** An {@link InstanceMethodsAroundInterceptor} This name should only stay in {@link String}, the real {@link Class}* type will trigger classloader failure. If you want to know more, please check on books about Classloader or* Classloader appointment mechanism.*/private InstanceMethodsAroundInterceptor interceptor;/*** @param instanceMethodsAroundInterceptorClassName class full name.*/public InstMethodsInterWithOverrideArgs(String instanceMethodsAroundInterceptorClassName, ClassLoader classLoader) {try {interceptor = InterceptorInstanceLoader.load(instanceMethodsAroundInterceptorClassName, classLoader);} catch (Throwable t) {throw new PluginException("Can't create InstanceMethodsAroundInterceptor.", t);}}/*** Intercept the target instance method.** @param obj          target class instance.* @param allArguments all method arguments* @param method       method description.* @param zuper        the origin call ref.* @return the return value of target instance method.* @throws Exception only throw exception because of zuper.call() or unexpected exception in sky-walking ( This is a*                   bug, if anything triggers this condition ).*/@RuntimeTypepublic Object intercept(@This Object obj, @AllArguments Object[] allArguments, @Origin Method method,@Morph OverrideCallable zuper) throws Throwable {EnhancedInstance targetObject = (EnhancedInstance) obj;MethodInterceptResult result = new MethodInterceptResult();try {interceptor.beforeMethod(targetObject, method, allArguments, method.getParameterTypes(), result);} catch (Throwable t) {LOGGER.error(t, "class[{}] before method[{}] intercept failure", obj.getClass(), method.getName());}Object ret = null;try {if (!result.isContinue()) {ret = result._ret();} else {ret = zuper.call(allArguments);}} catch (Throwable t) {try {interceptor.handleMethodException(targetObject, method, allArguments, method.getParameterTypes(), t);} catch (Throwable t2) {LOGGER.error(t2, "class[{}] handle method[{}] exception failure", obj.getClass(), method.getName());}throw t;} finally {try {ret = interceptor.afterMethod(targetObject, method, allArguments, method.getParameterTypes(), ret);} catch (Throwable t) {LOGGER.error(t, "class[{}] after method[{}] intercept failure", obj.getClass(), method.getName());}}return ret;}
}

可以看到实例方法的增强逻辑和静态方法的大致相同

这里面有个问题,为什么用zuper.call()执行原方法的调用,不能替换成method.invoke(clazz.newInstance())吗?

这里需要借助一个Java实时反编译工具friday,看看项目执行SkyWalking Agent之后反编译的结构

原始类:

@RestController
@RequestMapping("/api/hello")
public class UserController {@GetMappingpublic String sayHello() {return "hello";}}

增强后:

@RestController
@RequestMapping(value={"/test"})
public class TestController
implements EnhancedInstance {private volatile Object _$EnhancedClassField_ws;public static volatile /* synthetic */ InstMethodsInter delegate$mvblfc0;public static volatile /* synthetic */ InstMethodsInter delegate$hfbkh30;public static volatile /* synthetic */ ConstructorInter delegate$gr07501;private static final /* synthetic */ Method cachedValue$kkbY4FHP$ldstch2;public static volatile /* synthetic */ InstMethodsInter delegate$lvp69q1;public static volatile /* synthetic */ InstMethodsInter delegate$mpv7fs0;public static volatile /* synthetic */ ConstructorInter delegate$v0q1e31;private static final /* synthetic */ Method cachedValue$Hx3zGNqH$ldstch2;public TestController() {this(null);delegate$v0q1e31.intercept((Object)this, new Object[0]);}private /* synthetic */ TestController(auxiliary.YsFzTfDy ysFzTfDy) {}@RequestMappingpublic String test() {return (String)delegate$lvp69q1.intercept((Object)this, new Object[0], (Callable)new auxiliary.pEJy33Ip(this), cachedValue$Hx3zGNqH$ldstch2);}private /* synthetic */ String test$original$70VVkKcL() {return "sucess";}static {ClassLoader.getSystemClassLoader().loadClass("org.apache.skywalking.apm.dependencies.net.bytebuddy.dynamic.Nexus").getMethod("initialize", Class.class, Integer.TYPE).invoke(null, TestController.class, 544534948);cachedValue$Hx3zGNqH$ldstch2 = TestController.class.getMethod("test", new Class[0]);}final /* synthetic */ String test$original$70VVkKcL$accessor$Hx3zGNqH() {return this.test$original$70VVkKcL();}
}

然后还要再看一下SkyWalkingAgent.premain()

public static void premain(String agentArgs, Instrumentation instrumentation) throws PluginException {final PluginFinder pluginFinder;//省略...//指定byteBuddy要拦截的类agentBuilder.type(pluginFinder.buildMatch())//指定字节码增强的工具.transform(new Transformer(pluginFinder))//redefine和retransformation的区别在于是否保留修改前的内容.with(AgentBuilder.RedefinitionStrategy.RETRANSFORMATION).with(new RedefinitionListener()).with(new Listener())//将agent安装到instrumentation.installOn(instrumentation);//省略...            
}
  • redefineretransform的区别在于是否保留修改前的内容,从反编译的内容可以看到test()方法的内容已经被修改掉了,原方法被重命名为test o r i g i n a l original original+随机字符串
  • 新的test()方法中调用了InstMethodsInter的intercept()方法,最后传入的intercept()的method参数是被修改后的test()方法,不再指向原生的test()方法,如果在InstMethodsInter使用method.invoke(clazz.newInstance())就相当于自己调自己,就会死递归下去

总结

  • 构造方法
    • 是JDK类库的类
    • 不是JDK类库的类
      • 在原有的构造方法执行完后,在执行onContruct进行增强
  • 实例方法
    • 和静态方法相同

以上就是构造方法和实例方法增强逻辑。

在我们执行增强逻辑前,我们先看下插件拦截类的加载过程

  • 静态方法增强前的加载
//clazz 要修改的字节码的原生类
StaticMethodsAroundInterceptor interceptor = InterceptorInstanceLoader.load(staticMethodsAroundInterceptorClassName, clazz.getClassLoader());
  • 构造/实例方法前的加载
//当前拦截到的类的类加载器
interceptor = InterceptorInstanceLoader.load(instanceMethodsAroundInterceptorClassName, classLoader);

这个加载的方法很重要,它是将插件拦截类和业务执行类的类加载器进行融合的关键,也是skywalking的精髓所在,下篇文章我们来详细的分析下。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/143831.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

QT使用Socket与安卓Socket互发消息

背景:安卓设备通过usb网络共享给Linux,此时安卓设备与linux处于同一网络环境,符合使用socket的条件,linux做客户端,安卓做服务端 1.QT使用Socket (1).在工程文件中加入 QT network (2).导包以及写一些槽函数用做数据传输与状态接收 #ifndef MAINWINDOW_H #define MAINWINDOW…

【算法】算法题-20231116

这里写目录标题 一、合并两个有序数组&#xff08;力扣88 &#xff09;二、剑指 Offer 39. 数组中出现次数超过一半的数字三、移除元素&#xff08;力扣27&#xff09;四、找出字符串中第一个匹配项的下标&#xff08;28&#xff09; 一、合并两个有序数组&#xff08;力扣88 &…

在 HarmonyOS 上实现 ArkTS 与 H5 的交互

介绍 本篇 Codelab 主要介绍 H5 如何调用原生侧相关功能&#xff0c;并在回调中获取执行结果。以“获取通讯录”为示例分步讲解 JSBridge 桥接的实现。 相关概念 Web组件&#xff1a;提供具有网页显示能力的 Web 组件。 ohos.web.webview&#xff1a;提供 web 控制能力。 …

洋葱架构、三层架构及两者区别

前言 洋葱架构它的名称来源于洋葱的层次结构&#xff0c;即软件代码的各层次之间的关系。在这种架构中&#xff0c;应用程序的各个组件通过一系列层次结构被逐层包裹在一起&#xff0c;形成一个类似于洋葱的结构。 一、经典三层架构 三层架构是一种软件设计模式&#xff0c;…

【机器学习5】无监督学习聚类

相比于监督学习&#xff0c; 非监督学习的输入数据没有标签信息&#xff0c; 需要通过算法模型来挖掘数据内在的结构和模式。 非监督学习主要包含两大类学习方法&#xff1a; 数据聚类和特征变量关联。 1 K均值聚类及优化及改进模型 1.1 K-means 聚类是在事先并不知道任何样…

合肥中科深谷嵌入式项目实战——基于ARM语音识别的智能家居系统(二)

目录 基于ARM语音识别的智能家居系统 练习一 一、程序编译 练习二&#xff1a; 二、文件IO 三、文件IO常用API接口函数 1、打开文件 open&#xff08;&#xff09; 2、将数据内容写入文件 write&#xff08;&#xff09; 3、关闭&#xff08;保存&#xff09;文件 四、…

WPF中Dispatcher对象的用途是什么

在WPF (Windows Presentation Foundation) 中&#xff0c;Dispatcher 对象的主要用途是提供一个与UI线程关联的消息循环系统&#xff0c;这允许开发者在UI线程上安排和执行任务。由于WPF的UI元素不是线程安全的&#xff0c;因此任何对UI元素的访问都必须从创建该元素的线程&…

分类预测 | Matlab实现QPSO-SVM、PSO-SVM、SVM多特征分类预测对比

分类预测 | Matlab实现QPSO-SVM、PSO-SVM、SVM多特征分类预测对比 目录 分类预测 | Matlab实现QPSO-SVM、PSO-SVM、SVM多特征分类预测对比分类效果基本描述程序设计参考资料 分类效果 基本描述 1.Matlab实现QPSO-SVM、PSO-SVM、SVM分类预测对比&#xff0c;运行环境Matlab2018b…

uniapp生成自定义(分享)图片并保存到相册

需求描述 在一个页面中底部有个保存图片的功能&#xff0c;点击能够保存一张生成的自定义表格图片。 第一眼见到这个需求 自己会出现了两个问题 如何去处理图片中的自定义内容以及样式如何将自定义内容转化成图片 至于保存图片&#xff0c;uniapp有对应的api去实现uni.saveIma…

AIGC ChatGPT 4 与 Python 进行数据分析与可视化

Python是数据分析的理想工具,其优势主要体现在以下几个方面: 1. 丰富的库资源:Python有大量的数据分析和科学计算库,如pandas、NumPy、SciPy和Matplotlib等,这些库提供了数据操纵、处理、可视化、建模和算法功能,大大简化了数据分析的复杂性。 2. 高级编程语言:Python…

小样本目标检测(Few-Shot Object Detection)综述

背景 前言:我的未来研究方向就是这个,所以会更新一系列的文章,就关于FSOD,如果有相同研究方向的同学欢迎沟通交流,我目前研一,希望能在研一发文,目前也有一些想法,但是具体能不能实现还要在做的过程中慢慢评估和实现.写文的主要目的还是记录,避免重复劳动,我想用尽量简洁的语言…

【移远QuecPython】EC800M物联网开发板的MQTT协议腾讯云数据上报

【移远QuecPython】EC800M物联网开发板的MQTT协议腾讯云数据上报 文章目录 导入库初始化设置MQTT注册回调订阅发布功能开启服务发送消息函数打包调用测试效果附录&#xff1a;列表的赋值类型和py打包列表赋值BUG复现代码改进优化总结 py打包 导入库 from TenCentYun import TX…

Clickhouse学习笔记(15)—— Clickhouse备份

手动备份 参考官网&#xff1a;Backup and Restore | ClickHouse Docs 简单来说&#xff0c;就是我们可以通过ALTER TABLE ... FREEZE PARTITION ...命令为表分区创建一个本地副本&#xff0c;然后这个副本硬链接到/var/lib/clickhouse/shadow/文件夹&#xff0c;因此其不会耗…

Unity性能优化分析篇

性能优化是游戏项目开发中一个重要环节。游戏帧率过低&#xff0c;手机发烫&#xff0c; 包体太大&#xff0c;低端机上跑不起来等, 这些都需要来做优化&#xff0c;不管过去&#xff0c;现在&#xff0c;未来&#xff0c;性能优化都是永恒的话题。 而性能优化首先要掌握的是性…

BIO、NIO、AIO三者的区别及其应用场景(结合生活例子,简单易懂)

再解释三者之前我们需要先了解几个概念&#xff1a; 阻塞、非阻塞&#xff1a;是相较于线程来说的&#xff0c;如果是阻塞则线程无法往下执行&#xff0c;不阻塞&#xff0c;则线程可以继续往下 执行。同步、异步&#xff1a;是相较于IO来说的&#xff0c;同步需要等待IO操作完…

Outlook邮件视图设置怎么修复

故障现象 Outlook邮箱显示不对 故障截图 故障原因 邮箱视图设置不对 解决方案 1、在Outlook上方工具栏找到视图按钮&#xff0c;以此选择视图→视图设置→列&#xff0c;打开选择的列 2、在视图→邮件预览里面&#xff0c;选择1行&#xff0c;在阅读格式选择靠右&#xff…

AI创作系统ChatGPT网站源码+支持最新GPT-Turbo模型+支持DALL-E3文生图/AI绘画源码

一、AI创作系统 SparkAi创作系统是基于OpenAI很火的ChatGPT进行开发的Ai智能问答系统和Midjourney绘画系统&#xff0c;支持OpenAI-GPT全模型国内AI全模型。本期针对源码系统整体测试下来非常完美&#xff0c;可以说SparkAi是目前国内一款的ChatGPT对接OpenAI软件系统。那么如…

Nodejs操作缓存数据库-Redis

Hi I’m Shendi Nodejs专栏 Nodejs操作缓存数据库-Redis 在服务端开发中&#xff0c;缓存数据库也是不可或缺的&#xff0c;可以提高程序并发以及方便后续扩展&#xff0c;而目前最常用的莫过于Redis了 安装依赖 和之前的mysql一样&#xff0c;redis的依赖最常用的就是redis …

自学SLAM(8)《第四讲:相机模型与非线性优化》作业

前言 小编研究生的研究方向是视觉SLAM&#xff0c;目前在自学&#xff0c;本篇文章为初学高翔老师课的第四次作业。 文章目录 前言1.图像去畸变2.双目视差的使用3.矩阵微分4.高斯牛顿法的曲线拟合实验 1.图像去畸变 现实⽣活中的图像总存在畸变。原则上来说&#xff0c;针孔透…

SparkSQL项目实战

1 准备数据 我们这次Spark-sql操作所有的数据均来自Hive&#xff0c;首先在Hive中创建表&#xff0c;并导入数据。一共有3张表&#xff1a;1张用户行为表&#xff0c;1张城市表&#xff0c;1张产品表。 1&#xff09;将city_info.txt、product_info.txt、user_visit_action.txt…