Flink分布式缓存与累加器

◇◆丶佛笑我妖孽 提交于 2020-01-14 07:50:06

分布式缓存

Flink提供了一个分布式缓存,类似于hadoop,可以使用户在并行函数中很方便的读取本地文件,并把它放在taskmanager节点中,防止task重复拉取。此缓存的工作机制如下:程序注册一个文件或者目录(本地或者远程文件系统,例如hdfs或者s3),通过ExecutionEnvironment注册缓存文件并为它起一个名称。

当程序执行,Flink自动将文件或者目录复制到所有taskmanager节点的本地文件系统,仅会执行一次。用户可以通过这个指定的名称查找文件或者目录,然后从taskmanager节点的本地文件系统访问它。

其实分布式缓存就相当于spark的广播,把一个变量广播到所有的executor上,也可以看做是Flink的广播流,只不过这里广播的是一个文件.

示例

Step 1 : 注册一个文件,可以使用hdfs上的文件 也可以是本地文件进行测试

Step 2 : 使用文件

注意:

在用户函数中访问缓存文件或者目录(这里是一个map函数)。这个函数必须继承RichFunction,因为它需要使用RuntimeContext读取数据:

java分布式缓存代码如下:

/**
 * <p/> DataSet 分布式缓存 </li>
 * <li>@author: li.pan</li>
 * <li>Date: 2019/12/29 16:10 下午</li>
 * <li>Version: V1.0</li>
 * <li>Description: </li>
 */
public class JavaDataSetDistributedCacheApp {
​
    public static void main(String[] args) throws Exception {
​
        ExecutionEnvironment env =  ExecutionEnvironment.getExecutionEnvironment();
        String filePath = "file:///Users/lipan/workspace/flink_demo/flink-local-train/src/main/resources/sink/java/cache.txt";
​
        // step1: 注册一个本地/HDFS文件
        env.registerCachedFile(filePath, "lp-java-dc");
​
        DataSource<String> data = env.fromElements("hadoop","spark","flink","pyspark","storm");
​
        data.map(new RichMapFunction<String, String>() {
​
            List<String> list = new ArrayList<String>();
            // step2:在open方法中获取到分布式缓存的内容即可
            @Override
            public void open(Configuration parameters) throws Exception {
                File file = getRuntimeContext().getDistributedCache().getFile("lp-java-dc");
                List<String> lines = FileUtils.readLines(file);
                for(String line : lines) {
                    list.add(line);
                    System.out.println("line = [" + line + "]");
                }
            }
​
            @Override
            public String map(String value) throws Exception {
                return value;
            }
        }).print();
​
    }
}
​

scala分布式缓存代码如下:

/**
  * <p/>
  * <li>title: DataSet 分布式缓存</li>
  * <li>@author: li.pan</li>
  * <li>Date: 2019/11/23 2:15 下午</li>
  * <li>Version: V1.0</li>
  * <li>Description:
  * step1: 注册一个本地/HDFS文件
  * step2:在open方法中获取到分布式缓存的内容即可
  * </li>
  */

object DistributedCacheApp {

  def main(args: Array[String]): Unit = {

    val env = ExecutionEnvironment.getExecutionEnvironment

    val filePath = "file:///Users/lipan/workspace/flink_demo/flink-local-train/src/main/resources/sink/scala/cache.txt"

    // step1: 注册一个本地/HDFS文件
    env.registerCachedFile(filePath, "pk-scala-dc")

    import org.apache.flink.api.scala._
    val data = env.fromElements("hadoop", "spark", "flink", "pyspark", "storm")
    
    data.map(new RichMapFunction[String, String] {

      // step2:在open方法中获取到分布式缓存的内容即可
      override def open(parameters: Configuration): Unit = {
        val dcFile = getRuntimeContext.getDistributedCache().getFile("pk-scala-dc")

        val lines = FileUtils.readLines(dcFile) // java
        
        /**
          * 此时会出现一个异常:java集合和scala集合不兼容的问题
          */
        import scala.collection.JavaConverters._
        for (ele <- lines.asScala) { 
          println(ele)
        }
      }

      override def map(value: String): String = value
    }).print()
  }

}

累加器(Accumulators)

Accumulators(累加器)是非常简单的,通过一个add操作累加最终的结果,在job执行后可以获取最终结果。

最简单的累加器是counter(计数器):你可以通过Accumulator.add(V value)这个方法进行递增。在任务的最后,flink会把所有的结果进行合并,然后把最终结果发送到client端。累加器在调试或者你想更快了解你的数据的时候是非常有用的。

Flink现在有一下内置累加器,每个累加器都实现了Accumulator接口。 IntCounter, LongCounter 和 DoubleCounter:下面是一个使用计数器的例子。例如:分布式的单词统计程序。

示例:

Step 1 : 需要在你想要使用的地方创建一个自定义的transformation算子,在算子中创建一个累加器对象。

val counter = new LongCounter()

Step 2 : 注册这个累加器对象,通常在rich函数的open方法里面。在这里你也可以定义一个名字

 getRuntimeContext.addAccumulator("ele-counts-scala", counter)

Step 3 : 操作中使用这个累加器

counter.add(1)

Step 4 : 最终的总结果将会存储在从execute()中返回的JobExecutionResult对象中。(这个操作需要等待任务执行完成)

val num = jobResult.getAccumulatorResult[Long]("ele-counts-scala")

Java累加器代码如下:

/**
 * <p/>
 * <li>title: flink 计数器</li>
 * <li>@author: li.pan</li>
 * <li>Date: 2019/12/29 2:59 下午</li>
 * <li>Version: V1.0</li>
 * <li>Description:
 * Java实现通过一个add操作累加最终的结果,在job执行后可以获取最终结果
 * </li>
 */
public class JavaCounterApp {

    public static void main(String[] args) throws Exception {

        ExecutionEnvironment env = ExecutionEnvironment.getExecutionEnvironment();
        DataSource<String> data = env.fromElements("hadoop","spark","flink","pyspark","storm");

        DataSet<String> info = data.map(new RichMapFunction<String, String>() {

            LongCounter counter = new LongCounter();

            @Override
            public void open(Configuration parameters) throws Exception {
                super.open(parameters);

                getRuntimeContext().addAccumulator("ele-counts-java", counter);
            }

            @Override
            public String map(String value) throws Exception {
                counter.add(1);
                return value;
            }
        });

        String filePath = "file:///Users/lipan/workspace/flink_demo/flink-local-train/src/main/resources/sink/java/";
        info.writeAsText(filePath, FileSystem.WriteMode.OVERWRITE).setParallelism(2);
        JobExecutionResult jobResult = env.execute("CounterApp");

        // step3: 获取计数器
        long num = jobResult.getAccumulatorResult("ele-counts-java");

        System.out.println("num = [" + num + "]");
    }
}

Scala累加器代码如下:

/**
  * <p/>

  * <li>Description: flink 计数器</li>
  * <li>@author: panli@0226@sina.com</li>

  * <li>Date: 2019-04-14 21:53</li>

  * Scala实现通过一个add操作累加最终的结果,在job执行后可以获取最终结果
  */
object CountApp {

  def main(args: Array[String]): Unit = {

    val env = ExecutionEnvironment.getExecutionEnvironment
    val data = env.fromElements("hadoop", "spark", "flink", "pyspark", "storm")


    val info = data.map(new RichMapFunction[String, String]() {
      // step1:定义计数器
      val counter = new LongCounter()

      override def open(parameters: Configuration): Unit = {
        // step2: 注册计数器
        getRuntimeContext.addAccumulator("ele-counts-scala", counter)
      }

      override def map(in: String): String = {
        counter.add(1)
        in
      }
    })

    val filePath = "file:///Users/lipan/workspace/flink_demo/flink-local-train/src/main/resources/sink/scala/"
    info.writeAsText(filePath, WriteMode.OVERWRITE).setParallelism(2)
    val jobResult = env.execute("CounterApp")

    // step3: 获取计数器
    val num = jobResult.getAccumulatorResult[Long]("ele-counts-scala")

    println("num: " + num)
  }

}

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!