众所周知,flink作为流计算引擎,处理源源不断的数据是其本意,但是在处理数据的过程中,往往可能需要一些参数的传递,那么有哪些方法进行参数的传递?在什么时候使用?这里尝试进行简单的总结。

  • 使用configuration

  在main函数中定义变量

  1. // Class in Flink to store parameters
  2. Configuration configuration = new Configuration();
  3. configuration.setString("genre", "Action");
  4.  
  5. lines.filter(new FilterGenreWithParameters())
  6. // Pass parameters to a function
  7. .withParameters(configuration)
  8. .print();

  使用参数的function需要继承自一个rich的function,这样才可以在open方法中获取相应的参数。

  1. class FilterGenreWithParameters extends RichFilterFunction<Tuple3<Long, String, String>> {
  2.  
  3. String genre;
  4.  
  5. @Override
  6. public void open(Configuration parameters) throws Exception {
  7. // Read the parameter
  8. genre = parameters.getString("genre", "");
  9. }
  10.  
  11. @Override
  12. public boolean filter(Tuple3<Long, String, String> movie) throws Exception {
  13. String[] genres = movie.f2.split("\\|");
  14.  
  15. return Stream.of(genres).anyMatch(g -> g.equals(genre));
  16. }
  17. }
  • 使用ParameterTool

使用configuration虽然传递了参数,但显然不够动态,每次参数改变,都涉及到程序的变更,既然main函数能够接受参数,flink自然也提供了相应的承接的机制,即ParameterTool。

如果使用ParameterTool,则在参数传递上如下

  1. public static void main(String... args) {
  2. // Read command line arguments
  3. ParameterTool parameterTool = ParameterTool.fromArgs(args);
  4.  
  5. final ExecutionEnvironment env = ExecutionEnvironment.getExecutionEnvironment();
  6. env.getConfig().setGlobalJobParameters(parameterTool);
  7. ...
  8.  
  9. // This function will be able to read these global parameters
  10. lines.filter(new FilterGenreWithGlobalEnv())
  11. .print();
  12. }

如上面代码,使用parameterTool来承接main函数的参数,通过env来设置全局变量来进行分发,那么在继承了rich函数的逻辑中就可以使用这个全局参数。

  1. class FilterGenreWithGlobalEnv extends RichFilterFunction<Tuple3<Long, String, String>> {
  2.  
  3. @Override
  4. public boolean filter(Tuple3<Long, String, String> movie) throws Exception {
  5. String[] genres = movie.f2.split("\\|");
  6. // Get global parameters
  7. ParameterTool parameterTool = (ParameterTool) getRuntimeContext().getExecutionConfig().getGlobalJobParameters();
  8. // Read parameter
  9. String genre = parameterTool.get("genre");
  10.  
  11. return Stream.of(genres).anyMatch(g -> g.equals(genre));
  12. }
  13. }
  • 使用broadcast变量

在上面使用configuration和parametertool进行参数传递会很方便,但是也仅仅适用于少量参数的传递,如果有比较大量的数据传递,flink则提供了另外的方式来进行,其中之一即是broadcast,这个也是在其他计算引擎中广泛使用的方法之一。

  1. DataSet<Integer> toBroadcast = env.fromElements(1, 2, 3);
  2. // Get a dataset with words to ignore
  3. DataSet<String> wordsToIgnore = ...
  4.  
  5. data.map(new RichFlatMapFunction<String, String>() {
  6.  
  7. // A collection to store words. This will be stored in memory
  8. // of a task manager
  9. Collection<String> wordsToIgnore;
  10.  
  11. @Override
  12. public void open(Configuration parameters) throws Exception {
  13. // Read a collection of words to ignore
  14. wordsToIgnore = getRuntimeContext().getBroadcastVariable("wordsToIgnore");
  15. }
  16.  
  17. @Override
  18. public String map(String line, Collector<String> out) throws Exception {
  19. String[] words = line.split("\\W+");
  20. for (String word : words)
  21. // Use the collection of words to ignore
  22. if (wordsToIgnore.contains(word))
  23. out.collect(new Tuple2<>(word, 1));
  24. }
  25. // Pass a dataset via a broadcast variable
  26. }).withBroadcastSet(wordsToIgnore, "wordsToIgnore");

在第3行定义了需要进行广播的数据集,在第27行指定了将此数据集进行广播的目的地。

广播的变量会保存在tm的内存中,这个也必然会使用tm有限的内存空间,也因此不能广播太大量的数据。

那么,对于数据量更大的广播需要,要如何进行?flink也提供了缓存文件的机制,如下。

  • 使用distributedCache

首先还是需要在定义dag图的时候指定缓存文件:

  1. ExecutionEnvironment env = ExecutionEnvironment.getExecutionEnvironment();
  2.  
  3. // Register a file from HDFS
  4. env.registerCachedFile("hdfs:///path/to/file", "machineLearningModel")
  5.  
  6. ...
  7.  
  8. env.execute()

flink本身支持指定本地的缓存文件,但一般而言,建议指定分布式存储比如hdfs上的文件,并为其指定一个名称。

使用起来也很简单,在rich函数的open方法中进行获取。

  1. class MyClassifier extends RichMapFunction<String, Integer> {
  2.  
  3. @Override
  4. public void open(Configuration config) {
  5. File machineLearningModel = getRuntimeContext().getDistributedCache().getFile("machineLearningModel");
  6. ...
  7. }
  8.  
  9. @Override
  10. public Integer map(String value) throws Exception {
  11. ...
  12. }
  13. }

上面的代码忽略了对文件内容的处理。

在上面的几个方法中,应该说参数本身都是static的,不会变化,那么如果参数本身随着时间也会发生变化,怎么办?

嗯,那就用connectStream,其实也是流的聚合了。

  • 使用connectStream

使用ConnectedStream的前提当然是需要有一个动态的流,比如在主数据之外,还有一些规则数据,这些规则数据会通过Restful服务来发布,假如我们的主数据来自于kafka,

那么,就可以如下:

  1. DataStreamSource<String> input = (DataStreamSource) KafkaStreamFactory
  2. .getKafka08Stream(env, srcCluster, srcTopic, srcGroup);
  3.  
  4. DataStream<Tuple2<String, String>> appkeyMeta = env.addSource(new AppKeySourceFunction(), "appkey")
  5.  
  6. ConnectedStreams<String, Tuple2<String, String>> connectedStreams = input.connect(appkeyMeta.broadcast());
  7.  
  8. DataStream<String> cleanData = connectedStreams.flatMap(new DataCleanFlatMapFunction())

其实可以看到,上面的代码中做了四件事,首先在第1行定义了获取主数据的流,在第4行定义了获取规则数据的流,在AppKeySourceFunction中实现了读取Restful的逻辑,

在第6行实现了将规则数据广播到主数据中去,最后在第8行实现了从connectedStream中得到经过处理的数据。其中的关键即在于DataCleanFlatMapFunction。

  1. public class DataCleanFlatMapFunction extends RichCoFlatMapFunction<String, Tuple2<String, String>, String>{
  2.  
  3. public void flatMap1(String s, Collector<String> collector){...}
  4.  
  5. public void flatMap2(Tuple2<String, String> s, Collector<String> collector) {...}
  6.  
  7. }

这是一段缩减的代码,关键在于第一行,首先这个函数需要实现RichCoFlatMapFunction这个抽象类,其次在类实现中,flatMap2会承接规则函数,flatMap1会承接主函数。

当然,参数可以从client发送到task,有时候也需要从task发回到client,一般这里就会使用accumulator。

这里先看一个简单的例子,实现单词的计数以及处理文本的记录数:

  1. DataSet<String> lines = ...
  2.  
  3. // Word count algorithm
  4. lines.flatMap(new FlatMapFunction<String, Tuple2<String, Integer>>() {
  5. @Override
  6. public void flatMap(String line, Collector<Tuple2<String, Integer>> out) throws Exception {
  7. String[] words = line.split("\\W+");
  8. for (String word : words) {
  9. out.collect(new Tuple2<>(word, 1));
  10. }
  11. }
  12. })
  13. .groupBy(0)
  14. .sum(1)
  15. .print();
  16.  
  17. // Count a number of lines in the text to process
  18. int linesCount = lines.count()
  19. System.out.println(linesCount);

上面的代码中,第14行实现了单词的计算,第18行实现了处理记录的行数,但很可惜,这里会产生两个job,仅仅第18行一句代码,就会产生一个job,无疑是不高效的。

flink提供了accumulator来实现数据的回传,亦即从tm传回到JM。

flink本身提供了一些内置的accumulator:

  • IntCounterLongCounterDoubleCounter – allows summing together int, long, double values sent from task managers
  • AverageAccumulator – calculates an average of double values
  • LongMaximumLongMinimumIntMaximumIntMinimumDoubleMaximumDoubleMinimum – accumulators to determine maximum and minimum values for different types
  • Histogram – used to computed distribution of values from task managers

首先需要定义一个accumulator,然后在某个自定义函数中来注册它,这样在客户端就可以获取相应的的值。

  1. lines.flatMap(new RichFlatMapFunction<String, Tuple2<String, Integer>>() {
  2.  
  3. // Create an accumulator
  4. private IntCounter linesNum = new IntCounter();
  5.  
  6. @Override
  7. public void open(Configuration parameters) throws Exception {
  8. // Register accumulator
  9. getRuntimeContext().addAccumulator("linesNum", linesNum);
  10. }
  11.  
  12. @Override
  13. public void flatMap(String line, Collector<Tuple2<String, Integer>> out) throws Exception {
  14. String[] words = line.split("\\W+");
  15. for (String word : words) {
  16. out.collect(new Tuple2<>(word, 1));
  17. }
  18.  
  19. // Increment after each line is processed
  20. linesNum.add(1);
  21. }
  22. })
  23. .groupBy(0)
  24. .sum(1)
  25. .print();
  26.  
  27. // Get accumulator result
  28. int linesNum = env.getLastJobExecutionResult().getAccumulatorResult("linesNum");
  29. System.out.println(linesNum);

当然,如果内置的accumulator不能满足需求,可以自定义accumulator,只需要继承两个接口之一即可,Accumulator或者SimpleAccumulato。

上面介绍了几种参数传递的方式,在日常的使用中,可能不仅仅是使用其中一种,或许是某些的组合,比如通过parametertool来传递hdfs的路径,再通过filecache来读取缓存。

如何在flink中传递参数的更多相关文章

  1. C# ADO.NET SqlDataAdapter中传递参数

    ADO.NET的SQL语句中,往往不是静态的语句,而是需要接受传递过来的参数,比如典型的登录功能,需要查找指定的用户名: string sqlQuery = "SELECT * FROM W ...

  2. 关于一些url中传递参数有空格问题

    1.关于一些url中传递参数有空格问题: url.replace(/ /g, "%20") 从上面的例子中可以看到可以用:replace(/ /g, "%20" ...

  3. 【openresty】向lua代码中传递参数

    前面介绍FormInputNginxModule模块时,明白了openresty如何获取post提交的数据. 然后,如果需要通过lua处理这些数据,需要把数据作为参数传递到lua中,lua获取了这些数 ...

  4. mfc 在VC的两个对话框类中传递参数的三种方法

    弄了好久,今天终于把在VC中的对话框类之间传递参数的问题解决了,很开心,记录如下: 1. 我所建立的工程是一个基于MFC对话框的应用程序,一共有三个对话框,第一个对话框为主对话框,所对应的类为CTMD ...

  5. JQuery中如何click中传递参数

    代码如下: click(data,fn)中的data其实是json对象,取的时候,只能通过当前的事件源来取,data是默认放在event中的,所以这里的data是eventdata,引用的时候也使用e ...

  6. Struct2 向Action中传递参数(中文乱码问题)

    就是把视图上的值传递到Action定义的方法中 也就是把数据从前台传递到后台 三种方式: 1.  使用action属性接收参数 比如jsp页面: <body> 使用action属性接收参数 ...

  7. ASP.net button类控件click事件中传递参数

    单击Button会同时触发这两个事件,但先执行Click,后执行Command,在button控件中加上参数属性 CommandArgument='' 在click响应函数中可以用以下代码获得传递的参 ...

  8. sys.argv向脚本中传递参数

    可以向脚本中传递无限多个参数,其值是一个列表,默认sys.argv[0]内容是脚本文件路径加文件名 test.py文件中的内容如下: #! /usr/bin/python3import sys pri ...

  9. URL中传递参数给视图函数

    1. 采用在url中使用变量的方式: 在path的第一个参数中,使用<参数名>的方式可以传递参数.然后在视图函数中也要写一个参数,视图函数中的参数必须和url中的参数名称保持一致,不然就找 ...

随机推荐

  1. java 用接口实现加减乘除计算器

    class Test{ public static void main(String[] args) { fun i=new fun(); jiafa s1=new jiafa(); jianfa s ...

  2. HyperLedger Fabric 1.4 智能合约 Helloworld运行(9)

    9.1 Helloworld案例简介       通过执行官方End-2-End案例,初始了解Fabric网络的运行流程及yaml配置,官方End-2-End案例把执行过程集成,通过一条命令即可完成全 ...

  3. Applied Cloud Deep Semantic Recognition: Advanced Anomaly Detection(应用云深层语义识别:高级异态检测)

    亚马逊链接 引言 (by Mehdi Roopaei & Paul Rad) 异态检测与情境感知 在数据分析领域,异态检测讲的是在一个数据集中,发现到其中不符合预期模式的物体,动作,行为或事件 ...

  4. vue相关ajax库的使用

    相关库: vue-resource: vue插件, 多用于vue1.x axios: 第三方库, 多用于vue2.x vue-resource使用 // 引入模块 import VueResource ...

  5. MySql 使用explain分析查询

    今天写了个慢到哭的查询,想用explain分析下执行计划,后来发现explain也是有局限性的: EXPLAIN不会告诉你关于触发器.存储过程的信息或用户自定义函数对查询的影响情况 •EXPLAIN不 ...

  6. 二进制描述子 BRIEF(ORB), BRISK, FREAK

    二进制描述子设计原则体现在三个部分: 采样pattern 方向orientation compensation 配对sampling pairs ORB基于BRIEF: BRISK是用于OKVIS的描 ...

  7. 海思NB-IOT模块HI2115芯片I2C通信

    1. 首先确定硬件上I2C的引脚,手册上并没有,海思技术支持说是14和15脚,我们用的是12和13脚,问题在于,如果是硬件I2C应该不能随便换个引脚吧,难道是模拟的时序? 2. 下一个奇怪的地方,这个 ...

  8. solr 常见的问题整理 -费元星

    本文是我在开发过程中遇到的一些问题的整理,有些摘自网上别人的方法. 1. org.apache.solr.client.solrj.SolrServerException: Timeout occur ...

  9. hdu1159Common Subsequence(动态规划)

    Common Subsequence Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Other ...

  10. 【SpringCloud】第五篇: 路由网关(zuul)

    前言: 必需学会SpringBoot基础知识 简介: spring cloud 为开发人员提供了快速构建分布式系统的一些工具,包括配置管理.服务发现.断路器.路由.微代理.事件总线.全局锁.决策竞选. ...