Flink第七章:状态编程

news2024/11/29 20:59:17

系列文章目录

Flink第一章:环境搭建
Flink第二章:基本操作.
Flink第三章:基本操作(二)
Flink第四章:水位线和窗口
Flink第五章:处理函数
Flink第六章:多流操作
Flink第七章:状态编程


文章目录

  • 系列文章目录
  • 前言
  • 一、Keyed State(按键分区)
    • 1.KeyedStateTest.scala
    • 2.PeriodicPVExample.scala
    • 3.TwoStreamJoinExample.scala
    • 4.FakeWindowExample.scala
    • 5.AverageTimestampExample.scala
  • 二、Operator State(算子状态)
    • 1.BufferingSinkExample.scala
  • 三、Broadcast State(广播状态)
    • 1.BroadcastStateExample.scala
  • 总结


前言

这次我们来学习Flink中的状态学习部分,创建以下scala文件
在这里插入图片描述


一、Keyed State(按键分区)

1.KeyedStateTest.scala

这个文件里有几个常用的状态创建

package com.atguigu.chapter06

import com.atguigu.chapter02.Source.{ClickSource, Event}
import org.apache.flink.api.common.functions.{AggregateFunction, ReduceFunction, RichFlatMapFunction}
import org.apache.flink.api.common.state._
import org.apache.flink.configuration.Configuration
import org.apache.flink.streaming.api.scala._
import org.apache.flink.util.Collector

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

    env.addSource(new ClickSource)
      .assignAscendingTimestamps(_.timestamp)
      .keyBy(_.user)
      .flatMap(new MyFlatMap)

    env.execute()

  }

  class MyFlatMap extends RichFlatMapFunction[Event, String] {
    // 定义状态
    var valueState: ValueState[Event] = _
    var listState: ListState[Event] = _
    var mapState: MapState[String, Long] = _
    var reduceState:ReducingState[Event]= _
    var aggState:AggregatingState[Event,String]= _

    override def open(parameters: Configuration): Unit = {
      valueState = getRuntimeContext.getState(new ValueStateDescriptor[Event]("my-value", classOf[Event]))
      listState = getRuntimeContext.getListState(new ListStateDescriptor[Event]("my-list", classOf[Event]))
      mapState = getRuntimeContext.getMapState(new MapStateDescriptor[String, Long]("my-map", classOf[String], classOf[Long]))

      reduceState=getRuntimeContext.getReducingState(new ReducingStateDescriptor[Event]("my-reduce",
        new ReduceFunction[Event] {
          override def reduce(t: Event, t1: Event): Event = Event(t.user,t.url,t1.timestamp)
        },classOf[Event]
      ))

      aggState=getRuntimeContext.getAggregatingState(new AggregatingStateDescriptor[Event,Long,String]("my-agg",
        new AggregateFunction[Event,Long,String] {
          override def createAccumulator(): Long = 0L

          override def add(in: Event, acc: Long): Long = acc+1

          override def getResult(acc: Long): String = "当前聚合状态为:"+acc.toString

          override def merge(acc: Long, acc1: Long): Long = ???
        },classOf[Long]
      ))

    }

    override def flatMap(in: Event, collector: Collector[String]): Unit = {
      // 对状态进行操作
      println("值状态为:" + valueState.value())
      valueState.update(in)
      println("值状态为:" + valueState.value())

      listState.add(in)
      println("------------")
      val count: Long =if (mapState.contains(in.user)) mapState.get(in.user) else 0
      mapState.put(in.user,count+1)
      println(s"用户 ${in.user} 的访问频次为: ${mapState.get(in.user)}")
      println("-------------")
      reduceState.add(in)
      println(reduceState.get())

      println("-------------")
      aggState.add(in)
      println(aggState.get())

      println("=================")

    }
  }
}

在这里插入图片描述

2.PeriodicPVExample.scala

按键分区中值状态编程案例

我们这里会使用用户 id 来进行分流,然后分别统计每个用户的 pv 数据,由于我们并不想每次 pv 加一,就将统计结果发送到下游去,所以这里我们注册了一个定时器,用来隔一段时间发送 pv 的统计结果,这样对下游算子的压力不至于太大。具体实现方式是定义一个用来保存定时器时间戳的值状态变量。当定时器触发并向下游发送数据以后,便清空储存定时器时间戳的状态变量,这样当新的数据到来时,发现并没有定时器存在,就可以注册新的定时器了,注册完定时器之后将定时器的时间戳继续保存在状态变量中。

package com.atguigu.chapter06

import com.atguigu.chapter02.Source.{ClickSource, Event}
import org.apache.flink.api.common.state.{ValueState, ValueStateDescriptor}
import org.apache.flink.streaming.api.functions.KeyedProcessFunction
import org.apache.flink.streaming.api.scala._
import org.apache.flink.util.Collector

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

    env.addSource(new ClickSource)
      .assignAscendingTimestamps(_.timestamp)
      .keyBy(_.user)
      .process(new PeriodicPv)
      .print()

    env.execute()
  }

  class PeriodicPv extends KeyedProcessFunction[String, Event, String] {
    // 定义值状态,保存当前用户的pv数据
    lazy val countState: ValueState[Long] = getRuntimeContext.getState(new ValueStateDescriptor[Long]("count", classOf[Long]))

    //定义值状态,保存定时器的时间戳
    lazy val timerTsState: ValueState[Long] = getRuntimeContext.getState(new ValueStateDescriptor[Long]("time-Ts", classOf[Long]))
    
    override def processElement(value: Event, ctx: KeyedProcessFunction[String, Event, String]#Context, out: Collector[String]): Unit = {
      // 每来一个数据,就将状态中的count+1
      val count: Long = countState.value()
      countState.update(count + 1)

      // 注册定时器,每隔10秒输出一次统计结果
      if (timerTsState.value() == 0L) {
        ctx.timerService().registerEventTimeTimer(value.timestamp + 10 * 1000L)
        //更新状态
        timerTsState.update(value.timestamp + 10 * 1000L)
      }
    }

    // 定时器触发
    override def onTimer(timestamp: Long, ctx: KeyedProcessFunction[String, Event, String]#OnTimerContext, out: Collector[String]): Unit = {
      out.collect(s"用户 ${ctx.getCurrentKey}的pv值为:${countState.value()}")
      // 清理状态
      timerTsState.clear()
    }
  }
}

在这里插入图片描述
10s统计一次,并且不断累加,有点像全局窗口.

3.TwoStreamJoinExample.scala

列表状态编程

SELECT * FROM A INNER JOIN B WHERE A.id = B.id;
这样一条 SQL 语句要慎用,因为 Flink 会将 A 流和 B 流的所有数据都保存下来,然后进行 join。不过在这里我们可以用列表状态变量来实现一下这个 SQL 语句的功能。

package com.atguigu.chapter06

import org.apache.flink.api.common.state.{ListState, ListStateDescriptor}
import org.apache.flink.streaming.api.functions.co.CoProcessFunction
import org.apache.flink.streaming.api.scala._
import org.apache.flink.util.Collector

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

    val stream1: DataStream[(String, String, Long)] = env.fromElements(
      ("a","stream-1",1000L),
      ("b","stream-1",2000L),
    ).assignAscendingTimestamps(_._3)

    val stream2: DataStream[(String, String, Long)] = env.fromElements(
      ("a","stream-2",3000L),
      ("b","stream-2",4000L),
    ).assignAscendingTimestamps(_._3)

    // 连接两条流进行Join操作
    stream1.keyBy(_._1)
      .connect(stream2.keyBy(_._1))
      .process(new TwoStreamJoin)
      .print()

    env.execute()
  }

  class TwoStreamJoin extends CoProcessFunction[(String, String, Long),(String, String, Long),String] {
    // 定义列表状态,保存流中已经到达的数据
    lazy val stream1ListState: ListState[(String, String, Long)] = getRuntimeContext.getListState(new ListStateDescriptor[(String, String, Long)]("stream1-list", classOf[(String, String, Long)]))
    lazy val stream2ListState: ListState[(String, String, Long)] = getRuntimeContext.getListState(new ListStateDescriptor[(String, String, Long)]("stream2-list", classOf[(String, String, Long)]))


    override def processElement1(value1: (String, String, Long), ctx: CoProcessFunction[(String, String, Long), (String, String, Long), String]#Context, out: Collector[String]): Unit = {
      // 直接添加到列表状态中
      stream1ListState.add(value1)
      //遍历另一条流中已经到达的数据,输出配对信心
      import scala.collection.convert.ImplicitConversions._
      for (value2<-stream2ListState.get()){
        out.collect(value1+"=>"+value2)
      }

    }

    override def processElement2(value2: (String, String, Long), ctx: CoProcessFunction[(String, String, Long), (String, String, Long), String]#Context, out: Collector[String]): Unit = {
      // 直接添加到列表状态中
      stream2ListState.add(value2)
      //遍历另一条流中已经到达的数据,输出配对信心
      import scala.collection.convert.ImplicitConversions._
      for (value1<-stream1ListState.get()){
        out.collect(value1+"=>"+value2)
      }
    }
  }
}

在这里插入图片描述

4.FakeWindowExample.scala

映射状态编程

映射状态的用法和 Java 中的 HashMap 很相似。在这里我们可以通过 MapState 的使用来探
索一下窗口的底层实现,也就是我们要用映射状态来完整模拟窗口的功能。这里我们模拟一个
滚动窗口。我们要计算的是每一个 url 在每一个窗口中的 pv 数据。我们之前使用增量聚合和
全窗口聚合结合的方式实现过这个需求。这里我们用 MapState 再来实现一下。

package com.atguigu.chapter06

import com.atguigu.chapter02.Source.{ClickSource, Event}
import org.apache.flink.api.common.state.{MapState, MapStateDescriptor}
import org.apache.flink.streaming.api.functions.KeyedProcessFunction
import org.apache.flink.streaming.api.scala._
import org.apache.flink.util.Collector

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

    env.addSource(new ClickSource)
      .assignAscendingTimestamps(_.timestamp)
      .keyBy(_.url)
      .process(new FakeWindow(10000L)) //10秒的滚动窗口
      .print()

    env.execute()
  }

  class FakeWindow(size:Long) extends KeyedProcessFunction[String,Event,String]{
    //定义一个映射状态,用来保存一个窗口的pv值
    lazy val windowMapSate: MapState[Long, Long] = getRuntimeContext.getMapState(new MapStateDescriptor[Long, Long]("window-pv", classOf[Long], classOf[Long]))

    override def processElement(value: Event, ctx: KeyedProcessFunction[String, Event, String]#Context, out: Collector[String]): Unit = {
      //集散当前数据落入窗口的启示时间戳
      val start: Long = value.timestamp / size * size
      val end: Long = start + size

      // 注册一个定时器,用力触发窗口计算
      ctx.timerService().registerEventTimeTimer(end-1)

      // 更新状态 count+1
      if (windowMapSate.contains(start)){
        val pv: Long = windowMapSate.get(start)
        windowMapSate.put(start,pv+1)
      } else {
        windowMapSate.put(start,1L)
      }
    }


    override def onTimer(timestamp: Long, ctx: KeyedProcessFunction[String, Event, String]#OnTimerContext, out: Collector[String]): Unit = {
      // 定时器触发,窗口输出结果
      val start: Long = timestamp + 1 - size

      val pv: Long = windowMapSate.get(start)

      // 窗口输出结果
      out.collect(s"url: ${ctx.getCurrentKey} 浏览量为: ${pv} 窗口为:${start}-${start+size}")

      //窗口销毁
      windowMapSate.remove(start)

    }
  }
}

在这里插入图片描述

5.AverageTimestampExample.scala

聚合状态编程

我们举一个简单的例子,对用户点击事件流每 5 个数据统计一次平均时间戳。这是一个类似计数窗口(CountWindow)求平均值的计算,这里我们可以使用一个有聚合状态的
RichFlatMapFunction 来实现。

package com.atguigu.chapter06

import com.atguigu.chapter02.Source.{ClickSource, Event}
import org.apache.flink.api.common.functions.{AggregateFunction, RichFlatMapFunction}
import org.apache.flink.api.common.state.{AggregatingState, AggregatingStateDescriptor, ValueState, ValueStateDescriptor}
import org.apache.flink.streaming.api.scala._
import org.apache.flink.util.Collector

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

    val stream: DataStream[Event] = env.addSource(new ClickSource)
      .assignAscendingTimestamps(_.timestamp)

    stream
      .keyBy(_.url)
      .flatMap(new AvgTimestamp)
      .print("input")

    stream.print("input")



    env.execute()
  }

  class AvgTimestamp extends RichFlatMapFunction[Event, String] {
    // 定义一个聚合状态
    lazy val avgTsAggState: AggregatingState[Event, Long] = getRuntimeContext.getAggregatingState(new AggregatingStateDescriptor[Event, (Long, Long), Long](
      "avg-ts",
      new AggregateFunction[Event, (Long, Long), Long] {
        override def createAccumulator(): (Long, Long) = (0L, 0L)

        override def add(in: Event, acc: (Long, Long)): (Long, Long) = (acc._1 + in.timestamp, acc._2 + 1)

        override def getResult(acc: (Long, Long)): Long = acc._1 / acc._2

        override def merge(acc: (Long, Long), acc1: (Long, Long)): (Long, Long) = ???
      },
      classOf[(Long,Long)]
    ))

    // 定义一个值状态,保存当前到达的数据个数

    lazy val countState: ValueState[Long] = getRuntimeContext.getState(new ValueStateDescriptor[Long]("count", classOf[Long]))

    override def flatMap(in: Event, collector: Collector[String]): Unit = {
      avgTsAggState.add(in)

      // 更新count值
      val count: Long = countState.value()
      countState.update(count+1)

      if (countState.value()==5){
        collector.collect(s"${in.user}的平均时间戳为: ${avgTsAggState.get()}")
        countState.clear()
      }
    }
  }
}

在这里插入图片描述

二、Operator State(算子状态)

1.BufferingSinkExample.scala

在下面的例子中,自定义的 SinkFunction 会在CheckpointedFunction 中进行数据缓存,然后统一发送到下游。这个例子演示了列表状态的平均分割重组(event-split redistribution)。

package com.atguigu.chapter06

import com.atguigu.chapter02.Source.{ClickSource, Event}
import org.apache.flink.api.common.state.{ListState, ListStateDescriptor}
import org.apache.flink.runtime.state.{FunctionInitializationContext, FunctionSnapshotContext}
import org.apache.flink.streaming.api.checkpoint.CheckpointedFunction
import org.apache.flink.streaming.api.functions.sink.SinkFunction
import org.apache.flink.streaming.api.scala._

import scala.collection.mutable.ListBuffer

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

    env.addSource(new ClickSource)
      .assignAscendingTimestamps(_.timestamp)
      .addSink(new BufferingSink(10))

    env.execute()

  }

  // 实现自定义SinkFunction
  class BufferingSink(threshold: Int) extends SinkFunction[Event] with CheckpointedFunction {
    // 定义列表状态,保存要缓冲的数据
    var bufferedState: ListState[Event] = _
    // 定义本地变量列表
    val bufferedList: ListBuffer[Event] = ListBuffer[Event]()

    override def invoke(value: Event, context: SinkFunction.Context): Unit = {
      // 缓冲数据
      bufferedList+=value

      // 判断是否达到阈值
      if (bufferedList.size==threshold){
        // 输出到外部系统,打印
        bufferedList.foreach(data=>println(data))
        println("=======输出完毕==============")

        // 清空缓冲
        bufferedList.clear()
      }
    }

    override def snapshotState(context: FunctionSnapshotContext): Unit = {
      // 清空状态
      bufferedState.clear()
      for (data <- bufferedList){
        bufferedState.add(data)
      }
    }

    override def initializeState(context: FunctionInitializationContext): Unit = {
      bufferedState = context.getOperatorStateStore.getListState(new ListStateDescriptor[Event]("buffered-list", classOf[Event]))
      // 判断如果是从故障中恢复,那么就将状态中的数据添加到局部变量中
      if (context.isRestored) {
        import scala.collection.convert.ImplicitConversions._
        for (data <- bufferedState.get()) {
          bufferedList += data
        }
      }
    }
  }
}

在这里插入图片描述

三、Broadcast State(广播状态)

1.BroadcastStateExample.scala

行为匹配案例

package com.atguigu.chapter06

import org.apache.flink.api.common.state.{BroadcastState, MapStateDescriptor, ReadOnlyBroadcastState, ValueState, ValueStateDescriptor}
import org.apache.flink.streaming.api.datastream.BroadcastStream
import org.apache.flink.streaming.api.functions.co.KeyedBroadcastProcessFunction
import org.apache.flink.streaming.api.scala._
import org.apache.flink.util.Collector

// 声明样例类
case class Action(userid: String, action: String)

case class Pattern(action1: String, action2: String)

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

    // 定义数据流
    val actionStream: DataStream[Action] = env.fromElements(
      Action("Alice", "login"),
      Action("Alice", "pay"),
      Action("Bob", "login"),
      Action("Bob", "buy")
    )

    // 定义模式流,读取指定的行为模式
    val patternStream: DataStream[Pattern] = env.fromElements(
      Pattern("login", "pay"),
      Pattern("login", "buy")
    )

    // 定义广播状态的描述器
    val patterns = new MapStateDescriptor[Unit, Pattern]("patterns", classOf[Unit], classOf[Pattern])
    val broadcastStream: BroadcastStream[Pattern] = patternStream.broadcast(patterns)

    // 连接两条流,进行处理
    actionStream.keyBy(_.userid)
      .connect(broadcastStream)
      .process(new PatternEvaluation)
      .print()

    env.execute()
  }

  // 实现自定义的KeyedbroadcastProcessFunction
  class PatternEvaluation extends KeyedBroadcastProcessFunction[String,Action,Pattern,(String,Pattern)]{
    // 定义值状态,保存上一次用户行为
    lazy val prevActionState: ValueState[String] = getRuntimeContext.getState(new ValueStateDescriptor[String]("prev-action", classOf[String]))

    override def processElement(value: Action, ctx: KeyedBroadcastProcessFunction[String, Action, Pattern, (String, Pattern)]#ReadOnlyContext, out: Collector[(String, Pattern)]): Unit = {
      // 从广播状态中获取行为数据
      val pattern= ctx.getBroadcastState(new MapStateDescriptor[Unit, Pattern]("patterns", classOf[Unit], classOf[Pattern]))
        .get(Unit)

      // 从值状态中获取上次的行为
      val prevAction: String = prevActionState.value()

      if (pattern != null && prevAction != null){
        if (pattern.action1==prevAction && pattern.action2==value.action){
          out.collect((ctx.getCurrentKey,pattern))
        }
      }


      // 保存状态
      prevActionState.update(value.action)
    }

    override def processBroadcastElement(value: Pattern, ctx: KeyedBroadcastProcessFunction[String, Action, Pattern, (String, Pattern)]#Context, out: Collector[(String, Pattern)]): Unit = {
      val bcState: BroadcastState[Unit, Pattern] = ctx.getBroadcastState(new MapStateDescriptor[Unit, Pattern]("patterns", classOf[Unit], classOf[Pattern]))

      bcState.put(Unit,value)
    }
  }
}

在这里插入图片描述


总结

这次记录就到这里.

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

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

相关文章

土壤热通量传感器的应用

土壤热通量传感器又称“土壤热通量板”、“热流计”&#xff0c;主要用于测量土壤的能量平衡和土壤层的热导率。土壤热通量传感器采用热电堆测量温度梯度&#xff0c;该热电堆有两种不同的金属材料组成。热电堆探测器接受热辐射&#xff0c;热辐射能使两个不同材料的结点之间产…

如何在华为OD机试中获得满分?Java实现【单核CPU任务调度】一文详解

✅创作者:陈书予 🎉个人主页:陈书予的个人主页 🍁陈书予的个人社区,欢迎你的加入: 陈书予的社区 🌟专栏地址: Java华为OD机试真题(2022&2023) 文章目录 1. 题目描述2. 输入描述3. 输出描述4. Java算法源码5. 测试6.解题思路1. 题目描述 现在有一个CPU和一些任务…

每天一道面试题之==和equals的区别是什么?

&#xff1d;&#xff1d;和equals的区别是什么&#xff1f; ""是一个关系运算符&#xff0c;关系运算符可以用来进行数据和数据之间的比较&#xff0c;而在java中数据类型大致可以分为两大类分别是基本数据类型和引用数据类型。 基本数据类型包含 byte&#xff0c…

谢谢所有可爱的粉丝友友们

前言&#xff1a; 其实我很早之前就申请了CSDN账号&#xff0c;其中陆陆续续也在写博客&#xff0c;但是没有坚持下来&#xff0c;大家可以看到我的前期博客&#xff0c;少之又少&#xff0c;我甚至只要看一下我的博客内容&#xff0c;我就知道我当时在哪里&#xff0c;我当时在…

[RSA议题分析] eBPF Warfare - Detecting Kernel eBPF Rootkits with Tracee

文章目录 简介议题分析基础知识用户空间与内核空间hookrootkit追踪技术 eBPF架构Tracee - 一个运行时安全检查工具 RootKit种类与各个阶段的攻防LD_PRELOAD RootKitKernel Module RooKitKernel RootKit HidingKernel RootKit Hooksyscall table hookingfile operations hooking…

<数据结构>NO6.堆的实现|堆的应用

&#x1f407;本文用到的所有代码都放在我的gitee仓库了&#x1f407;syseptember的gitee仓库https://gitee.com/syseptember/data-structure/tree/4f0b1f9f56e3b0bee72fa0563c23a6917b3252e8/Heap/Heap 目录 堆的概念 堆的实现 堆的应用 堆排序 时间复杂度分析 TopK问题 …

做功能测试好几年,一直都像是给人“打杂”的!直到这天我开始……

“做功能测试这几年&#xff0c;一直都像是给人打杂的&#xff01;”这句话&#xff0c;想必很多测试人员都有过同感。曾经&#xff0c;我们每天要重复执行繁琐的测试流程&#xff0c;手动输入大量数据、进行各种操作、检查每一个细节&#xff0c;整个过程反复无常&#xff0c;…

用 AI 轻松管理数据收集和分布! #TallyForms

工作中需要面对各种各样让人眼花缭乱的表格&#x1f92f; 此时一款让数据收集和整理变得轻松的平台简直就是打工人们的福音&#xff0c;TallyForms 就拥有自定义表单、实时分析和高效管理的全方位解决方案&#xff01; TallyForms TallyForms 是一个免费的在线表单生成器&am…

[论文阅读] Explicit Visual Prompting for Low-Level Structure Segmentations

[论文地址] [代码] [CVPR 23] Abstract 我们考虑了检测图像中低层次结构的通用问题&#xff0c;其中包括分割被操纵的部分&#xff0c;识别失焦像素&#xff0c;分离阴影区域&#xff0c;以及检测隐藏的物体。每个问题通常都有一个特定领域的解决方案&#xff0c;我们表明&am…

高压放大器在大学教研领域的实际应用

在大学教研领域中&#xff0c;高压放大器可以用于多种实际应用。下面将介绍其中几个典型的应用场景。 1、激光切割 适用高校学院&#xff1a;机械学院 应用场景&#xff1a;机械制造、各类材料的切割 2、超声雾化 适用高校学院&#xff1a;医学院、机械学院、物理学院 应用场景…

C4D R26 渲染学习笔记(1):C4D版本选择和初始UI框介绍

C4D版本知识 C4D通过R来进行版本区分&#xff0c;现在2023年5月22日最新版的是R26。说一下特殊版本。 C4D版本介绍特点R19OC快乐版3.07最高版本&#xff0c;OC是C4D最具性价比的渲染器&#xff0c;OC学习成本低&#xff0c;渲染速度快&#xff0c;但是注意OC 3.07只支持10系N…

MySQL8.0数据库超详细安装教程全过程

1、官网下载MySQL8.0地址&#xff1a;MySQL :: Download MySQL Installer (Archived Versions) 2、 双击安装包进行安装 3、自定义安装 4、选择MySQL Server8.0 5、创建MYSQL数据存储目录及安装目录 6、配置安装路径及数据存储目录 7、确认继续 8、选择MySQL&#xff0c;下一步…

我4年测试,已失业3个月.....

我做测试4年&#xff0c;一线城市薪水拿到15K&#xff0c;中间还修了一个专升本&#xff0c;这个年限不说资深肯定也是配得上经验丰富的。今年行情不好人尽皆知&#xff0c;但我还是对我的薪水不是很满意&#xff0c;于是打算出去面试&#xff0c;希望可以搏一个高薪。 但真到面…

分布式项目 11 在项目中使用jsonp发送请求并且处理

在项目中使用jsonp技术 01.相关子系统的搭建 第一步&#xff1a;创建一个新的子系统&#xff0c;叫做jt-sso 选中jt父级项目&#xff0c;然后鼠标右键进行new&#xff0c;然后选中maven Model&#xff0c;进行项目的创建&#xff0c;具体操 作如下图所示&#xff1a; 第二步…

STM32单片机(三)第一节:GPIO输出

❤️ 专栏简介&#xff1a;本专栏记录了从零学习单片机的过程&#xff0c;其中包括51单片机和STM32单片机两部分&#xff1b;建议先学习51单片机&#xff0c;其是STM32等高级单片机的基础&#xff1b;这样再学习STM32时才能融会贯通。 ☀️ 专栏适用人群 &#xff1a;适用于想要…

5个超好用的在线抠图网站,告别繁琐的PS,快马住!

本期给大家推荐5个超好用的在线抠图网站&#xff0c;几秒钟就能搞定你的需求&#xff0c;告别繁琐的PS&#xff0c;重点是免费哦&#xff0c;赶紧收藏起来吧&#xff01; 1、菜鸟图库 https://www.sucai999.com/default/crx/removebg?vNTYwNDUx 菜鸟图库是一个专门为新手设计…

Go网络通信

Go中HTTP协议客户端实现 Go语言标准库内置了net/http包&#xff0c;涵盖了HTTP客户端和服务端具体的实现方式。内置的net/http包提供了最简洁的HTTP客户端实现方式&#xff0c;无须借助第三方网络通信库&#xff0c;就可以直接使用HTTP中用得最多的GET和POST方式请求数据。 fun…

一道很简单的面试题,怎么那么多人不会?

大厂面试题分享 面试题库 前后端面试题库 &#xff08;面试必备&#xff09; 推荐&#xff1a;★★★★★ 地址&#xff1a;前端面试题库 web前端面试题库 VS java后端面试题库大全 最近帮公司招聘&#xff0c;主要负责一面&#xff0c;所以基本上问的基础多一点。但是我在…

【云计算与虚拟化】第五章—— vCenter Server的高级功能

第五章—— vCenter Server的高级功能 1、在ESXi01中&#xff0c;新建一个资源池&#xff0c;命名为polo&#xff0c;将ESXi01上的虚拟机加入其中,CPU资源设置如下&#xff1a;份额正常&#xff0c;预留自定义&#xff0c;限制自定义&#xff0c;不可扩展预留&#xff0c;内存…

新版电脑微信怎么锁屏?不能升级到新版本又怎么锁?

#微信新增锁定功能# 许多小伙伴在工作或家里用电脑时都不想让别人私自看到自己微信的聊天记录&#xff0c;手机端还好&#xff0c;毕竟会自动锁屏&#xff0c;而且开屏也简单&#xff0c;只需指纹或人脸、手势就行&#xff0c;但电脑端就比较麻烦了&#xff0c;只要人一离开&a…