The way to use Redis in Apache Flink - redis

I am using the Flink and want to insert the result value into the Redis.
When I googled the Redis, I found the redis-connector included in the Apache bahir.
So I am able to insert the result value into the redis using the reids-connector in the Apache bahir.
However, I think that I am also able to connect to the Redis using the Jedis.
I had the experiment showing that I was able to connect the redis and found the value inserted into the redis using jedis as shown in the code below.
DataStream<String> messageStream = env.addSource(new FlinkKafkaConsumer<>(flinkParams.getRequired("topic"), new SimpleStringSchema(), flinkParams.getProperties())).setParallelism(Math.min(hosts * cores, kafkaPartitions));
messageStream.keyBy(new KeySelector<String, String>() {
#Override
public String getKey(String s) throws Exception {
return s;
}
}).flatMap(new RedisConnector());
In the RedisConnector module, without the redis-connector in the Apache bahir, I also successfully connected to the redis and found the message processed after the Flink.
The example code is shown as below
public class ProcessorCommon {
private static final Logger logger = LoggerFactory.getLogger(ProcessorCommon.class);
private Jedis jedis;
private Set<DummyPair> dummy;
public ProcessorCommon(String redisServerHostName) {
this.jedis = new Jedis(redisServerHostName);
}
public void writeToRedis(String key, String value) {
this.jedis.set(key, value);
}
public String getFromRedis(String key) {
return this.jedis.get(key);
}
public void close() {
this.jedis.close();
}
}
So I am wondering that there is a difference between using redis-connector in the bahir and Jedis.

There is currently no real Redis connector maintained by the Flink community. The Redis connector in Bahir is rather outdated. There is a new Redis Streams connector in the works, which can be found at https://github.com/apache/flink-connector-redis-streams

Related

No physical database known of type cassandra -AWS keyspace

We are connecting our microservices to aws keyspace(Cassandra) through dbaas.
Getting error
cloud.dbaas.client.exceptions.CreateDbException: MicroserviceRestClientResponseException{message=404 Not Found: "No physical database known of type cassandra
Even getting same error from dbaas pods logs.
I already configured below parameters
spring.data.cassandra.ssl
spring.data.cassandra.contact-points
spring.data.cassandra.local-datacenter
spring.data.cassandra.port
spring.data.cassandra.password
spring.data.cassandra.username
You will want to reference the external configuration. See the following Amazon Keyspaces spring example.
https://github.com/aws-samples/amazon-keyspaces-spring-app-example/
#Configuration
public class AppConfig {
private final String username = System.getenv("AWS_MCS_SPRING_APP_USERNAME");
private final String password = System.getenv("AWS_MCS_SPRING_APP_PASSWORD");
File driverConfig = new File(System.getProperty("user.dir")+"/application.conf");
#Primary
public #Bean
CqlSession session() throws NoSuchAlgorithmException {
return CqlSession.builder().
withConfigLoader(DriverConfigLoader.fromFile(driverConfig)).
withAuthCredentials(username, password).
withSslContext(SSLContext.getDefault()).
withKeyspace("keyspace_name").
build();
}
}

Flink statefun and confluent schema registry compatibility

I'm trying to egress to confluent kafka from flink statefun. In confluent git repo
in order to schema check and put data to kafka topic all we need to do is use kafka client ProducerRecord object with avro object.
But in statefun we need to override "ProducerRecord<byte[], byte[]> serialize" method for kafka egress. This causes the following error.
Caused by: org.apache.kafka.common.errors.SerializationException: Error registering Avro schema: "bytes"
Schema registery and statefun kafka egress seem to be incompatible. Are there any workaround ?
It is possible to use Confluent Schema Registry with Statefun Egress.
In order to do so, you first register your schema manually with the schema registry and then supply KafkaEgressSerializer a byte[] serialized by KafkaAvroSerializer instance.
Code below is the gist of it and is in compliance with the first one in Igal's workaround suggestions:
public class SpecificRecordFromAvroSchemaSerializer implements KafkaEgressSerializer<SpecificRecordGeneratedFromAvroSchema> {
private static String KAFKA_TOPIC = "kafka_topic";
private static CachedSchemaRegistryClient schemaRegistryClient = new CachedSchemaRegistryClient(
"http://schema-registry:8081",
1_000
);
private static KafkaAvroSerializer kafkaAvroSerializer = new KafkaAvroSerializer(schemaRegistryClient);
static {
try {
schemaRegistryClient.register(
KAFKA_TOPIC + "-value", // assuming subject name strategy is TopicNameStrategy (default)
SpecificRecordGeneratedFromAvroSchema.getClassSchema()
);
} catch (IOException e) {
e.printStackTrace();
} catch (RestClientException e) {
e.printStackTrace();
}
}
#Override
public ProducerRecord<byte[], byte[]> serialize(SpecificRecordGeneratedFromAvroSchema specificRecordGeneratedFromAvroSchema) {
byte[] valueData = kafkaAvroSerializer.serialize(
KAFKA_TOPIC,
specificRecordGeneratedFromAvroSchema
);
return new ProducerRecord<>(
KAFKA_TOPIC,
String.valueOf(System.currentTimeMillis()).getBytes(),
valueData
);
}
}
Schema registry is not directly supported at this version of stateful functions,
but few workarounds are possible:
Connect to the schema registry by your self from the KafkaEgressSerializer class. In your linked example that would need to be happening here.
Provide your own instance of a FlinkKafkaProducer that is based on (see
AvroDeserializationSchema)
Mange the schemas outside of stateful functions, but serialize your Avro record to bytes. Make sure to remove the schema registry from the properties that being passed to the KafkaProducer

How does Lettuce dynamic command interface work with Redis modules?

Trying to send RedisTimeSeries commands thru Lettuce (Java) to Redis. It worked for simple commands such as TS.Create but I couldn't get slightly more complicated commands to work (such TS.ADD which takes key, score, value as args) or TS.Range (which takes args and returns List) to work.
Redis is running on Linux (Ubuntu running on Windows 10 thru WSL), RedisTimeSeries is installed onto Redis. Redis and RedisTimeSeries commands have been tested using Redis-cli on linux, they work fine. I use VS Code + JDK 13.0 + Maven to build and test a Java client for Redis. So far Redis commands supported by Lettuce are working thru the client, plus some simple RedisTimeSeries commands.
Code snippet:
RedisCommands<String, String> syncCommands = MyRedisClient.getSyncCommands(connection);
// this works:
RedisCodec<String, String> codec = StringCodec.UTF8;
String result = syncCommands.dispatch(TS.CREATE, new StatusOutput<>(codec),new CommandArgs<>(codec).addKey("myTS"));
System.out.println("Custom Command TS.CREATE " + result);
//custom command definition:
public enum TS implements ProtocolKeyword{
CREATE;
public final byte[] bytes;
private TS() {
bytes = "TS.CREATE".getBytes(StandardCharsets.US_ASCII);
}
#Override
public byte[] getBytes() {
return bytes;
}
}
But when I switched everything to test TS.ADD, it's doesn't work even though I added additional args accordingly.
e.g.
String result = syncCommands.dispatch(TS.ADD, new StatusOutput<>(codec),new CommandArgs<>(codec).addKey("myTS").addValue("1000001").addValue("2.199"));
Here is the exception from the run:
Exception in thread "main" io.lettuce.core.RedisException: java.lang.IllegalStateException
at io.lettuce.core.LettuceFutures.awaitOrCancel(LettuceFutures.java:129)
at io.lettuce.core.FutureSyncInvocationHandler.handleInvocation(FutureSyncInvocationHandler.java:69)
at io.lettuce.core.internal.AbstractInvocationHandler.invoke(AbstractInvocationHandler.java:80)
at com.sun.proxy.$Proxy0.dispatch(Unknown Source)
at MyRedisClient.main(MyRedisClient.java:72)
Sorry for seeing this so late. If you haven't already found a solution I had initially implemented RediSearch commands using the dynamic commands.
public interface RediSearchCommands extends Commands {
#Command("FT.SUGADD ?0 ?1 ?2")
Long sugadd(String key, String string, double score);
...
}
public void testSuggestions() {
RedisCommandFactory factory = new RedisCommandFactory(client.connect());
RediSearchCommands commands = factory.getCommands(RediSearchCommands.class);
commands.sugadd(key, "Herbie Hancock", 1.0);
}
Full source: https://github.com/RediSearch/lettusearch/commit/567de42c147e0f07184df444cd1ae9798ae2514e

Streaming objects from S3 Object using Spring Aws Integration

I am working on a usecase where I am supposed to poll S3 -> read the stream for the content -> do some processing and upload it to another bucket rather than writing the file in my server.
I know I can achieve it using S3StreamingMessageSource in Spring aws integration but the problem I am facing is that I do not know on how to process the message stream received by polling
public class S3PollerConfigurationUsingStreaming {
#Value("${amazonProperties.bucketName}")
private String bucketName;
#Value("${amazonProperties.newBucket}")
private String newBucket;
#Autowired
private AmazonClientService amazonClient;
#Bean
#InboundChannelAdapter(value = "s3Channel", poller = #Poller(fixedDelay = "100"))
public MessageSource<InputStream> s3InboundStreamingMessageSource() {
S3StreamingMessageSource messageSource = new S3StreamingMessageSource(template());
messageSource.setRemoteDirectory(bucketName);
messageSource.setFilter(new S3PersistentAcceptOnceFileListFilter(new SimpleMetadataStore(),
"streaming"));
return messageSource;
}
#Bean
#Transformer(inputChannel = "s3Channel", outputChannel = "data")
public org.springframework.integration.transformer.Transformer transformer() {
return new StreamTransformer();
}
#Bean
public S3RemoteFileTemplate template() {
return new S3RemoteFileTemplate(new S3SessionFactory(amazonClient.getS3Client()));
}
#Bean
public PollableChannel s3Channel() {
return new QueueChannel();
}
#Bean
IntegrationFlow fileStreamingFlow() {
return IntegrationFlows
.from(s3InboundStreamingMessageSource(),
e -> e.poller(p -> p.fixedDelay(30, TimeUnit.SECONDS)))
.handle(streamFile())
.get();
}
}
Can someone please help me with the code to process the stream ?
Not sure what is your problem, but I see that you have a mix of concerns. If you use messaging annotations (see #InboundChannelAdapter in your config), what is the point to use the same s3InboundStreamingMessageSource in the IntegrationFlow definition?
Anyway it looks like you have already explored for yourself a StreamTransformer. This one has a charset property to convert your InputStreamfrom the remote S3 resource to the String. Otherwise it returns a byte[]. Everything else is up to you what and how to do with this converted content.
Also I don't see reason to have an s3Channel as a QueueChannel, since the start of your flow is pollable anyway by the #InboundChannelAdapter.
From big height I would say we have more questions to you, than vise versa...
UPDATE
Not clear what is your idea for InputStream processing, but that is really a fact that after S3StreamingMessageSource you are going to have exactly InputStream as a payload in the next handler.
Also not sure what is your streamFile(), but it must really expect InputStream as an input from the payload of the request message.
You also can use the mentioned StreamTransformer over there:
#Bean
IntegrationFlow fileStreamingFlow() {
return IntegrationFlows
.from(s3InboundStreamingMessageSource(),
e -> e.poller(p -> p.fixedDelay(30, TimeUnit.SECONDS)))
.transform(Transformers.fromStream("UTF-8"))
.get();
}
And the next .handle() will be ready for String as a payload.

Spring-data-redis with redis after a while get Exception: could not get a resource from the pool

I'm using spring-data-redis to access redis(one machine) with xml config file, on beginning, everything is ok, but after some minutes, i run my
test again, i got "could not get a resource from the pool" exception, i haved searched some answers, i guess this reason is the connections did
not return to the pool, how to solve this problem, why this problem can be occur, i'm using redis-3.2.6 spring-data-redis1.8 jedis2.9, below is my config
#Redis settings
redis.host=27.57.100.3
redis.port=6379
redis.pass=
maxTotal=5
maxIdle=3
minIdle=1
maxWaitMillis=10000
testOnBorrow=true
testOnReturn=true
testWhileIdle=true
timeBetweenEvictionRunsMillis=30000
numTestsPerEvictionRun=10
minEvictableIdleTimeMillis=60000
softMinEvictableIdleTimeMillis=10000
blockWhenExhausted=true
And here is my code :
#Autowired
StringRedisTemplate stringRedisTemplate
#Test
public void test(){
ValueOperations<String, String> vop = stringRedisTemplate.opsForValue();
String k = "k";
String v = "v";
vop.set(k, v);
String value = vop.get(k);
}
maxTotal=5, I thought 5 is too small, you can set it to e.g,20.