Aprenda a crear, registrar y usar funciones definidas por el usuario (UDF) de Java en trabajos de Flink CDC. Cuando las funciones integradas no satisfacen sus necesidades, las UDF personalizadas ofrecen formas flexibles de ampliar las capacidades de procesamiento de datos.
Funciones definidas por el usuario (UDF)
Si las funciones integradas de Flink CDC no cubren su lógica de transformación de datos (por ejemplo, cifrar o descifrar campos, analizar formatos de datos personalizados o invocar una biblioteca de terceros), escriba una UDF de Java e invoquela como cualquier otra función integrada.
El flujo de trabajo general consta de tres pasos:
Definir: Escriba una clase de Java que implemente la interfaz de la UDF.
Registrar: Declare la UDF en la sección
pipelinede la configuración de su trabajo de Flink CDC.Usar: Invoque la UDF en las expresiones de
projectionyfilterdentro de un bloquetransform.
Definir una UDF de Java
Para escribir una UDF en Java, primero cree un proyecto de Maven y agregue la siguiente dependencia base a su archivo pom.xml:
<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-cdc-common</artifactId>
<version>${Apache Flink CDC version}</version>
<scope>provided</scope>
</dependency>
Seleccione la versión de Apache Flink CDC que corresponda a la versión de su motor Ververica Runtime (VVR):
|
Versión del motor VVR |
Versión de Apache Flink CDC |
|
11.7 y posteriores |
3.6.0 |
|
11.3 a 11.6 |
3.5.0 |
|
11.0 a 11.2 |
3.4.0 |
|
8.0.11 |
3.3.0 |
|
8.0.10 y anteriores |
3.2.1 |
Una clase de Java debe cumplir los siguientes requisitos para funcionar como una UDF de Flink CDC:
Implementar la interfaz
org.apache.flink.cdc.common.udf.UserDefinedFunctionTener un constructor público sin parámetros
Tener como mínimo un método público llamado
eval
De forma opcional, puede sobrescribir los siguientes métodos para obtener mayor control:
getReturnType: Especifique explícitamente el tipo de retorno cuandoevaltenga un tipo de retorno ambiguo.openyclose: Agregue lógica de inicialización y limpieza para la gestión del ciclo de vida.
El siguiente ejemplo define una UDF que incrementa un número entero en 1:
public class AddOneFunctionClass implements UserDefinedFunction {
public Object eval(Integer num) {
return num + 1;
}
@Override
public DataType getReturnType() {
// The return type of eval is ambiguous (Object).
// Use getReturnType to declare the exact CDC type.
return DataTypes.INT();
}
@Override
public void open() throws Exception {
// Initialize resources here, for example, open a database connection.
}
@Override
public void close() throws Exception {
// Release resources here, for example, close a database connection.
}
}
Mapeo de tipos
La siguiente tabla muestra la correspondencia entre los tipos de columna de CDC y los tipos de Java utilizados en los parámetros del método eval, los valores de retorno y getReturnType:
|
Tipo de columna CDC |
Clase de Java |
|
BOOLEAN |
|
|
TINYINT |
|
|
SMALLINT |
|
|
INTEGER |
|
|
BIGINT |
|
|
FLOAT |
|
|
DOUBLE |
|
|
DECIMAL |
|
|
DATE |
|
|
TIME |
|
|
TIMESTAMP |
|
|
TIMESTAMP_TZ |
|
|
TIMESTAMP_LTZ |
|
|
CHAR / VARCHAR / STRING |
|
|
BINARY / VARBINARY / BYTES |
|
|
ARRAY |
|
|
MAP |
|
|
ROW |
|
|
VARIANT |
|
La ruta de la clase VARIANT difiere de la ruta de la clase Variant en Flink SQL.
Registrar una UDF de Java
Agregue una entrada user-defined-function a la sección pipeline de la configuración de su trabajo de Flink CDC:
pipeline:
user-defined-function:
- name: inc
classpath: org.apache.flink.cdc.udf.examples.java.AddOneFunctionClass
- name: format
classpath: org.apache.flink.cdc.udf.examples.java.FormatFunctionClass
En la pestaña Configurations tab del borrador de su trabajo, cargue el archivo JAR correspondiente al classpath especificado como dependencia adicional en la sección More Configurations. El campo name establece el alias que se usa en las expresiones; no es necesario que coincida con el nombre de la clase.
UDF de Python (vista previa pública)
Puede escribir UDF en línea directamente en Python dentro de los trabajos de ingesta YAML de Flink CDC, sin necesidad de crear un proyecto de Maven ni compilar paquetes JAR adicionales.
Para definir UDF con Python, debe actualizar a Ververica Runtime (VVR) 11.9 (incluidas las ediciones de vista previa) o posterior.
Esta es una función experimental.
Reglas de sintaxis
Una UDF de Python debe cumplir los siguientes requisitos:
Se debe definir una función de nivel superior llamada
evalenpython-code.La función
evaldebe declarar una anotación de tipo de retorno.El número de parámetros de la función
evaldebe coincidir con el número de argumentos pasados al invocar la UDF.Las anotaciones de tipo de parámetro no son obligatorias, pero se recomienda mantenerlas para mejorar la legibilidad del código.
Actualmente, las UDF de Python solo admiten funciones escalares. Cada invocación devuelve un único valor.
Los valores
NULLde SQL en los datos de entrada se convierten aNoneen Python.Una función de Python puede devolver
None. En ese caso, la salida seráNULLen SQL.
Por ejemplo, la siguiente UDF de Python elimina los espacios en blanco de la cadena de entrada y la convierte a minúsculas:
def eval(value: str) -> str:
return value.strip().lower()
Puede definir funciones auxiliares, importar módulos o declarar constantes en python-code, pero la función eval que realiza la transformación de datos debe estar en el nivel superior del código. Por ejemplo:
import re
EMAIL_PATTERN = re.compile(r"\s+")
def normalize(value):
return EMAIL_PATTERN.sub("", value).lower()
def eval(value: str) -> str:
if value is None: return None
return normalize(value)
Puede importar paquetes integrados de Python en el nivel superior del bloque de código. Para consultar la lista completa, consulte Pre-installed packages.
Mapeo de tipos
Flink CDC determina el tipo de columna de salida de una UDF según la anotación de tipo de retorno de la función eval. Actualmente, solo se admiten los siguientes tipos de parámetros y valores de retorno:
|
Anotación de tipo de retorno en Python |
Tipo de columna CDC |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Registrar una UDF de Python
Para registrar una UDF de Python, configure el parámetro python-code bajo pipeline.user-defined-function en un trabajo de ingesta de Flink CDC. La sintaxis de cadena multilínea | de YAML le permite escribir bloques de código Python con sangría de forma natural:
pipeline:
user-defined-function:
- name: normalize_email
python-code: |
def eval(value: str) -> str:
if value is None:
return None
return value.strip().lower()
- name: double_age
python-code: |
def eval(value: int) -> int:
if value is None:
return None
return value * 2
En este caso, name es el nombre de la función que se utiliza en las expresiones de transformación y no necesita coincidir con el nombre de la función de Python. La función que Flink CDC invoca en el código en línea siempre es eval.
Manejo de valores nulos y excepciones en UDF de Python
Los valores NULL en un pipeline de ingesta corresponden a None en Python. Si se invoca una UDF con un argumento NULL, la función eval de Python recibe None. Si la función eval de Python devuelve None, se utiliza NULL como valor de retorno de la UDF.
Si la función eval de Python genera una excepción en tiempo de ejecución, el operador de transformación desencadena un failover. Si existe la posibilidad de que su UDF falle, capture las excepciones mediante instrucciones try/except de Python para mantener la estabilidad del trabajo.
Parámetros de UDF de Python
Las UDF de Python admiten los siguientes parámetros.
|
Parámetro |
Descripción |
Obligatorio |
Tipo de dato |
Valor predeterminado |
Observaciones |
|
|
Nombre de la UDF registrada en el módulo de transformación. |
Sí |
STRING |
Ninguna |
Los nombres de las UDF deben ser únicos dentro de un mismo trabajo. |
|
|
Código fuente en línea de la UDF de Python. |
Sí |
STRING |
Ninguna |
Debe contener una función de nivel superior llamada |
|
|
Dependencias adicionales utilizadas por la UDF de Python. |
No |
LIST<STRING> |
Ninguna |
Si una UDF de Python requiere dependencias adicionales, empaquételas en un archivo .zip y cárguelo como dependencia adicional. Nota
Los archivos de dependencias adicionales se colocan en el directorio Por ejemplo, si el archivo de dependencia adicional es |
Usar una UDF
Después de registrar una UDF, puede invocarla directamente en el bloque de transformación, igual que una función integrada:
transform:
- source-table: db\.*
projection: "*, inc(inc(inc(id))) as inc_id, format(id, 'id -> %d') as formatted_id"
filter: inc(id) < 100
Compatibilidad con UDF de Flink SQL
Las UDF de Flink SQL que extienden ScalarFunction pueden registrarse e invocarse directamente como UDF de Flink CDC. Se aplican las siguientes limitaciones:
Parametrización: No se admiten clases
ScalarFunctionparametrizadas.Anotaciones de tipo: Las anotaciones
TypeInformational estilo de Flink se ignoran.Hooks de ciclo de vida: Los hooks de ciclo de vida
openycloseno se invocan.