Redis异步维表
概述
- 之前和大家聊过维表,也聊过FLINK中如何使用维表,目前FLINK自带的维表有两种,一种是JDBC维表,一种是HBASE维表。其中JDBC维表支持缓存,HBASE暂不支持
- 两种维表也都是同步维表,性能较弱
- 我之前也说过会在后面的教程实现以下异步第三方维表,加上一个叫
啤酒鸭
的问我相关的问题,就抽空写了一把 - redis异步客户端我用的是
Lettuce
,大家也可以用Redission
,Jedis
是同步的,大家千万注意 - 不过和我之前说的一样,缓存会遇到旧数据的问题,所以也相当于给大家留了一个小作业~~(主要没时间写,写完代码和博客就快下班了)~~ ,通过定时器,定时更新缓存的数据,保证尽可能拿到最新的维表数据
- 下面的代码在我的github库里面都有,包括以前教程的代码也是一样
TablesFactory
- 想通过DDL来定义我们的维表的话,就必须得实现
StreamTableSourceFactory
,StreamTableSinkFactory
其中之一或者两者全部实现。一个对应数据源表,一个对应于数据结果表,需要哪个实现哪个吧 - 之后需要实现4个方法
createStreamTableSink
创建流类型tableSinkcreateStreamTableSource
创建流类型tableSourcerequiredContext
只有DDL语句WITH里面的参数&值和该方法传递的参数完全一致,DDL才能映射到这个工厂类supportedProperties
支持的参数&值,用于验证
- 同时,需要在
resource
目录下建META-INF/services/org.apache.flink.table.factories.TableFactory
路径以及文件,并在文件里面写入你的工厂类全路径。主要是为了通过SPI来发现你的工厂类 - 下面贴一下代码
package factory; import org.apache.flink.api.common.typeinfo.TypeInformation; import org.apache.flink.api.java.tuple.Tuple2; import org.apache.flink.table.api.TableSchema; import org.apache.flink.table.descriptors.DescriptorProperties; import org.apache.flink.table.descriptors.JDBCValidator; import org.apache.flink.table.descriptors.SchemaValidator; import org.apache.flink.table.factories.StreamTableSinkFactory; import org.apache.flink.table.factories.StreamTableSourceFactory; import org.apache.flink.table.sinks.StreamTableSink; import org.apache.flink.table.sources.StreamTableSource; import org.apache.flink.table.types.utils.TypeConversions; import org.apache.flink.table.utils.TableSchemaUtils; import org.apache.flink.types.Row; import source.RedisLookupTableSource; import util.RedisValidator; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import static org.apache.flink.table.descriptors.Schema.*; import static org.apache.flink.table.descriptors.Schema.SCHEMA_NAME; import static util.RedisValidator.*; public class RedisTableSourceSinkFactory implements StreamTableSourceFactory<Row>, StreamTableSinkFactory<Tuple2<Boolean, Row>> { //数据输出使用 @Override public StreamTableSink<Tuple2<Boolean, Row>> createStreamTableSink(Map<String, String> properties) { throw new IllegalArgumentException("unSupport sink now"); } //数据源使用,维表也算,其实感觉维表应该独立开 @Override public StreamTableSource<Row> createStreamTableSource(Map<String, String> properties) { //校验参数 DescriptorProperties descriptorProperties = getValidatedProperties(properties); TableSchema schema = TableSchemaUtils.getPhysicalSchema(descriptorProperties.getTableSchema(SCHEMA)); RedisLookupTableSource.Builder builder = RedisLookupTableSource.builder() .setFieldNames(schema.getFieldNames()) .setFieldTypes(schema.getFieldTypes()) .setIp(descriptorProperties.getString(CONNECTOR_IP)) .setPort(Integer.parseInt(descriptorProperties.getString(CONNECTOR_PORT))); //当缓存相关参数为空时,不会出现异常 descriptorProperties.getOptionalLong(CONNECTOR_LOOKUP_CACHE_MAX_ROWS).ifPresent(builder::setCacheMaxSize); descriptorProperties.getOptionalLong(CONNECTOR_LOOKUP_CACHE_TTL).ifPresent(builder::setCacheExpireMs); return builder.build(); } //redis维表 需要参数值是这样的 @Override public Map<String, String> requiredContext() { Map<String, String> context = new HashMap<>(); context.put(CONNECTOR_TYPE, CONNECTOR_TYPE_VALUE_REDIS); context.put(CONNECTOR_PROPERTY_VERSION, "1"); // backwards compatibility return context; } //需要的参数 @Override public List<String> supportedProperties() { List<String> properties = new ArrayList<>(); properties.add(CONNECTOR_IP); properties.add(CONNECTOR_PORT); properties.add(CONNECTOR_VERSION); properties.add(CONNECTOR_LOOKUP_CACHE_MAX_ROWS); properties.add(CONNECTOR_LOOKUP_CACHE_TTL); // schema properties.add(SCHEMA + ".#." + SCHEMA_DATA_TYPE); properties.add(SCHEMA + ".#." + SCHEMA_TYPE); properties.add(SCHEMA + ".#." + SCHEMA_NAME); return properties; } private DescriptorProperties getValidatedProperties(Map<String, String> properties) { final DescriptorProperties descriptorProperties = new DescriptorProperties(true); descriptorProperties.putProperties(properties); new SchemaValidator(true, false, false).validate(descriptorProperties); new RedisValidator().validate(descriptorProperties); return descriptorProperties; } }
TableSource
- 有了工厂,下面就得有工厂的实现
- 我们需要实现
LookupableTableSource
,StreamTableSource
这两个类,并且实现这些个方法getLookupFunction
返回真正去redis拿数据的工人类(同步模式)getAsyncLookupFunction
返回真正去redis拿数据的工人类(异步模式)isAsyncEnabled
是否是异步模式getTableSchema
表结构getDataStream
获取数据流,我们这只支持维表使用,所以直接返回空吧getProducedDataType
产生的数据类型
package source; import lookup.RedisLookupFunction; import org.apache.flink.api.common.typeinfo.TypeInformation; import org.apache.flink.api.java.typeutils.RowTypeInfo; import org.apache.flink.streaming.api.datastream.DataStream; import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment; import org.apache.flink.table.api.TableSchema; import org.apache.flink.table.functions.AsyncTableFunction; import org.apache.flink.table.functions.TableFunction; import org.apache.flink.table.sources.LookupableTableSource; import org.apache.flink.table.sources.StreamTableSource; import org.apache.flink.table.types.DataType; import org.apache.flink.table.types.utils.TypeConversions; import org.apache.flink.types.Row; public class RedisLookupTableSource implements LookupableTableSource<Row>, StreamTableSource<Row> {