One place for hosting & domains

      funciones

      Cómo ejecutar funciones sin servidores usando OpenFAaS en Kubernetes de DigitalOcean


      El autor seleccionó la Free and Open Source Fund para recibir una donación como parte del programa Write for DOnations.

      Introducción

      Normalmente, para alojar una aplicación de software en Internet se requiere gestión, planificación y seguimiento de infraestructuras para un sistema monolítico. A diferencia de este enfoque tradicional, la arquitectura sin servidores (también conocida como función como servicio o “FaaS”) descompone su aplicación en funciones. Estas funciones son entidades sin estados previos, autocontenidas, incluso activadas y funcionalmente completas que se comunican a través de las API que gestiona en lugar del aprovisionamiento de hardware subyacente o infraestructuras explícitas. Las funciones son escalables por diseño, portátiles, de configuración más rápida y más fáciles de probar que las aplicaciones comunes. Para funcionar, la arquitectura sin servidor en principio requiere un método para encapsular y organizar funciones independiente de la plataforma.

      OpenFaaS es un marco de código abierto que permite implementar la arquitectura sin servidor en Kubernetes usando contenedores de Docker para almacenar y ejecutar funciones. Permite que cualquier programa se encapsule como un contenedor y se administre como una función a través de la línea de comandos o la IU web integrada. OpenFaaS tiene una excelente compatibilidad para métricas y ofrece autoescalado para funciones cuando se incrementa la demanda.

      A través de este tutorial, implementará OpenFaaS a su clúster de Kubernetes de DigitalOcean en su dominio y lo protegerá usando certificados TLS gratuitos de Let’s Encrypt. También explorará su IU web e implementará funciones existentes y nuevas usando faas-cli, la herramienta de línea de comandos oficial. Al final, tendrá un sistema flexible para implementar funciones sin servidor.

      Requisitos previos

      • Un clúster de Kubernetes de DigitalOcean con su conexión configurada como kubectl predeterminado. El clúster debe tener al menos 8 GB de RAM y 4 núcleos de CPU disponibles para OpenFAaS (necesitará más si el uso es mayor). Verá las instrucciones para configurar kubectl en el paso Establecer conexión con su clúster cuando cree su clúster. Para crear un clúster de Kubernetes en DigitalOcean, consulte la Guía de inicio rápido de Kubernetes.
      • Docker instalado en su máquina local. Siguiendo los pasos 1 y 2 para su distribución, consulte Cómo instalar Docker.
      • Una cuenta en Docker Hub para almacenar imágenes de Docker que creará durante este tutorial.
      • faas-cli, la herramienta oficial de CLI para gestionar OpenFaaS, debe estar instalada en su máquina local. Si desea acceder a instrucciones para varias plataformas, consulte los documentos oficiales.
      • Un administrador de paquetes de Helm instalado en su máquina local. Para ello, complete el paso 1 y añada el repositorio stable del paso 2 del tutorial Cómo instalar software en clústeres de Kubernetes con el gestor de paquetes de Helm 3.
      • El controlador de Ingress de Nginx y Cert-Manager instalados en su clúster usando Helm para exponer OpenFaaS usando los recursos de Ingress. Para hacerlo, consulte Cómo configurar un Ingress de Nginx en Kubernetes de DigitalOcean usando Helm.
      • Un nombre de dominio registrado por completo para alojar OpenFaaS apuntando al equilibrador de carga utilizado por el Ingress de Nginx. Para este tutorial, se utilizará openfaas.your_domain en todo momento. Puede adquirir un nombre de dominio en Namecheap, obtener uno gratuito en Freenom o utilizar un registrador de dominios que elija.

      Nota: El nombre de dominio que use en este tutorial debe ser distinto del que use en el tutorial “Cómo configurar un Ingress de Nginx en Kubernetes de DigitalOcean”, de los requisitos previos.

      Paso 1: Instalar OpenFAaS usando Helm

      En este paso, instalará OpenFaaS en su clúster de Kubernetes usando Helm y lo expondrá en su dominio.

      Como parte del requisito previo del controlador de Ingress de Nginx, creó servicios de ejemplo y un Ingress. No los necesitará en este tutorial, por lo que puede eliminarlos ejecutando los siguientes comandos:

      • kubectl delete -f hello-kubernetes-first.yaml
      • kubectl delete -f hello-kubernetes-second.yaml
      • kubectl delete -f hello-kubernetes-ingress.yaml

      Debido a que implementará funciones como objetos de Kubernetes, le resultará útil almacenarlas y hacer lo mismo con el propio OpenFaaS en espacios de nombres separados en su clúster. El espacio de nombre de OpenFaaS se llamará openfaas y el de las funciones se llamará openfaas-fn. Créelos en su clúster ejecutando el siguiente comando:

      • kubectl apply -f https://raw.githubusercontent.com/openfaas/faas-netes/master/namespaces.yml

      Verá el siguiente resultado:

      Output

      namespace/openfaas created namespace/openfaas-fn created

      A continuación, deberá añadir el repositorio de Helm de OpenFaaS, en el que se aloja el chart de OpenFaaS. Para hacer esto, ejecute el siguiente comando:

      • helm repo add openfaas https://openfaas.github.io/faas-netes/

      Helm mostrará el siguiente resultado:

      Output

      "openfaas" has been added to your repositories

      Actualice la caché del chart de Helm:

      Verá el siguiente resultado:

      Output

      Hang tight while we grab the latest from your chart repositories... ...Successfully got an update from the "openfaas" chart repository ...Successfully got an update from the "jetstack" chart repository ...Successfully got an update from the "stable" chart repository Update Complete. ⎈ Happy Helming!⎈

      Antes de instalar OpenFaaS, deberá personalizar algunos parámetros del chart. Los almacenará en su máquina local, en un archivo llamado values.yaml. Cree y abra el archivo con su editor de texto:

      Añada las siguientes líneas:

      values.yaml

      functionNamespace: openfaas-fn
      generateBasicAuth: true
      
      ingress:
        enabled: true
        annotations:
          kubernetes.io/ingress.class: "nginx"
        hosts:
          - host: openfaas.your_domain
            serviceName: gateway
            servicePort: 8080
            path: /
      

      Primero, especifique el espacio de nombre donde se almacenarán las funciones asignando openfaas-fn a la variable functionNamespace. Al fijar generateBasicAuth en el valor true, ordenará a Helm que configure la autenticación obligatoria cuando acceda a la IU web de OpenFaaS y que genere una combinación de nombre de usuario y contraseña de administrador para usted.

      A continuación, habilite la creación de Ingress y configúrelo para que use el controlador de Ingress de Nginx y proporcione el servicio de OpenFaaS gateway en su dominio.

      Recuerde sustituir openfaas.your_domain por el dominio que desee de los requisitos previos. Cuando termine, guarde y cierre el archivo.

      Por último, instale OpenFaaS en el espacio de nombres openfass con los valores personalizados:

      • helm upgrade openfaas --install openfaas/openfaas --namespace openfaas -f values.yaml

      Verá lo siguiente:

      Output

      Release "openfaas" does not exist. Installing it now. NAME: openfaas LAST DEPLOYED: ... NAMESPACE: openfaas STATUS: deployed REVISION: 1 TEST SUITE: None NOTES: To verify that openfaas has started, run: kubectl -n openfaas get deployments -l "release=openfaas, app=openfaas" To retrieve the admin password, run: echo $(kubectl -n openfaas get secret basic-auth -o jsonpath="{.data.basic-auth-password}" | base64 --decode)

      Según se muestra en el resultado, la instalación se realizó correctamente. Ejecute el siguiente comando para ver la contraseña de la cuenta admin:

      • echo $(kubectl -n openfaas get secret basic-auth -o jsonpath="{.data.basic-auth-password}" | base64 --decode) | tee openfaas-password.txt

      La contraseña decodificada se escribe en el resultado y en un archivo llamado openfaas-password.txt al mismo tiempo usando tee. En el resultado, verá que esa es su contraseña de OpenFaaS para la cuenta admin.

      Puede ver los contenedores de OpenFaaS disponibles ejecutando el siguiente comando:

      • kubectl -n openfaas get deployments -l "release=openfaas, app=openfaas"

      Cuando todas las implementaciones se muestren como ready, escriba CTRL + C para salir.

      Ahora podrá dirigirse al dominio especificado en su navegador web. Introduzca admin como nombre de usuario y la contraseña correspondiente cuando se le solicite. Verá la IU web de OpenFaaS:

      OpenFaaS: panel de control vacío

      Instaló OpenFaaS correctamente y expuso su panel de control en su dominio. A continuación, lo protegerá usando certificados TLS gratuitos de Let´s Encrypt.

      Paso 2: Habilitar TLS para su dominio

      En este paso, protegerá su dominio expuesto usando certificados de Let´s Encrypt proporcionados por cert-manager.

      Para hacerlo, deberá editar la configuración de ingress en values.yaml. Ábralo​​​ para editarlo:

      Añada las líneas destacadas:

      values.yaml

      generateBasicAuth: true
      
      ingress:
        enabled: true
        annotations:
          kubernetes.io/ingress.class: "nginx"
          cert-manager.io/cluster-issuer: letsencrypt-prod
        tls:
          - hosts:
              - openfaas.your_domain
            secretName: openfaas-crt
        hosts:
          - host: openfaas.your_domain
            serviceName: gateway
            servicePort: 8080
            path: /
      

      El bloque tls define el secreto en el que se almacenarán los certificados de sus sitios (enumerados en hosts), lo que el ClusterIssuer letsencrypt-prod emite. Normalmente, el Secret especificado debe ser diferente para cada Ingress de su clúster.

      Recuerde sustituir openfaas.your_domain por el dominio que desee, y luego guarde y cierre el archivo.

      Aplique los cambios a su clúster ejecutando el siguiente comando:

      • helm upgrade openfaas --install openfaas/openfaas --namespace openfaas -f values.yaml

      Verá el siguiente resultado:

      Output

      Release "openfaas" has been upgraded. Happy Helming! NAME: openfaas LAST DEPLOYED: ... NAMESPACE: openfaas STATUS: deployed REVISION: 2 TEST SUITE: None NOTES: To verify that openfaas has started, run: kubectl -n openfaas get deployments -l "release=openfaas, app=openfaas" To retrieve the admin password, run: echo $(kubectl -n openfaas get secret basic-auth -o jsonpath="{.data.basic-auth-password}" | base64 --decode)

      Deberá esperar unos minutos para que los servidores de Let´s Encrypt emitan un certificado para su dominio. Mientras tanto, puede rastrear su progreso observando el resultado del siguiente comando:

      • kubectl describe certificate openfaas-crt -n openfaas

      El final del resultado será similar a este:

      Output

      Events: Type Reason Age From Message ---- ------ ---- ---- ------- Normal GeneratedKey 24m cert-manager Generated a new private key Normal Requested 16m cert-manager Created new CertificateRequest resource "openfaas-crt-1017759607" Normal Issued 16m cert-manager Certificate issued successfully

      Cuando en la última línea del resultado se muestre Certificate issued successfully, podrá salir pulsando CTRL + C. Reinice su dominio en su navegador para probar. En la parte izquierda de la barra de direcciones de su navegador, visualizará el candado. Este indicará que la conexión es segura.

      De esta manera, protegió su dominio de OpenFaaS usando certificados TLS gratuitos de Let´s Encrypt. Ahora usará la IU web y administrará funciones desde ella.

      Paso 3: Implementar funciones a través de la IU web

      En esta sección, explorará la IU web de OpenFaaS y luego implementará, administrará e invocará funciones desde ella.

      La IU web de OpenFaaS consta de dos partes principales: en el lado izquierdo, una columna en la que se enumerarán las funciones implementadas y el panel central, donde verá información detallada sobre una función seleccionada y podrá interactuar con ella.

      Para implementar una nueva función, haga clic en el botón Deploy New Function que está debajo del logo de OpenFaaS, en la parte superior izquierda. Verá un cuadro de diálogo en el que se le solicitará elegir una función:

      OpenFaaS: cuadro de diálogo Deploy a New Function

      En la pestaña FROM STORE, se enumeran funciones previamente creadas desde la OpenFass Function Store que puede implementar directamente. Cada función tiene una descripción breve y puede seleccionar el ícono del enlace a la derecha de una función para ver su código fuente. Para implementar una función de almacén desde esta lista, selecciónela y haga clic en el botón DEPLOY.

      También puede suministrar su propia función pasando a la pestaña CUSTOM:

      OpenFaaS: implementar una función personalizada

      Aquí, deberá especificar una imagen de Docker de su función que esté configurada específicamente para OpenFaaS y disponible en un registro de Docker (como Docker Hub). En este paso, implementará una función ya creada desde la tienda de OpenFaaS y luego, en los siguientes pasos, creará e implementará funciones personalizadas en Docker Hub.

      Aplicará la función NodeInfo, que ofrece datos sobre la máquina en la que se implementa, como los de arquitectura de CPU, número de núcleos, memoria RAM total disponible y tiempo de actividad (en segundos).

      Desde la lista de funciones de la tienda, seleccione NodeInfo y haga clic en DEPLOY. Pronto aparecerá en la lista de funciones implementadas.

      OpenFaaS: implementación de NodeInfo

      Selecciónelo. En la parte central de la pantalla, verá información básica sobre la función implementada.

      OpenFaaS: información sobre la función implementada

      El estado de la función se actualiza en tiempo real y pronto debería pasar a ser Ready. Si su valor permanece en Not Ready durante mucho tiempo, es probable que su clúster carezca de los recursos para aceptar un nuevo pod. Puede consultar Cómo redimensionar Droplets si necesita información para solucionar el inconveniente.

      Una vez que el valor sea Ready, podrá acceder a la función implementada en la URL indicada. Para probar esto, puede visitar la URL en su navegador o invocarla desde el panel Invoke function situado debajo de la información de la función.

      OpenFaaS: Invoke function implementada

      Puede seleccionar entre Text, JSON y Download para indicar el tipo de respuesta que espera. Si desea que la solicitud sea POST en vez de GET, puede suministrar datos de la solicitud en el campo Request body.

      Para invocar la función nodeinfo, haga clic en el botón INVOKE. OpenFaaS creará y ejecutará una solicitud HTTP según las opciones seleccionadas y completará los campos de respuesta con los datos recibidos.

      OpenFaaS: respuesta de la función nodeinfo

      El estado de respuesta es HTTP 200 OK, lo cual significa que la solicitud se ejecutó correctamente. El cuerpo de respuesta contiene información del sistema que recopila la función NodeInfo, lo que significa que es posible acceder a ella de forma adecuada y que funciona correctamente.

      Para eliminar una función, selecciónela de la lista y haga clic en el ícono de la papelera de la esquina superior derecha de la página. Cuando se le solicite, haga clic en OK para confirmar. El estado de la función cambiará a Not Ready (es decir, que se eliminará del clúster) y esta pronto desaparecerá también de la IU.

      En este paso, utilizó la IU web de OpenFaaS, además de implementar y administrar funciones desde ella. Ahora aprenderá a implementar y administrar funciones de OpenFaaS usando la línea de comandos.

      Paso 4: Administrar funciones usando faas-cli

      En esta sección, configurará la faas-cli para que funcione con su clúster. A continuación, implementará y gestionará sus funciones existentes a través de la línea de comandos.

      Para evitar tener que especificar su dominio de OpenFaaS cada vez que ejecute faas-cli, lo almacenará en una variable de entorno llamada OPENFAAS_URL, cuyo valor tomará y usará faas-cli automáticamente durante la ejecución.

      Abra .bash_profile en su directorio de inicio para editarlo:

      Añada la siguiente línea:

      ~/.bash_profile

      . . .
      export OPENFAAS_URL=https://openfaas.your_domain
      

      Recuerde sustituir openfaas.your_domain por su dominio, y luego guarde y cierre el archivo.

      Para evitar tener que iniciar sesión de nuevo, evalúe manualmente el archivo:

      Ahora, asegúrese de tener faas-cli instalado en su máquina local. Si aún no lo instaló, hágalo siguiendo las instrucciones descritas en los documentos oficiales.

      A continuación, configure sus credenciales de inicio de sesión ejecutando el siguiente comando:

      • cat ~/openfaas-password.txt | faas-cli login --username admin --password-stdin

      El resultado tendrá este aspecto:

      Output

      Calling the OpenFaaS server to validate the credentials... credentials saved for admin https://openfaas.your_domain

      Para implementar una función desde el almacén, ejecute el siguiente comando:

      • faas store deploy function_name

      Puede intentar implementar nodeinfo ejecutando lo siguiente:

      • faas store deploy nodeinfo

      Verá un resultado como el siguiente:

      Output

      Deployed. 202 Accepted. URL: https://openfaas.your_domain/function/nodeinfo

      Para enumerar las funciones implementadas, ejecute faas list:

      Aparecerán sus funciones existentes:

      Output

      Function Invocations Replicas nodeinfo 0 1

      Para obtener información detallada sobre una función implementada, utilice faas describe:

      El resultado será similar a este:

      Name:                nodeinfo
      Status:              Ready
      Replicas:            1
      Available replicas:  1
      Invocations:         0
      Image:               functions/nodeinfo-http:latest
      Function process:
      URL:                 https://openfaas.your_domain/function/nodeinfo
      Async URL:           https://openfaas.your_domain/async-function/nodeinfo
      Labels:              faas_function : nodeinfo
                           uid : 514253614
      Annotations:         prometheus.io.scrape : false
      

      Puede invocar una función con faas invoque:

      Recibirá el siguiente mensaje:

      Output

      Reading from STDIN - hit (Control + D) to stop.

      A continuación, podrá proporcionar un cuerpo de solicitud. Si lo hace, el método será POST en vez de GET. Cuando termine con la entrada de datos o desee que la solicitud sea GET, presione CTRL + D. La faas-cli ejecutará la solicitud inferida y mostrará la respuesta, de igual manera que la IU web.

      Para eliminar una función, ejecute faas remove:

      Recibirá el siguiente resultado:

      Output

      Deleting: nodeinfo. Removing old function.

      Ejecute faas list de nuevo para ver que nodeinfo se eliminó:

      Output

      Function Invocations Replicas

      En este paso, implementó, enumeró, invocó y eliminó funciones de su clúster desde la línea de comandos usando faas-cli. En el siguiente paso, creará su propia función y la implementará en su clúster.

      Paso 5: Crear e implementar una nueva función

      Ahora, creará una función de ejemplo Node.JS usando faas-cli y la implementará en su clúster.

      La función resultante que creará se empaquetará como un contenedor de Docker y se publicará en Docker Hub. Para poder publicar contenedores, deberá iniciar sesión ejecutando el siguiente comando:

      Ingrese su nombre de usuario y contraseña de Docker Hub cuando se le solicite para finalizar el proceso de inicio de sesión.

      Almacene la función de ejemplo Node.JS en una carpeta llamada sample-js-function. Créela usando el siguiente comando:

      Diríjase a ella:

      Complete el directorio con la plantilla de una función de JS ejecutando el siguiente comando:

      • faas new sample-js --lang node

      El resultado tendrá el siguiente aspecto:

      Output

      2020/03/24 17:06:08 No templates found in current directory. 2020/03/24 17:06:08 Attempting to expand templates from https://github.com/openfaas/templates.git 2020/03/24 17:06:10 Fetched 19 template(s) : [csharp csharp-armhf dockerfile go go-armhf java11 java11-vert -x java8 node node-arm64 node-armhf node12 php7 python python-armhf python3 python3-armhf python3-debian ru by] from https://github.com/openfaas/templates.git Folder: sample-js created. ___ _____ ____ / _ _ __ ___ _ __ | ___|_ _ __ _/ ___| | | | | '_ / _ '_ | |_ / _` |/ _` ___ | |_| | |_) | __/ | | | _| (_| | (_| |___) | ___/| .__/ ___|_| |_|_| __,_|__,_|____/ |_| Function created in folder: sample-js Stack file written: sample-js.yml ...

      Como se muestra en el resultado, el código para la función se encuentra en la carpeta sample-js, mientras que la configuración de OpenFaaS para la función se encuentra en el archivo sample-js.yaml. En el directorio sample-js (que se parece a un proyecto normal de Node.Js) hay dos archivos: handler.js y package.json.

      handler.js contiene código de JS real que mostrará una respuesta cuando se invoque la función. El contenido del operador tiene el siguiente aspecto:

      sample-js-function/sample-js/handler.js

      "use strict"
      
      module.exports = async (context, callback) => {
          return {status: "done"}
      }
      

      Exporta una lambda function con dos parámetros, un context con datos de la solicitud y un callback que puede usar para transmitir datos de respuesta, en vez de solo devolverlo.

      Abra este archivo para editarlo:

      • nano sample-js/handler.js

      Cambie la línea resaltada como se indica:

      sample-js-function/sample-js/handler.js

      "use strict"
      
      module.exports = async (context, callback) => {
          return {status: "<h1>Hello Sammy!</h1>"}
      }
      

      Cuando termine, guarde y cierre el archivo. Cuando se invoque, esta función de OpenFaaS escribirá Hello Sammy! en la respuesta.

      A continuación, abra el archivo de configuración para su edición:

      Tendrá el siguiente aspecto:

      sample-js-function/sample-js.yml

      version: 1.0
      provider:
        name: openfaas
        gateway: https://openfaas.your_domain
      functions:
        sample-js:
          lang: node
          handler: ./sample-js
          image: sample-js:latest
      

      Para el provider, especifica openfaas y una puerta de enlace predeterminada. A continuación, define la función sample-js, especifica su lenguaje (node), su operador y nombre de imagen de Docker, que deberá modificar para incluir su nombre de usuario de la cuenta de Docker Hub, como se indica:

      sample-js-function/sample-js.yml

      version: 1.0
      provider:
        name: openfaas
        gateway: http://127.0.0.1:8080
      functions:
        sample-js:
          lang: node
          handler: ./sample-js
          image: your_docker_hub_username/sample-js:latest
      

      Guarde y cierre el archivo.

      A continuación, cree la imagen de Docker, envíela a Docker Hub e impleméntela en su clúster al mismo tiempo ejecutando el siguiente comando:

      Habrá muchos resultados (principalmente de Docker), que terminarán de forma similar a esta:

      Output

      . . . [0] < Pushing sample-js [your_docker_hub_username/sample-js:latest] done. [0] Worker done. Deploying: sample-js. Deployed. 202 Accepted. URL: https://openfaas.your_domain/function/sample-js

      Invoque su función recién implementada para asegurarse de que funcione:

      Pulse CTRL + D. Verá el siguiente resultado:

      Output

      <h1>Hello Sammy!</h1>

      Esto significa que la función se empaquetó e implementó correctamente.

      Puede eliminar la función ejecutando lo siguiente:

      Con esto. habrá creado e implementado correctamente una función de Node.JS personalizada en su instancia de OpenFaaS de su clúster.

      Conclusión

      Implementó OpenFaaS en su clúster de Kubernetes de DigitalOcean y está listo para implementar funciones previamente creadas y personalizadas, y para acceder a ellas. Ahora podrá implementar la Function como una arquitectura de servicio, que puede aumentar el uso de recursos y mejorar el rendimiento de sus aplicaciones.

      Si desea obtener más información sobre funciones de OpenFaaS avanzadas, como ajuste de escala automático para sus funciones implementadas y la supervisión del rendimiento de estas, consulte los documentos oficiales.



      Source link

      Cómo configurar y invocar funciones en Go


      Introducción

      Una función es una sección de código que, una vez definida, se puede volver a utilizar. Las funciones se utilizan para facilitar más la comprensión de su código mediante su división en tareas pequeñas y comprensibles que se pueden utilizar más de una vez en su programa.

      Go se entrega con una potente biblioteca estándar que cuenta con muchas funciones predefinidas. Las siguientes son algunas que probablemente ya conozca del paquete fmt:

      • fmt.Println(), que imprime objetos en una salida estándar (en mayoría de los casos, su terminal).
      • fmt.Printf(), que le permite dar formato a su resultado impreso.

      Los nombres de las funciones incluyen paréntesis y pueden contener parámetros.

      En este tutorial, repasaremos la forma de definir sus propias funciones para utilizarlas en sus proyectos de codificación.

      Cómo definir una función

      Comencemos convirtiendo el clásico programa “Hello, World!” en una función.

      Crearemos un nuevo archivo de texto en nuestro editor de texto preferido e invocaremos el programa hello.go. A continuación, definiremos la función.

      Las funciones se definen usando la palabra clave func. Luego, se muestra un nombre que usted elige y un conjunto de paréntesis que contienen cualquier parámetro que la función tome (pueden estar vacíos). Las líneas de código de las funciones se escriben entre llaves {}.

      En este caso, definiremos una función llamada hello():

      hello.go

      func hello() {}
      

      Con esto, se establece la instrucción inicial para crear una función.

      A partir de aquí, añadiremos una segunda línea para proporcionar las instrucciones de lo que hace la función. En este caso, imprimiremos Hello, World! en la consola:

      hello.go

      func hello() {
          fmt.Println("Hello, World!")
      }
      

      Ahora, nuestra función está definida por completo, pero si ejecutamos el programa en este momento no ocurrirá nada porque no invocamos la función.

      Por lo tanto, dentro de nuestro bloque main() de la función, invocaremos la función con hello():

      hello.go

      package main
      
      import "fmt"
      
      func main() {
          hello()
      }
      
      func hello() {
          fmt.Println("Hello, World!")
      }
      

      Ahora, ejecutaremos el programa:

      Obtendrá el siguiente resultado:

      Output

      Hello, World!

      Observe que también implementamos una función llamada main(). main() es una función especial que indica al compilador que este es el punto en el que se debe** iniciar** el programa. Para cualquier programa que quiera que sea ejecutable (un programa que pueda ejecutarse desde la línea de comandos), necesitará una función main(). La función main() debe aparecer una sola vez, debe estar en el paquete main() y no debe recibir ni mostrar argumentos. Esto permite la ejecución del programa en cualquier programa de Go. Como indica el siguiente ejemplo:

      main.go

      package main
      
      import "fmt"
      
      func main() {
          fmt.Println("this is the main section of the program")
      }
      

      Las funciones pueden ser más complejas que la función hello() que definimos. Entre otras opciones, podemos usar bucles for e instrucciones condicionales dentro de nuestro bloque de función.

      Por ejemplo, la siguiente función utiliza una instrucción condicional para verificar si la entrada de la variable name contiene una vocal. Luego, emplea un bucle for para la iteración sobre las letras de la cadena name.

      names.go

      package main
      
      import (
          "fmt"
          "strings"
      )
      
      func main() {
          names()
      }
      
      func names() {
          fmt.Println("Enter your name:")
      
          var name string
          fmt.Scanln(&name)
          // Check whether name has a vowel
          for _, v := range strings.ToLower(name) {
              if v == 'a' || v == 'e' || v == 'i' || v == 'o' || v == 'u' {
                  fmt.Println("Your name contains a vowel.")
                  return
              }
          }
          fmt.Println("Your name does not contain a vowel.")
      }
      

      La función name() que definimos aquí configura una variable name con entrada y, luego, establece una instrucción condicional dentro de un bucle for. Esto indica cómo se puede organizar el código dentro de la definición de una función. Sin embargo, dependiendo de lo que pretendamos de nuestro programa y de la forma en que queramos configurar nuestro código, tal vez nos convenga definir la instrucción condicional y el bucle for como dos funciones separadas.

      La definición de funciones dentro de un programa hace que nuestro código sea modular y reutilizable para que podamos invocar a las mismas funciones sin tener que volver a escribirlas.

      Trabajar con parámetros

      Hasta ahora, examinamos funciones con paréntesis vacíos que no toman argumentos, pero podemos definir parámetros en las definiciones de las funciones dentro de sus paréntesis.

      Un parámetro es una entidad con nombre en la definición de una función que especifica un argumento que la función puede aceptar. En Go, debe especificar el tipo de datos para cada parámetro.

      Vamos a crear un programa que repite una palabra una cantidad de veces especificada. Tomará un parámetro string llamado word y un parámetro int llamado reps que indicará la cantidad de veces que se debe repetir la palabra.

      repeat.go

      package main
      
      import "fmt"
      
      func main() {
          repeat("Sammy", 5)
      }
      
      func repeat(word string, reps int) {
          for i := 0; i < reps; i++ {
              fmt.Print(word)
          }
      }
      

      Establecimos el valor Sammy para el parámetro word y el valor 5 para el parámetro reps. Estos valores corresponden a cada parámetro en el orden en el que se proporcionaron. La función repeat tiene un bucle for que ​​se repetirá la cantidad de veces especificada en el parámetro reps. En cada repetición, se imprime el valor del parámetro word.

      Este es el resultado del programa:

      Output

      SammySammySammySammySammy

      Si tiene un conjunto de parámetros con el mismo valor, puede omitir la repetición de la especificación de tipo. Crearemos un pequeño programa que tenga en cuenta los parámetros x, y y z, todos ellos valores int. Crearemos una función que añada los parámetros juntos en diferentes configuraciones. La función imprimirá la suma de ellas. Luego, invocaremos la función y le pasaremos números.

      add_numbers.go

      package main
      
      import "fmt"
      
      func main() {
          addNumbers(1, 2, 3)
      }
      
      func addNumbers(x, y, z int) {
          a := x + y
          b := x + z
          c := y + z
          fmt.Println(a, b, c)
      }
      

      Cuando creamos la firma de la función addNumbers, no necesitamos volver a especificar el tipo; solo lo hicimos al final.

      Pasamos el número 1 para el parámetro x, el 2 para el y y el 3 para el z. Estos valores corresponden a cada parámetro en el orden en el que se proporcionan.

      El programa realiza los siguientes cálculos sobre la base de los valores que pasamos a los parámetros:

      a = 1 + 2
      b = 1 + 3
      c = 2 + 3
      

      La función también imprime a, b y c, y según este cálculo esperamos que a sea igual a 3, b a 4, y c a 5. Ejecutaremos el programa:

      Output

      3 4 5

      Cuando pasamos 1, 2 y 3 como parámetros a la función addNumbers(), recibimos el resultado esperado.

      Los parámetros son argumentos que suelen definirse como variables en las definiciones de las funciones. Les puede asignar valores cuando ejecute el método y pasar los argumentos a la función.

      Mostrar un valor

      Puede pasar un valor de un parámetro a una función y una función también puede producir un valor.

      Una función puede producir un valor con la instrucción return, que producirá el cierre de una función y, de forma opcional, pasará una expresión al autor de la llamada. El tipo de datos que mostrado también debe especificarse.

      Hasta ahora, usamos la instrucción fmt.Println() en lugar de return en nuestras funciones. Crearemos un programa que, en lugar de generar impresiones, mostrará una variable.

      En un nuevo archivo de texto llamado double.go, crearemos un programa que duplicará el parámetro x y mostrará la variable y. Realizaremos una invocación para imprimir la variable result, que se forma ejecutando la función double() con el valor 3 pasado:

      double.go

      package main
      
      import "fmt"
      
      func main() {
          result := double(3)
          fmt.Println(result)
      }
      
      func double(x int) int {
          y := x * 2
          return y
      }
      
      

      Podemos ejecutar el programa y ver el resultado:

      Output

      6

      El número entero 6 se muestra como resultado, que es lo esperado al multiplicar 3 por 2.

      Si una función especifica una devolución, debe mostrarla como parte del código. Si no lo hace, verá un error de compilación.

      Podemos demostrar esto excluyendo la línea con la instrucción return:

      double.go

      package main
      
      import "fmt"
      
      func main() {
          result := double(3)
          fmt.Println(result)
      }
      
      func double(x int) int {
          y := x * 2
          // return y
      }
      
      

      Ahora, ejecutaremos el programa de nuevo:

      Output

      ./double.go:13:1: missing return at end of function

      Sin usar la instrucción return aquí, el programa no puede realizar la compilación.

      Las funciones se cerrarán de inmediato cuando hallen una instrucción return, aun cuando no se encuentre al final de la función:

      return_loop.go

      package main
      
      import "fmt"
      
      func main() {
          loopFive()
      }
      
      func loopFive() {
          for i := 0; i < 25; i++ {
              fmt.Print(i)
              if i == 5 {
                  // Stop function at i == 5
                  return
              }
          }
          fmt.Println("This line will not execute.")
      }
      

      Aquí, iteramos un bucle for, e indicamos a este que ejecute 25 iteraciones. Sin embargo, dentro del bucle for, hay una instrucción condicional if que comprueba si el valor de i es igual a 5. Si lo es, emitiremos una instrucción return. Debido a que se trata de la función loopFive, una instrucción return en cualquier punto de la función la cerrará. Como resultado, nunca llegamos a la última línea de esta función para imprimir la instrucción. This line will not execute..

      El uso de la instrucción return dentro del bucle for finaliza la función, por lo que la línea que se encuentra fuera del bucle no se ejecutará. Si, en su lugar, hubiéramos usado una instrucción break, solo el bucle se habría cerrado en ese momento y la última línea fmt.Println() se habría ejecutado.

      La instrucción return cierra una función y puede mostrar un valor si se especifica en la firma de la función.

      Mostrar varios valores

      Se puede especificar más de un valor de devolución para una función. Examinaremos el programa repeat.go y haremos que muestre dos valores. El primero será el valor repetido y el segundo será un error si el parámetro reps no es un valor superior a 0:

      repeat.go

      package main
      
      import "fmt"
      
      func main() {
          val, err := repeat("Sammy", -1)
          if err != nil {
              fmt.Println(err)
              return
          }
          fmt.Println(val)
      }
      
      func repeat(word string, reps int) (string, error) {
          if reps <= 0 {
              return "", fmt.Errorf("invalid value of %d provided for reps. value must be greater than 0.", reps)
          }
          var value string
          for i := 0; i < reps; i++ {
              value = value + word
          }
          return value, nil
      }
      

      Lo primero que hace la función repeat es verificar si el argumento reps es un valor válido. Cualquier valor que no sea superior a 0 provocará un error. Dado a que pasamos el valor -1, esta rama del código se ejecutará. Tenga en cuenta que cuando se realiza la devolución desde la función, se deben proporcionar los valores de devolución string y error. Debido a que los argumentos proporcionados produjeron un error, pasaremos una cadena en blanco para el primer valor mostrado y el error para el segundo.

      En la función main(), podemos recibir los dos valores de devolución declarando dos variables nuevas: value y err. Debido a que podría haber un error en la devolución, verificaremos si recibimos un error antes de continuar con nuestro programa. En este ejemplo, recibimos un error. Imprimiremos el error y aplicaremos return para la función main() a fin de cerrar el programa.

      Si no se produjo un error, imprimiremos el valor de devolución de la función.

      Nota: Se recomienda mostrar solo dos o tres valores. Además, todos los errores se deben mostrar siempre como el último valor de devolución de una función.

      Al ejecutar el programa, se obtendrá el siguiente resultado:

      Output

      invalid value of -1 provided for reps. value must be greater than 0.

      En esta sección, analizamos la manera de usar la instrucción return para mostrar varios valores de una función.

      Conclusión

      Las funciones son bloques de código de instrucciones que realizan acciones en un programa, lo cual contribuye a que nuestro código sea reutilizable y modular.

      Para obtener más información sobre cómo hacer que su código sea más modular, puede leer nuestra guía sobre Cómo escribir paquetes en Go.



      Source link

      Cómo usar funciones variádicas en Go


      Introducción

      Una función variádica acepta cero, uno o más valores como único argumento. Si bien las funciones variádicas son atípicas, pueden utilizarse para que su código sea más limpio y legible.

      Las funciones variádicas son más comunes de lo que parecen. La más común es la función Println del paquete fmt.

      func Println(a ...interface{}) (n int, err error)
      

      Una función con un parámetro precedido por una serie de elipses (...) se considera como una función variádica. La ellipsis implica que el parámetro que se proporciona puede ser cero, uno o más valores. En el paquete fmt.Println, se indica que el parámetro a es variádico.

      Crearemos un programa que utilice la función fmt.Println y que pase cero, uno o más valores:

      print.go

      package main
      
      import "fmt"
      
      func main() {
          fmt.Println()
          fmt.Println("one")
          fmt.Println("one", "two")
          fmt.Println("one", "two", "three")
      }
      

      La primera vez que invocamos fmt.Println, no pasamos argumentos. La segunda vez que invocamos fmt.Println solo se ejecuta un único argumento con el valor de one. Luego pasaremos one y two, y por último one, two y three.

      Ejecutaremos el programa con el siguiente comando:

      Veremos el siguiente resultado:

      Output

      one one two one two three

      La primera línea del resultado está vacía. Esto se debe a que no aprobamos argumentos la primera vez que se invocó a fmt.Println. La segunda vez se imprime el valor one. Luego se imprime one y two, y por último one, two y three.

      Ahora que vimos la forma de invocar una función variádica, veremos la forma de definir una propia.

      Definir una función variádica

      Podemos definir una función variádica usando puntos suspensivos (...) en frente del argumento. Crearemos un programa que salude a la gente cuando se envíen sus nombres a la función:

      hello.go

      package main
      
      import "fmt"
      
      func main() {
          sayHello()
          sayHello("Sammy")
          sayHello("Sammy", "Jessica", "Drew", "Jamie")
      }
      
      func sayHello(names ...string) {
          for _, n := range names {
              fmt.Printf("Hello %sn", n)
          }
      }
      

      Creamos la función sayHello que solo toma un único parámetro llamado names. El parámetro es variádico, ya que disponemos puntos suspensivos (...) antes del tipo de datos: ...string. Esto indica a Go que la función puede aceptar cero, uno o muchos argumentos.

      La función sayHello recibe el parámetro names como un slice. Debido a que el tipo de datos es un string, el parámetro names puede tratarse como un segmento de cadenas (​​​​​​[]string​​​​) dentro del cuerpo de la función. Podemos crear un bucle con el operador range e iterar el segmento de cadenas.

      Si ejecutamos el programa, obtendremos el siguiente resultado:

      Output

      Hello Sammy Hello Sammy Hello Jessica Hello Drew Hello Jamie

      Observe que no hubo impresiones la primera vez que invocamos a sayHello. Esto se debe a que el parámetro variádico fue un slice vacío de string. Debido a que repetimos el segmento, no hay elementos para iterar y nunca se invoca a fmt.Printf.

      Modificaremos el programa para detectar que no se enviaron valores:

      hello.go

      package main
      
      import "fmt"
      
      func main() {
          sayHello()
          sayHello("Sammy")
          sayHello("Sammy", "Jessica", "Drew", "Jamie")
      }
      
      func sayHello(names ...string) {
          if len(names) == 0 {
              fmt.Println("nobody to greet")
              return
          }
          for _, n := range names {
              fmt.Printf("Hello %sn", n)
          }
      }
      

      Ahora, usando una instrucción if, si no se pasan valores la extensión de names será 0 e imprimiremos nobody to greet:

      Output

      nobody to greet Hello Sammy Hello Sammy Hello Jessica Hello Drew Hello Jamie

      El uso de un parámetro de variable puede hacer que su código sea más legible. Crearemos una función que una las palabras con un delimitador especificado. Primero, crearemos este programa sin una función de variable para mostrar cómo se leería:

      join.go

      package main
      
      import "fmt"
      
      func main() {
          var line string
      
          line = join(",", []string{"Sammy", "Jessica", "Drew", "Jamie"})
          fmt.Println(line)
      
          line = join(",", []string{"Sammy", "Jessica"})
          fmt.Println(line)
      
          line = join(",", []string{"Sammy"})
          fmt.Println(line)
      }
      
      func join(del string, values []string) string {
          var line string
          for i, v := range values {
              line = line + v
              if i != len(values)-1 {
                  line = line + del
              }
          }
          return line
      }
      

      En este programa, pasaremos una coma (,) como delimitador a la función join. Luego, pasaremos un segmento de valores que se unirán. Este es el resultado:

      Output

      Sammy,Jessica,Drew,Jamie Sammy,Jessica Sammy

      Debido a que la función toma un segmento de cadena como parámetro values, debimos ajustar todas nuestras palabras en un segmento cuando invocamos la función join. Esto puede dificultar la lectura del código.

      Ahora, escribiremos la misma función, pero usaremos una función variádica:

      join.go

      package main
      
      import "fmt"
      
      func main() {
          var line string
      
          line = join(",", "Sammy", "Jessica", "Drew", "Jamie")
          fmt.Println(line)
      
          line = join(",", "Sammy", "Jessica")
          fmt.Println(line)
      
          line = join(",", "Sammy")
          fmt.Println(line)
      }
      
      func join(del string, values ...string) string {
          var line string
          for i, v := range values {
              line = line + v
              if i != len(values)-1 {
                  line = line + del
              }
          }
          return line
      }
      

      Si ejecutamos el programa, podemos ver que obtenemos el mismo resultado que el programa anterior:

      Output

      Sammy,Jessica,Drew,Jamie Sammy,Jessica Sammy

      Si bien ambas versiones de la función join hacen exactamente lo mismo mediante programación, la versión variádica de la función es mucho más fácil de leer cuando se invoca.

      Orden de argumentos variádicos

      Solo puede disponer de un parámetro variádico en una función y debe ser el último parámetro definido en esta. La definición de parámetros en una función variádica en cualquier orden que no sea el último parámetro dará como resultado un error de compilación:

      join.go

      package main
      
      import "fmt"
      
      func main() {
          var line string
      
          line = join(",", "Sammy", "Jessica", "Drew", "Jamie")
          fmt.Println(line)
      
          line = join(",", "Sammy", "Jessica")
          fmt.Println(line)
      
          line = join(",", "Sammy")
          fmt.Println(line)
      }
      
      func join(values ...string, del string) string {
          var line string
          for i, v := range values {
              line = line + v
              if i != len(values)-1 {
                  line = line + del
              }
          }
          return line
      }
      

      Esta vez, disponemos el parámetro values primero en la función join. Esto provocará el siguiente error de compilación:

      Output

      ./join_error.go:18:11: syntax error: cannot use ... with non-final parameter values

      Cuando se define cualquier función variádica, solo el último parámetro puede ser variádico.

      Expandir argumentos

      Hasta ahora, vimos que podemos pasar cero, uno o más valores a una función variádica. Sin embargo, habrá ocasiones en que dispongamos de un segmento de valores y queramos enviarlos a una función variádica.

      Veamos nuestra función join de la última sección para ver qué sucede:

      join.go

      package main
      
      import "fmt"
      
      func main() {
          var line string
      
          names := []string{"Sammy", "Jessica", "Drew", "Jamie"}
      
          line = join(",", names)
          fmt.Println(line)
      }
      
      func join(del string, values ...string) string {
          var line string
          for i, v := range values {
              line = line + v
              if i != len(values)-1 {
                  line = line + del
              }
          }
          return line
      }
      

      Si ejecutamos este programa, veremos un error de compilación:

      Output

      ./join-error.go:10:14: cannot use names (type []string) as type string in argument to join

      Aunque la función variable convertirá el parámetro values ...string en un segmento de cadenas strings[], no podemos pasar un segmento de cadenas como argumento. Esto se debe a que el compilador espera argumentos discretos de cadenas.

      Para hallar una solución a esto, podemos explandir un factor logístico agregándole como sufijo un conjunto de puntos suspensivos (...) y convertirlo en argumentos discretos que se pasarán a una función variádica:

      join.go

      package main
      
      import "fmt"
      
      func main() {
          var line string
      
          names := []string{"Sammy", "Jessica", "Drew", "Jamie"}
      
          line = join(",", names...)
          fmt.Println(line)
      }
      
      func join(del string, values ...string) string {
          var line string
          for i, v := range values {
              line = line + v
              if i != len(values)-1 {
                  line = line + del
              }
          }
          return line
      }
      

      Esta vez, cuando llamamos a la función join, expandimos el segmento names agregando puntos suspensivos (...).

      Esto permite que el programa ahore funcione como se espera:

      Output

      Sammy,Jessica,Drew,Jamie

      Es importante tener en cuenta que de todas formas podemos pasar cero, uno o más argumentos y un segmento que expandamos. A continuación, se ofrece el código que pasa todas las variaciones que hemos visto hasta ahora:

      join.go

      package main
      
      import "fmt"
      
      func main() {
          var line string
      
          line = join(",", []string{"Sammy", "Jessica", "Drew", "Jamie"}...)
          fmt.Println(line)
      
          line = join(",", "Sammy", "Jessica", "Drew", "Jamie")
          fmt.Println(line)
      
          line = join(",", "Sammy", "Jessica")
          fmt.Println(line)
      
          line = join(",", "Sammy")
          fmt.Println(line)
      
      }
      
      func join(del string, values ...string) string {
          var line string
          for i, v := range values {
              line = line + v
              if i != len(values)-1 {
                  line = line + del
              }
          }
          return line
      }
      

      Output

      Sammy,Jessica,Drew,Jamie Sammy,Jessica,Drew,Jamie Sammy,Jessica Sammy

      De esta manera, sabrá pasar cero, uno o muchos argumentos, y un segmento que expanda, a una función variádica.

      Conclusión

      En este tutorial, vio cómo las funciones variádicas pueden limpiar su código. Si bien no siempre necesitará usarlas, pueden resultarle útiles:

      • si determina que creará un segmento temporal solo para pasar a una función;
      • cuando el número de parámetros de entrada se desconoce o variará cuando se invoque;
      • para que su código sea más legible.

      Para obtener más información sobre la creación e invocación de funciones, puede leer Cómo definir e invocar funciones en Go.



      Source link