Flink

DataFlow

数据的分类

  • 有界数据

    • 离线的计算

      • 效率:硬件相同的情况下:时间
  • 无界数据

    • 实时的计算

      • 效率:硬件环境相同的情况下,吞吐量:数据处理的数量、延迟:结果输出的时间—数据接收的时间

状态

  • 有状态
  • 无状态

窗口函数

  • 滚动窗口

    • 数量–时间
    • 不重复
  • 滑动窗口

    • 可以重复
    • 滑动的间隔–窗口的宽度
  • 会话窗口

    • 不会重复
    • 基于某一个用户的未操作时间

时间

  • 事件时间
  • 到达时间
  • 处理时间

Flink简介

大数据计算引擎

  • 第一代: MapReducer

    • 批处理:Mapper, Reducer
    • Hadoop的MapReducer将计算分为两个阶段, 分别为Map和Reducer. 对于上层应用来说, 就不得不想方设法去拆分算法, 甚至于不得不在上层应用实现多个Job的串联, 以完成一个完整的算法, 例如迭代计算.
  • 第二代: DAG框架 (Tez) + MapReducer

    • 批处理 1个Tez = MR (1) + MR (2) + … + MR (n) 相比MR效率有所提升
  • 第三代: Spark

    • 批处理, 流处理, SQL高层API支持 自带DAG 内存迭代计算, 性能较之前大幅提升
  • 第四代: Flink

    • 批处理, 流处理, SQL高层API支持 自带DAG 流式计算性能更高, 可靠性更高

介绍

  • 2014 年 Flink 作为主攻流计算的大数据引擎开始在开源大数据行业内崭露头角。区别于Storm、
    Spark Streaming 以及其他流式计算引擎的是:它不仅是一个高吞吐、低延迟的计算引擎,同时还提供很多高级的功能。比如它提供了有状态的计算,支持状态管理,支持强一致性的数据语义以及支持 基于Event Time的WaterMark对延迟或乱序的数据进行处理等

特点

  • 分布式、高性能、随时可用以及准确的流处理应用程序打造的开源流处理框架

  • 框架和分布式处理引擎,用于对无界和有界数据流进行有状态计算。Flink 被设计在所有常见的集群环境中运行,以内存执行速度和任意规模来执行计算。

  • Flink流处理的特点

    • 同时支持高吞吐,低延迟,高性能

    • 支持事件时间(Event Time) 概念

      • Process TIme,大多数框架窗口计算采用的都是系统时间(process time),也是事件传
        输到计算框架处理时,系统主机的当前时间。
      • Event Time,Flink支持基于事件时间(Event Time)语义进行窗口计算,也就是使用事
        件产生的时间,这种基于事件驱动的机制使得事件即使乱序到达,流系统也能够计算出
        精确的结果,保持了事件原本产生时的时序性,尽可能避免网络传输或硬件系统的影
        响。
    • 支持有状态计算

    • 支持高度灵活的窗口(Window)操作。

    • 基于轻量级的分布式快照(CheckPoint)来实现容错

    • 基于JVM实现独立的内存管理

    • Save Point保存点

      • CheckPoint是自动的,做容错。Save Point是手动的,处理程序升级。

Flink数据分类

  • 有界数据

    • 具有起始时间和截止时间。它可以在执行任何的计算之前,先通过摄取所有数据后再来处理有
      界流。处理有界流不需要有序摄取,因为可以对有界数据集进行排序。有界流的处理也称为批
      处理。

    • 特点

      • 有定义流的开始,也有定义流的结束。
      • 有界流可以在摄取所有数据后再进行计算。
      • 有界流所有数据可以被排序,所以并不需要有序摄取。
      • 有界流处理通常被称为批处理
  • 无界数据

    • 它有开始时间但没有截止时间,它们在生成时提供数据,但不会被终止。无界流必须连续处理
      数据,即必须在摄取事件后立即处理事件。它无法等待所有输入数据到达,因为输入是无界
      的,如果是这样,在任何时间点都不会完成。处理无界数据通常要求以特定顺序摄取事件,例
      如事件发生的顺序,以便能够推断结果完整性。

    • 特点

      • 有定义流的开始,但没有定义流的结束。它们会无休止地产生数据。
      • 无界流的数据必须持续处理,即数据被摄取后需要立刻处理。我们不能等到所有数据都到达再处理,因为输入是无限的,在任何时候输入都不会完成。
      • 处理无界数据通常要求以特定顺序摄取事件,例如事件发生的顺序,以便能够推断结果的完整性。
    • Flink 擅长精确的时间控制和状态化,使得 Flink 的运行时(runtime)能够运行任何处理无界流的应用。

性能对比

  • Flink是一行一行处理,而SparkStream是基于数据片集合(RDD)进行小批量处理,所以Spark在流式处理方面,不可避免增加一些延时。
  • Flink的流式计算跟Storm性能差不多,支持毫秒级计算,而Spark则只能支持秒级计算。
  • Spark和Flink全部都运行在Hadoop Yarn上, 性能为Flink > Spark > Hadoop (MR) , 迭代次数越多越明显.
  • 性能上, Flink优于Spark和Hadoop最主要的原因是Flink支持增量迭代, 具有对迭代自动优化的功能.

Flink的编程模型

Api分层

  • SQL&Table API

    • SQL 构建在Table 之上,都需要构建Table 环境。
    • 不同的类型的Table 构建不同的Table 环境中。
    • Table 可以与DataStream或者DataSet进行相互转换。
    • Streaming SQL不同于存储的SQL,最终会转化为流式执行计划
  • DataStream/DataSet API

    • ProcessFunction多了一些算子。DataStream API为许多通用的流处理操作提供了原语。
    • DataSet API 是批处理API,处理有限的数据集。
    • DataStream API是流处理API,处理无限的数据集。
  • Stateful Stream processing

    • ProcessFunction是Flink最底层的接口。
    • ProcessFunction可以处理一或者两条输入数据流中的单个事件或者归入一个特定窗口内的多个事件。
    • 它提供了对时间和状态的细粒度控制。
    • 虽然灵活性高,但开发比较复杂,需要具备一定的编码能力。
  • 扩展库

    • 复杂事件处理CEP,Gelly做图计算的,是一个可扩展的图形处理和分析库。

程序与数据流

  • 每个flink程序由source operator + transformation operator + sink operator组成
  • Flink程序的基本构建是在流和转换操作上的, 执行时,Flink程序映射到流数据上,由流和转换运算符组成。每个数据流都以一个或多个源开头,并以一个或多个接收器结束。数据流类似于任意有向无环图 (DAG)

集群架构

架构模型

  • Flink运行时架构主要包括四个不同的组件,它们会在运行流处理应用程序时协同工作:

    • 作业管理器(JobManager)
    • 资源管理器(ResourceManager)
    • 任务管理器(TaskManager)
    • 分发器(Dispatcher)

角色分配

  • JobManager

    • 管理任务,申请资源(slot插槽)
  • ResourceManager

    • 管理资源,掌握各个节点当前主机的情况
  • TaskManager

    • 管理任务,申请资源
  • Dispatcher

    • 提供Flink外部访问接口,用于接受客户端的请求

集群搭建

Standalone模式

FlinkOnYarn模式

  • Yarn-Session模式

    • 所有的Flink任务共用一个资源,当开启一个新的Job的时候,开始分配Container
  • Single-job模式

    • 每次Job都会形成一个新的yarn任务,任务和任务之间相互独立

TaskSlot与Parallelism

逻辑执行计划图

  • 查看执行图

  • 图形解释

    • Flink 中的执行图可以分成四层:StreamGraph -> JobGraph -> ExecutionGraph -> 物理执行图。

      • StreamGraph:

        • 根据用户通过 Stream API 编写的代码生成的最初的图。用来表示程序的拓扑结构。
      • JobGraph:

        • StreamGraph经过优化后生成了 JobGraph,提交给 JobManager 的数据结构。主要的优化为,将多个符合条件的节点 chain 在一起作为一个节点,这样可以减少数据在节点之间流动所需要的序列化/反序列化/传输消耗。
      • ExecutionGraph:

        • JobManager 根据 JobGraph 生成ExecutionGraph。
        • ExecutionGraph是JobGraph的并行化版本,是调度层最核心的数据结构。
      • 物理执行图:

        • JobManager 根据 ExecutionGraph 对 Job 进行调度后,在各个TaskManager 上部署Task 后形成的“图”
        • 并不是一个具体的数据结构。

Slot与Task

  • 一个TaskManager可以同时执行多个任务(tasks)。

    • 这些任务可以是同一个算子的子任务(数据并行)
    • 这些任务可以是来自不同算子(任务并行)
    • 这些任务可以是另一个不同应用程序(作业并行)。
  • TaskManager提供了一定数量的处理插槽(processing slots),用于控制可以并行执行的任务数。

  • 一个slot可以执行应用的一个分片,也就是应用中每一个算子的一个并行任务。

  • Slot对资源的隔离仅仅是对内存进行隔离,策略是均分

    • 比如taskmanager的管理内存是3GB,假如有两个slot,那么每个slot就仅仅有1.5GB内存可用。
  • 插槽的数量通常与每个TaskManager的可用CPU内核数成比例。一般情况下你的slot数是你每个TM的cpu的核数

Flink任务并行度

  • Flink程序由多个任务(转换/运算符,数据源和接收器)组成,Flink中的程序本质上是并行和分布式的。
  • 在执行期间,流具有一个或多个流分区,并且每个operator具有一个或多个operator子任务。
  • operator子任务彼此独立,并且可以在不同的线程中执行,这些线程又可能在不同的机器或容器上执行。
  • operator子任务的数量是该特定operator的并行度。同一程序的不同operator可能具有不同的并行度。

任务并行度设置

  • 算子级别

    • setParallelism()
  • 执行环境级别

  • 客户端级别

  • 系统级别

Operator Chains(操作链)

  • Flink出于分布式执行的目的,将operator的subtask链接在一起形成task。

  • 每个task在一个线程中执行。

  • 将operators链接成task是非常有效的优化:

    • 它可以减少线程与线程间的切换和数据缓冲的开销,并在降低延迟的同时提高整体吞吐量。
  • 链接的行为可以在编程API中进行指定开启操作链(默认) 和 禁用操作链的

窗口机制

滑动窗口

  • 每隔一段驱动后,取出一个窗口大小的数据
  • 有两个参数:间隔和窗口大小

翻滚窗口

  • 上一个窗口紧挨着下一个窗口数据无重叠,数据无间隙

会话窗口

  • 基于某一会话间隔时间

全局窗口

  • 所有的数据囊括进去

window窗口的聚合函数

  • 增量聚合函数
  • 全量窗口函数

Time与WaterMark

Time(时间)

  • Processing time(处理时间)

    • 处理时间是指当前机器处理该条事件的时间。
    • 它是当数据流入到具体某个算子时候相应的系统时间。他提供了最小的延时和最佳的性能。但是在分布式和异步环境中,处理时间不能提供确定性,因为它对事件到达系统的速度和数据流在系统的各个operator之间处理的速度很敏感。
  • Event Time(事件时间)

    • 事件时间是每个事件在其生产设备上发生的时间。此时间通常在进入Flink之前嵌入到记录中,并且可以从每个记录中提取该事件时间戳。事件时间对于乱序、延时、或者数据重放等情况,都能给出正确的结间水位线(wartermark),这是指示事件时间进度的机制。水位线机制在后面部分中会描述。
    • 果。事件时间依赖于事件本身,而跟物理时钟没有关系。基于事件时间的程序必须指定如何生成事件时事件时间处理通常存在一定的延时,因此需要为延时和无序的事件等待一段时间。因此,使用事件时间编程通常需要与处理时间相结合。
  • Ingestion time(摄入时间)

    • 摄入时间是数据进入Flink框架的时间,是在Source Operator中设置的。ProcessingTime相比可以提供更可预测的结果,因为摄入时间的时间戳比较稳定(在源处只记录一次),同一数据在流经不同窗口操作时将使用相同的时间戳,而对于ProcessingTime同一数据在流经不同窗口算子会有不同的处理时间戳。
  • 设置时间特性

    • streamEnv.setStreamTimeCharacteristic(TimeCharacteristic.EventTime)

WaterMark(水位线)

Watermark的产生方式

  • 其中一种方式为在数据源完成的,即利用SourceFunction在应用读入数据流的时候分配时间戳与水位线。

  • 另一种方式是通过实现接口的自定义函数,该方式又包括两种实现方式:

    • 一种为周期性生成水位线,即实现AssignerWithPeriodicWatermarks接口,周期性的生成水位线
    • 另一种为定点生成水位线,即实AssignerWithPunctuatedWatermarks接口。每条数据都会产生水位线

Watermark的迟到的数据

  • 现实中很难生成一个完美的水位线,水位线就是在延迟与准确性之前做的一种权衡。那么,如果生成的水位线过于紧迫,即水位线可能会大于后来数据的时间戳,这就意味着数据有延迟,关于延迟数据的处理,Flink提供了一些机制,具体如下:

    • 直接将迟到的数据丢弃
    • 将迟到的数据输出到单独的数据流中,即使用sideOutputLateData(new OutputTag<>())实现侧输出
    • 根据迟到的事件更新并发出结果

Flink数据源Operator

基于文件

  • readTextFile(path) - 读取text文件的数据
  • readFile(fileInputFormat, path) - 通过自定义的读取方式,来读取文件的数据

基于socket

  • socketTextStream 从socket端口中读取数据

基于集合

  • fromCollection(Collection) - 从collection集合里读取数据,从而形成一个数据流,集合里的元素类型需要一致
  • fromElements(T …) - 从数组里读取数据,从而形成一个数据流,集合里的元素类型需要一致。
  • generateSequence(from, to) - 创建一个数据流,数据源里的数据从from到to的数字。

自定义source

  • addSource - 自定义一个数据源,比如FlinkKafkaConsumer,从kafka里读数据。

Flink转换操作Operator

流的分类

  • 基本转换算子

    • map
    • flatmap
    • filter
  • 键控流转换算子

    • keyby

      • 滚动聚合

        • sum():在输入流上对指定的字段做滚动相加操作。
        • min():在输入流上对指定的字段求最小值。
        • max():在输入流上对指定的字段求最大值。
        • minBy():在输入流上针对指定字段求最小值,并返回包含当前观察到的最小值的事件。
        • maxBy():在输入流上针对指定字段求最大值,并返回包含当前观察到的最大值的事件。
      • Reduce

        • 滚动聚合的泛化实现
  • 多流转换算子

    • union
    • connect
  • 分布式转换算子

    • shuffle

      • 随机
    • rebalance

      • 负载均衡
    • rescale

      • 将数据发送到接下来的并行运行的任务中的一部分任务中
    • broadcast()

      • 将输入流的所有数据复制并发送到下游算子的所有并行任务中去。
    • global()

      • 将所有的输入流数据都发送到下游算子的第一个并行任务中去。这个操作需要很谨慎,因为将所有数据发送到同一个task,将会对应用程序造成很大的压力。

数据类型

类型

  • Primitives(原始数据类型)

  • Java和Scala的Tuples(元组)

  • Scala的样例类

  • POJO类型

    • 公有类
    • 无参数的公有构造器
    • 所有的字段都是公有的,可以通过getters和setters访问。
    • 所有字段的数据类型都必须是Flink支持的数据类型。
    • Types.POJO(Person.class);
  • 一些特殊的类型

UDF函数

函数类

  • Flink暴露了所有udf函数的接口(实现方式为接口或者抽象类)。例如MapFunction, FilterFunction,ProcessFunction等等。

富函数

  • DataStream API提供的所有转换操作函数,都拥有它们的“富”版本,并且我们在使用常规函数或者匿名函数的地方来使用富函数。

    • RichMapFunction
    • RichFlatMapFunction
    • RichFilterFunction
  • 使用富函数时可以实现额外的方法:

    • open()方法是rich function的初始化方法

      • 当一个算子例如map或者filter被调用之前open()会被调用。
      • open()函数通常用来做一些只需要做一次即可的初始化工作。
    • close()方法是生命周期中的最后一个调用的方法

      • 通常用来做一些清理工作。
    • getRuntimeContext()方法提供了函数的RuntimeContext的一些信息

      • 例如函数执行的并行度,当前子任务的索引,当前子任务的名字。同时还它还包含了访问分区状态的方法

Flink输出数据

writeAsText() // 将计算结果输出成text文件

writeAsCsv(…) // 将计算结果输出成csv文件

print() // 将计算结果打印到控制台

writeUsingOutputFormat() //自定义输出方式。

writeToSocket // 将计算结果输出到某台机器的端口上。

状态

某个算子或者key当前的状态,因为处理的实时数据,我们需要对这个状态进行操作或者管理

分类

  • 算子的类型

    • operator State

      • ListCheckPoint
      • snapshotState–拍摄快照
      • restartState–恢复快照拍摄的数据
    • Keyed State

      • 实现Rich Function
  • 算例的管理

    • managed state

      • flink管理
    • raw state

      • 自己管理

检查点

  • 保存算子的当前状态,当集群出现故障的时候可以进行恢复,默认情况下需要手动开启,flink可以恢复检查点的数据,数据源也可以重复被消费,在流式计算中,算子的状态非常宝贵,因为有的状态很难进行重现,分布式快照:集群中的算子可以并行的去拍摄快照,不需要等待一起去拍

    • 栅栏barriers

恢复Recovery

  • Flink恢复时的机制是十分直接的:在系统失效时,Flink选择最近的已完成的检查点k,系统接下来重部署整个数据流图,然后给每Operator在检查点k时的相应状态。数据源则被设置为从数据流的Sk位置开始读取。例如,在Apache Kafka执行恢复时,系统会通知消费者从偏移Sk开始获取数据

连接器

Flink 内置了一些基本数据源(source)和接收器(sink),除了这些之外,除此之外它还提供了其他的连接器用于与各种第三方系统进行连接。目前支持如下系统的连接:

  • Apache Kafka (source/sink)
  • Elasticsearch (sink)
  • Hadoop FileSystem (sink)
  • RabbitMQ (source/sink)
  • Apache NiFi (source/sink)
  • Apache Cassandra (sink)
  • Amazon Kinesis Streams (source/sink)
  • Twitter Streaming API (source)

Flink Table & SQL API

TableEnvironment

TableAPI语法

  • Scan, Projection, and Filter
  • Column Operations
  • Aggregations
  • Joins
  • Set Operations
  • OrderBy, Offset & Fetch
  • Insert
  • Window

FlinkSQL 语法

CEP

一个或多个由简单事件构成的事件流通过一定的规则匹配,然后输出用户想得到的数据,满足规则的复杂事件。

特征

  • 目标:从有序的简单事件流中发现一些高阶特征
  • 输入:一个或多个由简单事件构成的事件流
  • 处理:识别简单事件之间的内在联系,多个符合一定规则的简单事件构成复杂事件
  • 输出:满足规则的复杂事件

Pattern API

  • 单个模式

    • 一个模式可以是一个单例或者循环模式。

      • 单例模式只接受一个事件
      • 循环模式可以接受多个事件。
    • 在FlinkCEP中,你可以通过这些方法指定循环模式

      • pattern.oneOrMore() ,指定期望一个给定事件出现一次或者多次的模式
      • pattern.times(#ofTimes) ,指定期望一个给定事件出现特定次数的模式
      • pattern.times(#fromTimes, #toTimes) ,指定期望一个给定事件出现次数在一个最小值和最大值中间的模式
      • pattern.greedy() 方法让循环模式变成贪心的
    • 条件

      • 对每个模式你可以指定一个条件来决定一个进来的事件是否被接受进入这个模式
      • 指定判断事件属性的条件可以通过 pattern.where() 、 pattern.or() 或者pattern.until() 方法
      • IterativeCondition 或者 SimpleCondition 。
  • 组合模式

    • 连续策略

        1. 严格连续: 期望所有匹配的事件严格的一个接一个出现,中间没有任何不匹配的事件。
        1. 松散连续: 忽略匹配的事件之间的不匹配的事件。
        1. 不确定的松散连续: 更进一步的松散连续,允许忽略掉一些匹配事件的附加匹配。
    • 指定模式之间的连续策略的方法

        1. next() ,指定严格连续,
        1. followedBy() ,指定松散连续,
        1. followedByAny() ,指定不确定的松散连续。
        1. notNext() ,如果不想后面直接连着一个特定事件
        1. notFollowedBy() ,如果不想一个特定事件发生在两个事件之间的任何地方。
        1. pattern.within() 方法指定一个模式应该在10秒内发生
  • 循环模式

    • oneOrMore() 和 times() ,默认是松散连续。如果想使用严格连续,你需要使用 consecutive()
      方法明确指定, 如果想使用不确定松散连续,你可以使用 allowCombinations() 方法。
  • 匹配后跳过策略

    • AfterMatchSkipStrategy

      • NO_SKIP

        • 所有成功的匹配结果都会被保留
      • SKIP_TO_NEXT

        • 丢弃所有的子集
      • SKIP_PAST_LAST_EVENT

        • 每种开头相同的保留一个
      • SKIP_TO_FIRST

        • 只保留第一个开头的所有组合
      • SKIP_TO_LAST

        • 用上个模式的结尾作为本次匹配的开头
  • 检测模式

    • 在指定了要寻找的模式后,该把它们应用到输入流上来发现可能的匹配了。为了在事件流上运行你的模式,需要创建一个 PatternStream 。 给定一个输入流 input ,一个模式 pattern 和一个可选的用来对使用事件时间时有同样时间戳或者同时到达的事件进行排序的比较器 comparator
  • CEP的时间处理

Flink数据反压

1.5之前

1.5之后

大数据之Flink流式计算引擎相关推荐

  1. Flink系列之Flink流式计算引擎基础理论

    声明:         文章中代码及相关语句为自己根据相应理解编写,文章中出现的相关图片为自己实践中的截图和相关技术对应的图片,若有相关异议,请联系删除.感谢.转载请注明出处,感谢. By luoye ...

  2. 为什么阿里会选择 Flink 作为新一代流式计算引擎?

    本文由 [AI前线]原创,ID:ai-front,原文链接:t.cn/ROISIr3 [AI前线导读]2017 年 10 月 19日,阿里巴巴的高级技术专家王绍翾(花名"大沙")将 ...

  3. Flink流式计算从入门到实战 一

    文章目录 一.理解Flink与流计算 1.初识Flink 2.Flink的适用场景 3.流式计算梳理 二.Flink安装部署 1.Flink的部署方式 2.获取Flink 3.实验环境与前置软件 4. ...

  4. Flink 流式计算在节省资源方面的简单分析

    本文由小米的王加胜同学分享,文章介绍了 Apache Flink 在小米的发展,从 Spark Streaming 迁移到 Flink ,在调度计算与调度数据.Mini batch 与 streami ...

  5. Flink流式计算从入门到实战 三

    文章目录 四.Flink DataStream API 1.Flink程序的基础运行模型 2.Environment 运行环境 3.Source 3.1 基于File的数据源 3.2 基于Socket ...

  6. TDengine3.0流式计算引擎语法规则介绍

    小 T 导读:TDengine 3.0 引入了全新的流式计算引擎,既支持时间驱动的流式计算,也支持事件驱动的流式计算.本文将对新的流式计算引擎的语法规则进行详细介绍,方便开发者及企业使用. TDeng ...

  7. Flink流式计算从入门到实战 四

    文章目录 六.Flink Table API 和Flink SQL 1.Table API和SQL是什么? 2.如何使用Table API 3.基础编程框架 3.1 创建TableEnvironmen ...

  8. Flink流式计算从入门到实战 二

    文章目录 三.Flink运行架构 1.JobManager和TaskManager 2.并发度与Slots 3.开发环境搭建 4.提交到集群执行 5.并行度分析 6.Flink整体运行流程 Flink ...

  9. 大数据准实时流式系统设计(一)——基于大数据框架设计

    前段时间负责了公司一个新的项目,项目不属于直接面向用户的线上实时响应系统,要求做到尽快毫秒级或者秒级响应的准实时系统.结合以前学习的一些大数据理论方面和参与的准实时系统方面的经验,对准实时系统架构设计 ...

最新文章

  1. 如何选择合适的分布式机器学习平台
  2. layui获取checkbox选中值_小程序之十二 获取多选按钮数值及后续想法
  3. 多学一点(五)——在Linux下安装配置Apache
  4. Groovy 教程-- Groovy 入门
  5. log4j的日志级别以及配置
  6. 要重复多少次变成潜意识_速读记忆相关:量变到质变,首先要做的是不断重复...
  7. 学习LSL:Locate-Globally-Segment-locally
  8. 方差分析——单因素方差分析
  9. 康熙不愧大帝——且看他是怎样交班的
  10. 7-36 大炮打蚊子(15 分)
  11. EPICS数据通过MQTT物联网协议上云
  12. 跨时钟域同步-结绳法
  13. C语言 输入一行字符,分别统计出其中英文字母、空格、数字和其他字符的个数
  14. 在面试软件测试工作时如何巧妙提出加薪要求,你确定不看嘛?
  15. 【302】302 Found 如何解决???
  16. 第一次面试实习-Web前端开发
  17. PSD 格式文件除了 PS ,还能用什么软件打开?
  18. 嵌入式Linux启动流程
  19. java 单选题_JAVA单选题参考题库
  20. LeetCode Word Break II

热门文章

  1. Word转PDF,PNG,HTML神器XDOC
  2. c语言程序while,C语言之while循环
  3. win11什么时候发布的_2021京考公告什么时候发布?报考时需要避开哪些热门职位?...
  4. springcloud解决运行出现Failed to determine a suitable driver class
  5. 大数据平台ambari
  6. 路由器WAN口和LAN口的区别
  7. 【Gradle】Gradle配置全局阿里云镜像仓库
  8. adb查看和设置时间
  9. gin集成支付宝支付
  10. php怎么获取网页内的视频教程,PHP怎样用正则抓取页面中的网址