源码结构

RedisSink

package org.apache.flink.streaming.connectors.redis;

import org.apache.flink.configuration.Configuration;
import org.apache.flink.streaming.api.functions.sink.RichSinkFunction;
import org.apache.flink.streaming.connectors.redis.common.config.FlinkJedisClusterConfig;
import org.apache.flink.streaming.connectors.redis.common.config.FlinkJedisConfigBase;
import org.apache.flink.streaming.connectors.redis.common.config.FlinkJedisPoolConfig;
import org.apache.flink.streaming.connectors.redis.common.config.FlinkJedisSentinelConfig;
import org.apache.flink.streaming.connectors.redis.common.container.RedisCommandsContainer;
import org.apache.flink.streaming.connectors.redis.common.container.RedisCommandsContainerBuilder;
import org.apache.flink.streaming.connectors.redis.common.mapper.RedisCommand;
import org.apache.flink.streaming.connectors.redis.common.mapper.RedisDataType;
import org.apache.flink.streaming.connectors.redis.common.mapper.RedisCommandDescription;
import org.apache.flink.streaming.connectors.redis.common.mapper.RedisMapper; import org.apache.flink.util.Preconditions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import java.io.IOException; /**
* A sink that delivers data to a Redis channel using the Jedis client.
* <p> The sink takes two arguments {@link FlinkJedisConfigBase} and {@link RedisMapper}.
* <p> When {@link FlinkJedisPoolConfig} is passed as the first argument,
* the sink will create connection using {@link redis.clients.jedis.JedisPool}. Please use this when
* you want to connect to a single Redis server.
* <p> When {@link FlinkJedisSentinelConfig} is passed as the first argument, the sink will create connection
* using {@link redis.clients.jedis.JedisSentinelPool}. Please use this when you want to connect to Sentinel.
* <p> Please use {@link FlinkJedisClusterConfig} as the first argument if you want to connect to
* a Redis Cluster.
*
* <p>Example:
*
* <pre>
*{@code
*public static class RedisExampleMapper implements RedisMapper<Tuple2<String, String>> {
*
* private RedisCommand redisCommand;
*
* public RedisExampleMapper(RedisCommand redisCommand){
* this.redisCommand = redisCommand;
* }
* public RedisCommandDescription getCommandDescription() {
* return new RedisCommandDescription(redisCommand, REDIS_ADDITIONAL_KEY);
* }
* public String getKeyFromData(Tuple2<String, String> data) {
* return data.f0;
* }
* public String getValueFromData(Tuple2<String, String> data) {
* return data.f1;
* }
*}
*JedisPoolConfig jedisPoolConfig = new JedisPoolConfig.Builder()
* .setHost(REDIS_HOST).setPort(REDIS_PORT).build();
*new RedisSink<String>(jedisPoolConfig, new RedisExampleMapper(RedisCommand.LPUSH));
*}</pre>
*
* @param <IN> Type of the elements emitted by this sink
*/
public class RedisSink<IN> extends RichSinkFunction<IN> { private static final long serialVersionUID = 1L; private static final Logger LOG = LoggerFactory.getLogger(RedisSink.class); /**
* This additional key needed for {@link RedisDataType#HASH} and {@link RedisDataType#SORTED_SET}.
* Other {@link RedisDataType} works only with two variable i.e. name of the list and value to be added.
* But for {@link RedisDataType#HASH} and {@link RedisDataType#SORTED_SET} we need three variables.
* <p>For {@link RedisDataType#HASH} we need hash name, hash key and element.
* {@code additionalKey} used as hash name for {@link RedisDataType#HASH}
* <p>For {@link RedisDataType#SORTED_SET} we need set name, the element and it's score.
* {@code additionalKey} used as set name for {@link RedisDataType#SORTED_SET}
*/
private String additionalKey;
private RedisMapper<IN> redisSinkMapper;
private RedisCommand redisCommand; private FlinkJedisConfigBase flinkJedisConfigBase;
private RedisCommandsContainer redisCommandsContainer; /**
* Creates a new {@link RedisSink} that connects to the Redis server.
*
* @param flinkJedisConfigBase The configuration of {@link FlinkJedisConfigBase}
* @param redisSinkMapper This is used to generate Redis command and key value from incoming elements.
*/
public RedisSink(FlinkJedisConfigBase flinkJedisConfigBase, RedisMapper<IN> redisSinkMapper) {
Preconditions.checkNotNull(flinkJedisConfigBase, "Redis connection pool config should not be null");
Preconditions.checkNotNull(redisSinkMapper, "Redis Mapper can not be null");
Preconditions.checkNotNull(redisSinkMapper.getCommandDescription(), "Redis Mapper data type description can not be null"); this.flinkJedisConfigBase = flinkJedisConfigBase; this.redisSinkMapper = redisSinkMapper;
RedisCommandDescription redisCommandDescription = redisSinkMapper.getCommandDescription();
this.redisCommand = redisCommandDescription.getCommand();
this.additionalKey = redisCommandDescription.getAdditionalKey();
} /**
* Called when new data arrives to the sink, and forwards it to Redis channel.
* Depending on the specified Redis data type (see {@link RedisDataType}),
* a different Redis command will be applied.
* Available commands are RPUSH, LPUSH, SADD, PUBLISH, SET, PFADD, HSET, ZADD.
*
* @param input The incoming data
*/
@Override
public void invoke(IN input) throws Exception {
String key = redisSinkMapper.getKeyFromData(input);
String value = redisSinkMapper.getValueFromData(input); switch (redisCommand) {
case RPUSH:
this.redisCommandsContainer.rpush(key, value);
break;
case LPUSH:
this.redisCommandsContainer.lpush(key, value);
break;
case SADD:
this.redisCommandsContainer.sadd(key, value);
break;
case SET:
this.redisCommandsContainer.set(key, value);
break;
case PFADD:
this.redisCommandsContainer.pfadd(key, value);
break;
case PUBLISH:
this.redisCommandsContainer.publish(key, value);
break;
case ZADD:
this.redisCommandsContainer.zadd(this.additionalKey, value, key);
break;
case HSET:
this.redisCommandsContainer.hset(this.additionalKey, key, value);
break;
default:
throw new IllegalArgumentException("Cannot process such data type: " + redisCommand);
}
} /**
* Initializes the connection to Redis by either cluster or sentinels or single server.
*
* @throws IllegalArgumentException if jedisPoolConfig, jedisClusterConfig and jedisSentinelConfig are all null
*/
@Override
public void open(Configuration parameters) throws Exception {
this.redisCommandsContainer = RedisCommandsContainerBuilder.build(this.flinkJedisConfigBase);
} /**
* Closes commands container.
* @throws IOException if command container is unable to close.
*/
@Override
public void close() throws IOException {
if (redisCommandsContainer != null) {
redisCommandsContainer.close();
}
}
}

RedisSink类继承了RichSinkFunction类,扩展了其中的open、invoke、close方法。open方法在sink打开时执行一次,在RedisSink中,其创建了一个RedisCommandsContainer对象,该对象其实是封装了对redis的操作,包含连接redis以及不同数据类型的写入操作;close方法中执行了RedisCommandsContainer对象的close方法,其实时关闭redis连接;每当数据流入时,就会调用invoke方法,该方法中根据不同的redis数据类型,调用RedisCommandsContainer对象的不同方法将数据写入redis。

FlinkJedisConfigBase

RedisSink的构造方法中需要传入一个FlinkJedisConfigBase对象,该对象主要是用来设置一些redis连接参数,比如IP、用户、密码、连接超时等信息,在后续创建RedisCommandsContainer对象时使用,FlinkJedisConfigBase是一个抽象类,具体的实现类有FlinkJedisPoolConfig、FlinkJedisSentinelConfig、FlinkJedisClusterConfig三种,分别对应了redis、redis哨兵、redis集群不同的连接模式,比较简单,就不贴代码了。

RedisMapper接口

RedisSink的构造方法中还需要RedisMapper实现对象,需要用户自定义类实现该接口,主要是用来定义数据如何映射成redis的key和value,另外是返回一个RedisDataTypeDescription对象,该对象其实是包含了操作的redis数据类型信息

package org.apache.flink.streaming.connectors.redis.common.mapper;

import org.apache.flink.api.common.functions.Function;

import java.io.Serializable;

/**
* Function that creates the description how the input data should be mapped to redis type.
*<p>Example:
*<pre>{@code
*private static class RedisTestMapper implements RedisMapper<Tuple2<String, String>> {
* public RedisDataTypeDescription getCommandDescription() {
* return new RedisDataTypeDescription(RedisCommand.PUBLISH);
* }
* public String getKeyFromData(Tuple2<String, String> data) {
* return data.f0;
* }
* public String getValueFromData(Tuple2<String, String> data) {
* return data.f1;
* }
*}
*}</pre>
*
* @param <T> The type of the element handled by this {@code RedisMapper}
*/
public interface RedisMapper<T> extends Function, Serializable { /**
* Returns descriptor which defines data type.
*
* @return data type descriptor
*/
RedisCommandDescription getCommandDescription(); /**
* Extracts key from data.
*
* @param data source data
* @return key
*/
String getKeyFromData(T data); /**
* Extracts value from data.
*
* @param data source data
* @return value
*/
String getValueFromData(T data);
}

RedisCommandDescription、RedisDataType、RedisCommand

RedisCommandDescription、RedisDataType、RedisCommand三个类用了表示操作的redis数据类型,比较简单,不在描述,支持的数据类型在RedisDataType枚举中

package org.apache.flink.streaming.connectors.redis.common.mapper;

/**
* All available data type for Redis.
*/
public enum RedisDataType { /**
* Strings are the most basic kind of Redis value. Redis Strings are binary safe,
* this means that a Redis string can contain any kind of data, for instance a JPEG image or a serialized Ruby object.
* A String value can be at max 512 Megabytes in length.
*/
STRING, /**
* Redis Hashes are maps between string fields and string values.
*/
HASH, /**
* Redis Lists are simply lists of strings, sorted by insertion order.
*/
LIST, /**
* Redis Sets are an unordered collection of Strings.
*/
SET, /**
* Redis Sorted Sets are, similarly to Redis Sets, non repeating collections of Strings.
* The difference is that every member of a Sorted Set is associated with score,
* that is used in order to take the sorted set ordered, from the smallest to the greatest score.
* While members are unique, scores may be repeated.
*/
SORTED_SET, /**
* HyperLogLog is a probabilistic data structure used in order to count unique things.
*/
HYPER_LOG_LOG, /**
* Redis implementation of publish and subscribe paradigm. Published messages are characterized into channels,
* without knowledge of what (if any) subscribers there may be.
* Subscribers express interest in one or more channels, and only receive messages
* that are of interest, without knowledge of what (if any) publishers there are.
*/
PUBSUB
}

RedisCommandsContainer

RedisCommandsContainer接口定义了redis操作的方法,具体的实现类有RedisContainer、RedisClusterContainer,其中RedisContainer是用在直连redis和redis哨兵模式中,而RedisClusterContainer是用在集群模式中,具体代码不贴了,里面主要是调用了Jedis的API。

package org.apache.flink.streaming.connectors.redis.common.container;

import java.io.IOException;
import java.io.Serializable; /**
* The container for all available Redis commands.
*/
public interface RedisCommandsContainer extends Serializable { /**
* Sets field in the hash stored at key to value.
* If key does not exist, a new key holding a hash is created.
* If field already exists in the hash, it is overwritten.
*
* @param key Hash name
* @param hashField Hash field
* @param value Hash value
*/
void hset(String key, String hashField, String value); /**
* Insert the specified value at the tail of the list stored at key.
* If key does not exist, it is created as empty list before performing the push operation.
*
* @param listName Name of the List
* @param value Value to be added
*/
void rpush(String listName, String value); /**
* Insert the specified value at the head of the list stored at key.
* If key does not exist, it is created as empty list before performing the push operation.
*
* @param listName Name of the List
* @param value Value to be added
*/
void lpush(String listName, String value); /**
* Add the specified member to the set stored at key.
* Specified members that are already a member of this set are ignored.
* If key does not exist, a new set is created before adding the specified members.
*
* @param setName Name of the Set
* @param value Value to be added
*/
void sadd(String setName, String value); /**
* Posts a message to the given channel.
*
* @param channelName Name of the channel to which data will be published
* @param message the message
*/
void publish(String channelName, String message); /**
* Set key to hold the string value. If key already holds a value, it is overwritten,
* regardless of its type. Any previous time to live associated with the key is
* discarded on successful SET operation.
*
* @param key the key name in which value to be set
* @param value the value
*/
void set(String key, String value); /**
* Adds all the element arguments to the HyperLogLog data structure
* stored at the variable name specified as first argument.
*
* @param key The name of the key
* @param element the element
*/
void pfadd(String key, String element); /**
* Adds the specified member with the specified scores to the sorted set stored at key.
*
* @param key The name of the Sorted Set
* @param score Score of the element
* @param element element to be added
*/
void zadd(String key, String score, String element); /**
* Close the Jedis container.
*
* @throws IOException if the instance can not be closed properly
*/
void close() throws IOException;
}

RedisCommandsContainerBuilder

RedisCommandsContainerBuilder类是根据不同的FlinkJedisConfigBase实现类来创建不同的RedisCommandsContainer对象

package org.apache.flink.streaming.connectors.redis.common.container;

import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.apache.flink.streaming.connectors.redis.common.config.FlinkJedisClusterConfig;
import org.apache.flink.streaming.connectors.redis.common.config.FlinkJedisConfigBase;
import org.apache.flink.streaming.connectors.redis.common.config.FlinkJedisPoolConfig;
import org.apache.flink.streaming.connectors.redis.common.config.FlinkJedisSentinelConfig;
import org.apache.flink.util.Preconditions;
import redis.clients.jedis.JedisCluster;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisSentinelPool; /**
* The builder for {@link RedisCommandsContainer}.
*/
public class RedisCommandsContainerBuilder { /**
* Initialize the {@link RedisCommandsContainer} based on the instance type.
* @param flinkJedisConfigBase configuration base
* @return @throws IllegalArgumentException if jedisPoolConfig, jedisClusterConfig and jedisSentinelConfig are all null
*/
public static RedisCommandsContainer build(FlinkJedisConfigBase flinkJedisConfigBase){
if(flinkJedisConfigBase instanceof FlinkJedisPoolConfig){
FlinkJedisPoolConfig flinkJedisPoolConfig = (FlinkJedisPoolConfig) flinkJedisConfigBase;
return RedisCommandsContainerBuilder.build(flinkJedisPoolConfig);
} else if (flinkJedisConfigBase instanceof FlinkJedisClusterConfig) {
FlinkJedisClusterConfig flinkJedisClusterConfig = (FlinkJedisClusterConfig) flinkJedisConfigBase;
return RedisCommandsContainerBuilder.build(flinkJedisClusterConfig);
} else if (flinkJedisConfigBase instanceof FlinkJedisSentinelConfig) {
FlinkJedisSentinelConfig flinkJedisSentinelConfig = (FlinkJedisSentinelConfig) flinkJedisConfigBase;
return RedisCommandsContainerBuilder.build(flinkJedisSentinelConfig);
} else {
throw new IllegalArgumentException("Jedis configuration not found");
}
} /**
* Builds container for single Redis environment.
*
* @param jedisPoolConfig configuration for JedisPool
* @return container for single Redis environment
* @throws NullPointerException if jedisPoolConfig is null
*/
public static RedisCommandsContainer build(FlinkJedisPoolConfig jedisPoolConfig) {
Preconditions.checkNotNull(jedisPoolConfig, "Redis pool config should not be Null"); GenericObjectPoolConfig genericObjectPoolConfig = new GenericObjectPoolConfig();
genericObjectPoolConfig.setMaxIdle(jedisPoolConfig.getMaxIdle());
genericObjectPoolConfig.setMaxTotal(jedisPoolConfig.getMaxTotal());
genericObjectPoolConfig.setMinIdle(jedisPoolConfig.getMinIdle()); JedisPool jedisPool = new JedisPool(genericObjectPoolConfig, jedisPoolConfig.getHost(),
jedisPoolConfig.getPort(), jedisPoolConfig.getConnectionTimeout(), jedisPoolConfig.getPassword(),
jedisPoolConfig.getDatabase());
return new RedisContainer(jedisPool);
} /**
* Builds container for Redis Cluster environment.
*
* @param jedisClusterConfig configuration for JedisCluster
* @return container for Redis Cluster environment
* @throws NullPointerException if jedisClusterConfig is null
*/
public static RedisCommandsContainer build(FlinkJedisClusterConfig jedisClusterConfig) {
Preconditions.checkNotNull(jedisClusterConfig, "Redis cluster config should not be Null"); GenericObjectPoolConfig genericObjectPoolConfig = new GenericObjectPoolConfig();
genericObjectPoolConfig.setMaxIdle(jedisClusterConfig.getMaxIdle());
genericObjectPoolConfig.setMaxTotal(jedisClusterConfig.getMaxTotal());
genericObjectPoolConfig.setMinIdle(jedisClusterConfig.getMinIdle()); JedisCluster jedisCluster = new JedisCluster(jedisClusterConfig.getNodes(), jedisClusterConfig.getConnectionTimeout(),
jedisClusterConfig.getMaxRedirections(), genericObjectPoolConfig);
return new RedisClusterContainer(jedisCluster);
} /**
* Builds container for Redis Sentinel environment.
*
* @param jedisSentinelConfig configuration for JedisSentinel
* @return container for Redis sentinel environment
* @throws NullPointerException if jedisSentinelConfig is null
*/
public static RedisCommandsContainer build(FlinkJedisSentinelConfig jedisSentinelConfig) {
Preconditions.checkNotNull(jedisSentinelConfig, "Redis sentinel config should not be Null"); GenericObjectPoolConfig genericObjectPoolConfig = new GenericObjectPoolConfig();
genericObjectPoolConfig.setMaxIdle(jedisSentinelConfig.getMaxIdle());
genericObjectPoolConfig.setMaxTotal(jedisSentinelConfig.getMaxTotal());
genericObjectPoolConfig.setMinIdle(jedisSentinelConfig.getMinIdle()); JedisSentinelPool jedisSentinelPool = new JedisSentinelPool(jedisSentinelConfig.getMasterName(),
jedisSentinelConfig.getSentinels(), genericObjectPoolConfig,
jedisSentinelConfig.getConnectionTimeout(), jedisSentinelConfig.getSoTimeout(),
jedisSentinelConfig.getPassword(), jedisSentinelConfig.getDatabase());
return new RedisContainer(jedisSentinelPool);
}
}

整体下来,flink-redis-connector源码比较简洁,可以作为自定义flink sink的入门学习。

Flink读写Redis(二)-flink-redis-connector代码学习的更多相关文章

  1. Redis(二)、Redis持久化RDB和AOF

    一.Redis两种持久化方式 对Redis而言,其数据是保存在内存中的,一旦机器宕机,内存中的数据会丢失,因此需要将数据异步持久化到硬盘中保存.这样,即使机器宕机,数据能从硬盘中恢复. 常见的数据持久 ...

  2. Flink读写Kafka

    Flink 读写Kafka 在Flink中,我们分别用Source Connectors代表连接数据源的连接器,用Sink Connector代表连接数据输出的连接器.下面我们介绍一下Flink中用于 ...

  3. 二、Redis基本操作——String(实战篇)

    小喵万万没想到,上一篇博客,居然已经被阅读600次了!!!让小喵感觉压力颇大.万一有写错的地方,岂不是会误导很多筒子们.所以,恳请大家,如果看到小喵的博客有什么不对的地方,请尽快指正!谢谢! 小喵的唠 ...

  4. Flink读写Redis(三)-读取redis数据

    自定义flink的RedisSource,实现从redis中读取数据,这里借鉴了flink-connector-redis_2.11的实现逻辑,实现对redis读取的逻辑封装,flink-connec ...

  5. Flink的DataSource三部曲之二:内置connector

    欢迎访问我的GitHub https://github.com/zq2599/blog_demos 内容:所有原创文章分类汇总及配套源码,涉及Java.Docker.Kubernetes.DevOPS ...

  6. [你必须知道的NOSQL系列]专题二:Redis快速入门

    一.前言 在前一篇博文介绍了MongoDB基本操作,本来打算这篇博文继续介绍MongoDB的相关内容的,例如索引,主从备份等内容的,但是发现这些内容都可以通过官方文档都可以看到,并且都非常详细,所以这 ...

  7. Redis哨兵模式(sentinel)学习总结及部署记录(主从复制、读写分离、主从切换)

    Redis的集群方案大致有三种:1)redis cluster集群方案:2)master/slave主从方案:3)哨兵模式来进行主从替换以及故障恢复. 一.sentinel哨兵模式介绍Sentinel ...

  8. Redis进阶实践之二十 Redis的配置文件使用详解

    一.引言 写完上一篇有关redis使用lua脚本的文章,就有意结束Redis这个系列的文章了,当然了,这里的结束只是我这个系列的结束,但是要学的东西还有很多.但是,好多天过去了,总是感觉好像还缺点什么 ...

  9. Redis(二十一):Redis性能问题排查解决手册(转)

    性能相关的数据指标 通过Redis-cli命令行界面访问到Redis服务器,然后使用info命令获取所有与Redis服务相关的信息.通过这些信息来分析文章后面提到的一些性能指标. info命令输出的数 ...

随机推荐

  1. MathType中的条件概率的输入

    条件概率公式是高中数学的概率知识中比较常用的一个公式,今天我们来介绍一下在MathType中如何输入条件概率公式. 具体步骤如下: 步骤一 打开专业的公式编辑软件MathType 7,在输入框中输入& ...

  2. iOS 搜索条使用详解

    在ios开发中搜索条的使用挺常见的,不过之前一直没用到也没细细研究,最近做外包项目的时候刚好用到,在这里记录一下使用的过程,只要理解了原理,其实还是比较简单的!上传的图片有点大,刚好可以看清楚它的使用 ...

  3. 【Luogu U41492】树上数颜色——树上启发式合并(dsu on tree)

    (这题在洛谷主站居然搜不到--还是在百度上偶然看到的) 题目描述 给一棵根为1的树,每次询问子树颜色种类数 输入输出格式 输入格式: 第一行一个整数n,表示树的结点数 接下来n-1行,每行一条边 接下 ...

  4. vue前端静态页面Github Pages线上预览实现

    一.前期准备之项目编译 此处记录如何解决vue2.0 打包之后,打开index.html出现空白页的问题,附上@参考地址 打包之前修改三个文件 第一步,找到build文件,在webpack.prod. ...

  5. mysql hash join

    面阿里问到的. "mysql join的实现有几种方式?" "啥?不就一种吗?" 转载: 简单介绍:https://zhuanlan.zhihu.com/p/9 ...

  6. mfc 位图本地存储 本地位图读取显示

    一.读取CImage //在绘图函数中直接使用参数pDC,无需定义 CDC* pDC = GetDC(): //读去位图路径,根据实际情况修改 CString loatImagePath = TEXT ...

  7. C语言项目——工程化编程的案例分析

    一.VSCode安装及环境配置 初始在Win下安装Mingw-w64/GCC 和 GDB,在VSCode下打开项目案例,发现在linktable中需要包含pthread头文件.此文件是基于Linux系 ...

  8. python之切片操作,实现一个trim()函数,去除字符串首尾的空格.

    # -*- coding: utf-8 -*- def trim(s): if len(s)==0: return '' if s[:1]==' ': return trim(s[1:]) elif ...

  9. 第4.6节 print、import及断言

    一.print函数 前面第二章介绍了print的语法,其语法如下: print(value, ..., sep=' ', end='\n', file=sys.stdout, flush=False) ...

  10. git——同步本地文件到github上

    参考教程: 1.https://blog.csdn.net/weixin_37769855/article/details/99439904 2.https://www.liaoxuefeng.com ...