Flink-Scala版学习——转换算子

news2024/11/18 2:46:38

目录

一、基本转换算子

1.map

2.filter

3.flatMap

3.聚合算子Aggregation

(1)keyBy

(2)简单聚合:sum、min、minBy、max、maxBy

(3)归约聚合:reduce

二、UDF

三、富函数类

四、物理分区

1.随机分区(shuffle)

2. 轮询分区(Round-Robin)  

 3. 重缩放分区(rescale)

4.广播(broadcast)——不常用

5.全局分区(global)——不常用

6. 自定义分区(Custom)


一、基本转换算子

1.map

package com.atguigu.transform
import com.atguigu.chapter05.Event
import org.apache.flink.api.common.functions.MapFunction
import org.apache.flink.streaming.api.scala._
object MapTest {
  def main(args: Array[String]): Unit = {
    val env: StreamExecutionEnvironment = StreamExecutionEnvironment.getExecutionEnvironment
    env.setParallelism(1)

    val stream: DataStream[Event] = env.fromElements(
      Event("Mary", "./home", 1000L),
      Event("zhangsan", "./cart", 2000L))

    // TODO 提取每次点击事件的用户名
    // 1.使用匿名函数
    stream.map(_.user).print("1:")

    // 2.也可以实现MapFunction接口,需要定义一个类
    stream.map(new UserExtractor).print("2:")

    env.execute()
  }

  class UserExtractor extends MapFunction[Event,String] {
    override def map(t: Event): String = {
      t.user
    }
  }
}

// 结果:
1:> Mary
2:> Mary
1:> zhangsan
2:> zhangsan

2.filter

import com.atguigu.chapter05.Event
import org.apache.flink.api.common.functions.FilterFunction
import org.apache.flink.streaming.api.scala._

object FilterTest {
  def main(args: Array[String]): Unit = {
    val env: StreamExecutionEnvironment = StreamExecutionEnvironment.getExecutionEnvironment
    env.setParallelism(1)

    val stream: DataStream[Event] = env.fromElements(
      Event("Mary", "./home", 1000L),
      Event("zhangsan", "./cart", 2000L))

    // TODO 过滤出用户为Marry的点击事件
    // 1.使用匿名函数
    stream.filter(_.user == "Mary").print("1:")

    // 2.也可以实现FilterFunction接口,需要定义一个类
    stream.filter(new UserFilter).print("2:")

    env.execute()
  }

  class UserFilter extends FilterFunction[Event] {
    override def filter(t: Event): Boolean = t.user == "zhangsan"
  }
}

// 结果:
1:> Event(Mary,./home,1000)
2:> Event(zhangsan,./cart,2000)

3.flatMap

import com.atguigu.chapter05.Event
import org.apache.flink.api.common.functions.FlatMapFunction
import org.apache.flink.streaming.api.scala._
import org.apache.flink.util.Collector

object FlatMapTest {
  def main(args: Array[String]): Unit = {
    val env: StreamExecutionEnvironment = StreamExecutionEnvironment.getExecutionEnvironment
    env.setParallelism(1)

    val stream: DataStream[Event] = env.fromElements(
      Event("Mary", "./home", 1000L),
      Event("zhangsan", "./cart", 2000L),
      Event("lisi", "./cart", 3000L)
    )

    // TODO 找出用户Mary的点击记录
    val value: DataStream[Event] = stream.flatMap(new FlatMapFunction[Event, Event] {
      override def flatMap(t: Event, collector: Collector[Event]): Unit = {
        if (t.user.equals("Mary")) {
          collector.collect(t)
        } else if (t.user.equals("zhangsan")) {
          collector.collect(t)
        }
      }
    })
    value.print("1:")
    // 结果:
    1:> Event(Mary,./home,1000)
    1:> Event(zhangsan,./cart,2000)

    // TODO 采用自定义类实现
    stream.flatMap(new MyFlatMap).print("2:")
    // 结果:
    2:> Mary
    2:> zhangsan
    2:> ./cart

    env.execute()
  }

  // 自定义实现FlatMapFunction
  class MyFlatMap extends FlatMapFunction[Event, String] {
    override def flatMap(t: Event, collector: Collector[String]): Unit = {
      // TODO 如果当前数据是Mary的点击事件,那么就直接输出User
      if (t.user == "Mary") {
        collector.collect(t.user)
      } else if (t.user == "zhangsan") {
        collector.collect(t.user)
        collector.collect(t.url)
      }
    }
  }
}

3.聚合算子Aggregation

(1)keyBy

import com.atguigu.chapter05.Event
import org.apache.flink.api.common.functions.FlatMapFunction
import org.apache.flink.api.java.functions.KeySelector
import org.apache.flink.streaming.api.scala._
import org.apache.flink.util.Collector

object KayByTest {
  def main(args: Array[String]): Unit = {
    val env: StreamExecutionEnvironment = StreamExecutionEnvironment.getExecutionEnvironment
    env.setParallelism(4)

    val stream: DataStream[Event] = env.fromElements(
      Event("Mary", "./home", 1000L),
      Event("wangwu", "./home", 1000L),
      Event("zhaoliu", "./home", 1000L),
      Event("xiaoming", "./home", 1000L),
      Event("Mary", "./home", 1000L),
      Event("zhangsan", "./cart", 2000L),
      Event("zhangsan", "./home", 2000L),
      Event("lisi", "./cart", 3000L)
    )

    // TODO 指定 Event 的 user 属性作为 key

    // 方法一:
    stream.keyBy(_.user).print()
    // 方法二:
    stream.keyBy(new MyKeySelector).print()

    // 结果:相同的key放在同一个分区
    3> Event(wangwu,./home,1000)
    1> Event(xiaoming,./home,1000)
    4> Event(lisi,./cart,3000)
    2> Event(Mary,./home,1000)
    3> Event(zhangsan,./cart,2000)
    2> Event(zhaoliu,./home,1000)
    3> Event(zhangsan,./home,2000)
    2> Event(Mary,./home,1000)
    
    env.execute()
  }

  class MyKeySelector extends KeySelector[Event, String] {
    override def getKey(in: Event): String = in.user
  }
}

(2)简单聚合:sum、min、minBy、max、maxBy

import com.atguigu.chapter05.Event
import org.apache.flink.api.common.functions.FlatMapFunction
import org.apache.flink.api.java.functions.KeySelector
import org.apache.flink.streaming.api.scala._
import org.apache.flink.util.Collector

object KayByTest {
  def main(args: Array[String]): Unit = {
    val env: StreamExecutionEnvironment = StreamExecutionEnvironment.getExecutionEnvironment
    env.setParallelism(1)

    val stream: DataStream[Event] = env.fromElements(
      Event("Mary", "./home", 9000L),
      Event("wangwu", "./id", 8000L),
      Event("zhaoliu", "./home", 6000L),
      Event("xiaoming", "./home", 1000L),
      Event("Mary", "./cart", 10000L),
      Event("zhangsan", "./cart", 2000L),
      Event("zhangsan", "./home", 8000L),
      Event("zhangsan", "./home", 6000L),
      Event("lisi", "./cart", 3000L)
    )

    // TODO 指定 Event 的 user 属性作为 key
    // TODO 简单聚合
    // TODO sum():对值按key进行累加
    stream.keyBy(_.user).sum("timestamp").print()
    /*Event(Mary,./home,9000)
    Event(wangwu,./home,8000)
    Event(zhaoliu,./home,6000)
    Event(xiaoming,./home,1000)
    Event(Mary,./home,10000)
    Event(zhangsan,./cart,8000)
    Event(zhangsan,./cart,10000)
    Event(lisi,./cart,3000)*/

    // TODO minBy()则会返回包含字段最小值的整条数据。
    stream.keyBy(_.user).minBy("timestamp").print()
    /*Event(Mary,./home,9000)
    Event(wangwu,./id,8000)
    Event(zhaoliu,./home,6000)
    Event(xiaoming,./home,1000)
    Event(Mary,./home,9000)
    Event(zhangsan,./cart,2000)
    Event(zhangsan,./cart,2000)
    Event(zhangsan,./cart,2000)
    Event(lisi,./cart,3000)*/

    // TODO min()只计算指定字段的最小值,其他字段会保留最初第一个数据的值
    stream.keyBy(_.user).min("timestamp").print()
    /*Event(Mary,./home,9000)
    Event(wangwu,./id,8000)
    Event(zhaoliu,./home,6000)
    Event(xiaoming,./home,1000)
    Event(Mary,./home,9000)
    Event(zhangsan,./cart,2000)
    Event(zhangsan,./cart,2000)
    Event(zhangsan,./cart,2000)
    Event(lisi,./cart,3000)*/

    // TODO maxBy:则会返回包含字段最大值的整条数据。
     stream.keyBy(_.user).maxBy("timestamp").print()
    /*  Event(Mary,./home,9000)
        Event(wangwu,./id,8000)
        Event(zhaoliu,./home,6000)
        Event(xiaoming,./home,1000)
        Event(Mary,./cart,10000)
        Event(zhangsan,./cart,2000)
        Event(zhangsan,./home,8000)
        Event(zhangsan,./home,8000)
        Event(lisi,./cart,3000)*/

    // TODO max:只计算指定字段的最大值,其他字段会保留最初第一个数据的值
    stream.keyBy(_.user).max("timestamp").print()
    // 结果出现了两次Event(Mary,./home,9000)说明第一次读取的数据是最大值,第二次读取的数据比前一次小,就返回前一次读取的内容
    // 并且会返回的内容会被更新
    /*Event(Mary,./home,9000)
      Event(wangwu,./id,8000)
      Event(zhaoliu,./home,6000)
      Event(xiaoming,./home,1000)
      Event(Mary,./home,10000)
      Event(zhangsan,./cart,2000)
      Event(zhangsan,./cart,8000)
      Event(zhangsan,./cart,8000)
      Event(lisi,./cart,3000)*/

    env.execute()
  }

  class MyKeySelector extends KeySelector[Event, String] {
    override def getKey(in: Event): String = in.user
  }
}

(3)归约聚合:reduce

import com.atguigu.chapter05.Event
import org.apache.flink.api.java.functions.KeySelector
import org.apache.flink.streaming.api.scala._

object ReduceTest {
  def main(args: Array[String]): Unit = {
    val env: StreamExecutionEnvironment = StreamExecutionEnvironment.getExecutionEnvironment
    env.setParallelism(1)

    val stream: DataStream[Event] = env.fromElements(
      Event("Mary", "./home", 9000L),
      Event("zhaoliu", "./home", 6000L),
      Event("Mary", "./cart", 10000L),
      Event("zhangsan", "./cart", 2000L),
      Event("zhangsan", "./home", 8000L),
      Event("zhangsan", "./home", 6000L),
    )

    // TODO reduce归约聚合,求最活跃的用户
    stream.map(x=>(x.user, 1L))// 长整型统计
      .keyBy(_._1)
      .reduce((x, y) => (x._1, x._2 + y._2))
      .keyBy(_ => true) // 将所有数据按照同样的key分到同一个组中
      .reduce((x, y) => if (x._2 > y._2) x else y) // 选取当前最活跃的用户
      .print()
   /* (Mary,1)
    (zhaoliu,1)
    (Mary,2)
    (Mary,2)
    (zhangsan,2)
    (zhangsan,3)*/

    env.execute()
  }
}

二、UDF

        对于大部分操作而言,都需要传入一个用户自定义函数(UDF),实现相关操作的接口,来完成处理逻辑的定义。Flink 暴露了所有 UDF 函数的接口,具体实现方式为接口或者抽象类,例如 MapFunction、FilterFunction、ReduceFunction 等。 所以最简单直接的方式,就是自定义一个函数类,实现对应的接口。

import com.atguigu.chapter05.Event
import org.apache.flink.api.common.functions.FilterFunction
import org.apache.flink.streaming.api.scala._

object UdfTest {
  def main(args: Array[String]): Unit = {
    val env: StreamExecutionEnvironment = StreamExecutionEnvironment.getExecutionEnvironment
    env.setParallelism(1)

    val stream: DataStream[Event] = env.fromElements(
      Event("Mary", "./home", 9000L),
      Event("zhaoliu", "./home", 6000L),
      Event("Mary", "./cart", 10000L),
      Event("zhangsan", "./cart", 2000L),
      Event("zhangsan", "./home", 8000L),
      Event("zhangsan", "./home", 6000L)
    )

    // TODO 测试UDF的用法,筛选url中包含某个关键字home的Event事件
    // TODO 方法一:实现一个自定义的函数类
    stream.filter(new MyFilterFunction).print()

    // TODO 方法二:
    stream.filter(new FilterFunction[Event] {
      override def filter(t: Event): Boolean = t.url.contains("home")
    }).print()

    // TODO 方法三:
    stream.filter(new MyFilterFunction2("home")).print()

    env.execute()
  }

  class MyFilterFunction() extends FilterFunction[Event] {
    override def filter(t: Event): Boolean = t.url.contains("home")
  }

  class MyFilterFunction2(url: String) extends FilterFunction[Event] {
    override def filter(t: Event): Boolean = t.url.contains(url)
  }
}

三、富函数类

import com.atguigu.chapter05.Event
import org.apache.flink.api.common.functions.RichMapFunction
import org.apache.flink.configuration.Configuration
import org.apache.flink.streaming.api.scala._

object RichFunctionTest {
  def main(args: Array[String]): Unit = {
    val env: StreamExecutionEnvironment = StreamExecutionEnvironment.getExecutionEnvironment
//    env.setParallelism(1)
    env.setParallelism(2)

    val stream: DataStream[Event] = env.fromElements(
      Event("Mary", "./home", 9000L),
      Event("zhaoliu", "./home", 6000L),
      Event("Mary", "./cart", 10000L),
      Event("zhangsan", "./cart", 2000L),
      Event("zhangsan", "./home", 8000L),
      Event("zhangsan", "./home", 6000L),
    )

    // TODO 自定义一个RichMapFunction,测试富函数类的功能
    stream.map(new MyRichMapFunction).print()

    // 当并行度为1时:
    /*索引号为:0的任务开始
    9000
    6000
    10000
    2000
    8000
    6000
    索引号为:0的任务结束*/
    
    
    // 当并行度为2时:
    /*
    索引号为:0的任务开始
    索引号为:1的任务开始
    2> 9000
    2> 10000
    1> 6000
    2> 8000
    1> 2000
    1> 6000
    索引号为:1的任务结束
    索引号为:0的任务结束*/
    
    env.execute()
  }

  class MyRichMapFunction extends RichMapFunction[Event, Long] {
    override def open(parameters: Configuration): Unit = {
      println("索引号为:" + getRuntimeContext.getIndexOfThisSubtask + "的任务开始")
    }

    override def close(): Unit = {
      println("索引号为:" + getRuntimeContext.getIndexOfThisSubtask + "的任务结束")
    }

    override def map(in: Event): Long = in.timestamp
  }
}

四、物理分区

        keyBy()操作就是一种按照键的哈希值来进行重新分区的操作。只不过这种分区操作只能保证把数据按key“分开”,至于分得均不均匀、每个key的数据具体会分到哪一区去,这些是完全无从控制的,这时,为了避免出现上述这种数据倾斜的现象,我们就要手动地对数据进行物理分区。

        所谓物理分区就是人为控制分区策略,精准地调配数据,告诉每个数据到底去哪里,重新进行负载均衡,将数据流较为平均地发送到下游任务操作分区中去。

1.随机分区(shuffle)

        最简单的重分区方式就是直接“洗牌”。通过调用 DataStream 的 shuffle()方法,将数据随机地分配到下游算子的并行任务中去。 随机分区服从均匀分布(uniform distribution),所以可以把流中的数据随机打乱,均匀地传递到下游任务分区。

shuffle实现:

import com.atguigu.chapter05.{ClickSource, Event}
import org.apache.flink.streaming.api.scala._

object ShuffleTest {
  def main(args: Array[String]): Unit = {
    val env: StreamExecutionEnvironment = StreamExecutionEnvironment.getExecutionEnvironment
    env.setParallelism(1)

    // TODO 读取自定义的数据源
    val stream: DataStream[Event] = env.addSource(new ClickSource)

    // TODO 洗牌之后打印输出
    stream.shuffle.print().setParallelism(4)

    env.execute()
  }
}

2> Event(Cary,./fav,1681456335725)
2> Event(Bob,./cart,1681456336737)
3> Event(Bob,./pord?id=1,1681456337746)
3> Event(Cary,./pord?id=2,1681456338754)
1> Event(Cary,./cart,1681456339760)
2> Event(Alice,./pord?id=2,1681456340767)
3> Event(Alice,./cart,1681456341773)
2> Event(Alice,./home,1681456342781)
4> Event(Marry,./home,1681456343789)
1> Event(Bob,./fav,1681456344796)
1> Event(Marry,./pord?id=1,1681456345805)

ClickSource类:

import org.apache.flink.streaming.api.functions.source.SourceFunction

import java.util.Calendar
import scala.util.Random

class ClickSource extends SourceFunction[Event] {
  // 标志位
  var running = true

  override def run(sourceContext: SourceFunction.SourceContext[Event]): Unit = {

    // 随机数生成器
    val random = new Random()
    val users: Array[String] = Array("Marry", "Alice", "Bob", "Cary")
    val urls: Array[String] = Array("./home", "./cart", "./fav", "./pord?id=1", "./pord?id=2", "./pord?id=3")

    // 用标志位作为循环判断条件,不停地发出数据
    while (running) {
      val event: Event = Event(users(random.nextInt(users.length)), urls(random.nextInt(urls.length)), Calendar.getInstance.getTimeInMillis)
      // 调用sourceContext方法向下游发送数据
      sourceContext.collect(event)
      // 每隔1s发送一条数据
      Thread.sleep(1000)
    }
  }
  override def cancel(): Unit = running = false
}

2. 轮询分区(Round-Robin)  

        轮询也是一种常见的重分区方式。简单来说就是“发牌”,按照先后顺序将数据做依次分发,如图所示。通过调用DataStream的.rebalance()方法,就可以实现轮询重分区。rebalance()使用的是 Round-Robin 负载均衡算法,可以将输入流数据平均分配到下游的并行任务中去。

import com.atguigu.chapter05.{ClickSource, Event}
import org.apache.flink.streaming.api.scala._

object ReblanceTest {
  def main(args: Array[String]): Unit = {
    val env: StreamExecutionEnvironment = StreamExecutionEnvironment.getExecutionEnvironment
    env.setParallelism(1)

    // TODO 读取自定义的数据源
    val stream: DataStream[Event] = env.addSource(new ClickSource)

    // TODO 下面两种写法都是轮询分区
    stream.print().setParallelism(4)
    stream.rebalance.print("reblance").setParallelism(4)

    // 结果:
    reblance:1> Event(Cary,./pord?id=1,1681456987398)
    reblance:2> Event(Cary,./pord?id=1,1681456988408)
    reblance:3> Event(Cary,./home,1681456989417)
    reblance:4> Event(Alice,./pord?id=3,1681456990424)
    reblance:1> Event(Bob,./cart,1681456991437)
    reblance:2> Event(Cary,./pord?id=3,1681456992438)
    reblance:3> Event(Bob,./cart,1681456993445)
    reblance:4> Event(Cary,./cart,1681456994456)
    reblance:1> Event(Marry,./home,1681456995468)
    reblance:2> Event(Cary,./home,1681456996480)
    reblance:3> Event(Marry,./pord?id=2,1681456997488)

    env.execute()
  }
}

 3. 重缩放分区(rescale)

        重缩放分区和轮询分区非常相似。当调用 rescale()方法时,其实底层也是使用 Round-Robin算法进行轮询,但是只会将数据轮询发送到下游并行任务的一部分中。也就是说,“发牌人”如果有多个,那么 rebalance()的方式是每个发牌人都面向所有人发牌;而rescale()的做法是分成小团体,发牌人只给自己团体内的所有人轮流发牌。

package com.atguigu.partition

import com.atguigu.chapter05.{ClickSource, Event}
import org.apache.flink.streaming.api.functions.source.{RichParallelSourceFunction, SourceFunction}
import org.apache.flink.streaming.api.scala._

object RescaleTest {
  def main(args: Array[String]): Unit = {
    val env: StreamExecutionEnvironment = StreamExecutionEnvironment.getExecutionEnvironment
    env.setParallelism(1)

    // TODO 读取自定义的数据源
    // TODO 定义2次输出,相当于2个数据源,分到4个分区中
    val stream: DataStream[Int] = env.addSource(new RichParallelSourceFunction[Int] {
      override def run(sourceContext: SourceFunction.SourceContext[Int]): Unit = {
        for (i <- 0 to 7) {
          if (getRuntimeContext.getIndexOfThisSubtask == (i + 1) % 2) {
            // TODO 奇数分到3和4分区;偶数分到1和2分区
            sourceContext.collect(i + 1)
          }
        }
      }

      override def cancel(): Unit = ???
    }).setParallelism(2)

    // TODO 轮询分区
    stream.rebalance.print("rescale").setParallelism(4)
    rescale:1> 4
    rescale:2> 6
    rescale:4> 2
    rescale:3> 8
    rescale:4> 5
    rescale:1> 7
    rescale:3> 3
    rescale:2> 1

    // TODO 重缩放分区
    stream.rescale.print("rescale").setParallelism(4)
    rescale:4> 3
    rescale:3> 1
    rescale:1> 2
    rescale:2> 4
    rescale:1> 6
    rescale:3> 5
    rescale:4> 7
    rescale:2> 8

    env.execute()
  }
}

注意:rebalance与rescale的区别:

4.广播(broadcast)——不常用

        经过广播之后,数据会在不同的分区都保留一份,可能进行重复处理。可以通过调用 DataStream 的 broadcast()方法,将输入数据复制并发送到下游算子的所有并行任务中去。

    // TODO 读取自定义的数据源
    val stream: DataStream[Event] = env.addSource(new ClickSource)
    // TODO 广播分区:每隔1秒一次性输出4个数据,且4个并行子任务输出的内容是一样的,特殊场合下才会使用
    stream.broadcast.print("broadcast").setParallelism(4)

broadcast:3> Event(Cary,./pord?id=1,1681461381359)
broadcast:2> Event(Cary,./pord?id=1,1681461381359)
broadcast:1> Event(Cary,./pord?id=1,1681461381359)
broadcast:4> Event(Cary,./pord?id=1,1681461381359)
broadcast:1> Event(Alice,./fav,1681461382364)
broadcast:2> Event(Alice,./fav,1681461382364)
broadcast:3> Event(Alice,./fav,1681461382364)
broadcast:4> Event(Alice,./fav,1681461382364)

5.全局分区(global)——不常用

        全局分区也是一种特殊的分区方式。这种做法非常极端,通过调用.global()方法,会将所有的输入流数据都发送到下游算子的第一个并行子任务中去。这就相当于强行让下游任务并行度变成了 1,所以使用这个操作需要非常谨慎,可能对程序造成很大的压力。

// TODO 读取自定义的数据源
 val stream: DataStream[Event] = env.addSource(new ClickSource)

// TODO 全局分区:每一次只生成一条数据,而且每一条数据都在第1个分区
 stream.global.print().setParallelism(4)

1> Event(Cary,./home,1681461414206)
1> Event(Bob,./home,1681461415222)
1> Event(Alice,./fav,1681461416225)
1> Event(Alice,./home,1681461417236)
1> Event(Bob,./fav,1681461418242)

6. 自定义分区(Custom)

        当Flink提供的所有分区策略都不能满足用户的需求时,我们可以通过使用partitionCustom()方法来自定义分区策略。

        在调用时,方法需要传入两个参数,第一个是自定义分区器(Partitioner)对象,第二个是应用分区器的字段,它的指定方式与keyBy指定key基本一样:可以通过字段名称指定,也可以通过字段位置索引来指定,还可以实现一个KeySelector接口。

import org.apache.flink.api.common.functions.Partitioner
import org.apache.flink.streaming.api.scala._

object CustomTest {
  def main(args: Array[String]): Unit = {
    val env: StreamExecutionEnvironment = StreamExecutionEnvironment.getExecutionEnvironment
    env.setParallelism(1)

    // TODO 读取自定义的数据源
    val stream: DataStream[Int] = env.fromElements(1, 2, 3, 4, 5, 6, 7, 8)
    val ds: DataStream[Int] = stream.partitionCustom(new Partitioner[Int] {
      override def partition(k: Int, i: Int): Int = {
        k % 2
      }
    }, x => x)
    ds.print("partitionCustom").setParallelism(4)

    // 结果:
    partitionCustom:1> 2
    partitionCustom:2> 1
    partitionCustom:1> 4
    partitionCustom:2> 3
    partitionCustom:1> 6
    partitionCustom:2> 5
    partitionCustom:1> 8
    partitionCustom:2> 7

    env.execute()
  }
}

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

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

相关文章

4月20日第壹简报,星期四,农历三月初一,谷雨

4月20日第壹简报&#xff0c;星期四&#xff0c;农历三月初一&#xff0c;谷雨坚持阅读&#xff0c;静待花开1. 已致29人死亡&#xff0c;26人为患者&#xff01;北京长峰医院火灾事故因院内施工作业火花引发&#xff0c;院长王某玲等12人被刑拘。2. 海南发布旅游产品参考价格&…

第二十三章 案例TodoList之数据更新

本小节&#xff0c;我们要实现点击复选框&#xff0c;修改任务项的实时状态&#xff0c;但是Item组件和App组件是祖孙关系&#xff0c;不是父子关系&#xff0c;我们还能使用props进行通信吗&#xff1f;答案是可以的。 在App组件定义一个更新的函数并传递给Item组件 1、定义…

车载网络 - Autosar网络管理 - 网络管理报文

三、网络管理报文 NM报文的ID一般定义为&#xff1a;基础ID源地址&#xff0c;每个节点应分配一个唯一的标识符&#xff08;ECU地址&#xff09;Node_ID&#xff0c;网络管理报文一般会统一一个基地址&#xff0c;这个是根据主机厂不同而不同&#xff0c;有些是用0x400 - 0x4FF…

多通道振弦传感器无线采集仪如何外接数字传感器

多通道振弦传感器无线采集仪如何外接数字传感器 数字传感器的数据接入逻辑 VS 设备支持在 RS485 接口外接数字传感器&#xff0c; 可进行单类型、多类型数字传感器接入。 单类型数字传感器&#xff1a;使用寄存器 DS_SENSOR(282)来设置单类型数字传感器的类型和数量&#xff08…

用Flutter开发一款企业级App(开眼Flutter-OpenEye)

先贴项目地址&#xff1a;WinWang/open_eye: Flutter 开眼APP&#xff1a;整体项目架构基于Getx搭建&#xff0c;完成路由&#xff0c;依赖注入&#xff1b;网络请求框架基于RetrofitDio实现&#xff0c;配合官方JsonSerialize实现解析&#xff1b;封装项目页面多状态&#xff…

新媒体运营团队如何协同工作?

随着媒体形式的不断丰富和演变&#xff0c;社交、短视频等软件的盛行&#xff0c;新媒体运营成为近几年新兴的行业。 在新媒体团队工作过程中可能会遇到以下问题&#xff1a; 1&#xff09;热点不断更迭&#xff0c;文档资料需要快速流通&#xff1b; 2&#xff09;通过传统的…

US-DAPQ比例放大器指令接线

序号 端子名称 功能 1 CMD_P 压力阀指令 2 CMD_P- 压力阀指令- 3 CMD_Q 流量阀指令 4 CMD_Q- 流量阀指令- 5/6 N.C. 不接 7 VREF_5V 参考电压5V 8 VREF_0V 参考电压0V 9 SOL_P 压力阀线圈 10 SOL_P- 压力阀线圈 11 PWR 电源 12 PWR- 电源- 13 SOL_Q- 流量阀线圈 1…

mysql知识

1.执行步骤 2.Write-Ahead Logging 技术 这两种日志有以下三点不同。 ①redo log 是 InnoDB 引擎特有的&#xff1b;binlog 是 MySQL 的 Server 层实现的&#xff0c;所有引擎都可以使用。 ②redo log 是物理日志&#xff0c;记录的是“在某个数据页上做了什么修改”&#x…

【Linux】项目自动化构建工具 —— make/Makefile

前言&#xff1a; 在上一期的博文中&#xff0c;我们对 Linux 下的编译器 - gcc/g的使用进行了详细的讲解&#xff0c;今天我将给大家讲解的是关于 【Linux】项目自动化构建工具 —— make/Makefile 的详细使用教程&#xff01;&#xff01; 本文目录 &#xff08;一&#x…

ai智能文章生成器-ai论文写作

在数字时代&#xff0c;营销推广策略已经向数字化方向发展。今天我们要介绍的是一款名为“智能ai写作免费”的软件&#xff0c;它可以让营销人员轻松地创作新的内容&#xff0c;并且其中不需要过多的技术知识或文学背景。这款软件可以为许多企业和机构带来创造性的帮助。 智能A…

婴幼儿口水巾围兜出口美国CPC认证

婴幼儿口水巾、围兜在美国销售需要办理CPC认证&#xff0c;即儿童安全产品证书。 什么是CPC认证&#xff1f;美国亚马逊儿童玩具和儿童用品CPC认证是指颁发书面儿童产品证书&#xff08;CPC、&#xff0c;其中制造商或进口商证明其儿童产品符合所有适用的儿童产品安全规则。儿童…

被盗的ChatGPT账户在暗网热销,ChatGPT的隐私和安全问题依旧值得关注

在过去的一个月&#xff0c;Check Point研究人员在暗网上观察到了与ChatGPT相关的各种讨论和交易。暗网上最新的活动包括泄露和免费发布ChatGPT账户的凭据&#xff0c;以及交易被盗的ChatGPT账户。 根据Check Point进行的一项研究&#xff0c;从今年3月以来&#xff0c;被盗的…

QPSK调制解调FPGA实现成果展示:

目录 QPSK调制解调使用参数&#xff1a; 调制&#xff1a; 解调&#xff1a; FPGA工程架构&#xff1a; 仿真参数&#xff1a; 仿真展示&#xff1a; 调制&#xff1a; 解调&#xff1a; MATLAB星座图展示&#xff1a; QPSK调制解调使用参数&#xff1a; 采样率为4M&…

Revit插件 | 建模助手下半年规划大揭秘!

​hi&#xff0c;这里是建模助手。 在上周推送『建模助手年度版本大更新』后&#xff0c;很多盆友和我们反馈这次的升级&#xff0c;真被细节打动。 版本的兼容性更给力了&#xff0c;避免了某版本的Revit不可用的现象&#xff1b; 有求必应板块更细致了&#xff0c;新增了阶…

breakpad编译指南(Windows)

在编译breakpad时候&#xff0c;遇到各种各样的问题&#xff0c;做些记录&#xff0c;以便后续参考 Windows下已有SetUnhandledExceptionFilter可以抓取dmp&#xff0c;为什么还要研究breakpad&#xff1f;因为breakpad是跨平台的&#xff0c;相关资料参考如下&#xff1a; 02…

迅为i.MX6ULL开发板I2C总线实现driver驱动

上一章节我们写了 client.c&#xff0c;并且我们已经成功地把它加载到内核里面。i2c 用非设备树实现&#xff0c;我们需要 用 i2c_board_info 这个结构体来描述我们的 i2c 设备&#xff0c;如果我们用设备树的方法来实现&#xff0c;我们直接在设备树 的节点下面添加创建对应设…

关于File.io的学习记录(读取文本)

可以通过字节流&#xff08;FileInputStream&#xff09;、字符流&#xff08;InputStreamReader&#xff09;、字符缓冲流&#xff08;BufferedReader&#xff09;读取文本中的数据。 1、FileInputStream读取文本 public void read(){String path "fileTest.txt";F…

JDK新增史上最无用提案!竟是为了简化Hello World?

前两天JDK 20更新了&#xff0c;很多人表示很失望&#xff0c;但是我万万没想到的是&#xff0c;还有更令人失望的。 OpenJDK最近又增加了一个新提案&#xff0c;JEP 445&#xff0c;这个提案的主要内容是要简化Hello World的写法。并且该新特性即将在Java 21中作为预览功能推出…

凌恩生物文献分享|多组学联合,病毒与宿主关联分析又升级了!

病毒是地球上数量最多的生物实体&#xff0c;在调控宿主群落组成、推动宿主进化及影响土壤元素的生物地球化学循环等方面起着非常重要的作用。病毒基因组结构简单&#xff0c;一般只含有单一核酸&#xff0c;探究病毒基因组在病毒的毒力系统、病毒的基因组进化与演变历程方面具…

FlinkX的安装与使用(异构数据同步工具——flinkx)

异构数据同步工具——flinkx - 知乎 一、概要简介 FlinkX是由袋鼠云开源基于Flink的分布式离线和实时相结合的数据同步框架&#xff0c;既可以采集静态的数据比如&#xff1a;MYSQL,HDFS等&#xff0c;也可以采集实时变化的数据比如&#xff1a;MYSQL BINLOG,KAFKA等。目前官方…