You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@flink.apache.org by tz...@apache.org on 2020/03/22 07:52:34 UTC
[flink-statefun] 06/11: [FLINK-16124] [kinesis] Implement runtime
GenericKinesisSinkProvider
This is an automated email from the ASF dual-hosted git repository.
tzulitai pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/flink-statefun.git
commit d0c74a754280aa31949b5aea96e50ac645aa9c52
Author: Tzu-Li (Gordon) Tai <tz...@apache.org>
AuthorDate: Fri Mar 20 13:23:50 2020 +0800
[FLINK-16124] [kinesis] Implement runtime GenericKinesisSinkProvider
---
.../flink/io/kinesis/KinesisSinkProvider.java | 2 +-
.../polyglot/GenericKinesisEgressSerializer.java | 62 +++++++++++++++
.../polyglot/GenericKinesisSinkProvider.java | 92 ++++++++++++++++++++++
.../polyglot/KinesisEgressSpecJsonParser.java | 43 ++++++++++
.../io/kinesis/GenericKinesisSinkProviderTest.java | 50 ++++++++++++
.../src/test/resources/generic-kinesis-egress.yaml | 32 ++++++++
.../flink/io/kinesis/PolyglotKinesisIOTypes.java | 4 +
7 files changed, 284 insertions(+), 1 deletion(-)
diff --git a/statefun-flink/statefun-flink-io-bundle/src/main/java/org/apache/flink/statefun/flink/io/kinesis/KinesisSinkProvider.java b/statefun-flink/statefun-flink-io-bundle/src/main/java/org/apache/flink/statefun/flink/io/kinesis/KinesisSinkProvider.java
index c1156b6..c969a0d 100644
--- a/statefun-flink/statefun-flink-io-bundle/src/main/java/org/apache/flink/statefun/flink/io/kinesis/KinesisSinkProvider.java
+++ b/statefun-flink/statefun-flink-io-bundle/src/main/java/org/apache/flink/statefun/flink/io/kinesis/KinesisSinkProvider.java
@@ -26,7 +26,7 @@ import org.apache.flink.statefun.sdk.kinesis.egress.KinesisEgressSpec;
import org.apache.flink.streaming.api.functions.sink.SinkFunction;
import org.apache.flink.streaming.connectors.kinesis.FlinkKinesisProducer;
-final class KinesisSinkProvider implements SinkProvider {
+public final class KinesisSinkProvider implements SinkProvider {
@Override
public <T> SinkFunction<T> forSpec(EgressSpec<T> spec) {
diff --git a/statefun-flink/statefun-flink-io-bundle/src/main/java/org/apache/flink/statefun/flink/io/kinesis/polyglot/GenericKinesisEgressSerializer.java b/statefun-flink/statefun-flink-io-bundle/src/main/java/org/apache/flink/statefun/flink/io/kinesis/polyglot/GenericKinesisEgressSerializer.java
new file mode 100644
index 0000000..db3db58
--- /dev/null
+++ b/statefun-flink/statefun-flink-io-bundle/src/main/java/org/apache/flink/statefun/flink/io/kinesis/polyglot/GenericKinesisEgressSerializer.java
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.flink.statefun.flink.io.kinesis.polyglot;
+
+import com.google.protobuf.Any;
+import com.google.protobuf.InvalidProtocolBufferException;
+import org.apache.flink.statefun.flink.io.generated.KinesisEgressRecord;
+import org.apache.flink.statefun.sdk.kinesis.egress.EgressRecord;
+import org.apache.flink.statefun.sdk.kinesis.egress.KinesisEgressSerializer;
+
+public final class GenericKinesisEgressSerializer implements KinesisEgressSerializer<Any> {
+
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ public EgressRecord serialize(Any value) {
+ final KinesisEgressRecord kinesisEgressRecord = asKinesisEgressRecord(value);
+
+ final EgressRecord.Builder builder =
+ EgressRecord.newBuilder()
+ .withData(kinesisEgressRecord.getValueBytes().toByteArray())
+ .withStream(kinesisEgressRecord.getStream())
+ .withPartitionKey(kinesisEgressRecord.getPartitionKey());
+
+ final String explicitHashKey = kinesisEgressRecord.getExplicitHashKey();
+ if (explicitHashKey != null && !explicitHashKey.isEmpty()) {
+ builder.withExplicitHashKey(explicitHashKey);
+ }
+
+ return builder.build();
+ }
+
+ private static KinesisEgressRecord asKinesisEgressRecord(Any message) {
+ if (!message.is(KinesisEgressRecord.class)) {
+ throw new IllegalStateException(
+ "The generic Kinesis egress expects only messages of type "
+ + KinesisEgressRecord.class.getName());
+ }
+ try {
+ return message.unpack(KinesisEgressRecord.class);
+ } catch (InvalidProtocolBufferException e) {
+ throw new RuntimeException(
+ "Unable to unpack message as a " + KinesisEgressRecord.class.getName(), e);
+ }
+ }
+}
diff --git a/statefun-flink/statefun-flink-io-bundle/src/main/java/org/apache/flink/statefun/flink/io/kinesis/polyglot/GenericKinesisSinkProvider.java b/statefun-flink/statefun-flink-io-bundle/src/main/java/org/apache/flink/statefun/flink/io/kinesis/polyglot/GenericKinesisSinkProvider.java
new file mode 100644
index 0000000..ad8fc1f
--- /dev/null
+++ b/statefun-flink/statefun-flink-io-bundle/src/main/java/org/apache/flink/statefun/flink/io/kinesis/polyglot/GenericKinesisSinkProvider.java
@@ -0,0 +1,92 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.flink.statefun.flink.io.kinesis.polyglot;
+
+import static org.apache.flink.statefun.flink.io.kinesis.polyglot.AwsAuthSpecJsonParser.optionalAwsCredentials;
+import static org.apache.flink.statefun.flink.io.kinesis.polyglot.AwsAuthSpecJsonParser.optionalAwsRegion;
+import static org.apache.flink.statefun.flink.io.kinesis.polyglot.KinesisEgressSpecJsonParser.clientConfigProperties;
+import static org.apache.flink.statefun.flink.io.kinesis.polyglot.KinesisEgressSpecJsonParser.optionalMaxOutstandingRecords;
+
+import com.google.protobuf.Any;
+import org.apache.flink.shaded.jackson2.com.fasterxml.jackson.databind.JsonNode;
+import org.apache.flink.statefun.flink.io.kinesis.KinesisSinkProvider;
+import org.apache.flink.statefun.flink.io.spi.JsonEgressSpec;
+import org.apache.flink.statefun.flink.io.spi.SinkProvider;
+import org.apache.flink.statefun.sdk.io.EgressIdentifier;
+import org.apache.flink.statefun.sdk.io.EgressSpec;
+import org.apache.flink.statefun.sdk.kinesis.egress.KinesisEgressBuilder;
+import org.apache.flink.statefun.sdk.kinesis.egress.KinesisEgressSpec;
+import org.apache.flink.streaming.api.functions.sink.SinkFunction;
+
+public final class GenericKinesisSinkProvider implements SinkProvider {
+
+ private final KinesisSinkProvider delegateProvider = new KinesisSinkProvider();
+
+ @Override
+ public <T> SinkFunction<T> forSpec(EgressSpec<T> spec) {
+ final KinesisEgressSpec<T> kinesisEgressSpec = asKinesisEgressSpec(spec);
+ return delegateProvider.forSpec(kinesisEgressSpec);
+ }
+
+ private static <T> KinesisEgressSpec<T> asKinesisEgressSpec(EgressSpec<T> spec) {
+ if (!(spec instanceof JsonEgressSpec)) {
+ throw new IllegalArgumentException("Wrong type " + spec.type());
+ }
+ JsonEgressSpec<T> casted = (JsonEgressSpec<T>) spec;
+
+ EgressIdentifier<T> id = casted.id();
+ validateConsumedType(id);
+
+ JsonNode specJson = casted.specJson();
+
+ KinesisEgressBuilder<T> kinesisEgressBuilder = KinesisEgressBuilder.forIdentifier(id);
+
+ optionalAwsRegion(specJson).ifPresent(kinesisEgressBuilder::withAwsRegion);
+ optionalAwsCredentials(specJson).ifPresent(kinesisEgressBuilder::withAwsCredentials);
+ optionalMaxOutstandingRecords(specJson)
+ .ifPresent(kinesisEgressBuilder::withMaxOutstandingRecords);
+ clientConfigProperties(specJson)
+ .entrySet()
+ .forEach(
+ entry ->
+ kinesisEgressBuilder.withClientConfigurationProperty(
+ entry.getKey(), entry.getValue()));
+
+ kinesisEgressBuilder.withSerializer(serializerClass());
+
+ return kinesisEgressBuilder.build();
+ }
+
+ private static void validateConsumedType(EgressIdentifier<?> id) {
+ Class<?> consumedType = id.consumedType();
+ if (Any.class != consumedType) {
+ throw new IllegalArgumentException(
+ "Generic Kinesis egress is only able to consume messages types of "
+ + Any.class.getName()
+ + " but "
+ + consumedType.getName()
+ + " is provided.");
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ private static <T> Class<T> serializerClass() {
+ // this cast is safe, because we've already validated that the consumed type is Any.
+ return (Class<T>) GenericKinesisEgressSerializer.class;
+ }
+}
diff --git a/statefun-flink/statefun-flink-io-bundle/src/main/java/org/apache/flink/statefun/flink/io/kinesis/polyglot/KinesisEgressSpecJsonParser.java b/statefun-flink/statefun-flink-io-bundle/src/main/java/org/apache/flink/statefun/flink/io/kinesis/polyglot/KinesisEgressSpecJsonParser.java
new file mode 100644
index 0000000..594623c
--- /dev/null
+++ b/statefun-flink/statefun-flink-io-bundle/src/main/java/org/apache/flink/statefun/flink/io/kinesis/polyglot/KinesisEgressSpecJsonParser.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.flink.statefun.flink.io.kinesis.polyglot;
+
+import java.util.Map;
+import java.util.OptionalInt;
+import org.apache.flink.shaded.jackson2.com.fasterxml.jackson.core.JsonPointer;
+import org.apache.flink.shaded.jackson2.com.fasterxml.jackson.databind.JsonNode;
+import org.apache.flink.statefun.flink.common.json.Selectors;
+
+final class KinesisEgressSpecJsonParser {
+
+ private KinesisEgressSpecJsonParser() {}
+
+ private static final JsonPointer MAX_OUTSTANDING_RECORDS_POINTER =
+ JsonPointer.compile("/maxOutstandingRecords");
+ private static final JsonPointer CLIENT_CONFIG_PROPS_POINTER =
+ JsonPointer.compile("/clientConfigProperties");
+
+ static OptionalInt optionalMaxOutstandingRecords(JsonNode ingressSpecNode) {
+ return Selectors.optionalIntegerAt(ingressSpecNode, MAX_OUTSTANDING_RECORDS_POINTER);
+ }
+
+ static Map<String, String> clientConfigProperties(JsonNode ingressSpecNode) {
+ return Selectors.propertiesAt(ingressSpecNode, CLIENT_CONFIG_PROPS_POINTER);
+ }
+}
diff --git a/statefun-flink/statefun-flink-io-bundle/src/test/java/org/apache/flink/statefun/flink/io/kinesis/GenericKinesisSinkProviderTest.java b/statefun-flink/statefun-flink-io-bundle/src/test/java/org/apache/flink/statefun/flink/io/kinesis/GenericKinesisSinkProviderTest.java
new file mode 100644
index 0000000..2a6b19b
--- /dev/null
+++ b/statefun-flink/statefun-flink-io-bundle/src/test/java/org/apache/flink/statefun/flink/io/kinesis/GenericKinesisSinkProviderTest.java
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.flink.statefun.flink.io.kinesis;
+
+import static org.apache.flink.statefun.flink.io.testutils.YamlUtils.loadAsJsonFromClassResource;
+import static org.hamcrest.CoreMatchers.instanceOf;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.google.protobuf.Any;
+import org.apache.flink.shaded.jackson2.com.fasterxml.jackson.databind.JsonNode;
+import org.apache.flink.statefun.flink.io.kinesis.polyglot.GenericKinesisSinkProvider;
+import org.apache.flink.statefun.flink.io.spi.JsonEgressSpec;
+import org.apache.flink.statefun.sdk.io.EgressIdentifier;
+import org.apache.flink.streaming.api.functions.sink.SinkFunction;
+import org.apache.flink.streaming.connectors.kinesis.FlinkKinesisProducer;
+import org.junit.Test;
+
+public class GenericKinesisSinkProviderTest {
+
+ @Test
+ public void exampleUsage() {
+ JsonNode egressDefinition =
+ loadAsJsonFromClassResource(getClass().getClassLoader(), "generic-kinesis-egress.yaml");
+ JsonEgressSpec<?> spec =
+ new JsonEgressSpec<>(
+ PolyglotKinesisIOTypes.GENERIC_KINESIS_EGRESS_TYPE,
+ new EgressIdentifier<>("foo", "bar", Any.class),
+ egressDefinition);
+
+ GenericKinesisSinkProvider provider = new GenericKinesisSinkProvider();
+ SinkFunction<?> source = provider.forSpec(spec);
+
+ assertThat(source, instanceOf(FlinkKinesisProducer.class));
+ }
+}
diff --git a/statefun-flink/statefun-flink-io-bundle/src/test/resources/generic-kinesis-egress.yaml b/statefun-flink/statefun-flink-io-bundle/src/test/resources/generic-kinesis-egress.yaml
new file mode 100644
index 0000000..902822d
--- /dev/null
+++ b/statefun-flink/statefun-flink-io-bundle/src/test/resources/generic-kinesis-egress.yaml
@@ -0,0 +1,32 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+egress:
+ meta:
+ type: statefun.kinesis.io/generic-egress
+ id: com.mycomp.foo/bar
+ spec:
+ awsRegion:
+ type: custom-endpoint
+ endpoint: https://localhost:4567
+ id: us-west-1
+ awsCredentials:
+ type: profile
+ profileName: john-doe
+ profilePath: /path/to/profile/config
+ maxOutstandingRecords: 9999
+ clientConfigProperties:
+ - ThreadingModel: POOLED
+ - ThreadPoolSize: 10
diff --git a/statefun-flink/statefun-flink-io/src/main/java/org/apache/flink/statefun/flink/io/kinesis/PolyglotKinesisIOTypes.java b/statefun-flink/statefun-flink-io/src/main/java/org/apache/flink/statefun/flink/io/kinesis/PolyglotKinesisIOTypes.java
index c6b384e..252e59d 100644
--- a/statefun-flink/statefun-flink-io/src/main/java/org/apache/flink/statefun/flink/io/kinesis/PolyglotKinesisIOTypes.java
+++ b/statefun-flink/statefun-flink-io/src/main/java/org/apache/flink/statefun/flink/io/kinesis/PolyglotKinesisIOTypes.java
@@ -18,6 +18,7 @@
package org.apache.flink.statefun.flink.io.kinesis;
+import org.apache.flink.statefun.sdk.EgressType;
import org.apache.flink.statefun.sdk.IngressType;
public final class PolyglotKinesisIOTypes {
@@ -26,4 +27,7 @@ public final class PolyglotKinesisIOTypes {
public static final IngressType ROUTABLE_PROTOBUF_KINESIS_INGRESS_TYPE =
new IngressType("statefun.kinesis.io", "routable-protobuf-ingress");
+
+ public static final EgressType GENERIC_KINESIS_EGRESS_TYPE =
+ new EgressType("statefun.kinesis.io", "generic-egress");
}