大数据技术之Spark Streaming概述

news2024/11/15 7:27:56

前言

数据处理延迟的长短

  • 实时数据处理:毫秒级别
  • 离线数据处理:小时 or 天

数据处理的方式

  • 流式(streaming)数据处理
  • 批量(batch)数据处理

spark Streaming也是基于sparkCore,所以底层的核心没有变化。我们可以理解将spark Streaming为准实时(以秒、分钟为单位)、微批次的数据处理框架。
spark Streaming用于流式数据的处理。spark Streaming支持的数据输入源很多,如Kafka、flume、twitter、简单的TCP嵌套字等。数据输入后可以用spark的高度抽象原语如:map、reduce、join、window等进行运算。其结果也能保存在很多地方,如HDFS,数据库。
在这里插入图片描述
和Spark基于RDD的概念类似,Spark Streaming使用离散化流(discretized stream)作为抽象表示,叫做DStream。DStream是随着时间推移而收到的数据的序列。在内部,每个时间区间收到的数据都作为RDD存在,而DStream是由这些RDD所组成的序列(因此得名“离散化”)。所以简单来说,DStream就是对RDD在实时数据处理场景的一种封装。

一、spark概述

1.1 背压机制

Spark 1.5 以前版本,用户如果要限制 Receiver 的数据接收速率,可以通过设置静态配制参数“spark.streaming.receiver.maxRate”的值来实现,此举虽然可以通过限制接收速率,来适配当前的处理能力,防止内存溢出,但也会引入其它问题。比如:producer 数据生产高于 maxRate,当前集群处理能力也高于maxRate,这就会造成资源利用率下降等问题。
为了更好的协调数据接收速率与资源处理能力,1.5 版本开始 Spark Streaming 可以动态控制数据接收速率来适配集群数据处理能力。背压机制(即 Spark Streaming Backpressure): 根据JobScheduler 反馈作业的执行信息来动态调整 Receiver 数据接收率。
通过属性“spark.streaming.backpressure.enabled”来控制是否启用 backpressure 机制,默认值false,即不启用。

二、Dstream入门

2.1 什么是DStream

DStream是SS提供的基本抽象,其表现数据的连续流。这个输入数据流可以来自于源,也可以来自于转换输入流产生的已处理数据流。
内部而言,一个DStream以一系列连续的RDDs所展现,这些RDD是Spark对于不变的,分布式数据集的抽象。一个DStream中的每个RDD都包含来自一定间隔的数据,如下图:
在这里插入图片描述
在DStream上使用的任何操作都会转换为针对底层RDD的操作。
在这里插入图片描述
DStream

2.2 WordCount案例实操

需求:使用netcat工具向9999端口不断发送数据,通过SparkStreaming读取端口数据并统计不同单词出现的次数

1)添加依赖

<dependency>
	<groupId>org.apache.spark</groupId>
	<artifactId>spark-streaming_2.12</artifactId>
	<version>3.0.0</version>
</dependency>

2)编写代码

package org.example

import org.apache.spark.SparkConf
import org.apache.spark.streaming.{Seconds, StreamingContext}

object Word_Count {
  def main(args: Array[String]): Unit = {
    // TODO 创建环境对象
    // StreamingContext创建时,需要传递两个参数
    // 第一个参数表示环境配置
    val conf = new SparkConf().setMaster("local").setAppName("SparkStream")
    val ssc = new StreamingContext(conf,Seconds(3))

    // TODO 逻辑处理
    // 获取端口数据
    val lines = ssc.socketTextStream("localhost",9999)
    val words = lines.flatMap(_.split(" "))
    val wordToOne = words.map((_,1))
    val wordToCount = wordToOne.reduceByKey(_+_)
    wordToCount.print()

    // TODO 关闭环境
    // 由于SparkStreaming采集器是长期执行的任务,所以不能直接关闭
    // 如果main方法执行完毕,应用程序也会自动结束。不能让main方法执行完毕。
    // ssc.stop()
    
    // 1. 启动采集器
    ssc.start()
    // 2. 等待采集器的关闭
    ssc.awaitTermination()
  }
}

2.3 WordCount解析

Discretized Stream 是 Spark Streaming 的基础抽象,代表持续性的数据流和经过各种 Spark 原语操作后的结果数据流。在内部实现上,**DStream 是一系列连续的 RDD 来表示**。每个 RDD 含有一段时间间隔内的数据。

三、DStream创建

3.1 Kafka数据源

Kafka在数据源的采集过程中分为两个版本:ReceiverAPIDirectAPI

ReceiverAPI:需要专门的Executor作为接收器,采集和计算的速率不一定相同,可能会导致数据的积压。
DirectAPI:由计算的Executor节点主动消费Kafka的数据,速率由自身控制。

3.2 Kafka 0-10 Direct 模式

1)需求:

通过 SparkStreaming 从 Kafka 读取数据,并将读取过来的数据做简单计算,最终打印到控制台。

2)导入依赖

    <dependency>
      <groupId>org.apache.spark</groupId>
      <artifactId>spark-streaming-kafka-0-10_2.12</artifactId>
      <version>3.0.0</version>
    </dependency>
    <dependency>
 		<groupId>com.fasterxml.jackson.core</groupId>
		 <artifactId>jackson-core</artifactId>
		 <version>2.10.1</version>
	</dependency>

在这里插入图片描述

3)读取数据

1. 从命令行读取数据

val lines = streamingContext.socketTextStream("localhost",9999)

2. 从Kafka读取数据

    val kafkaDStream: InputDStream[ConsumerRecord[String, String]] = KafkaUtils.createDirectStream(
      	  streamingContext,
     	  LocationStrategies.PreferConsistent,
      	  ConsumerStrategies.Subscribe(Set("events_raw"), kafkaParams)
    )

4)代码编写

package org.example

import java.util
import java.util.ArrayList

import org.apache.kafka.clients.consumer.{ConsumerConfig, ConsumerRecord}
import org.apache.kafka.clients.producer.{KafkaProducer, ProducerConfig, ProducerRecord}
import org.apache.kafka.streams.KeyValue
import org.apache.spark.SparkConf
import org.apache.spark.streaming.{Seconds, StreamingContext}
import org.apache.spark.streaming.dstream.InputDStream
import org.apache.spark.streaming.kafka010.{ConsumerStrategies, KafkaUtils, LocationStrategies}

object SparkStreamEventAttendeesrawToEventAttends {
  def main(args: Array[String]): Unit = {
  // 1. 创建SparkConf
    val conf = new SparkConf().setAppName("user_friends_raw").setMaster("local[*]")
    
  // 2. 创建streamingContext
    val streamingContext = new StreamingContext(conf,Seconds(5))

    streamingContext.checkpoint("checkpoint")

  // 3. 定义Kafka参数 
    val kafkaParams = Map(
      (ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG -> "192.168.136.20:9092"),
      (ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG -> "org.apache.kafka.common.serialization.StringDeserializer"),
      (ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG -> "org.apache.kafka.common.serialization.StringDeserializer"),
      (ConsumerConfig.GROUP_ID_CONFIG -> "eventsraw"),
      (ConsumerConfig.AUTO_OFFSET_RESET_CONFIG->"earliest")
    )

  // 4. 读取Kafka数据创建DStream
    val kafkaDStream: InputDStream[ConsumerRecord[String, String]] = 		 
    	KafkaUtils.createDirectStream(
      	  streamingContext,
     	  LocationStrategies.PreferConsistent,
      	  ConsumerStrategies.Subscribe(Set("events_raw"), kafkaParams)
    )

  // 5. 将每条消息的 KV 取出
  val valueDstream: DStream[String) = kafkaDstream,map(record => record.value())

  // 6. 计算wordCount
  valueDStream.flatMap(_.split(" ") )
	.map((_,_1))
	.reduceByKey(_+_)
	.print ()

  // 7. 开启任务
      streamingContext.start()
      streamingContext.awaitTermination()
  }
}

5)查看Kafka消费进度

kafka-consumer-groups.sh --describe --bootstrap-server 192.168.136.20:9092 --group events

四、DStream转换

DStream转换和RDD转换类似,对比RDD中的转换算子和行动算子,DStream也有Transformations(转换)和Output Operations(输出)两者。

4.1 无状态转换操作

相当于没有血缘关系的RDD。

函数名称目的示例
map()对DStream中的每个元素应用没定函数,返回由各元素输出的元素组成的DStreamds.map(x=>x+1)
flatMap()对DStream中的每个元素应用没定函数,返回由各元素输出的迭代器组成的DStreamds.flatMap(x=>x.split(" "))
filter()返回由给定DStream中通过筛选的元素组成的DStreamds.filter(x=>x!=1)
repartition()改变DStream的分区数ds.repartition(10)
reduceByKey()将每个批次中key相同的记录规约ds.reduceByKey((x,y)=>x+y)
groupByKey()将每个批次中的记录根据key分组ds.groupByKey()

在这里插入图片描述
无状态和有状态其实就是看是否保存某一个采集周期的数据。如果保存就是有状态,不保存就是无状态。

4.1.1 Transform

可以拿到最底层的RDD进行操作。DStream无法实现的功能可以通过Transform实现。

object SparkStreamKafkaSource {
  def main(args: Array[String]): Unit = {
  
	val conf: SparkConf = new SparkConf().setAppName("sparkKafkaStream").setMaster("local[*]")
    val streamingContext = new StreamingContext(conf, Seconds(5))

	val lines = streamingContext.socketTextStream("localhost",9999)
	
// Transform方法可以将底层的RDD获取到后进行操作
	val newRS: DStream[String] = lines.transform(rdd=>rdd)

    streamingContext.start()
    streamingContext.awaitTermination()

  }
}

lines.transform()lines.map() 都能够实现对算子的转换,那么有什么区别呢?

lines.map()
在这里插入图片描述在这里插入图片描述
lines.transform()
在这里插入图片描述

说明:
transform方法可以将底层RDD获取到后进行操作
1、DStream功能不完善
2、需要代码周期性的执行

4.1.2 join

object SparkStreamKafkaSource {
  def main(args: Array[String]): Unit = {
  
	val conf: SparkConf = new SparkConf().setAppName("sparkKafkaStream").setMaster("local[*]")
    val streamingContext = new StreamingContext(conf, Seconds(5))

	val data9998 = streamingContext.socketTextStream("localhost",9999)
	val data8868 = streamingContext.socketTextStream("localhost",8888)

	val map9999: DStream[(String, Int)] = data9999.map((_,8))
	val map8888: DStream[(String, Int)] = data8888.map((_,8))

// DS的join操作就是两个RDD的join
	val joinDS: DStream[String, (Int, Int)] = map9999.join(map8888)
	
	joinDS.print()

    streamingContext.start()
    streamingContext.awaitTermination()

  }
}

4.2 有状态转化操作

4.2.1 UpdateStateByKey

有状态转化操作由于需要将计算的结果保存至内存,所以需要设置检查点checkpoint。

object SparkStreamEventAttendeesrawToEventAttends {
  def main(args: Array[String]): Unit = {
  // 创建SparkConf
    val conf = new SparkConf().setAppName("user_friends_raw").setMaster("local[*]")
    
  // 创建streamingContext
    val streamingContext = new StreamingContext(conf,Seconds(5))

    streamingContext.checkpoint("checkpoint")
    
// 无状态数据操作。只对当前的 采集周期内 的数据进行处理。
// 在某些场合下,需要保留数据统计结果(状态),实现数据的汇总。
    val datas = streamingContext.socketTextStream("localhost",9999)
    val wordToOne = datas.map((_,1))
    val wordToCount = wordToOne.reduceByKey(_+_)
	wordToCount.print()

// updateStateByKey:根据key对数据的状态进行更新
// 传递的参数中含有两个值:
// 1:相同的key的value数据
// 2:缓冲区相同key的value值
	val state = wordToOne.updateStateByKey(
		(seq:Seq[Int],buff:Option[Int]) => {
			val newCount = buff.getOrElse(0)+seq.sum
			Option(newCount)
		}
	)
	state.print()

  // 开启任务
      streamingContext.start()
      streamingContext.awaitTermination()
  }
}

在这里插入图片描述

4.2.2 WindowOperations

WindowOperations可以设置窗口的大小和滑动窗口的间隔来动态的获取当前 Steaming 的允许状态。所有基于窗口的操作都需要两个参数,分别为窗口时长以及滑动步长

窗口时长:计算内容的时间范围;
滑动步长:隔多久触发一次计算。
这两者都必须为采集周期大小的整数倍。
在这里插入图片描述

package org.example.window

import org.apache.kafka.clients.consumer.{ConsumerConfig, ConsumerRecord}
import org.apache.spark.SparkConf
import org.apache.spark.streaming.{Seconds, StreamingContext}
import org.apache.spark.streaming.dstream.{DStream, InputDStream}
import org.apache.spark.streaming.kafka010.{ConsumerStrategies, KafkaUtils, LocationStrategies}

object SparkWindowDemo1 {
  def main(args: Array[String]): Unit = {
    val conf = new SparkConf().setAppName("sparkwindow1").setMaster("local[*]")
    val streamingContext = new StreamingContext(conf,Seconds(3))

    streamingContext.checkpoint("checkpoint")

    val kafkaParams = Map(
      (ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG -> "192.168.136.20:9092"),
      (ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG -> "org.apache.kafka.common.serialization.StringDeserializer"),
      (ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG -> "org.apache.kafka.common.serialization.StringDeserializer"),
      (ConsumerConfig.GROUP_ID_CONFIG -> "sparkwindow01"),
      (ConsumerConfig.AUTO_OFFSET_RESET_CONFIG->"latest")
    )
    val kafkaStream: InputDStream[ConsumerRecord[String, String]] = KafkaUtils.createDirectStream(
      streamingContext,
      LocationStrategies.PreferConsistent,
      ConsumerStrategies.Subscribe(Set("sparkkafkastu"), kafkaParams)
    )

// 滑动窗口,窗口的范围应该是采集周期的整数倍
// 窗口可以滑动,但是默认情况下,一个采集周期进行滑动
// 这样的话可能会出现重复数据的计算。为了避免这种情况,可以改变滑动的幅度(步长)
// window(Seconds(9)) => window(Seconds(9),Seconds(3))
    val winStream: DStream[(String, Int)] = kafkaStream.flatMap(x => x.value().trim.split("\\s+"))
      .map(x => (x, 1))
      .window(Seconds(9),Seconds(3))
      .reduceByKey(_+_)
      
    winStream.print()

    streamingContext.start()
    streamingContext.awaitTermination()
  }
}

通过图片我们可以看出:
1、window中的数据会重复计算
2、状态基于当前窗口进行操作
在这里插入图片描述

关于 Window 的操作还有如下方法:
(1)window(windowLength, slideInterval): 基于对源 DStream 窗化的批次进行计算返回一个新的 Dstream;
(2)countByWindow(windowLength, slideInterval): 返回一个滑动窗口计数流中的元素个数;
(3)reduceByWindow(func, windowLength, slideInterval): 通过使用自定义函数整合滑动区间流元素来创建一个新的单元素流;
(4)reduceByKeyAndWindow(func, windowLength, slideInterval, [numTasks]): 当在一个(K,V)对的 DStream 上调用此函数,会返回一个新(K,V)对的 DStream,此处通过对滑动窗口中批次数据使用 reduce 函数来整合每个 key 的 value 值。
(5)reduceByKeyAndWindow(func, invFunc, windowLength, slideInterval, [numTasks]): 这个函数是上述函数的变化版本,每个窗口的 reduce 值都是通过用前一个窗的 reduce 值来递增计算。通过 reduce 进入到滑动窗口数据并”反向 reduce”离开窗口的旧数据来实现这个操作。一个例子是随着窗口滑动对 keys 的“加”“减”计数。通过前边介绍可以想到,这个函数只适用于”可逆的 reduce 函数”,也就是这些 reduce 函数有相应的”反 reduce”函数(以参数 invFunc 形式传入)。如前述函数,reduce 任务的数量通过可选参数来配置。

reduceByWindowreduceByKeyAndWindow
reduceByWindow输入的是两个参数,没有(k,v)键值对,操作时需要对value进行指定。
reduceByKeyAndWindow输入的是(k,v)类型的键值对。可以直接对value进行操作。在这里插入图片描述

reduceByKeyAndWindow
当窗口范围比较大,但是滑动幅度比较小,那么可以采用增加数据和删除数据的方式。
无序重复计算,提高效率在这里插入图片描述

五、DStream输出

如果我们没有输出的语句,会直接抛出异常。
这是因为DStream与RDD的惰性求值类似,如果一个DStream没有被执行输出操作,那么DStream不会被求值。StreamingContext没有设定输出操作,那么整个context就不会启动。
在这里插入图片描述
输出操作如下:

print():在运行流程序的驱动结点上打印 DStream 中每一批次数据的最开始 10 个元素。用于开发和调试。
saveAsTextFiles(prefix, [suffix]):以 text 文件形式存储这个 DStream 的内容。每一批次的存储文件名基于参数中的 prefix 和suffix prefix-Time_IN_MS[.suffix]
saveAsObjectFiles(prefix, [suffix]):以 Java 对象序列化的方式将 Stream 中的数据保存为SequenceFiles 。每一批次的存储文件名基于参数中的为prefix-TIME_IN_MS[.suffix]
saveAsHadoopFiles(prefix, [suffix]):将 Stream 中的数据保存为 Hadoop file。s. 每一批次的存储文件名基于参数中的为prefix-TIME_IN_MS[.suffix]
foreachRDD(func)最通用的输出操作,即将函数 func 用于产生于 stream 的每一个RDD。其中参数传入的函数 func 应该实现将每一个 RDD 中数据推送到外部系统,如将RDD 存入文件或者通过网络将其写入数据库。

注意:
1) 连接不能写在 driver 层面(序列化)
2) 如果写在 foreach 则每个 RDD 中的每一条数据都创建,得不偿失;
3) 增加 foreachPartition,在分区创建(获取)。
package org.example

import java.util

import org.apache.kafka.clients.consumer.{ConsumerConfig, ConsumerRecord}
import org.apache.kafka.clients.producer.{KafkaProducer, ProducerConfig, ProducerRecord}
import org.apache.spark.SparkConf
import org.apache.spark.streaming.dstream.InputDStream
import org.apache.spark.streaming.kafka010.{ConsumerStrategies, KafkaUtils, LocationStrategies}
import org.apache.spark.streaming.{Seconds, StreamingContext}

object SparkStreamUserFriendrawToUserFriend {
  def main(args: Array[String]): Unit = {
    val conf = new SparkConf().setAppName("user_friends_raw").setMaster("local[*]")
    val streamingContext = new StreamingContext(conf,Seconds(5))

    streamingContext.checkpoint("checkpoint")

    val kafkaParams = Map(
      (ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG -> "192.168.136.20:9092"),
      (ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG -> "org.apache.kafka.common.serialization.StringDeserializer"),
      (ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG -> "org.apache.kafka.common.serialization.StringDeserializer"),
      (ConsumerConfig.GROUP_ID_CONFIG -> "uf"),
      (ConsumerConfig.AUTO_OFFSET_RESET_CONFIG->"earliest")
    )
    val kafkaStream: InputDStream[ConsumerRecord[String, String]] = KafkaUtils.createDirectStream(
      streamingContext,
      LocationStrategies.PreferConsistent,
      ConsumerStrategies.Subscribe(Set("user_friends_raw"), kafkaParams)
    )

    kafkaStream.foreachRDD(
      rdd=>{
        rdd.foreachPartition(x=>{
          val props = new util.HashMap[String,Object]()
          props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG,"192.168.136.20:9092")
          props.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG,"org.apache.kafka.common.serialization.StringSerializer")
          props.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG,"org.apache.kafka.common.serialization.StringSerializer")
          val producer = new KafkaProducer[String,String](props)
          x.foreach(y=>{
            val splits = y.value().split(",")
            if(splits.length==2){
              val userid = splits(0)
              val friends = splits(1).split("\\s+")
              for(friend<-friends){
                val record = new ProducerRecord[String,String]("user_friends2",userid+","+friend)
                producer.send(record)
              }
            }
          })
        })
      }
    )

    streamingContext.start()
    streamingContext.awaitTermination()
  }

}

五、关闭任务

流式任务需要7*24小时执行,但是代码升级需要主动停止程序时,没办法做到一个个进程去关闭,所有配置的关闭就显得尤为重要。

// 线程的关闭
val thread = new Thread()

thread.start()
thread.stop // 强制关闭

优雅的关闭

优雅的关闭就是指计算节点不再接受新的数据,而是将现有的数据处理完毕,然后关闭。
但是如果直接写一个stop()方法放在awaitTermination()方法之后,awaitTermination()会阻塞main线程,stop()方法无法被执行到。
所以如果想要关闭采集器,那么需要创建新的线程,而且需要在第三方程序中增加关闭状态。

ssc.start()

new Thread(
  new Runnable{
  	override def run(): Unit = {
	  Thread.sleep(5000)
	  val state: StreamingContextState = ssc.getState()
	  if ( state == StreamingContextState.ACTIVE ){
		  ssc.stop(true,true)
	  }
	  System.exit(0)
	}
  }
).start()

ssc.awaitTermination()

恢复数据

val ssc = StreamingContext.getActiveOrCreate("cp",()=>{
	val sparkConf = new SparkConf().setMaster("local[*]").setAppName("SparkStreaming")
	val ssc = new StreamingContext(sparkConf, Seconds(3))

	val lines = ssc.soctetTextStream("localhost",9999)
	val wordToOne = lines.map((_,1))

	wordToOne.print()	
	ssc
})

ssc.checkpoint("cp")

ssc.start()
ssc.awaitTermination()

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

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

相关文章

FreeRTOS系统学习第一步:新建 FreeRTOS 工程—软件仿真

创建一个FreeRTOS系统工程 1.新建工程文件夹2.Keil新建工程2.1 New Project2.2 Select Device For Target2.3 Manage Run-Time Environment 3. 在 KEIL 工程里面新建文件组3.1在 KEIL 工程里面添加文件 4. 编写 main 函数5. 调试配置5.1 设置软件仿真5.2 修改时钟大小在时钟相关…

你是否线上有使用ThreadLocal,如果结合多线程,请慎用

随着业务的增加&#xff0c;数据量的增加&#xff0c;多线程的使用会越来越频繁&#xff0c;提升单机的处理能力。 前些日子我们线上出现了一个比较严重的故障&#xff0c;这个故障是多线程使用不当引起的&#xff0c;挺有代表性的&#xff0c;所以分享给大家&#xff0c;希望…

前端学习笔记:CSS的引入,元素选择器

这是本人学习的总结&#xff0c;主要学习资料如下 马士兵教育 目录 1、引入CSS1.1、引入CSS的方式1.2、优先级 2、元素选择器2.1、基本选择器2.1.1、选择器2.1.2、优先级 2.2、关系选择器2.2.1、优先级 2.3、属性选择器2.4、伪类选择器 1、引入CSS 1.1、引入CSS的方式 第一个…

互斥锁深度理解与使用

大家好&#xff0c;我是易安! 我们知道一个或者多个操作在CPU执行的过程中不被中断的特性&#xff0c;称为“原子性”。理解这个特性有助于你分析并发编程Bug出现的原因&#xff0c;例如利用它可以分析出long型变量在32位机器上读写可能出现的诡异Bug&#xff0c;明明已经把变量…

SpringCloud--gateway 网关

在Spring Cloud中&#xff0c;使用Gateway网关访问服务可以有多种好处&#xff0c;包括但不限于以下几点&#xff1a; 统一入口管理&#xff1a;Gateway作为统一的服务入口&#xff0c;可以对所有的请求进行统一管理和控制&#xff0c;实现微服务集中管理。 动态路由&#xff…

056:cesium 七种方法设置颜色

第056个 点击查看专栏目录 本示例的目的是介绍如何在vue+cesium中设置颜色,这里用到了7种方法,查看API,还有很多种方法 直接复制下面的 vue+cesium源代码,操作2分钟即可运行实现效果. 文章目录 示例效果配置方式示例源代码(共115行)相关API参考:专栏目标示例效果 配置…

深入理解Go语言中的接口编程【17】

文章目录 接口接口接口类型为什么要使用接口接口的定义实现接口的条件接口类型变量值接收者和指针接收者实现接口的区别值接收者实现接口指针接收者实现接口下面的代码是一个比较好的面试题 类型与接口的关系一个类型实现多个接口多个类型实现同一接口接口嵌套 空接口空接口的定…

TCP教程:详解TCP连接过程

目录标题 一 、简述二 、TCP建立连接协议&#xff08;三次握手&#xff09;2.1 概述及目的2.2 第一次握手&#xff1a;客户端发送SYN报文2.3 第二次握手&#xff1a;服务器回应SYN-ACK报文2.4 第三次握手&#xff1a;客户端回应ACK报文2.5 顾客预定座位场景2.6底层原理2.7 TCP …

嵌入式之Samba服务器搭建

在嵌入式系统开发应用平台中&#xff0c;tftp、nfs和samba服务器是最常用的文件传输工具 tftp和nfs是在嵌入式Linux开发环境中经常使用的传输工具 samba则是Linux和Windows之间的文件传输工具。 下面演示在linux上搭建Samba服务器 sudo apt-get install samba chmod -R 77…

会场安排问题——算法实现(C实现)

问题描述&#xff1a;加入要在足够多的会场里安排一批活动&#xff0c;并希望使用尽可能少的会场。设计一个有效的贪心算法进行安排。&#xff08;这个问题实际上是著名的图着色问题。若每个活动作为图的一个顶点&#xff0c;不相容活动之间用边相连。使相连顶点着有不同颜色的…

数据库原理容易出错的点

一个数据库只存在一个内模式和一个模式&#xff0c;可以存在多个外模式除了删除表或视图的使用的是DELETE以外其他数据库对象均是使用DROP遵守两段封锁的协议的并发事务一定是可串行化的哪些情况下不适合创建索引&#xff1a; 对于查询过程中很少使用或参考的列对于那些只有很少…

【无人车】用于无人地面车辆的路径跟踪算法(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

CMIP6数据处理:WRF模式动力降尺度、单点降尺度、统计方法区域降尺度、SWAT数据、Biome-BGC数据制备

查看原文>>>WRF模式、CMIP6数据处理、机器学习气象海洋水文应用、python地球科学 目录 CMIP6月数据、日数据、全球VIPPHEN物候数据、ERA5数据 一、CMIP6中的模式比较计划 二、数据下载 三、基础知识 四、单点降尺度 五、统计方法的区域降尺度 六、基于WRF模式…

助力低碳出行 | 基于ACM32 MCU的电动滑板车方案

前言 随着智能科技的快速发展&#xff0c;电动滑板车的驱动系统也得到了长足的发展。国内外的电动滑板车用电机驱动系统分为传统刷式电机和无刷电机两种类型。其中&#xff0c;传统的刷式电机已经逐渐被无刷电机所取代&#xff0c;无刷电机的性能和寿命都更出色&#xff0c;已…

STM32F4 HAL库使用DMA进行ADC采样实时发送波形到串口显示(包含傅里叶变换)

1.总体逻辑 按下STM32F4的KEY0按键&#xff0c;通过外部中断的方式对按键进行检测&#xff0c;然后开启一次带DMA的固定点数的ADC采集&#xff0c;采集完成后在DMA的中断发送采集到的数据&#xff0c;然后清空数据区准备下一次的按键中断。电脑接受到串口数据后对数据进行简单…

大厂齐出海:字节忙种草,网易爱社交

配图来自Canva可画 随着国内移动互联网红利逐渐触顶&#xff0c;互联网市场日趋饱和&#xff0c;国内各互联网企业之间的竞争便愈发激烈起来。在此背景下&#xff0c;广阔的海外市场就成为了腾讯、阿里、字节、京东、拼多多、百度、网易、快手、B站等互联网公司关注和争夺的重…

算法--前缀和技巧 (蓝桥杯123-灵能传输--求和)

文章目录 什么是前缀和用途什么时候用java的前缀和例题[蓝桥杯 2022 省 A] 求和题目描述思路代码 [蓝桥杯 2021 国 ABC] 123题目描述思路代码 [蓝桥杯 2019 省 B] 灵能传输(蓝桥杯96%&#xff0c;洛谷ac)题目描述思路代码 什么是前缀和 如果一个数组a的元素为 a 1 , a 2 , a 3…

Springboot +Flowable,详细解释啥叫流程实例(一)

一.简介 上一篇中学习了Flowable 中的流程模板&#xff08;流程定义&#xff09;的部署问题&#xff0c;这一篇来学习什么叫流程实例。 部署之后的流程模板&#xff0c;还不能直接运行&#xff0c;例如我们部署了一个请假流程&#xff0c;现在 张三想要请假&#xff0c;他就需…

WhatsApp多开攻略,低成本高效率多开账号聊单的方法献上~

WhatsApp多开攻略&#xff01;低成本高效率多开账号聊单的方法献上~ WhatsApp多开是指在同一台设备上同时登录多个WhatsApp账号&#xff0c;这种技术通常被跨境电商从业者用于在不同的WhatsApp账号之间切换&#xff0c;以便更好地管理跨境电商业务。 图中工具&#xff1a; ss客…

lazada按关键字搜索商品API接口

lazada按关键字搜索商品 API接口&#xff0c;在 lazada上搜索产品&#xff0c;如果只需要搜索单个产品的话&#xff0c;那么直接在搜索框输入“关键字”即可&#xff0c;如果需要多个产品&#xff0c;那么则需要进行关键字扩展。 lazada按关键字搜索商品 API接口分为两部分&am…