spark:Structured Streaming介绍

文章目录

  • 1. Structured Streaming介绍
    • 1.1 实时计算和离线计算
      • 1.1.1 实时计算
      • 1.1.2 离线计算
    • 1.2 有界和无界数据
  • 2. 简单使用
  • 3. 编程模型
  • 4. 数据处理流程
    • 4.1 读取数据Source
      • 4.1.1 文件数据处理
    • 4.2 计算操作 Operation
    • 4.3 数据输出 Sink
      • 4.3.1 输出模式
      • 4.3.2 指定输出位置
      • 4.3.3 设置触发器Trigger
      • 4.3.4 checkpoint检查目录设置

1. Structured Streaming介绍

spark进行实时数据流计算时有两个工具:Spark Streaming Structured Streaming
Spark Streaming:编写RDD代码处理数据流,可以解决非结构化的流式数据。
Structured Streaming:编写df代码处理数据流,可以解决结构化和半结构化的流式数据。
Structured Streaming 是基于 Spark SQL 引擎构建的可扩展容错流处理引擎,是在Spark 2.X出来的流框架,采用了无界表(使用DataFrame)的概念,流数据相当于往一个表上不断追加行,数据会被转化为DataFrame和DataSets类型,可用采用SQL的方式操作结构化流数据。

1.1 实时计算和离线计算

1.1.1 实时计算

实时计算,通常也称为“实时流计算”“流式计算”
流数据处理是指实时、连续地处理数据流。数据在被产生或接收后立即处理,并不需要等待所有数据到齐。数据的处理和传输是“逐条”进行的。

  • 处理时间:由于数据被实时处理,系统响应时间非常短,通常在毫秒或者秒级。
  • 数据量:流数据通常是无限的,数据持续不断地被生成和处理,系统需要持续运行。

1.1.2 离线计算

离线计算,通常也称为“批处理”,表示那些离线批量、延时较高的静态数据处理过程。
批数据处理是指在一个预定时间内收集一批数据,然后一次性对这批数据进行处理。数据是成批处理的,而不是逐条处理的。

  • 处理时间:批处理通常不是实时的,处理的延迟可能是分钟、小时甚至更长,T+1.
  • 数据量:批处理通常在所有数据收集完毕后进行,这意味着处理的数据集是固定大小的(如每日、每小时的数据)。数据处理完成后自动结束。

1.2 有界和无界数据

  • 有界数据
    • 有起始位置,有结束位置。比如文件数据,有起始行,有结束行。
    • 有明确的数据容量大小。处理数据时就能知道处理的数据大小。
    • 在处理数据时,按批次处理。
    • 数据处理完程序就结束
    • 离线计算时处理的都是有界数据。
  • 无界数据
    • 有起始位置,没有结束位置,知道数据的起始位置在哪里,但是数据的结束位置不知道(因为数据在不断产生,什么时候结束不知道)。
    • 流式数据都是无界数据。
    • 无界数据的总量是不确定的,数据是不断产生的。
    • 数据有时效性(有效期)。
    • 处理无界数据时,程序是持续运行的

2. 简单使用

  • socket服务
    • 安装ncat服务
      • 在线安装
        • yum install nc
      • 离线安装
        • rpm -ivh ncat-7.93-1.x86_64.rpm
    • 启动服务绑定9999端口
      • ncat -lk 9999
        在这里插入图片描述
  • Structured Streaming代码程序
    • 使用的是SparkSQL,所以在进行代码编写时使用SparkSQL的方法进行编写。
    • 使用 SparkSession
2,李四,22,男
3,王五,20,男
4,赵六,20,男
4,赵六,32,男
5,赵六,42,男
# 读取scoket产生的实时数据流
from pyspark.sql import SparkSession

ss = SparkSession.builder.getOrCreate()

#数据流读取使用readStream
opt = {
    #指定读取socket服务的IP地址
    'host':'192.168.88.100',
    #指定读取端口
    'port':9999
}
#使用socket方式读取网站服务的数据,得到一个无界的dataframe
df = ss.readStream.format('socket').options(**opt).load()

#实时输出数据
df.writeStream.start(format='console', outputMode='append').awaitTermination()

运行结果:
在这里插入图片描述

3. 编程模型

在进行流式数据开发,代码实现的过程
Structured Streaming在处理数据时分为四个部分

  • Input Table输入数据表 无界表 readStream

  • Qurey 对数据进行查询计算 DSL或SQL计算过程

  • Result Table 保存计算结果

  • Output 输出结果 writerStream
    在这里插入图片描述

  • 读取指定数据源的数据得到一个无界表数据

  • 对无界表数据进行计算处理

  • 接收无界表计算的结果

  • 将接收到的结果进行输出

4. 数据处理流程

4.1 读取数据Source

支持读取的数据源

  • 文件数据
    • 从文件中读取数据
  • kafka数据 常用
    • 从kafka中读取数据
  • socket数据
    • 从网络端口读取数据
  • Rate数据
    • 框架自己产生数据,测试性能,优化参数

4.1.1 文件数据处理

option参数描述说明
maxFilesPerTrigger每个batch最多的文件数,默认是没有限制。比如我设置了这个值为1,那么同时增加了5个文件,这5个文件会每个文件作为一波数据,更新streaming dataframe。
latestFirst是否先处理最新的新文件, 当有大量文件积压时有用 (默认: false)。
fileNameOnly是否检查新文件只有文件名而不是完整路径(默认值:false) 将此设置为 true 时,以下文件将被视为同一个文件,因为它们的文件名“dataset.txt”相同: “file:///dataset.txt” “s3://a/数据集.txt " “s3n://a/b/dataset.txt” “s3a://a/b/c/dataset.txt”。
  • 格式
spark.readStream.csv()

spark.readStream.load(format='csv',**options)
#流式读取文件数据
from pyspark.sql import SparkSession

ss = SparkSession.builder.getOrCreate()
# 读取文件数据
# 指定目录  不要指定文件名
# df = ss.readStream.load(format='csv',path='hdfs://node1:8020/data_stream',schema='id int,name string,gender string,age int,cls string')
# df = ss.readStream.csv(path='hdfs://node1:8020/data_stream',header=False,sep=',',schema='id int,name string,gender string,age int,cls string')
# df = ss.readStream.text(path='hdfs://node1:8020/data_stream2')
df = ss.readStream.option('maxFilesPerTrigger',1).csv('hdfs://node1:8020/data_csv',header=False,sep=',',schema='id int,name string,gender string,age int,cls string')

#输出
df.writeStream.start(format='console',outputMode='append').awaitTermination()

运行结果:
在这里插入图片描述
注意

  • 读取文件数据时,不能指定某个具体文件,而是指定文件所在的目录。
    • 目录下的同一个文件只会被读取一次,处理过的文件数据不会再重新处理。
  • 文件的读取方式在实际开发中用的比较少,生产一条数据,就要生成一个文件。但是,如果将多条数据收集之后写入统一文件,就变成了和批处理一样的开发。
  • 实际开发中很少使用spark流读文件,可以使用flume工具流式读取文件,然后在通过spark读取产生flume。

4.2 计算操作 Operation

采用DSL的方式进行数据的计算
where 方法
groupby
orderby

#数据流的计算
from pyspark.sql import SparkSession, functions as F

ss = SparkSession.builder.getOrCreate()
#流式读取数据,转为无界的dataframe
opt = {
    # 指定读取的socket服务的ip地址
    'host':'192.168.88.100',
    # 指定读取的端口
    'port':9999
}
df = ss.readStream.load(format='socket',**opt)
#流式数据的计算
#SQL
df.createTempView('stu')
sql_str = """
select gender,count(id),sum(age) from (
  select split(value,',')[0] as id,
  split(value,',')[1] as name,
  cast(split(value,',')[2] as int) as age,
  split(value,',')[3] as gender
   from stu) t1
   group by gender
"""
df_res = ss.sql(sql_str)

#结果数据输出
df_res.writeStream.start(format='console',outputMode='complete').awaitTermination()

输入:
在这里插入图片描述
运行结果:
在这里插入图片描述

4.3 数据输出 Sink

在StructuredStreaming中定义好Result DataFrame/Dataset后,调用writeStream()返回DataStreamWriter对象,设置查询Query输出相关属性,启动流式应用运行,相关属性如下

  • 输出模式(output mode):指定写入输出接收器的内容。
  • 查询名称(query name):(可选)指定用于标识的查询的唯一名称。将数据输出到内存可以指定一个表名
  • 触发间隔 (Trigger interval):(可选)指定触发间隔。如果未指定,系统将在上一次处理完成后立即检查新数据的可用性。如果由于先前的处理尚未完成而错过触发时间,则系统将立即触发处理。 写入数据的时间间隔
  • 检查点位置(checkpoint Location):对于可以保证端到端容错的某些输出接收器,请指定系统将写入所有检查点信息的位置。这应该是与HDFS兼容的容错文件系统中的一个目录。 文件方式输出时,必须指定。

4.3.1 输出模式

outputMode指定输出模式

  • append模式,默认的模式,每次只能看到新增的行的内容,不支持聚合操作,一般在进行查询展示时使用。
  • complete模式,每次都是对所有数据进行处理,必须聚合操作。
  • update模式,当数据只有新增,没有聚合类似append;如果对数据进行聚合,只会显示更新的数据。
    append模式:
# 输出模式
from pyspark.sql import SparkSession

ss = SparkSession.builder.getOrCreate()


# 读取数据
df = ss.readStream.csv('hdfs://node1:8020/data_csv', header=False, sep=',',schema='id int,name string,gender string,age int,cls string')

# 数据计算
df_result = df.select(df.id,df.name,df.age,df.gender).where('age >= 20')

# 数据输出
# outputMode 输出模式
# append    输出最新的数据           支持 select,where
# complete  输出所有聚合计算后的数据   支持 groupby聚合计算
# update    输出最新的数据包括聚合计算 支持 groupby聚合计算 select,where
df_result.writeStream.start(format='console',outputMode='append').awaitTermination()

运行结果:
在这里插入图片描述
complete模式:

# 输出模式
from pyspark.sql import SparkSession

ss = SparkSession.builder.getOrCreate()


# 读取数据
df = ss.readStream.csv('hdfs://node1:8020/data_csv', header=False, sep=',',schema='id int,name string,gender string,age int,cls string')

# 数据计算
# df_result = df.select(df.id,df.name,df.age,df.gender).where('age >= 20')
df_result = df.groupby('gender').avg('age')
# 数据输出
# outputMode 输出模式
# append    输出最新的数据           支持 select,where
# complete  输出所有聚合计算后的数据   支持 groupby聚合计算
# update    输出最新的数据包括聚合计算 支持 groupby聚合计算
df_result.writeStream.start(format='console',outputMode='complete').awaitTermination()

运行结果:
在这里插入图片描述
update模式:

# 输出模式
from pyspark.sql import SparkSession

ss = SparkSession.builder.getOrCreate()


# 读取数据
df = ss.readStream.csv('hdfs://node1:8020/data_csv', header=False, sep=',',schema='id int,name string,gender string,age int,cls string')

# 数据计算
# df_result = df.select(df.id,df.name,df.age,df.gender).where('age >= 20')
df_result = df.groupby('gender').avg('age')
# 数据输出
# outputMode 输出模式
# append    输出最新的数据           支持 select,where
# complete  输出所有聚合计算后的数据   支持 groupby聚合计算
# update    输出最新的数据包括聚合计算 支持 groupby聚合计算
df_result.writeStream.start(format='console',outputMode='update').awaitTermination()

运行结果:
在这里插入图片描述
总结:三种模式的使用场景

  • append 适合没有聚合操作的计算结果输出,将数据输出到文件需要用的是append。
  • complete 适合进行聚合操作,并且显示所有的数据计算结果。
  • update 适合进行聚合操作,只展示新增数据的结果。

4.3.2 指定输出位置

  • File Sink 把结果输出到文件中,仅支持追加 append模式
  • Kafka Sink 把结果输出到kafka的topic中,append complete update都支持。
  • Foreach Sink ForeachBatch Sink ,可以接收函数/对象,其中可以定义复杂的计算逻辑,对数据进行处理。数据最终输出到哪里,自己决定,append complete update都支持。
  • console Sink,直接在终端中显示,append complete update都支持。
  • Memory Sink,把数据输出到内存中,以表的形式存在,可以使用SparkSQL进行查询,支持append complete模式。
    输出到文件
  • 格式
writeStream.start(path='输出文件路径',format='输出源',outputMode='输出模式',checkpointLocation='检查点路径')
  • 使用

from pyspark.sql import SparkSession,functions as F

ss=SparkSession.builder.getOrCreate()

opt = {
    # 指定读取的socket服务的ip地址
    'host': '192.168.88.100',
    # 指定读取的端口
    'port': 9999
}
#使用soc
df = ss.readStream.load(format='socket',**opt)

#数据计算
df_res = df.select(
    F.split('value',',')[0].alias('id'),
    F.split('value',',')[1].alias('name'),
    F.split('value', ',')[2].cast('int').alias('age'),
    F.split('value', ',')[3].alias('gender')
).where('age >30')

# 将数据输出到文件中
option ={
    'checkpointLocation':'hdfs://node1:8020/stream_checkpoint'
}
df_res.writeStream.start(format='csv',outputMode='append',path='hdfs://node1:8020/csv_data',**option).awaitTermination()

运行结果:
在这里插入图片描述
注意

  • 写文件需要指定checkpointLocation
  • 不支持聚合数据写入文件
    ForeachBatch:
    需要自定义方法,完成对数据的计算,然后按照需求写入对应位置。本质是将df输出的数据传递到一个方法中。
  • 格式
# 函数
def foreach_batch_function(df,df_id):
	# Transform and write batchDF    
	pass

streamingDF.writeStream.foreachBatch(foreach_batch_function).start()  
  • 使用
# 流数据输出到自定义函数中,将无界表就会转为有界表数据
from pyspark.sql import SparkSession

ss = SparkSession.builder.getOrCreate()

# 读取数据
df = ss.readStream.csv('hdfs://node1:8020/data_csv', header=False, sep=',',schema='id int,name string,gender string,age int,cls string')

# 数据计算
df_result = df.groupby('gender').avg('age')


# 数据输出
# 自定义函数
def func(df,batch_id):
    # 必须接受两个数据
    # df数据就是接受无界表的数据,将无界表转为了有界表的df
    # batch_id 处理数据的批次编号
    df.show()
    print(batch_id)
    # 将有界表数据写入文件
    df.write.csv('hdfs://node1:8020/foreach_data_csv',header=True,sep=',',mode='overwrite')


# 在输出时执行foreachBatch方法
df_result.writeStream.foreachBatch(func).start(outputMode='complete').awaitTermination()

运行结果:

4.3.3 设置触发器Trigger

触发器决定多久执行一次,在流式处理中,等一会儿(等多久)就是由触发器决定。

  • 格式
writeStream.trigger(processingTime=None, once=None, continuous=None).start().awaitTermination()
  • 默认触发器

    • 没有设置触发器,执行完上一个批次,立即执行下一个批次,下一批次没有数据则等待产生新数据, 请求时间间隔的长短由spark对数据计算的时间决定。
      • spark是基于内存计算的,所有计算时间会很多,官方的文档中 时间最短能达到1ms。
    • 固定时间间隔 processingTime=‘5 seconds’
      • 如果前一个微批处理在间隔内完成,则引擎将等待间隔结束,然后再启动下一个微批处理。
      • 如果前一个微批处理的完成时间比间隔时间长(即如果错过了一个间隔边界),那么下一个微批处理将在前一个完成后立即开始(即它不会等待下一个间隔边界)。
      • 如果没有新数据可用,则不会启动微批处理。
# 获取数据时的请求时间间隔设置
from pyspark.sql import SparkSession

ss = SparkSession.builder.getOrCreate()

# 读取数据
options = {
    'host':'192.168.88.100',
    'port':9999
}
df = ss.readStream.load(format='socket',**options)


# 数据计算
df_result = df.select(df.value)


# 结果输出
df.writeStream.trigger(processingTime='5 seconds').start(format='console',outputMode='append').awaitTermination()

运行结果:
在这里插入图片描述

  • 一次性微批处理 once=True 默认是false。

    • 只会执行一次,适用于初始化 关闭资源这种只执行一次的场景。
# 获取数据时的请求时间间隔设置
from pyspark.sql import SparkSession

ss = SparkSession.builder.getOrCreate()

# 读取数据
options = {
    'host':'192.168.88.100',
    'port':9999
}
df = ss.readStream.load(format='socket',**options)


# 数据计算
df_result = df.select(df.value)


# 结果输出
df.writeStream.trigger(once=True).start(format='console',outputMode='append').awaitTermination()

运行结果:
在这里插入图片描述

  • 以固定时间间隔连续处理 continuous=‘5 seconds’
    • 对固定间隔进行优化, 从而减低了延迟性的问题。
    • 和固定间隔微批处理行为上很像,无论是否有数据,到指定间隔时间后都会触发。
# 获取数据时的请求时间间隔设置
from pyspark.sql import SparkSession

ss = SparkSession.builder.getOrCreate()

# 读取数据
options = {
    'host':'192.168.88.100',
    'port':9999
}
df = ss.readStream.load(format='socket',**options)


# 数据计算
df_result = df.select(df.value)


# 结果输出
# trigger的continuous不支持文件数据源
df.writeStream.trigger(continuous='5 seconds').start(format='console',outputMode='append').awaitTermination()

运行结果:
在这里插入图片描述

4.3.4 checkpoint检查目录设置

当获取到数据进行计算时,有可能数据在计算时,会计算失败。此时spark会重新进行计算,此时就需要知道要计算哪个数据,就需要借助checkpoint机制,将当前计算的信息保存起立,方便重新进行计算。保证spark处理数据的容错

1、偏移量目录【offsets】:记录每个批次中的偏移量。为了保证给定的批次始终包含相同的数据,在处理数据前将其写入此日志记录。此日志中的第 N 条记录表示当前正在已处理,第 N-1 个条目指示哪些偏移已处理完成。
2、提交记录目录【commits】:记录已完成的批次,重启任务检查完成的批次与 offsets 批次记录比对,确定接下来运行的批次;
3、元数据文件【metadata】:metadata 与整个查询关联的元数据,目前仅保留当前job id
4、记录状态目录【state】:当有状态操作时,如累加聚合、去重、最大最小等场景,这个目录会被用来记录这些状态数据,根据配置周期性地生成.snapshot文件用于记录状态。

#流计算的检查点设置
from pyspark.sql import SparkSession, functions as F

ss = SparkSession.builder.getOrCreate()

#流式读取数据,转为无界的DataFrame
opt = {
    # 指定读取的socket服务的ip地址
    'host': '192.168.88.100',
    # 指定读取的端口
    'port': 9999
}
df = ss.readStream.load(format='socket',**opt)

#流式数据计算
#SQL
df.createTempView('stu')
sql_str = """
    select gender,count(id),sum(age) from (
    select 
    split(value,',')[0] as id, 
    split(value,',')[1] as name, 
    cast(split(value,',')[2] as int) as age,
    split(value,',')[3] as gender
    from stu) t1
    group by gender

"""
df_res = ss.sql(sql_str)
#结果数据输出
option = {
    #指定checkpoint 每个计算任务都有独立的checkpoint位置
    'checkpointLocation':'hdfs://node1:8020/checkpoint_stream'
}
df_res.writeStream.start(format='console',outputMode='complete',**option).awaitTermination()

运行结果:
在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Html/Vue浏览器下载并重命名文件

Html/Vue浏览器下载并重命名文件 row是上方图片的数据对象 download(row) {const link document.createElement(a);link.style.display none;// 设置下载地址link.setAttribute(href, row.url);// 设置文件名(这里可以重新设置名字,下载之后的文件就是你重新命名…

【数据结构与算法】链表(上)

记录自己所学&#xff0c;无详细讲解 无头单链表实现 1.项目目录文件 2.头文件 Slist.h #include <stdio.h> #include <assert.h> #include <stdlib.h> struct Slist {int data;struct Slist* next; }; typedef struct Slist Slist; //初始化 void SlistI…

死锁和活锁和线程饥饿

死锁 死锁是指两个或两个以上的线程在执行过程中&#xff0c;因争夺资源而造成的一种相互等待的现象&#xff0c;若无外力作用&#xff0c;它们都将无法推进下去。 原因&#xff1a;两个或两个以上的线程共同访问两个或多个相同的资源&#xff08;如对象锁&#xff09;&#…

Unity使用TriangleNet参考

TriangleNet下载如下&#xff1a; TriangleNet 效果如下&#xff1a; 代码参考如下&#xff1a; using System.Collections.Generic; using UnityEngine; using TriangleNet.Geometry;public class TestTriangleNet : MonoBehaviour {[SerializeField]Material material;voi…

耳夹式耳机哪个品牌音质好?五大优质音质的耳夹式耳机!

随着健康理念的传播&#xff0c;运动健身成为大众追求高品质生活的重要部分。传统耳机在运动场景下有局限性&#xff0c;稳定性差、清洁不便&#xff0c;影响运动体验。这时&#xff0c;耳夹式耳机出现&#xff0c;以独特设计和传音方式重塑运动音乐享受&#xff0c;无需入耳&a…

游戏推荐业务中基于 sentinel 的动态限流实践

作者&#xff1a;来自 vivo 互联网服务器团队- Gao Meng 本文介绍了一种基于 sentinel 进行二次开发的动态限流解决方案&#xff0c;包括什么是动态限流、为什么需要引入动态限流、以及动态限流的实现原理。 一、背景 1.1 当前的限流方案 随着互联网的发展及业务的增长&…

Flythings学习(四)串口通信

文章目录 1 串口编程基本步骤1.1 打开串口1.2 配置串口 1.3 读串口1.4 发送串口1.5 关闭串口 2 综合使用3 如何在软件上保证串口稳定通信4 flythings中的串口通讯5 协议接收部分使用和修改方法6 通讯协议数据怎么和UI控件对接 1 串口编程基本步骤 串口通信有5个步骤 1.打开串口…

Telegram——Bot 机器人/小程序入门指南

一、Bot 介绍 在 TG 中,机器人可以用于接收和发送消息、管理群组(在有权限的情况下可以封禁用户、删除消息、置顶消息等)、通过API进行编程操作、使用 Inline 查询功能在不同的聊天室中提供查询服务、创建自定义键盘按钮、发出账单并收款、接入小程序游戏等。 然而,Bot 默…

单片机中断概念以及示例

中断允许控制寄存器 CPU对中断系统所有中断以及某个中断源的开放和屏蔽是由中断允许寄存器IE控制的。 EX0(IE.0)&#xff0c;外部中断0允许位&#xff1b;EX01&#xff0c;打开外部中断0中断&#xff1b;EX00关闭外部中断0中断。 ET0(IE.1)&#xff0c;定时/计数器T0中断允许…

沪尚茗居装修秘籍:嵌入式蒸烤箱,让厨房生活更精彩

在装修厨房时&#xff0c;选择一款合适的嵌入式蒸烤箱不仅能提升烹饪效率&#xff0c;还能为厨房增添一份现代感。沪尚茗居深知用户对厨房电器的需求&#xff0c;从实际出发&#xff0c;为用户推荐选购嵌入式蒸烤箱的实用技巧&#xff0c;让厨房生活更加美好。    首先&…

Real-World Image Variation by Aligning Diffusion Inversion Chain

https://proceedings.neurips.cc/paper_files/paper/2023/file/61960fdfda4d4e95fa1c1f6e64bfe8bc-Paper-Conference.pdfhttps://rival-diff.github.io/ 问题引入 针对的是image varation generation这个任务&#xff0c;tuning free的&#xff1b; methods C C C表示condit…

【closerAI ComfyUI】电商模特一键换装解决方案来了!细节到位无瑕疵!再加上flux模型加持,这个工作流不服不行!

不得了了兄弟们。这应该是电商界的福音&#xff0c;电商模特一键换装解决方案来了&#xff01;细节到位无瑕疵&#xff01;再加上flux模型加持&#xff0c;这个工作流不服不行&#xff01; 这期我们主要讨论如何使用stable diffusion comfyUI 制作完美无瑕疵的换装工作流。** …

《Linux从小白到高手》综合应用篇:详解Linux系统调优之服务器硬件优化

List item 本篇介绍Linux服务器硬件调优。硬件调优主要包括CPU、内存、磁盘、网络等关键硬件组。 1. CPU优化 选择适合的CPU&#xff1a; –根据应用需求选择多核、高频的CPU&#xff0c;以满足高并发和计算密集型任务的需求。CPU缓存优化&#xff1a; –确保CPU缓存&#x…

C++和OpenGL实现3D游戏编程【连载15】——着色器初步

&#x1f525;C和OpenGL实现3D游戏编程【目录】 1、本节实现的内容 上一节我们介绍了通过VBO、VAO和EBO怎样将顶点发送到GPU显存&#xff0c;利用GPU与显存之间的高效处理速度&#xff0c;来提高我们的图形渲染效率。那么在此过程中&#xff0c;我们又可以通过着色器&#xff…

【C++网络编程】(二)Linux平台下UDP客户/服务端程序

Linux平台下UDP客户/服务端程序 图片来源&#xff1a;https://subingwen.cn/linux/udp/ UDP服务器无法直接检测客户端断开连接。 UDP 服务端 server.cpp #include <iostream> #include <cstdlib> // std::exit #include <cstring> // memset #i…

【数据结构与算法】单链表探秘:解锁数据结构中的灵动链条

大家好&#xff0c;我是小卡皮巴拉 文章目录 目录 一.单链表初探&#xff1a;构建数据结构的基石 1.1 单链表的概念及结构 1.2 节点的理解 1.3 链表的性质 二.单链表的作用&#xff1a;为何它是数据处理的优选 2.1 动态内存管理&#xff1a;内存世界的魔术师 2.2 高效的…

篇章九 【NPM】包管理工具

文章目录 一、认识NPM二、npm 镜像的设置与查看三、安装 NPM 工具1. 下载Node.js安装包2. 打开下载好的安装程序&#xff0c;点击下一步3. 选择接受许可协议&#xff0c;点击下一步4. 选择自己的安装路径&#xff08;默认是c盘&#xff09;&#xff0c;选择完成后&#xff0c;点…

喜讯AAA级!国信华源荣获全国水利建设信息化3A级信用

近日&#xff0c;中国水利工程协会发布了《2024年度水利建设市场主体&#xff08;供货、信息化单位&#xff09;信用评价结果公示》&#xff0c;国信华源荣获2024年度全国水利建设信息化单位AAA级信用评价。这一荣誉不仅是对国信华源在水利建设领域信息化能力和诚信经营的肯定&…

中波长线天线耦合的一个方法

围绕窗外墙外牵了10米的室外天线。 短波&#xff0c;fm都是很简单&#xff0c;一个夹子直接夹在拉杆天线上面&#xff0c;效果已经很好。 今天偶尔听到中波前面大约510khz的地方有个摩尔斯码。是成都附近机场的NDB。这个平时要在楼顶或者很空旷的地方才能收到。音量比较小&am…

vue2项目 实现上边两个下拉框,下边一个输入框 输入框内显示的值为[“第一个下拉框选中值“ -- “第二个下拉框选中的值“]

效果: 思路: 采用vue中 [computed:] 派生属性的方式实现联动效果,上边两个切换时,下边的跟随变动 demo代码: <template><div><!-- 第一个下拉框 --><select v-model"firstValue"><option v-for"option in options" :key&q…