Práctica técnica de productos secos | De flujo de trabajo a flujo de trabajo

El autor de este artículo: Scallion Pancake, ingeniero de front-end de Guanyuan, el equipo de aterrizaje desarrolla especificaciones, desarrolla calidad y velocidad, y se compromete a crear productos ABI más fáciles de usar.

fondo

Permítame darle un ejemplo simple: debido a las necesidades del trabajo, es posible que deba extraer datos de la base de datos todos los días, luego hacer un informe y finalmente enviarlo al líder correspondiente en forma de correo electrónico. Pero cada líder puede necesitar mirar cosas diferentes. Debe filtrar y procesar los datos antes de hacer un informe. Entonces, ¿este proceso repetitivo todos los días puede abstraerse en un flujo de trabajo específico y cada paso debe visualizarse? Conviértase en un nodo funcional , y luego conectarlos en serie en forma de tareas, y mostrarlos en la forma visualizada de DAG, ¿simplemente ejecutarlos regularmente todos los días? Para ello, necesitaremos un flujo de trabajo para estandarizar y automatizar este proceso.

¿Cuál es ese flujo de trabajo? ¿Qué es un DAG? Entremos en el contenido de hoy.

prefacio

Este artículo explicará los dos conceptos de flujo de trabajo y DAG en nuestro Universo (una de las tres principales líneas de productos de Guanyuan, es decir, la plataforma de desarrollo de datos inteligente de Guanyuan Data), y luego presentará otro contenido. El conjunto se divide en cuatro partes:

  1. Flujo de trabajo en la plataforma de desarrollo;
  2. Cómo implementar DAG de forma abstracta;
  3. Introducción a otros flujos de trabajo;
  4. Resumen y pensamiento basado en flujo de trabajo y DAG. Comencemos ~

1. Flujo de trabajo

Primero, presente brevemente el flujo de trabajo en Universe:

Realice las dependencias y el diseño de la secuencia de programación de varias tareas, visualice el proceso, diseñe y administre código bajo y configure nodos de tareas de forma rápida y altamente disponible para procesar una serie de tareas de datos; y puede cumplir con el tiempo acordado Se ejecuta después de que el evento es dependiente, llama a cada nodo de tarea de manera ordenada y completa automáticamente el proceso de procesamiento de datos.Tiene las ventajas de facilidad de uso, alta confiabilidad y alta escalabilidad.

De acuerdo con esta descripción, podemos resumir brevemente las dos capacidades principales del flujo de trabajo:

  1. Planificación;
  2. Configuración (nodo).

Estas dos competencias básicas se describen en detalle a continuación.

1.1 Programación

La plataforma de desarrollo admite la programación de tiempo basada en   expresiones  Cron y la programación de eventos basada en dependencias de datos de origen de entrada, y la programación de tiempo utiliza el  programador distribuido de cuarzo . Tiene las siguientes características:

  • alta disponibilidad
    • Realice la disposición visual de los nodos de tareas a través de DAG, sin la necesidad de costos complicados de aprendizaje de idiomas de plataforma, y ​​la programación de tareas está lista para usar;
    • Admite la configuración de varias relaciones de programación, como la programación secuencial, la programación exitosa y la programación fallida, y ajusta de manera flexible las estrategias de programación;
    • Admite la ejecución de flujos de trabajo de forma regular, diaria, semanal, mensual, etc., y los resultados de la ejecución se pueden enviar rápidamente a plataformas como DingTalk y Enterprise WeChat. Una vez configurado, está disponible continuamente.
  • Alta confiabilidad: la arquitectura distribuida multimaestro y multitrabajador descentralizada evita un punto único de falla y mejora la confiabilidad del sistema.
  • Alta escalabilidad: se pueden desarrollar tipos de tareas y procesos personalizados en función del SDK para conectarse sin problemas.

1.1.1 Programación de tiempos

Soporte para establecer el tiempo en forma de diario/semanal/mensual/anual y preciso en minutos y duración del intervalo (hora/minuto).

Por ejemplo: espero que el flujo de trabajo se ejecute a las 7 de la mañana y a las 21 de la noche todos los días, luego puedo elegir la forma de -7 en punto/21 en punto-00 en punto todos los días, o establecer el intervalo de minutos/horas para ejecutar.

1.1.2 Programación de eventos

Generalmente, los flujos de trabajo tienen dependencias de fuentes de datos, como conjuntos de datos/bases de datos. Cuando se actualizan todas las fuentes de datos dependientes, el flujo de trabajo se puede ejecutar automáticamente una vez.

1.2 Configuración

Sobre la base de una descripción de configuración convencional, se produce una interfaz de usuario interactiva para construir el objeto de destino.

El propósito de la programación es ejecutar el flujo de trabajo. La operación del flujo de trabajo depende de la configuración de diferentes nodos de tareas. Diferentes configuraciones inevitablemente tendrán diferentes componentes de interfaz de usuario. ¿Cómo podemos usar estructuras de datos conocidas para ensamblar una interfaz de usuario visual? La respuesta es configuración.

Leemos en función de una descripción de configuración (objeto), luego representamos los componentes correspondientes de acuerdo con la configuración y, al mismo tiempo, establecemos de forma centralizada los valores de los componentes en un objeto de configuración general, completando así una construcción desde la descripción hasta la interfaz de usuario y luego al proceso del objeto de destino. A continuación simplemente daré tres ejemplos para ilustrar el poder y el encanto de la configuración.

1.2.1 Capacidades básicas

Si necesitamos construir un objeto de destino de la siguiente manera:

{
    name: '',
    description: '',
}

Entonces tendremos la siguiente descripción de configuración:

[
    {
        fieldName: 'name',
        label: '名称',
        type: 'STRING',
        defaultValue: '',
    },
    {
        fieldName: 'description',
        label: '描述',
        type: 'TEXT',
        defaultValue: '',
    },
]

La interfaz de usuario resultante se ve así:

1.2.2 Capacidades dinámicas

Muchas veces necesitamos implementar dinámicamente un objeto de destino, ¿a qué te refieres? Es seleccionar diferentes valores de un atributo, usar dinámicamente un atributo para combinar en un nuevo objeto de destino, y luego correspondiente a la interfaz de usuario es seleccionar diferentes valores de atributo para mostrar diferentes componentes, lo que obviamente es imposible de lograr por confiando en nuestras capacidades básicas.

Por ejemplo, si quiero calcular el área de un gráfico, si un cuadrado necesita el atributo de longitud lateral y un círculo necesita el atributo de radio, entonces el objeto de destino y la interfaz de usuario se convertirán en:

  • Al elegir un cuadrado

{
    shape: 'square',
    side: 8,
}

  • Cuando se selecciona círculo
{
    shape: 'circle',
    radius: 4,
}
复制代码

Se puede ver  side que y   aparecen dinámicamente radius junto con él  , entonces simplemente podemos modificar la descripción de la configuración:shape

    {
        fieldName: 'shape',
        label: '图形',
        type: 'MODEL',
        model: {
            modelType: 'SELECT',
            labels: [ '圆', '正方形' ],
            values: [ 'circle', 'square' ],
        },
    },
    {
        fieldName: 'radius',
        label: '半径',
        type: 'NUMBER',
        dependsOnMap: {
            shape: [ 'circle' ],
        },
        defaultValue: 4,
    },
    {
        fieldName: 'side',
        label: '边长',
        type: 'NUMBER',
        dependsOnMap: {
            shape: [ 'square' ],
        },
        defaultValue: 8,
    },

Se puede ver que solo agregamos el atributo dependOnMap y luego lo adaptamos un poco al renderizar y construir objetos internamente, de modo que podamos elegir diferentes atributos para mostrar diferentes componentes.

Aquí hay una breve descripción del atributodependOnMap. Su valor clave debe ser un cierto nombre de campo, y su valor es una matriz, lo cual es conveniente para expandir la situación donde se permiten múltiples valores. De esta manera, se puede obtener el valor de acuerdo con el nombre del campo y en comparación con el valor en la configuración. Si es el mismo Luego, muestre el componente, la lógica central es la siguiente:

function isDependsOnMap (dependsOnMap, config) {
  const fieldNames = Object.keys(dependsOnMap || {})
  if (fieldNames.length === 0) return true
  return fieldNames.every(fieldName => {
    const values = dependsOnMap[fieldName] || []
    return values.indexOf(_get(config, fieldName)) > -1
  })
}

1.2.3 Capacidades complejas

En nuestra escritura diaria, también puede haber transferencia de datos entre componentes. Debido a las restricciones de objetos descritas en la configuración, en realidad somos independientes al renderizar cada componente y no hay conexión entre los componentes. Por esta razón, solo necesitamos implementar una capa de intercambio de datos en la parte superior. El componente 3 requiere la transmisión. los datos se colocan en la capa de intercambio de datos, y el componente 1 que necesita los datos puede obtenerlos directamente.

La configuración es la siguiente:

    {
        fieldName: 'fieldName1',
        label: '组件1',
        type: 'MODEL',
        model: {
            modelType: 'SELECT',
            labels: [ '圆', '正方形' ],
            values: [ 'circle', 'square' ],
            from: { fieldName: 'disabledFieldName' }, // 依赖于组件3里的设置,判读当前组件是否需要 disabled
        },
    },
    {
        fieldName: 'fieldName2',
        label: '组件2',
        type: 'NUMBER',
    },
    {
        fieldName: 'fieldName3',
        label: '组件3',
        type: 'MODEL',
        model: {
            modelType: 'BOOLEAN',
            targetSharedFieldName: 'disabledFieldName', // 往数据共享层设置数据的字段
        },
    },

Los atributos de configuración clave están en el componente 3  model.targetSharedFieldName y en el componente 1  model.from, y los dos pueden corresponderse entre sí. La implementación general es la siguiente:

const SharedContext = React.createContext({
  updateFieldValue: () => {}, // 更新字段 value
  getFieldValue: () => {}, // 获取字段 value
})

function Comp1 ({ definition }) {
  const { targetSharedFieldName } = definition.model
  const { updateFieldValue } = useContext(SharedContext)

  useEffect(() => {
    updateFieldValue(targetSharedFieldName, value)
  }, [ deps ])
}

function Comp2 ({ definition }) {
  const { from } = definition.model
  const { getFieldValue } = useContext(SharedContext)
  const value = getFieldValue(from)
}

Finalmente, simplemente una animación de interfaz de usuario de configuración compleja en la plataforma de desarrollo anterior, y sienta el poder y el encanto de la configuración:

1.2.4 Capacidades de servicio

Cuando necesitamos construir algunos objetos de destino tipo matriz, lo primero que pensamos es mostrar la interfaz de usuario en forma de lista, por lo que diseñamos algunos componentes de tipo servicio, que solo son responsables de representar la lista, pero el Los componentes de cada lista se basan en El tipo de los elementos de la matriz se determina. Por ejemplo, necesitamos un objeto de destino similar a una matriz:

{
    list: [
        { name: 'a', age: 12 },
        { name: 'b', age: 18 },
    ],
}

La descripción de la configuración correspondiente se puede escribir de la siguiente manera:

[
    {
        fieldName: 'list',
        label: '列表',
        type: 'MODEL',
        model: {
            modelType: 'LIST',
            definitions: [
                {
                    fieldName: 'name',
                    label: '名称',
                    type: 'STRING',
                },
                {
                    fieldName: 'age',
                    label: '年龄',
                    type: 'NUMBER',
                },
            ],
        },
    },
]

La interfaz de usuario correspondiente es la siguiente:

Este  LIST componente es un componente de tipo servicio que muestra objetos de matriz en forma de lista.

1.2.5 Capacidades de registro

Es posible que los componentes integrados no cumplan completamente con los requisitos de configuración, ya que la configuración es solo una convención, pero dibujar la interfaz de usuario mediante la creación de objetos es gratuito y las formas de visualización varían ampliamente. Por esta razón, proporcionamos un mecanismo de registro. Los usuarios pueden personalizar el tipo de componente registrado para dibujar el objeto de destino correspondiente.

1.3 Resumen

En base a estas excelentes capacidades de configuración, debe abstraerse, por lo que también se usa en los gráficos personalizados de BI. En base a esto, escribimos una biblioteca llamada  Lego . Como su nombre lo indica, esperamos que al construir algunas IU dedicadas a la configuración, sea tan simple como los bloques de construcción. La descripción (interfaz) está acordada y usted puede armarla.

Después de presentar el flujo de trabajo, también necesitamos una interfaz visual para describir el proceso, por lo que DAG es, sin duda, una buena forma de visualización.

Dos, DAG

El nombre completo de DAG  es Gráfico acíclico dirigido , que significa Gráfico acíclico dirigido en chino . Consiste en un número finito de vértices y "aristas dirigidas". Partiendo de cualquier vértice, pasando por varias aristas dirigidas, es imposible volver a este vértice. Por ejemplo, como se muestra en la siguiente figura:

Después de una comprensión simple del concepto de DAG, ¿cómo abstraer un DAG simple y fácil de usar para el escenario de flujo de trabajo de la plataforma de desarrollo? Primero determine qué información y estado se necesitan para dibujar un DAG:

  • Información de nodo (nodos)
  • Ubicación del nodo (ubicación)
  • Información de conexión (bordes)
  • Editar y estado de solo lectura\

Los primeros tres puntos son fáciles de entender. Deberían ser los tres elementos esenciales para dibujar DAG. Expliquemos sobre el cuarto punto, porque el flujo de trabajo de la plataforma de desarrollo tiene el concepto de estar en línea y fuera de línea. Una vez que se completa el desarrollo, se pondrá en línea y se ejecutará sin modificaciones. Si se trata de una especificación en el desarrollo del almacén, entonces nuestro flujo de trabajo tiene la distinción de editable sin conexión y de solo lectura en línea.

Primero comience con la edición y solo lectura, podemos dividir DAG en dos partes: Playground y Renderer, que se pueden usar de forma independiente. Playground corresponde al estado de edición y Renderer corresponde al estado de solo lectura. Playground debe generar la información del dibujo en el estado de edición en tiempo real, y Renderer es responsable de renderizar en tiempo real de acuerdo con la información del dibujo. Luego, determinemos qué capacidades deberían estar disponibles en los estados de edición y de solo lectura:

  • Patio de juegos
    • arrastre de nodos
    • Adiciones y eliminaciones de conexiones
    • Añadir/Copiar nodo
    • Nodos de selección de fotogramas para copia/eliminación por lotes
    • Diseño automático/operaciones de deshacer
  • renderizador
    • Acercar y alejar
    • arrastre de lona
    • clic de nodo

Luego piénsalo más, ¿qué capacidades debería tener nuestro DAG? Aquí simplemente enumero los siguientes puntos en combinación con el uso de la plataforma de desarrollo:

  • Proporcione una configuración de estilo (como tamaño de nodo/ancho de conexión, etc.)
  • Adaptación de la anchura y la altura del soporte
  • Conexiones y nodos de dibujo personalizados
  • Mejora de otras capacidades de dibujo (como la función de anotación, que no pertenece a la función de DAG en sí, pero se considera implementada como una función extendida)

Hasta ahora, nuestro DAG probablemente tenga una estructura completa y una dirección de implementación:

|- ConfigContext              --- 配置层
     |- Playground            --- 编辑层
        |- ResponsiveProvider --- 自适应宽高层(可选)
           |- Renderer        --- 只读层,只做展示
              |- Nodes        --- 节点
              |- Edges        --- 连线

Probablemente sea así en uso:

2.1 Uso de solo lectura

<ConfigContext.Provider value={
   
   { node: { width: 56, height: 56 } }}>
 <ResponsiveProvider>
  <Renderer nodes={nodes} location={location} edges={edges} />
 </ResponsiveProvider>
</ConfigContext.Provider>

2.2 Uso editorial

<ConfigContext.Provider value={
   
   { node: { width: 60, height: 60 } }}>
 <Playground nodes={nodes} location={location} edges={edges} />
</ConfigContext.Provider>

2.3 Uso de conexiones y nodos personalizados

<ConfigContext.Provider value={
   
   { node: { width: 56, height: 56 } }}>
 <Renderer nodes={nodes} location={location} edges={edges}>
  <Nodes>
   {(props) => <CustomNode />}
  </Nodes>
  <Edges>
   {(props) => <CustomEdge />}
  </Edges>
 </Renderer>
</ConfigContext.Provider>

2.4 Dibujo subyacente

Aquí elegimos svg porque svg es lo suficientemente potente en el dibujo, admite css para personalizar estilos y también es conveniente para vincular eventos. Con esta dirección, podemos determinar a qué etiquetas corresponden a su vez los siguientes elementos:

Dibuje aproximadamente la siguiente estructura:

La ampliación, reducción y movimiento del lienzo se establecen a través de la propiedad viewBox

De acuerdo con la estructura html, debemos preocuparnos por cómo generar la conexión.Aquí, principalmente calculamos una curva cuadrática de Bezier (curvas cuadráticas) a través de las posiciones de los dos nodos para obtener una curva perfecta con simetría inversa, de la siguiente manera:

Aquí se explica cómo implementar la siguiente curva Bezier cuadrática en la etiqueta de ruta. Primero dibuje la información que requiere tres puntos, como se muestra en la siguiente animación:

En segundo lugar, debido a que nuestra curva es simétrica inversa, solo necesitamos dibujar la mitad de ella. Esta mitad es una curva Bezier cuadrática, por lo que las posiciones de los tres puntos son fáciles de confirmar, de la siguiente manera:

Entre ellos, P0 es el punto de inicio y P4 es el punto final. Para facilitar el cálculo, P1 corresponde a 1/4 del espacio horizontal, y la altura es la misma que el punto de inicio, y P2 es 1/2 del espaciamiento horizontal y espaciamiento vertical Los puntos pueden ser sustituidos: d = M P0x P0y Q P1x P1y P2x P2y T P4x P4yDe esta manera, obtenemos una curva completa, que se compone de dos curvas de Bézier cuadráticas.

2.5 Diseño

Con los nodos y las conexiones, el diseño también es una parte muy importante. El arrastre manual obviamente no es lo suficientemente claro a veces. Si hay un algoritmo de diseño automático, será mucho más fácil. Aquí elegimos dagre como las herramientas de cálculo de diseño     automático . Hay tres algoritmos principales:

function rank(g) {
 switch(g.graph().ranker) {
  case "network-simplex": networkSimplexRanker(g); break;
  case "tight-tree": tightTreeRanker(g); break;
  case "longest-path": longestPathRanker(g); break;
  default: networkSimplexRanker(g);
 }
}

network-simplex Al igual  tight-tree que el diseño, el diseño se implementa de forma compacta. longest-path La diferencia es que si hay varios nodos finales, se garantiza que estos nodos se alinearán de arriba a abajo en lugar del diseño más cercano, como se muestra en la siguiente figura:

  • network-simplex y tight-tree

  • longest-path

3. Otros flujos de trabajo

Aquí no se presentará en detalle cómo usar estos flujos de trabajo, pero se tomarán prestadas algunas ideas sobre cómo dibujar y aplicar flujos de trabajo.

3.1 **** n8n

La plataforma de automatización del flujo de trabajo que no te encasilla, que nunca superas.

  n8n es compatible con el flujo de trabajo de programación de tiempos controlado por eventos (generalmente a través de ganchos de aplicaciones de terceros/monitoreo de modificación de archivos locales, etc.) y   expresión cron , y al mismo tiempo determina las dependencias entre nodos en el orden de entrega de datos. Es muy similar a nuestro flujo de trabajo, excepto que nuestro flujo de trabajo depende de la programación de tareas del nodo, no de los datos.

3.1.1 Aplicación

Entonces, ¿para qué es adecuado? Como se muestra abajo:

Si es un entusiasta del código abierto y desea conocer las noticias de inmediato cuando su Github Repo se destaca o se elimina de la estrella, puede usar el gancho de estrella abierta de github y luego enviarse un mensaje a través de slack. A través de la integración de plataformas de terceros, varias aplicaciones no relacionadas pueden conectarse en serie para desarrollar un flujo de trabajo conveniente. \

3.1.2 Resumen

En la actualidad, n8n ha integrado más de 200 aplicaciones, cubriendo la mayoría de las aplicaciones principales. Sin embargo, todavía faltan algunas aplicaciones domésticas, como DingTalk/Enterprise WeChat, etc., por lo que ha soportado con éxito nodos de desarrollo personalizados. Si está interesado, puede hacer clic  aquí  . En general, n8n es más como un flujo de trabajo para integrar aplicaciones y, por supuesto, también admite algunas funciones locales, como leer y escribir archivos, usar operaciones de git, etc. Puede integrar algunas operaciones comunes que deben escribirse y escribirse en nuestro trabajo o desarrollo diario en un flujo de trabajo, lo cual es conveniente para la vida diaria.

3.1.3 Referencia

A partir de su diseño de flujo de trabajo, quizás se puedan usar algunos puntos como referencia:

  • Al configurar un nodo, puede ver cuáles son los datos de salida del nodo anterior, lo cual es conveniente para la configuración en el paso actual
  • Después de configurar el nodo, se puede ejecutar inmediatamente y se pueden ver los datos de salida correspondientes
  • Hay algunas mejoras de visualización de datos en la conexión, como cuántas filas hay en los datos de salida
  • Se puede hacer clic directamente en los nodos para agregar y seleccionar el nodo posterior, lo que ahorra parte de la operación de conexión

3.1.4 Otros

Posteriormente, probé a ver si los nodos podían formar un bucle, y el resultado fue sí, pero la aplicación se quedó atascada en un bucle infinito, de la siguiente manera:

Los datos crecen infinitamente, ejecutando un bucle infinito.

3.2 naranja

Aprendizaje automático de código abierto y visualización de datos. Cree flujos de trabajo de análisis de datos visualmente, con una caja de herramientas grande y diversa.

3.2.1 Aplicación

Orange es más adecuado para el trabajo relacionado con ML. Es un poco como nuestro AI Flow, pero también integra funciones como flujo de datos/exploración de datos/análisis de gráficos en él. No necesita ir a otras páginas para configurar, procesar y ver por separado, y procesar datos en forma de flujo de trabajo Ver, procesar y analizar. Última imagen sencilla:

Hay un punto muy interesante, su conexión admite datos completos o datos seleccionados para la transmisión, como se muestra en la siguiente figura:

Luego, la forma de transferencia de datos se reflejará en la conexión. En cuanto a la conexión, muestra los puntos finales en forma de arcos (supongo que el uso de los arcos es para aumentar el área de conexión de los nodos y también adaptarse a los nodos circulares), si hay una conexión, será sólida. línea, y si no hay conexión, será Las líneas punteadas son muy amigables para mostrar el estado.

3.2.2 Resumen

La integración funcional de Orange es muy potente. Además de la conversión básica de datos, también tiene funciones como gráficos/modelos/evaluación, que es muy adecuado para el análisis de datos en la dirección de la IA.

4. Resumen y reflexión

El concepto de flujo de trabajo ha sido propuesto durante mucho tiempo, es una abstracción, generalización y descripción de las reglas de negocio entre el proceso y sus diferentes pasos de operación. La aparición del flujo de trabajo hace que nuestro proceso sea estandarizado y los pasos claros. El flujo de trabajo sobre el desarrollo de datos evita una serie de operaciones repetitivas y, al mismo tiempo, lo muestra en forma de DAG, lo que hace que el proceso sea más intuitivo. Por supuesto, DAG no se usa necesariamente en sistemas con restricciones de secuencia, como la programación, pero también se puede usar de otras formas, como la visualización de relaciones causales, como la relación de sangre de datos, o la visualización de gráficos familiares, y luego mejorar One La capa se puede usar incluso en una red de procesamiento de datos, donde los datos fluyen de un punto a otro, y no es necesario que se muestren necesariamente de forma visual, solo se requiere este concepto.

4.1 Posibilidades

De hecho, nuestro flujo de trabajo tiene poderosas capacidades de programación y funciones de configuración, pero está limitado por nodos funcionales limitados. Si podemos admitir nodos de configuración personalizados, los usuarios pueden tener más espacio para la imaginación en el desarrollo de datos. No se limita a estos nodos existentes para el flujo de trabajo. desarrollo.

Referencias

[1]  https://dolphinscheduler.apache.org/zh-cn/docs/latest/user_doc/about/introduction.html

[2]  https://en.wikipedia.org/wiki/Flujo de trabajo

[3]  https://en.wikipedia.org/wiki/Directed_acyclic_graph

[4]  https://github.com/biolab/orange3

[5]  https://github.com/n8n-io/n8n

Supongo que te gusta

Origin blog.csdn.net/GUANDATA_/article/details/125935346
Recomendado
Clasificación