.

  • 一 .前言
  • 二 .代码相关
    • 2.1. 属性
    • 2.2. 构造方法
    • 2.3. 资源相关
    • 2.4. 获取属性相关(需要子类实现)
  • 三 .实现类
    • 3.1. PhysicalTransformation
    • 3.2. UnionTransformation
    • 3.3. PartitionTransformation
      • 3.3.1. 属性
      • 3.3.2. 构造方法
      • 3.3.3. 获取属性相关
      • 3.3.4. ShuffleMode
      • 3.3.5. StreamPartitioner
        • 3.3.5.1. IOReadableWritable 定义的方法清单 :
        • 3.3.5.2. ChannelSelector 定义的方法清单 :
        • 3.3.5.3. StreamPartitioner 定义的清单 :
      • 3.3.6. SubtaskStateMapper
    • 3.4. SideOutputTransformation
    • 3.5. CoFeedbackTransformation
  • 四 .PhysicalTransformation 子类
    • 4.1. OneInputTransformation
      • 4.1.1. 属性
      • 4.1.2. 构造方法
    • 4.2. TwoInputTransformation
    • 4.3. AbstractMultipleInputTransformation
    • 4.4. MultipleInputTransformation
    • 4.5. KeyedMultipleInputTransformation

一 .前言

{@code Transformation} 代表创建DataStream 的操作.
每个数据流都有一个底层的{@code Transformation},它是所述数据流的起源。

类似DataStream#map 这样的API操作在底层都会构建一个 {@code Transformation}s 树 .

当 stream 程序执行的时候都会将这个graph使用StreamGraphGenerator 生成一个StreamGraph

{@code Transformation}不一定对应于运行时的物理操作。

一些操作仅仅是逻辑上的概念

 Source              Source+                   +|                   |v                   vRebalance          HashPartition+                   +|                   ||                   |+------>Union<------++|vSplit+|vSelect+vMap+|vSink

将在运行时生成此操作图:

Source              Source+                   +|                   ||                   |+------->Map<-------++|vSink

partitioning, union, split/select 之类的操作最终都会被编码在连接map算子的edges中.

二 .代码相关

2.1. 属性

这里定义了Transformation id ,以及相关资源的属性定义…

// 32768// Has to be equal to StreamGraphGenerator.UPPER_BOUND_MAX_PARALLELISMpublic static final int UPPER_BOUND_MAX_PARALLELISM = 1 << 15;// 分配 唯一ID 使用// This is used to assign a unique ID to every Transformationprotected static Integer idCounter = 0;public static int getNewNodeId() {idCounter++;return idCounter;}protected final int id;protected String name;// 输出类型通过TypeInformation类封装,// 用来生成序列化用的serializers和比较大小用的comparators,以及进行一些类型检查。protected TypeInformation<T> outputType;// 用于处理MissingTypeInfo。//// This is used to handle MissingTypeInfo.//// As long as the outputType has not been queried// it can still be changed using setOutputType().//// Afterwards an exception is thrown when trying to change the output type.protected boolean typeUsed;// 并行度private int parallelism;/*** The maximum parallelism for this stream transformation.** It defines the upper limit for dynamic scaling and the number of key groups used for partitioned state.*/private int maxParallelism = -1;/*** The minimum resources for this stream transformation. It defines the lower limit for dynamic* resources resize in future plan.*/private ResourceSpec minResources = ResourceSpec.DEFAULT;/*** 此stream转换的首选资源。* 它定义了未来计划中动态资源调整的上限。*** The preferred resources for this stream transformation.** It defines the upper limit for dynamic resource resize in future plan.*/private ResourceSpec preferredResources = ResourceSpec.DEFAULT;/***** Each entry in this map represents a operator scope use case that this transformation needs managed memory for.** The keys indicate the use cases, while the values are the* use-case-specific weights for this transformation.*** Managed memory reserved for a use case* will be shared by all the declaring transformations within a slot according to this weight.*/private final Map<ManagedMemoryUseCase, Integer> managedMemoryOperatorScopeUseCaseWeights =  new HashMap<>();/** Slot scope use cases that this transformation needs managed memory for. */private final Set<ManagedMemoryUseCase> managedMemorySlotScopeUseCases = new HashSet<>();/*** transformation 指定的 User-specified ID* job重启时依旧使用相同的 operator ID.* 使用内部的 静态的counter 自动生成id ,** User-specified ID for this transformation. This is used to assign the same operator ID across* job restarts.** There is also the automatically generated {@link #id}, which is assigned from a static counter. That field is independent from this.*/private String uid;private String userProvidedNodeHash;// 超时相关..protected long bufferTimeout = -1;// slot sharing 组..private String slotSharingGroup;@Nullable private String coLocationGroupKey;

2.2. 构造方法

只有一个构造方法, 根据name , 输出类型, 并行度构建Transformation.

    /*** 根据name , 输出类型, 并行度构建Transformation* Creates a new {@code Transformation} with the given name, output type and parallelism.** @param name The name of the {@code Transformation}, this will be shown in Visualizations and*     the Log* @param outputType The output type of this {@code Transformation}* @param parallelism The parallelism of this {@code Transformation}*/public Transformation(String name, TypeInformation<T> outputType, int parallelism) {this.id = getNewNodeId();this.name = Preconditions.checkNotNull(name);this.outputType = outputType;this.parallelism = parallelism;this.slotSharingGroup = null;}

2.3. 资源相关

名称 含义
setResources(ResourceSpec minResources, ResourceSpec preferredResources) 为该 Transformation这是最小和首选的资源
declareManagedMemoryUseCaseAtOperatorScope 声明此转换包含特定的运算符作用域托管内存用例。
declareManagedMemoryUseCaseAtSlotScope 声明此转换包含特定的solt作用域托管内存用例。

其他的就不细说了,就是对属性的 set/get 操作…

2.4. 获取属性相关(需要子类实现)

名称 含义
getTransitivePredecessors 获取前置的Transformation
getInputs 获取输入的Transformation

三 .实现类

Transformation 的实现类很多,所以我们先看部分一级的实现类.

实现类 描述
PhysicalTransformation 创建物理操作,它启用设置{@link ChainingStrategy}
UnionTransformation 此转换表示多个输入{@link Transformation Transformations}的并集。
这不会创建物理操作,只会影响上游操作与下游操作的连接方式。
PartitionTransformation 此转换表示输入元素分区的更改。
这不会创建物理操作,只会影响上游操作与下游操作的连接方式。
SideOutputTransformation 此transformation表示对具有给定{@link OutputTag}的上游操作的边输出的选择。
CoFeedbackTransformation xxx
FeedbackTransformation xxx

3.1. PhysicalTransformation

创建物理操作,它启用设置{@link ChainingStrategy}

PhysicalTransformation 继承Transformation抽象类, 新增了一个 setChainingStrategy的方法, 通过该方法可以定义operator 的连接方式.

    /** Sets the chaining strategy of this {@code Transformation}. */public abstract void setChainingStrategy(ChainingStrategy strategy);
  • ChainingStrategy 是一个枚举类.

定义算子的链接方案。
当一个操作符链接到前置线程时,意味着它们在同一个线程中运行。
一个operator可以包含多个步骤.
StreamOperator使用的默认值是{@link#HEAD},这意味着operator没有链接到它的前一个operator。
大多数的operators 将会以 {@link #ALWAYS} 复写. 意味着他们会尽可能的chained 前置operator。

类型 描述
ALWAYS 算子会尽可能的Chain在一起(为了优化性能,最好是使用最大数量的chain和加大算子的并行度)
NEVER 当前算子不会与前置和后置算子进行Chain
HEAD 当前算子允许被后置算子Chain,但不会与前置算子进行Chain
HEAD_WITH_SOURCES 与HEAD类似,但此策略会尝试Chain Source算子

3.2. UnionTransformation

UnionTransformation是Transformation的子类. 表示多个输入{@link Transformation Transformations}的并集。 这不会创建物理操作,只会影响上游操作与下游操作的连接方式。

UnionTransformation 只有一个属性 . private final List<Transformation<T>> inputs; 通过UnionTransformation的构造方法传入.
传入的时候会依次验证输入的类型和输出的类型是否一致.

 /*** 通过给定的Transformations 构建一个UnionTransformation* Creates a new {@code UnionTransformation} from the given input {@code Transformations}.** <p>The input {@code Transformations} must all have the same type.** @param inputs The list of input {@code Transformations}*/public UnionTransformation(List<Transformation<T>> inputs) {super("Union", inputs.get(0).getOutputType(), inputs.get(0).getParallelism());for (Transformation<T> input : inputs) {if (!input.getOutputType().equals(getOutputType())) {throw new UnsupportedOperationException("Type mismatch in input " + input);}}this.inputs = Lists.newArrayList(inputs);}

实现了getInputs 方法和getTransitivePredecessors方法获取输入流…

    @Overridepublic List<Transformation<?>> getInputs() {return new ArrayList<>(inputs);}@Overridepublic List<Transformation<?>> getTransitivePredecessors() {List<Transformation<?>> result = Lists.newArrayList();result.add(this);for (Transformation<T> input : inputs) {result.addAll(input.getTransitivePredecessors());}return result;}

3.3. PartitionTransformation

PartitionTransformation是Transformation的子类.
此转换表示输入元素分区的更改。
它不会创建物理操作,它只会影响上游操作与下游操作的连接方式。

3.3.1. 属性

PartitionTransformation 有三个属性 :

    // 输入private final Transformation<T> input;// 分区器private final StreamPartitioner<T> partitioner;// ShuffleMode : 如果是流的话 应该是: PIPELINEDprivate final ShuffleMode shuffleMode;

3.3.2. 构造方法

根据输入和StreamPartitioner 生成一个PartitionTransformation

   /*** Creates a new {@code PartitionTransformation} from the given input and {@link* StreamPartitioner}.** @param input The input {@code Transformation}* @param partitioner The {@code StreamPartitioner}* @param shuffleMode The {@code ShuffleMode}*/public PartitionTransformation(Transformation<T> input, StreamPartitioner<T> partitioner, ShuffleMode shuffleMode) {super("Partition", input.getOutputType(), input.getParallelism());this.input = input;this.partitioner = partitioner;this.shuffleMode = checkNotNull(shuffleMode);}

3.3.3. 获取属性相关

剩下的就是获取分区器, ShuffleMode 以及 获取输入相关的方法了…

/*** Returns the {@code StreamPartitioner} that must be used for partitioning the elements of the* input {@code Transformation}.*/public StreamPartitioner<T> getPartitioner() {return partitioner;}/** Returns the {@link ShuffleMode} of this {@link PartitionTransformation}. */public ShuffleMode getShuffleMode() {return shuffleMode;}@Overridepublic List<Transformation<?>> getTransitivePredecessors() {List<Transformation<?>> result = Lists.newArrayList();result.add(this);result.addAll(input.getTransitivePredecessors());return result;}@Overridepublic List<Transformation<?>> getInputs() {return Collections.singletonList(input);}

3.3.4. ShuffleMode

ShuffleMode 定义了operators 之间交换数据的模式.
ShuffleMode 是一个枚举类,一共有三种.

名称 描述
PIPELINED 生产者和消费者同时在线. 生产出的数据立即会被消费者消费…
BATCH 生产者先产生数据至完成&停止. 之后 消费者启动消费数据.
UNDEFINED shuffle mode : 未定义 , 由框架决定 shuffle mode. 框架最后将选择{@link ShuffleMode#BATCH}或{@link ShuffleMode#PIPELINED}中的一个。

3.3.5. StreamPartitioner


根据上图可以获得信息. StreamPartitioner实现了ChannelSelector 接口.
ChannelSelector实现了IOReadableWritable 接口.
IOReadableWritable 里面只有read和write接口.
ChannelSelector对ChannelSelector接口进行了扩展.
新增了setup/selectChannel/isBroadcast 三个方法.

3.3.5.1. IOReadableWritable 定义的方法清单 :

每个类必须自行选择他们自己的二进制序列&反序列化方式.
特别是,records 必须实现此接口,以便指定如何将其数据传输到二进制表示形式。
实现此接口时,请确保实现类具有默认(无参数)构造函数!

名称 描述
void read(DataInputView in) throws IOException; 从给定的数据输入视图读取对象的内部数据。
void write(DataOutputView out) throws IOException; 将对象的内部数据写入给定的数据输出视图。

3.3.5.2. ChannelSelector 定义的方法清单 :

{@link ChannelSelector} 决定数据记录如何写入到 logical channels .

名称 描述
setup 设置 初始化 channel selector 的数量.
selectChannel 返回数据写入的 logical channel 的索引
为broadcast channel selectors 调用此方法是非法的,
在这种情况下,此方法可能无法实现(例如,通过抛出{@link UnsupportedOperationException})。
isBroadcast 返回channel selector是否始终选择所有输出通道

3.3.5.3. StreamPartitioner 定义的清单 :

StreamPartitioner 是一个抽象类, 实现了ChannelSelector 接口 .

里面只有一个属性 protected int numberOfChannels; 通过 setup(int numberOfChannels)方法来设置 channel的数量.
同时实现 isBroadcast方法返回的值为 false . 重写了 equals , hashCode 方法 .

新增的抽象方法 copy .

定义了 getUpstreamSubtaskStateMapper & getDownstreamSubtaskStateMapper 方法.

定义了恢复 in-flight 数据期间上游重新回放时此分区程序的行为。

    /*** Defines the behavior of this partitioner, when upstream rescaled during recovery of in-flight* data.*/public SubtaskStateMapper getUpstreamSubtaskStateMapper() {return SubtaskStateMapper.ARBITRARY;}/*** Defines the behavior of this partitioner, when downstream rescaled during recovery of* in-flight data.*/public abstract SubtaskStateMapper getDownstreamSubtaskStateMapper();

3.3.6. SubtaskStateMapper

SubtaskStateMapper是一个枚举类 .
{@code SubtaskStateMapper}缩小了回放期间需要读取的子任务,以便在检查点中存储了in-flight中的数据时从特定子任务恢复。

旧子任务到新子任务的映射可以是唯一的,也可以是非唯一的。

唯一分配意味着一个特定的旧子任务只分配给一个新的子任务。

非唯一分配需要向下筛选。
这意味着接收方必须交叉验证反序列化记录是否真正属于新的子任务。

大多数{@code SubtaskStateMapper}只会产生唯一的赋值,因此是最优的
一些重缩放器,比如{@link#RANGE},创建了唯一映射和非唯一映射的混合,其中下游任务需要对一些映射的子任务进行过滤。

名称 描述 分区器
ARBITRARY 额外的状态被重新分配到其他子任务,而没有任何特定的保证(只匹配上行和下行)。 超类: StreamPartitioner [getUpstreamSubtaskStateMapper]
DISCARD_EXTRA_STATE 丢弃额外状态。如果所有子任务都已包含相同的信息(广播),则非常有用。 BroadcastPartitioner [getUpstreamSubtaskStateMapper]
FIRST 将额外的子任务还原到第一个子任务。 GlobalPartitioner
FULL 将状态复制到所有子任务。这种回放会造成巨大的开销,完全依赖于对下游数据进行过滤。 CustomPartitionerWrapper
RANGE 将旧范围重新映射到新范围 KeyGroupStreamPartitioner
ROUND_ROBIN 以循环方式重新分配子任务状态。 ShufflePartitioner,
RebalancePartitioner,
ForwardPartitioner ,
RescalePartitioner,
BroadcastPartitioner[getDownstreamSubtaskStateMapper]

3.4. SideOutputTransformation

SideOutputTransformation是Transformation的子类.
此transformation表示对具有给定{@link OutputTag}的上游操作的边输出的选择。

这个类有两个属性 一个输入 private final Transformation<?> input;,一个输出 private final OutputTag<T> tag;

输入和输出的参数由SideOutputTransformation构造方法指定.

    public SideOutputTransformation(Transformation<?> input, final OutputTag<T> tag) {super("SideOutput", tag.getTypeInfo(), requireNonNull(input).getParallelism());this.input = input;this.tag = requireNonNull(tag);}
  • OutputTag类型

{@link OutputTag}是一个类型化和命名的标记,用于标记操作符的边输出。
{@code OutputTag}必须始终是匿名内部类,以便Flink可以为泛型类型参数派生一个{@link TypeInformation}。
示例:

OutputTag<Tuple2<String, Long>> info = new OutputTag<Tuple2<String, Long>>(“late-data”){};

  1. 属性
    只有两个属性,一个id , 另外一个是typeInfo.
    private final String id;private final TypeInformation<T> typeInfo;
  1. 构造方法

有两个构造方法

    /*** Creates a new named {@code OutputTag} with the given id.** @param id The id of the created {@code OutputTag}.*/public OutputTag(String id) {Preconditions.checkNotNull(id, "OutputTag id cannot be null.");Preconditions.checkArgument(!id.isEmpty(), "OutputTag id must not be empty.");this.id = id;try {this.typeInfo = TypeExtractor.createTypeInfo(this, OutputTag.class, getClass(), 0);} catch (InvalidTypesException e) {throw new InvalidTypesException("Could not determine TypeInformation for the OutputTag type. "+ "The most common reason is forgetting to make the OutputTag an anonymous inner class. "+ "It is also not possible to use generic type variables with OutputTags, such as 'Tuple2<A, B>'.",e);}}
    /*** Creates a new named {@code OutputTag} with the given id and output {@link TypeInformation}.** @param id The id of the created {@code OutputTag}.* @param typeInfo The {@code TypeInformation} for the side output.*/public OutputTag(String id, TypeInformation<T> typeInfo) {Preconditions.checkNotNull(id, "OutputTag id cannot be null.");Preconditions.checkArgument(!id.isEmpty(), "OutputTag id must not be empty.");this.id = id;this.typeInfo = Preconditions.checkNotNull(typeInfo, "TypeInformation cannot be null.");}

3.5. CoFeedbackTransformation

CoFeedbackTransformation的子类.

这表示拓扑中的反馈点。
feedback 元素不需要和 上有的{@code Transformation} 匹配.
因为仅仅允许{@code CoFeedbackTransformation}之后的操作是 {@link org.apache.flink.streaming.api.transformations.TwoInputTransformation TwoInputTransformations}.

上游{@code Transformation}将连接到Co-Transform 的第一个输入,而反馈 edges 将连接到第二个输入。

同时保留了输入边和反馈边的划分。它们也可以有不同的分区策略。
然而,这要求反馈{@code Transformation}的并行性必须与输入{@code Transformation}的并行度匹配。

上游{@code Transformation}未连接到此{@code CoFeedbackTransformation}。
而是直接连接到 {@code TwoInputTransformation} 之后的 {@code CoFeedbackTransformation}.
这与批处理中的迭代不同。

  1. 属性
    只有两个属性
    private final List<Transformation<F>> feedbackEdges;private final Long waitTime;
  1. addFeedbackEdge
    /*** Adds a feedback edge. The parallelism of the {@code Transformation} must match the* parallelism of the input {@code Transformation} of the upstream {@code Transformation}.** @param transform The new feedback {@code Transformation}.*/public void addFeedbackEdge(Transformation<F> transform) {if (transform.getParallelism() != this.getParallelism()) {throw new UnsupportedOperationException("Parallelism of the feedback stream must match the parallelism of the original"+ " stream. Parallelism of original stream: "+ this.getParallelism()+ "; parallelism of feedback stream: "+ transform.getParallelism());}feedbackEdges.add(transform);}

四 .PhysicalTransformation 子类

PhysicalTransformation 的子类有点多, 挑几个类型瞄一眼. 其他的等有空的时候再看…

4.1. OneInputTransformation

此转换表示将{@link org.apache.flink.streaming.api.operators.OneInputStreamOperator} 应用于一个输入 {@link Transformation}.

4.1.1. 属性

    private final Transformation<IN> input;private final StreamOperatorFactory<OUT> operatorFactory;private KeySelector<IN, ?> stateKeySelector;private TypeInformation<?> stateKeyType;

4.1.2. 构造方法

    public OneInputTransformation(Transformation<IN> input,String name,StreamOperatorFactory<OUT> operatorFactory,TypeInformation<OUT> outputType,int parallelism) {super(name, outputType, parallelism);this.input = input;this.operatorFactory = operatorFactory;}

4.2. TwoInputTransformation

两个输入一个输出操作…

/*** This Transformation represents the application of a {@link TwoInputStreamOperator} to two input* {@code Transformations}. The result is again only one stream.** @param <IN1> The type of the elements in the first input {@code Transformation}* @param <IN2> The type of the elements in the second input {@code Transformation}* @param <OUT> The type of the elements that result from this {@code TwoInputTransformation}*/

4.3. AbstractMultipleInputTransformation

AbstractMultipleInputTransformation抽象类…

/*** Base class for transformations representing the application of a {@link* org.apache.flink.streaming.api.operators.MultipleInputStreamOperator} to input {@code* Transformations}. The result is again only one stream.** @param <OUT> The type of the elements that result from this {@code MultipleInputTransformation}*/
  • 属性
    protected final List<Transformation<?>> inputs = new ArrayList<>();protected final StreamOperatorFactory<OUT> operatorFactory;

4.4. MultipleInputTransformation

MultipleInputTransformation 是AbstractMultipleInputTransformation 子类.


/** {@link AbstractMultipleInputTransformation} implementation for non-keyed streams. */
@Internal
public class MultipleInputTransformation<OUT> extends AbstractMultipleInputTransformation<OUT> {public MultipleInputTransformation(String name,StreamOperatorFactory<OUT> operatorFactory,TypeInformation<OUT> outputType,int parallelism) {super(name, operatorFactory, outputType, parallelism);}public MultipleInputTransformation<OUT> addInput(Transformation<?> input) {inputs.add(input);return this;}
}

4.5. KeyedMultipleInputTransformation

KeyedMultipleInputTransformation 是AbstractMultipleInputTransformation 子类.

  • 新增了属性
 private final List<KeySelector<?, ?>> stateKeySelectors = new ArrayList<>();protected final TypeInformation<?> stateKeyType;

Flink 1.12.2 源码浅析 : Transformation 浅析相关推荐

  1. 【flink】Flink 1.12.2 源码浅析 : Task数据输入

    1.概述 转载:Flink 1.12.2 源码浅析 : Task数据输入 在 Task 中,InputGate 是对输入的封装,InputGate 是和 JobGraph 中 JobEdge 一一对应 ...

  2. 【flink】Flink 1.12.2 源码浅析 :Task数据输出

    1.概述 转载:Flink 1.12.2 源码浅析 :Task数据输出 Stream的计算模型采用的是PUSH模式, 上游主动向下游推送数据, 上下游之间采用生产者-消费者模式, 下游收到数据触发计算 ...

  3. 【flink】Flink 1.12.2 源码浅析 : StreamTask 浅析

    1.概述 转载:Flink 1.12.2 源码浅析 : StreamTask 浅析 在Task类的doRun方法中, 首先会构建一个运行环境变量RuntimeEnvironment . 然后会调用lo ...

  4. 【flink】Flink 1.12.2 源码浅析 : Task 浅析

    1.概述 转载:Flink 1.12.2 源码浅析 : Task 浅析 Task 表示TaskManager上并行 subtask 的一次执行. Task封装了一个Flink operator(也可能 ...

  5. 【Flink】Flink 1.12.2 源码浅析 : TaskExecutor

    1.概述 转载:Flink 1.12.2 源码浅析 : TaskExecutor TaskExecutor 是TaskManger的具体实现. 二 .TaskExecutorGateway TaskE ...

  6. 【flink】Flink 1.12.2 源码浅析 : yarn-per-job模式解析 TaskMasger 启动

    1.概述 转载:Flink 1.12.2 源码浅析 : yarn-per-job模式解析 [四] 上一篇: [flink]Flink 1.12.2 源码浅析 : yarn-per-job模式解析 Jo ...

  7. 【flink】Flink 1.12.2 源码浅析 : yarn-per-job模式解析 JobMasger启动 YarnJobClusterEntrypoint

    1.概述 转载:Flink 1.12.2 源码浅析 : yarn-per-job模式解析 [三] 上一章:[flink]Flink 1.12.2 源码浅析 : yarn-per-job模式解析 yar ...

  8. 【flink】Flink 1.12.2 源码浅析 : yarn-per-job模式解析 yarn 提交过程解析

    1.概述 转载:Flink 1.12.2 源码浅析 : yarn-per-job模式解析 [二] 请大家看原文去. 接上文Flink 1.12.2 源码分析 : yarn-per-job模式浅析 [一 ...

  9. 【flink】Flink 1.12.2 源码浅析 : yarn-per-job模式解析 从脚本到主类

    1.概述 转载:Flink 1.12.2 源码浅析 : yarn-per-job模式解析 [一] 可以去看原文.这里是补充专栏.请看原文 2. 前言 主要针对yarn-per-job模式进行代码分析. ...

  10. Flink 1.12.2 源码浅析 : yarn-per-job模式解析 [二]

    . 一 .前言 二 .启动解析 2.1. StreamExecutionEnvironment#execute 2.2. StreamExecutionEnvironment#executeAsync ...

最新文章

  1. 【BZOJ】2120: 数颜色
  2. CloudCompare基础架构介绍(PPT)
  3. html怎么随机放图片,HTML技术:如何在网页中图片的随机显示
  4. 通过一个具体的例子学习Threadlocal Test
  5. Gartner:阿里云蝉联全球第三、亚太第一
  6. 怎样计算一个整数的位数并把每一位上的数字保存下来
  7. 【Java从0到架构师】Linux 应用 - 软件包管理、软件安装
  8. 一到软考网络工程师试题
  9. mysql gtid 集群_Docker搭建MySQL主从集群,基于GTID
  10. 拓端tecdat|R语言用AR,MA,ARIMA 模型进行时间序列预测
  11. 数学建模算法与应用(目录)
  12. c语言无法打开源文件stdafx.h,VS2013/2012/2010 下无法打开 源 文件“stdafx.h”的解决方法...
  13. 程序员的超大文件下载方法
  14. php论坛制作教程,Discuz论坛专题页面制作教程
  15. WP模板阁怎么样?能买吗
  16. [ASP.NET]web实现用FTP上传、下载文件(附源码)
  17. dfs-全排列(UPC-方案数)
  18. C/C++ 链 表 头插法 尾插法
  19. Dynamic CRM9.0 环境安装部署手册步骤和遇到的一些问题解决方案(包含ADFS部署)
  20. 面对找不到工作的困难,小伙选择创业开酒吧,月收入竟达到了6万

热门文章

  1. Bmob后端云实现无后端开发APP
  2. python3 socket TCP 服务器 一对多转发信息
  3. wow服务器维护通告,新一轮大服务器实装公告:4月2日凌晨维护
  4. workman 搭建tcp服务器,和websocket互相通信
  5. 等到花儿也谢了的await
  6. 计算机考证决心书怎样写
  7. Altium Designer16 精心总结
  8. SP2-0734: unknown command beginning lsnrctl st... - rest of line ignored.
  9. 【ESP】乐鑫系列物联网芯片
  10. css3图片放大溢出,用canvas调整图像大小 - css溢出问题