Flink广播流 BroadcastStream

文章目录


前言

Flink中的广播流(BroadcastStream)是一种特殊的流处理方式,它允许将一个流(通常是一个较小的流)广播到所有的并行任务中,从而实现在不同任务间共享数据的目的。广播流在处理配置信息、小数据集或者全局变量等场景下特别有用,因为这些数据需要在所有任务中保持一致且实时更新。

广播流的使用通常涉及以下步骤:

  1. 定义MapStateDescriptor:首先需要定义一个MapStateDescriptor来描述要广播的数据的格式。这个描述器指定了数据的键值对类型。

  2. 创建广播流 :然后,需要将一个普通的流转换为广播流。这通常通过调用流的broadcast()方法实现,并将MapStateDescriptor作为参数传入。

  3. 连接广播流与非广播流 :一旦有了广播流,就可以将其与一个或多个非广播流(无论是Keyed流还是Non-Keyed流)连接起来。这通过调用非广播流的connect()方法完成,并将广播流作为参数传入。连接后的流是一个BroadcastConnectedStream,它提供了process()方法用于处理数据。

  4. 处理数据 :在process()方法中,可以编写逻辑来处理非广播流和广播流的数据。根据非广播流的类型(Keyed或Non-Keyed),需要传入相应的KeyedBroadcastProcessFunctionBroadcastProcessFunction类型的处理函数。

广播流的一个典型使用场景是在处理数据时需要实时动态改变配置。例如,当需要从MySQL数据库中实时查询和更新某些关键字过滤规则时,如果直接在计算函数中进行查询,可能会阻塞整个计算过程甚至导致任务停止。通过使用广播流,可以将这些配置信息广播到所有相关任务的实例中,然后在计算过程中直接使用这些配置信息,从而提高计算效率和实时性。

总的来说,Flink的广播流提供了一种有效的方式来实现不同任务间的数据共享和实时更新,适用于各种需要全局数据或配置的场景。


BroadcastStream代码示例

功能:将用户信息进行广播,从Kafka中读取用户访问记录,判断访问用户是否存在

java 复制代码
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import org.apache.commons.lang3.StringUtils;
import org.apache.flink.api.common.state.MapStateDescriptor;
import org.apache.flink.api.common.typeinfo.BasicTypeInfo;
import org.apache.flink.api.common.typeinfo.TypeHint;
import org.apache.flink.api.common.typeinfo.TypeInformation;
import org.apache.flink.api.java.tuple.Tuple2;
import org.apache.flink.shaded.jackson2.com.fasterxml.jackson.databind.JsonNode;
import org.apache.flink.shaded.jackson2.com.fasterxml.jackson.databind.node.ObjectNode;
import org.apache.flink.streaming.api.datastream.BroadcastConnectedStream;
import org.apache.flink.streaming.api.datastream.BroadcastStream;
import org.apache.flink.streaming.api.datastream.DataStreamSource;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.streaming.api.functions.co.BroadcastProcessFunction;
import org.apache.flink.streaming.connectors.kafka.FlinkKafkaConsumer;
import org.apache.flink.streaming.util.serialization.JSONKeyValueDeserializationSchema;
import org.apache.flink.util.Collector;

import flink.demo.data.UserVo;
/**
 * 多流connect,并进行join
 *
 */
public class BroadcastTest{

	public static void main(String[] args) throws Exception {
		final StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
		Properties proterties = new Properties();
		proterties.setProperty("bootstrap.servers", "10.168.88.88:9092");
		proterties.setProperty("group.id", "test");
		proterties.setProperty("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
		proterties.setProperty("value.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
//        proterties.setProperty("auto.offset.reset", "latest");

		FlinkKafkaConsumer<ObjectNode> consumerVisit= new FlinkKafkaConsumer<>("test",
				new JSONKeyValueDeserializationSchema(false), proterties);
		DataStreamSource<ObjectNode> streamSource = env.addSource(consumerVisit);
		
		DataStreamSource<Tuple2<String, List<UserVo>>> userStreamSource = env.addSource(new UserListSource());

		
		MapStateDescriptor<String, List<UserVo>> descriptor =
			      new MapStateDescriptor<>(
			          "userStream",
			          BasicTypeInfo.STRING_TYPE_INFO,
			          TypeInformation.of(new TypeHint<List<UserVo>>() {}));
		
		BroadcastStream<Tuple2<String, List<UserVo>>> broadcastStream = userStreamSource.broadcast(descriptor);
		// 将数据流和控制流进行连接,利用控制流中的数据来控制字符串的输出
		BroadcastConnectedStream<ObjectNode, Tuple2<String, List<UserVo>>> tmp=streamSource.connect(broadcastStream);
		tmp.process(new UserPvProcessor()).print();

		env.execute("kafkaTest");

	}


	private static class UserPvProcessor
			extends BroadcastProcessFunction<ObjectNode, Tuple2<String, List<UserVo>>, String> {
		private static final long serialVersionUID = 1L;
		MapStateDescriptor<String, List<UserVo>> descriptor =
			      new MapStateDescriptor<>(
			          "userStream",
			          BasicTypeInfo.STRING_TYPE_INFO,
			          TypeInformation.of(new TypeHint<List<UserVo>>() {}));

		@Override
		//用户信息处理
		public void processBroadcastElement(Tuple2<String, List<UserVo>> value, Context ctx, Collector<String> out)
				throws Exception {
			// 将接收到的控制数据放到 broadcast state 中  
			ctx.getBroadcastState(descriptor).put(value.f0, value.f1);
			// 打印控制信息
			System.out.println(Thread.currentThread().getName() + " 接收到用户信息 : "+value.f0+"   " + value.f1);
		}

		@Override
		//数据流
		public void processElement(ObjectNode element, ReadOnlyContext ctx, Collector<String> out) throws Exception {
			// 从 broadcast state 中拿到用户列表信息
			List<UserVo> userList = ctx.getBroadcastState(descriptor).get("userList");
			String time=LocalDateTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss"));
			if(userList!=null&&userList.size()>0) {
				
				Map<String,String> userMap=new HashMap<>();
				for(UserVo vo:userList) {
					userMap.put(vo.getUserid(), vo.getUserName());
				}
//				System.out.println(userMap);
				JsonNode value = element.get("value");
				String userid=value.get("user").asText();
				String userName=userMap.get(userid);
				if (StringUtils.isNotBlank(userName)) {
					out.collect(Thread.currentThread().getName()+"存在用户"+userid+"  "+userName +" "+time);
				}else {
					out.collect(Thread.currentThread().getName()+"不存在用户"+userid+" "+time );
				}
			}else {
				out.collect(Thread.currentThread().getName()+"不存在用户"+element.get("value")+" "+time );
			}
		}
	}
}

Broadcast 使用注意事项

  • 同一个 operator 的各个 task 之间没有通信,广播流侧(processBroadcastElement)可以能修改 broadcast state,而数据流侧(processElement)只能读 broadcast state.;
  • 需要保证所有 Operator task 对 broadcast state 的修改逻辑是相同的,否则会导致非预期的结果;
  • Operator tasks 之间收到的广播流元素的顺序可能不同:虽然所有元素最终都会下发给下游tasks,但是元素到达的顺序可能不同,所以更新state时不能依赖元素到达的顺序;
  • 每个 task 对各自的 Broadcast state 都会做快照,防止热点问题;
  • 目前不支持 RocksDB 保存 Broadcast state:Broadcast state 目前只保存在内存中,需要为其预留合适的内存
相关推荐
智慧化智能化数字化方案21 分钟前
华为IPD流程管理体系L1至L5最佳实践-解读
大数据·华为
PersistJiao2 小时前
在 Spark RDD 中,sortBy 和 top 算子的各自适用场景
大数据·spark·top·sortby
2301_811274312 小时前
大数据基于Spring Boot的化妆品推荐系统的设计与实现
大数据·spring boot·后端
Yz98762 小时前
hive的存储格式
大数据·数据库·数据仓库·hive·hadoop·数据库开发
青云交2 小时前
大数据新视界 -- 大数据大厂之 Hive 数据导入:多源数据集成的策略与实战(上)(3/ 30)
大数据·数据清洗·电商数据·数据整合·hive 数据导入·多源数据·影视娱乐数据
武子康2 小时前
大数据-230 离线数仓 - ODS层的构建 Hive处理 UDF 与 SerDe 处理 与 当前总结
java·大数据·数据仓库·hive·hadoop·sql·hdfs
武子康2 小时前
大数据-231 离线数仓 - DWS 层、ADS 层的创建 Hive 执行脚本
java·大数据·数据仓库·hive·hadoop·mysql
时差9532 小时前
Flink Standalone集群模式安装部署
大数据·分布式·flink·部署
锵锵锵锵~蒋2 小时前
实时数据开发 | 怎么通俗理解Flink容错机制,提到的checkpoint、barrier、Savepoint、sink都是什么
大数据·数据仓库·flink·实时数据开发
二进制_博客2 小时前
Flink学习连载文章4-flink中的各种转换操作
大数据·学习·flink