pyspark合并两个dataframe_PySpark源码解析,教你用Python调用高效Scala接口

2023-10-26

在数据科学领域,Python 一直占据比较重要的地位,仍然有大量的数据工程师在使用各类 Python 数据处理和科学计算的库,例如 numpy、Pandas、scikit-learn 等。

相较于Scala语言而言,Python具有其独有的优势及广泛应用性,因此Spark也推出了PySpark,在框架上提供了利用Python语言的接口,为数据科学家使用该框架提供了便利。

众所周知,Spark 框架主要是由 Scala 语言实现,同时也包含少量 Java 代码。Spark 面向用户的编程接口,也是 Scala。然而,在数据科学领域,Python 一直占据比较重要的地位,仍然有大量的数据工程师在使用各类 Python 数据处理和科学计算的库,例如 numpy、Pandas、scikit-learn 等。同时,Python 语言的入门门槛也显著低于 Scala。

为此,Spark 推出了 PySpark,在 Spark 框架上提供一套 Python 的接口,方便广大数据科学家使用。本文主要从源码实现层面解析 PySpark 的实现原理,包括以下几个方面:

PySpark 的多进程架构;
Python 端调用 Java、Scala 接口;
Python Driver 端 RDD、SQL 接口;
Executor 端进程间通信和序列化;
Pandas UDF;
总结。

PySpark项目地址:https://github.com/apache/spark/tree/master/python

1、PySpark 的多进程架构

PySpark 采用了 Python、JVM 进程分离的多进程架构,在 Driver、Executor 端均会同时有 Python、JVM 两个进程。当通过 spark-submit 提交一个 PySpark 的 Python 脚本时,Driver 端会直接运行这个 Python 脚本,并从 Python 中启动 JVM;而在 Python 中调用的 RDD 或者 DataFrame 的操作,会通过 Py4j 调用到 Java 的接口。

在 Executor 端恰好是反过来,首先由 Driver 启动了 JVM 的 Executor 进程,然后在 JVM 中去启动 Python 的子进程,用以执行 Python 的 UDF,这其中是使用了 socket 来做进程间通信。总体的架构图如下所示:

2、Python Driver 如何调用 Java 的接口

上面提到,通过 spark-submit 提交 PySpark 作业后,Driver 端首先是运行用户提交的 Python 脚本,然而 Spark 提供的大多数 API 都是 Scala 或者 Java 的,那么就需要能够在 Python 中去调用 Java 接口。这里 PySpark 使用了 Py4j 这个开源库。当创建 Python 端的 SparkContext 对象时,实际会启动 JVM,并创建一个 Scala 端的 SparkContext 对象。代码实现在 python/pyspark/context.py:

def _ensure_initialized(cls, instance=None, gateway=None, conf=None):
"""
Checks whether a SparkContext is initialized or not.
Throws error if a SparkContext is already running.
"""
with SparkContext._lock:
if not SparkContext._gateway:
SparkContext._gateway = gateway or launch_gateway(conf)
SparkContext._jvm = SparkContext._gateway.jvm

在 launch_gateway (python/pyspark/java_gateway.py) 中,首先启动 JVM 进程:

SPARK_HOME = _find_spark_home()
# Launch the Py4j gateway using Spark's run command so that we pick up the
# proper classpath and settings from spark-env.sh
on_windows = platform.system() == "Windows"
script = "./bin/spark-submit.cmd" if on_windows else "./bin/spark-submit"
command = [os.path.join(SPARK_HOME, script)]

然后创建 JavaGateway 并 import 一些关键的 class:

gateway = JavaGateway(
gateway_parameters=GatewayParameters(port=gateway_port, auth_token=gateway_secret,
auto_convert=True))
# Import the classes used by PySpark
java_import(gateway.jvm, "org.apache.spark.SparkConf")
java_import(gateway.jvm, "org.apache.spark.api.java.*")
java_import(gateway.jvm, "org.apache.spark.api.python.*")
java_import(gateway.jvm, "org.apache.spark.ml.python.*")
java_import(gateway.jvm, "org.apache.spark.mllib.api.python.*")
# TODO(davies): move into sql
java_import(gateway.jvm, "org.apache.spark.sql.*")
java_import(gateway.jvm, "org.apache.spark.sql.api.python.*")
java_import(gateway.jvm, "org.apache.spark.sql.hive.*")
java_import(gateway.jvm, "scala.Tuple2")

拿到 JavaGateway 对象,即可以通过它的 jvm 属性,去调用 Java 的类了,例如:

gateway = JavaGateway()

gateway = JavaGateway()
jvm = gateway.jvm
l = jvm.java.util.ArrayList()

然后会继续创建 JVM 中的 SparkContext 对象:

def _initialize_context(self, jconf):
"""
Initialize SparkContext in function to allow subclass specific initialization
"""
return self._jvm.JavaSparkContext(jconf)

# Create the Java SparkContext through Py4J
self._jsc = jsc or self._initialize_context(self._conf._jconf)

3、Python Driver 端的 RDD、SQL 接口

在 PySpark 中,继续初始化一些 Python 和 JVM 的环境后,Python 端的 SparkContext 对象就创建好了,它实际是对 JVM 端接口的一层封装。和 Scala API 类似,SparkContext 对象也提供了各类创建 RDD 的接口,和 Scala API 基本一一对应,我们来看一些例子。

def newAPIHadoopFile(self, path, inputFormatClass, keyClass, valueClass, keyConverter=None,
valueConverter=None, conf=None, batchSize=0):
jconf = self._dictToJavaMap(conf)
jrdd = self._jvm.PythonRDD.newAPIHadoopFile(self._jsc, path, inputFormatClass, keyClass,
valueClass, keyConverter, valueConverter,
jconf, batchSize)
return RDD(jrdd, self)

可以看到,这里 Python 端基本就是直接调用了 Java/Scala 接口。而 PythonRDD (core/src/main/scala/org/apache/spark/api/python/PythonRDD.scala),则是一个 Scala 中封装的伴生对象,提供了常用的 RDD IO 相关的接口。另外一些接口会通过 self._jsc 对象去创建 RDD。其中 self._jsc 就是 JVM 中的 SparkContext 对象。拿到 RDD 对象之后,可以像 Scala、Java API 一样,对 RDD 进行各类操作,这些大部分都封装在 python/pyspark/rdd.py 中。

这里的代码中出现了 jrdd 这样一个对象,这实际上是 Scala 为提供 Java 互操作的 RDD 的一个封装,用来提供 Java 的 RDD 接口,具体实现在 core/src/main/scala/org/apache/spark/api/java/JavaRDD.scala 中。可以看到每个 Python 的 RDD 对象需要用一个 JavaRDD 对象去创建。

对于 DataFrame 接口,Python 层也同样提供了 SparkSession、DataFrame 对象,它们也都是对 Java 层接口的封装,这里不一一赘述。

4、Executor 端进程间通信和序列化

对于 Spark 内置的算子,在 Python 中调用 RDD、DataFrame 的接口后,从上文可以看出会通过 JVM 去调用到 Scala 的接口,最后执行和直接使用 Scala 并无区别。而对于需要使用 UDF 的情形,在 Executor 端就需要启动一个 Python worker 子进程,然后执行 UDF 的逻辑。那么 Spark 是怎样判断需要启动子进程的呢?

在 Spark 编译用户的 DAG 的时候,Catalyst Optimizer 会创建 BatchEvalPython 或者 ArrowEvalPython 这样的 Logical Operator,随后会被转换成 PythonEvals 这个 Physical Operator。在 PythonEvals(sql/core/src/main/scala/org/apache/spark/sql/execution/SparkStrategies.scala)中:

object PythonEvals extends Strategy {
override def apply(plan: LogicalPlan): Seq[SparkPlan] = plan match {
case ArrowEvalPython(udfs, output, child, evalType) =>
ArrowEvalPythonExec(udfs, output, planLater(child), evalType) :: Nil
case BatchEvalPython(udfs, output, child) =>
BatchEvalPythonExec(udfs, output, planLater(child)) :: Nil
case _ =>
Nil
}
}

创建了 ArrowEvalPythonExec 或者 BatchEvalPythonExec,而这二者内部会创建 ArrowPythonRunner、PythonUDFRunner 等类的对象实例,并调用了它们的 compute 方法。由于它们都继承了 BasePythonRunner,基类的 compute 方法中会去启动 Python 子进程:

def compute(
inputIterator: Iterator[IN],
partitionIndex: Int,
context: TaskContext): Iterator[OUT] = {
// ......

val worker: Socket = env.createPythonWorker(pythonExec, envVars.asScala.toMap)
// Start a thread to feed the process input from our parent's iterator
val writerThread = newWriterThread(env, worker, inputIterator, partitionIndex, context)
writerThread.start()
val stream = new DataInputStream(new BufferedInputStream(worker.getInputStream, bufferSize))

val stdoutIterator = newReaderIterator(
stream, writerThread, startTime, env, worker, releasedOrClosed, context)
new InterruptibleIterator(context, stdoutIterator)

这里 env.createPythonWorker 会通过 PythonWorkerFactory(core/src/main/scala/org/apache/spark/api/python/PythonWorkerFactory.scala)去启动 Python 进程。Executor 端启动 Python 子进程后,会创建一个 socket 与 Python 建立连接。所有 RDD 的数据都要序列化后,通过 socket 发送,而结果数据需要同样的方式序列化传回 JVM。

对于直接使用 RDD 的计算,或者没有开启 spark.sql.execution.arrow.enabled 的 DataFrame,是将输入数据按行发送给 Python,可想而知,这样效率极低。

在 Spark 2.2 后提供了基于 Arrow 的序列化、反序列化的机制(从 3.0 起是默认开启),从 JVM 发送数据到 Python 进程的代码在 sql/core/src/main/scala/org/apache/spark/sql/execution/python/ArrowPythonRunner.scala。这个类主要是重写了 newWriterThread 这个方法,使用了 ArrowWriter 向 socket 发送数据:

val arrowWriter = ArrowWriter.create(root)
val writer = new ArrowStreamWriter(root, null, dataOut)
writer.start()

while (inputIterator.hasNext) {
val nextBatch = inputIterator.next()

while (nextBatch.hasNext) {
arrowWriter.write(nextBatch.next())
}

arrowWriter.finish()
writer.writeBatch()
arrowWriter.reset()

可以看到,每次取出一个 batch,填充给 ArrowWriter,实际数据会保存在 root 对象中,然后由 ArrowStreamWriter 将 root 对象中的整个 batch 的数据写入到 socket 的 DataOutputStream 中去。ArrowStreamWriter 会调用 writeBatch 方法去序列化消息并写数据,代码参考 ArrowWriter.java#L131。

protected ArrowBlock writeRecordBatch(ArrowRecordBatch batch) throws IOException {
ArrowBlock block = MessageSerializer.serialize(out, batch, option);
LOGGER.debug("RecordBatch at {}, metadata: {}, body: {}",
block.getOffset(), block.getMetadataLength(), block.getBodyLength());
return block;
}

在 MessageSerializer 中,使用了 flatbuffer 来序列化数据。flatbuffer 是一种比较高效的序列化协议,它的主要优点是反序列化的时候,不需要解码,可以直接通过裸 buffer 来读取字段,可以认为反序列化的开销为零。我们来看看 Python 进程收到消息后是如何反序列化的。

Python 子进程实际上是执行了 worker.py 的 main 函数 (python/pyspark/worker.py):

if __name__ == '__main__':
# Read information about how to connect back to the JVM from the environment.
java_port = int(os.environ["PYTHON_WORKER_FACTORY_PORT"])
auth_secret = os.environ["PYTHON_WORKER_FACTORY_SECRET"]
(sock_file, _) = local_connect_and_auth(java_port, auth_secret)
main(sock_file, sock_file)

这里会去向 JVM 建立连接,并从 socket 中读取指令和数据。对于如何进行序列化、反序列化,是通过 UDF 的类型来区分:

eval_type = read_int(infile)
if eval_type == PythonEvalType.NON_UDF:
func, profiler, deserializer, serializer = read_command(pickleSer, infile)
else:
func, profiler, deserializer, serializer = read_udfs(pickleSer, infile, eval_type)

在 read_udfs 中,如果是 PANDAS 类的 UDF,会创建 ArrowStreamPandasUDFSerializer,其余的 UDF 类型创建 BatchedSerializer。我们来看看 ArrowStreamPandasUDFSerializer(python/pyspark/serializers.py):

def dump_stream(self, iterator, stream):
import pyarrow as pa
writer = None
try:
for batch in iterator:
if writer is None:
writer = pa.RecordBatchStreamWriter(stream, batch.schema)
writer.write_batch(batch)
finally:
if writer is not None:
writer.close()

def load_stream(self, stream):
import pyarrow as pa
reader = pa.ipc.open_stream(stream)
for batch in reader:
yield batch

可以看到,这里双向的序列化、反序列化,都是调用了 PyArrow 的 ipc 的方法,和前面看到的 Scala 端是正好对应的,也是按 batch 来读写数据。对于 Pandas 的 UDF,读到一个 batch 后,会将 Arrow 的 batch 转换成 Pandas Series。

def arrow_to_pandas(self, arrow_column):
from pyspark.sql.types import _check_series_localize_timestamps

# If the given column is a date type column, creates a series of datetime.date directly
# instead of creating datetime64[ns] as intermediate data to avoid overflow caused by
# datetime64[ns] type handling.
s = arrow_column.to_pandas(date_as_object=True)

s = _check_series_localize_timestamps(s, self._timezone)
return s

def load_stream(self, stream):
"""
Deserialize ArrowRecordBatches to an Arrow table and return as a list of pandas.Series.
"""
batches = super(ArrowStreamPandasSerializer, self).load_stream(stream)
import pyarrow as pa
for batch in batches:
yield [self.arrow_to_pandas(c) for c in pa.Table.from_batches([batch]).itercolumns()]

5、Pandas UDF

前面我们已经看到,PySpark 提供了基于 Arrow 的进程间通信来提高效率,那么对于用户在 Python 层的 UDF,是不是也能直接使用到这种高效的内存格式呢?答案是肯定的,这就是 PySpark 推出的 Pandas UDF。区别于以往以行为单位的 UDF,Pandas UDF 是以一个 Pandas Series 为单位,batch 的大小可以由 spark.sql.execution.arrow.maxRecordsPerBatch 这个参数来控制。这是一个来自官方文档的示例:

def multiply_func(a, b):
return a * b

multiply = pandas_udf(multiply_func, returnType=LongType())

df.select(multiply(col("x"), col("x"))).show()

上文已经解析过,PySpark 会将 DataFrame 以 Arrow 的方式传递给 Python 进程,Python 中会转换为 Pandas Series,传递给用户的 UDF。在 Pandas UDF 中,可以使用 Pandas 的 API 来完成计算,在易用性和性能上都得到了很大的提升。

6、总结

PySpark 为用户提供了 Python 层对 RDD、DataFrame 的操作接口,同时也支持了 UDF,通过 Arrow、Pandas 向量化的执行,对提升大规模数据处理的吞吐是非常重要的,一方面可以让数据以向量的形式进行计算,提升 cache 命中率,降低函数调用的开销,另一方面对于一些 IO 的操作,也可以降低网络延迟对性能的影响。

然而 PySpark 仍然存在着一些不足,主要有:

进程间通信消耗额外的 CPU 资源;
编程接口仍然需要理解 Spark 的分布式计算原理;
Pandas UDF 对返回值有一定的限制,返回多列数据不太方便。

Databricks 提出了新的 Koalas 接口来使得用户可以以接近单机版 Pandas 的形式来编写分布式的 Spark 计算作业,对数据科学家会更加友好。而 Vectorized Execution 的推进,有望在 Spark 内部一切数据都是用 Arrow 的格式来存放,对跨语言支持将会更加友好。同时也能看到,在这里仍然有很大的性能、易用性的优化空间,这也是我们平台近期的主要发力方向之一。

陈绪,汇量科技(Mobvista)高级算法科学家,负责汇量科技大规模数据智能计算引擎和平台的研发工作。在此之前陈绪是阿里巴巴高级技术专家,负责阿里集团大规模机器学习平台的研发。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

pyspark合并两个dataframe_PySpark源码解析,教你用Python调用高效Scala接口 的相关文章

  • 对比学习simSiam(一)--Exploring Simple Siamese Representation Learning总体理解

    1 从名字上把握 sim是我们熟知的相似的那个单词 这个Siam是孪生的意思 这里使用这个来命名应该是为了指出孪生的重要性 这里的核心其实是在提出一个思想 对比学习这种由孪生网络结构构成的无监督学习的关键其实是孪生网络 两个网络有其中一方停
  • PyQt入门(8)-常用控件(下)

    目录 1 QListWidget 2 QTreeWidget 3 QTableWidget 1 QListWidget QListWidget是一个QListView的便捷类 提供一个列表视图 大数据量的情况下QListView确实更加灵活
  • Java 第一个程序 HelloWorld

    目录 1 常用 DOS 命令 2 Path 环境变量的配置 3 HelloWorld 编写和执行 4 HelloWorld 详解 1 常用 DOS 命令 在接触集成开发环境之前 我们需要使用命令行窗口对 Java 程序进行编译和运行 所以需
  • LeetCode每日一题2021.11.21—12.01

    2021 11 21 559 N叉树的最大深度 题目 思路 深度遍历 广度优先遍历 每次出队要把队列所有的元素拿出来 代码 Definition for a Node class Node public int val vector
  • 常用的C语言学习网站

    1 C语言网 C语言网 www dotcpp com 不仅提供C语言 还包括C java 算法与数据结构等课程在内的各种入门教程 视频录像 编程经验 编译器教程及软件下载 题解博客 源码分享等优质资源 提倡边学边练边分享 同时提供对口的IT
  • windows 通过ssh连接到Linux主机

    windows 通过ssh连接到Linux主机 文章目录 windows 通过ssh连接到Linux主机 1 ssh的认识 2 ssh的安全验证 3 连接方法 4 windows 通过ssh连接到Linux主机 1 ssh的认识 SSH 为
  • pixelBook2017原系统ChromeOS改windows

    部分内容引用于CSDN博主 行走的病毒 的原创文章 遵循CC 4 0 BY SA版权协议 转载请附上原文出处链接及本声明 原文链接 https blog csdn net DZTlaila article details 103843172
  • redis

    django redis 使用 结合 django redis 配置 django settings CACHES default BACKEND django redis cache RedisCache LOCATION redis 1
  • 手机连不上 mac 的解决办法

    原文地址 http mobile 51cto com aprogram 386942 htm http www miui com thread 1413676 1 1 html 小米2及其他Android手机无法连接mac解决方案 2013
  • Intellij IDEA运行报Command line is too long解法

    报错内容 Error running ServiceStarter Command line is too long Shorten command line for ServiceStarter or also for Applicati
  • 向女性程序员致敬!

    今天是3月8日 国际妇女节 先祝我娘节日快乐 再祝广大女性们节日快乐 这里特别祝福一下程序媛们 你们冒着脱发 单身 没周末 xxxxxx等各种高风险在互联网事业中与程序猿们同甘共苦 一起撑起了互联网的半边天 而且听闻历史上第一位程序员也是女
  • 【Android开发】用户界面设计-在代码中控制UI界面

    效果图 实现方法 MainActivity package com example test import android app ActionBar LayoutParams import android app Activity imp
  • 三种电源防反接电路(二极管、PMOS)

    最近偶然看到PMOS防反接电路 感觉挺实用的 做个记录 软件 LTspice 二极管串联 以常用的5V 2A为例 常用二极管串联在电路中 在电源反接时 二极管承担所有的电压 有效防止电源反接损坏后级设备 但是 二极管上压降较大 损耗较高 使
  • 了解Golang基本数据类型

    文章目录 前言 一 整数数字 二 浮点数字 请注意 与前面的代码一样 Go 会从使用的值推断其数据类型 三 布尔型 四 字符串 五 常见转义字符 五 默认值 六 类型转换 总结 前言 Go 是一种强类型语言 这意味着你声明的每个变量都绑定到
  • Springboot日志级别

    一 开启Springboot详细日志 在application properties文件中添加以下代码 logging level root debug 二 sql打印在控制台 在application properties文件中添加以下代
  • 预测数值型数据:回归源码分析(1)

    回归模型比较简单 这里先简单介绍下 后面遇到难点再具体分析 回归的一般方法 1 收集数据 采用任意方法收集数据 2 准备数据 回归需要数值型数据 标称型数据将被转成二值型数据 3 分析数据 绘出数据的可视化二维图将有助于对数据做出理解和分析
  • 零基础入门microbit教程

    1 什么是microbit 1 micro bit 百度百科 micro bit 是一款由英国广播电视公司 BBC 推出的专为青少年编程教育设计的微型电脑开发板 2 官网介绍 The Micro bit Educational Founda
  • OC语言——点语法和成员变量的4种作用域及property和synthesize的使用

    点语法 点语法的本质还是方法调用 Person p Person new 点语法的本质还是方法调用 p age 10 p setAge 10 一 点语法注意点 implementation Person setter方法中的死循环 void
  • LLVM - 学习笔记一

    1 工具和库 LLVM中的独立工具 opt 在IR级对程序进行优化的工具 输入必须是LLVM的bitcode 生成的输出文件必须具有相同的类型 llc 通过特定后端将LLVM bitcode转换成目标汇编或目标问价的工具 llvm mc 能

随机推荐

  • Hadoop之MapReduce工作原理

    Hadoop由两部分组成 分别是分布式文件系统HDFS和分布式计算框架MapReduce 其中 分布式文件系统HDFS主要用于大规模数据的分布式存储 而MapReduce则构建在分布式文件系统上 对于存储在分布式文件系统的数据进行分布式计算
  • 【对比Java学Kotlin】在 foreach 中使用 break&continue

    正常情况下 我们只能在 loop 中使用 break 和 continue 但是 foreach 是扩展函数 不属于 loop 的范畴 如果我们想在 foreach 中达到 break 和 continue 的效果 只能使用 return
  • echo source 命令与 setup.bash与.bashrc文件

    编译完毕后键 echo source catkin ws devel setup bash gt gt bashrc source bashrc 本文分析这两条命令 echo 与source 以及setup bash文件与 bashrc两个
  • Flutter 布局Row(水平方向布局)、Column(垂直方向布局)、Wrap(可以自动换行的布局)、Flex(弹性布局)、Stack(叠层布局)、

    1 线性布局 Row 水平方向布局 Row 表示水平方向子组件的布局顺序 是从左往右还是从右往左 默认为系统当前Locale环境的文本方向 如中文 英语都是从左往右 而阿拉伯语是从右往左 TextDirection textDirectio
  • 通过模拟退火改进的Elman神经网络(Matlab代码实现)

    目录 1 概述 2 运行结果 3 参考文献 4 Matlab代码 1 概述 神经网络是一个庞大的体系和概念 根据处理信息的不同方式来区分不同的network 比如根据处理信息结果的传递方向 分前馈型与反馈型 前馈型网络会根据输出数值来调整网
  • dede php调用指定文章,DedeCMS调用指定文章内容的两种实现方法

    有时候我们需要dedecms能够调用指定文章的内容 尤其是在建企业网站的时候 需要在首页调用网站简介联系我们什么的 今天 织梦技术研究中心就给大家介绍两种实现调用指定文章内容的方法 方法一 打开include inc arcpart vie
  • Golang基础 函数详解 函数基础

    文章目录 01 函数声明 02 更多样的参数列表 03 更灵活的返回值列表 参考资料 函数是一个固定的 可重复使用的程序段 子程序 它在实现单一或相关联功能的同时 还可以带有入口和出口 所谓的入口 就是函数参数即形参 通过这个入口把函数的参
  • 最强 Verilog 中 IP核 调用实现及思想

    写在前面 无论是在 ISE 还是 Vivado 中 关于 IP核 的调用都是非常方便的 所以对于初学者来说最关键的不是在 IP Catalog 中设置相关的 IP核 参数 而是在生成相关的 IP核 后该怎么做 也即如何让这些 IP核 为项目
  • 海思3516系列芯片SPI速率慢问题深入研究与优化(基于PL022 SPI 控制器)

    海思3516系列芯片SPI速率慢问题深入分析与优化 基于PL022 SPI 控制器 我在某个海思主控的项目中需要使用SPI接口来驱动一块液晶屏 液晶屏主控为 st7789 分辨率 240x240 图像格式 RGB565 查阅海思相关手册可知
  • HDU - 1598之为达目的不择手段(并查集的应用)

    find the most comfortable road Time Limit 1000 1000 MS Java Others Memory Limit 32768 32768 K Java Others Total Submissi
  • docker快速搭建redis集群(两种暴露宿主网络的方法)

    宿主机IP 192 168 123 181 方案一 host网络模式 1 新建6个容器节点 for port in seq 4001 4006 do docker run itd name redis port network host v
  • Java线程:volatile关键字

    本文转载至 http lavasoft blog 51cto com 62575 222076 Java线程 volatile关键字 Java 语言包含两种内在的同步机制 同步块 或方法 和 volatile 变量 这两种机制的提出都是为了
  • 自制个人图床

    如何自制个人图床 有时候我们想要将自己的图片以链接的形式展示 就得需要使用图床 或者上传到自己的服务器 别人的图床会担心图片链接过期 然而自己的服务器会占用内存资源 所以我们就自制个人图床 首先你得有服务器和域名 好了废话不多说直接上教程
  • 2021-10-21

    当打开一个页面 需要第一行显示当前用户能够领取奖励的按钮 应用场景 1 当某些游戏有在线领奖的活动 比如在线10分钟 20分钟 以此类推可以领取一些奖励 当有很多时 页面装不下的时候 我们希望显示的第一个就是玩家可以领取的奖励 比如10分钟
  • C++—类和对象

    文章目录 1 类 2 对象 2 1 创建对象 2 2 对象的操作 2 3 构造函数 2 4 析构函数 3 静态成员 4 this指针 5 友元 一切我们研究的事物 都可以叫做对象 对象具有状态 操作和行为 通常用一个数值来描述对象的状态 对
  • DVWA ----Buete Force

    DVWA Buete Force 暴力破解 low 直接使用Burip suite来进行暴力破解 medium 与low的方法一样 但是在破解速度上比较慢 因为在源代码中多了sleep 函数 high 同样使用Burip suite进行暴力
  • RK3588开发板上使用Qt+OpenCV捕获摄像头图像

    在Qt下没有专门的视频采集与播放工具 这里使用了OpenCV所带的库函数捕获摄像头的视频图像 硬件环境 讯为RK3588开发板 OV5695 MIPI接口 摄像头 软件版本 OS ubuntu20 04镜像固件 QT 5 12 8 Qt C
  • 安全运营场景下的语言模型应用

    接上篇 将安全运营的定义为 使用算法能力提取关键信息 以此来规避算法误判漏判带来的责任问题 同时提升运营人员的工作效率 在这篇尝试对语言模型的使用方法做一下讨论和分享 1 语言模型 先聊一下语言模型 这里刻意规避了 大模型 这个词 主要是对
  • 【Python】循环语句

    目录 1 while 循环 2 for 循环 3 continue 4 break 1 while 循环 基本语法格式 while 条件 循环体 条件为真 则执行循环体代码 条件为假 则结束循环 例1 打印 1 10 的整数 num 1 w
  • pyspark合并两个dataframe_PySpark源码解析,教你用Python调用高效Scala接口

    在数据科学领域 Python 一直占据比较重要的地位 仍然有大量的数据工程师在使用各类 Python 数据处理和科学计算的库 例如 numpy Pandas scikit learn 等 相较于Scala语言而言 Python具有其独有的优