Definición de controladores de funciones de Lambda en Go - AWS Lambda

Definición de controladores de funciones de Lambda en Go

El controlador de la función de Lambda es el método del código de la función que procesa eventos. Cuando se invoca una función, Lambda ejecuta el método del controlador. La función se ejecuta hasta que el controlador devuelve una respuesta, se cierra o se agota el tiempo de espera.

En esta página, se describe cómo trabajar con los controladores de funciones de Lambda en Go, incluida la configuración del proyecto, las convenciones de nomenclatura y las prácticas recomendadas. Esta página también incluye un ejemplo de una función de Lambda de Go que recibe información sobre un pedido, genera un recibo en un archivo de texto y coloca este archivo en un bucket de Amazon Simple Storage Service (S3). Para obtener información sobre cómo implementar la función después de escribirla, consulte Implementar funciones de Lambda en Go con archivos .zip o Implemente funciones Go Lambda con imágenes de contenedor.

Configuración del proyecto de controlador de Go

Una función de Lambda escrita en Go se crea como ejecutable de Go. Puede inicializar un proyecto de función de Lambda de Go de la misma manera que inicializa cualquier otro proyecto de Go mediante el siguiente comando go mod init:

go mod init example-go

En este caso, example-go es el nombre del módulo. Puede sustituirlo por cualquier valor. Este comando inicializa el proyecto y genera el archivo go.mod que enumera las dependencias del proyecto.

Use el comando go get para agregar cualquier dependencia externa al proyecto. Por ejemplo, en todas las funciones de Lambda de Go, debe incluir el paquete github.com/aws/aws-lambda-go/lambda, que implementa el modelo de programación de Lambda para Go. Incluye este paquete con el siguiente comando go get:

go get github.com/aws/aws-lambda-go

El código de la función debe estar en un archivo de Go. En el siguiente ejemplo, el nombre de este archivo es main.go. En este archivo, implementa la lógica de la función básica en un método de controlador, así como una función main() que llama a este controlador.

Código de función de Lambda de Go de ejemplo

El siguiente ejemplo de código de función de Lambda de Go recibe información sobre un pedido, genera un recibo en un archivo de texto y coloca este archivo en un bucket de Amazon S3.

ejemplo Función de Lambda main.go
package main import ( "context" "encoding/json" "fmt" "log" "os" "strings" "github.com/aws/aws-lambda-go/lambda" "github.com/aws/aws-sdk-go-v2/config" "github.com/aws/aws-sdk-go-v2/service/s3" ) type Order struct { OrderID string `json:"order_id"` Amount float64 `json:"amount"` Item string `json:"item"` } var ( s3Client *s3.Client ) func init() { // Initialize the S3 client outside of the handler, during the init phase cfg, err := config.LoadDefaultConfig(context.TODO()) if err != nil { log.Fatalf("unable to load SDK config, %v", err) } s3Client = s3.NewFromConfig(cfg) } func uploadReceiptToS3(ctx context.Context, bucketName, key, receiptContent string) error { _, err := s3Client.PutObject(ctx, &s3.PutObjectInput{ Bucket: &bucketName, Key: &key, Body: strings.NewReader(receiptContent), }) if err != nil { log.Printf("Failed to upload receipt to S3: %v", err) return err } return nil } func handleRequest(ctx context.Context, event json.RawMessage) error { // Parse the input event var order Order if err := json.Unmarshal(event, &order); err != nil { log.Printf("Failed to unmarshal event: %v", err) return err } // Access environment variables bucketName := os.Getenv("RECEIPT_BUCKET") if bucketName == "" { log.Printf("RECEIPT_BUCKET environment variable is not set") return fmt.Errorf("missing required environment variable RECEIPT_BUCKET") } // Create the receipt content and key destination receiptContent := fmt.Sprintf("OrderID: %s\nAmount: $%.2f\nItem: %s", order.OrderID, order.Amount, order.Item) key := "receipts/" + order.OrderID + ".txt" // Upload the receipt to S3 using the helper method if err := uploadReceiptToS3(ctx, bucketName, key, receiptContent); err != nil { return err } log.Printf("Successfully processed order %s and stored receipt in S3 bucket %s", order.OrderID, bucketName) return nil } func main() { lambda.Start(handleRequest) }

Este archivo main.go contiene las siguientes secciones de código:

  • package main: en Go, el paquete que contiene la función func main() debe tener siempre el nombre main.

  • Bloque import: utilice este bloque para incluir las bibliotecas que requiere la función de Lambda.

  • Bloque type Order struct {}: defina la forma del evento de entrada esperado en esta estructura de Go.

  • Bloque var (): utilice este bloque para definir cualquier variable global que vaya a utilizar en la función de Lambda.

  • func init() {}: incluya cualquier código que desee que Lambda ejecute durante la fase de inicialización de este método init().

  • func uploadReceiptToS3(...) {}: este es un método auxiliar al que hace referencia el método del controlador principal handleRequest.

  • func handleRequest(ctx context.Context, event json.RawMessage) error {}: este es el método del controlador principal, que contiene la lógica principal de la aplicación.

  • func main() {}: este es un punto de entrada obligatorio para el controlador de Lambda. El argumento del método lambda.Start() es el método del controlador principal.

Para que esta función funcione correctamente, su rol de ejecución debe permitir la acción s3:PutObject. Además, asegúrese de definir la variable de entorno RECEIPT_BUCKET. Tras una invocación correcta, el bucket de Amazon S3 debe contener un archivo de recibo.

Convenciones de nomenclatura de controladores

En el caso de las funciones de Lambda de Go, es posible utilizar cualquier nombre para el controlador. En este ejemplo, el nombre del método del controlador es handleRequest. Para hacer referencia al valor del controlador en su código, puede utilizar la variable de entorno _HANDLER.

Para las funciones de Go implementadas que utilizan un paquete de implementación .zip, el archivo ejecutable que contiene el código de la función debe tener el nombre bootstrap. Además, el archivo bootstrap debe estar en la raíz del archivo .zip. Para las funciones de Go que utilizan una imagen de contenedor, puede utilizar cualquier nombre para el archivo ejecutable.

Definición del objeto de evento de entrada y acceso a él

El formato de entrada más común y estándar de las funciones de Lambda es JSON. En este ejemplo, la función espera una entrada similar a la siguiente:

{ "order_id": "12345", "amount": 199.99, "item": "Wireless Headphones" }

Al trabajar con funciones de Lambda en Go, puede definir la forma del evento de entrada esperado como una estructura de Go. En este ejemplo, definimos una estructura para representar Order:

type Order struct { OrderID string `json:"order_id"` Amount float64 `json:"amount"` Item string `json:"item"` }

Esta estructura coincide con la forma de entrada esperada. Después de definir la estructura, puede escribir una firma de controlador que incluya un tipo de JSON genérico compatible con la biblioteca estándar encoding/json. A continuación, puede deserializarla en la estructura mediante la función func Unmarshal. Esto se ilustra en las primeras líneas del controlador:

func handleRequest(ctx context.Context, event json.RawMessage) error { // Parse the input event var order Order if err := json.Unmarshal(event, &order); err != nil { log.Printf("Failed to unmarshal event: %v", err) return err ... }

Tras esta deserialización, puede acceder a los campos de la variable order. Por ejemplo, order.OrderID recupera el valor de "order_id" de la entrada original.

nota

El paquete encoding/json solo puede acceder a los campos exportados. Para que se exporten, los nombres de campo en la estructura de eventos deben estar en mayúsculas.

Acceso y uso del objeto de contexto de Lambda

El objeto de contexto de Lambda contiene información sobre la invocación, la función y el entorno de ejecución. En este ejemplo, declaramos esta variable como ctx en la firma del controlador:

func handleRequest(ctx context.Context, event json.RawMessage) error { ... }

La entrada ctx context.Context es un argumento opcional en el controlador de funciones. Para obtener más información sobre las firmas de controlador aceptadas, consulte Firmas de controlador válidas para los controladores de Go.

Si hace llamadas a otros servicios mediante el AWS SDK, el objeto de contexto es obligatorio en algunas áreas clave. Por ejemplo, para inicializar correctamente los clientes del SDK, puede cargar la configuración correcta del AWS SDK mediante el objeto de contexto de la siguiente manera:

// Load AWS SDK configuration using the default credential provider chain cfg, err := config.LoadDefaultConfig(ctx)

Las propias llamadas al SDK pueden requerir el objeto de contexto como entrada. Por ejemplo, la llamada s3Client.PutObject acepta el objeto de contexto como primer argumento:

// Upload the receipt to S3 _, err = s3Client.PutObject(ctx, &s3.PutObjectInput{ ... })

Además de las solicitudes del AWS SDK, también puede usar el objeto de contexto para supervisar las funciones. Para obtener más información acerca del objeto de contexto, consulte Uso del objeto de contexto Lambda para recuperar la información de la función Go.

Firmas de controlador válidas para los controladores de Go

Al crear un controlador de funciones de Lambda en Go tiene varias opciones, pero debe cumplir las reglas siguientes:

  • El controlador debe ser una función.

  • El controlador puede aceptar entre 0 y 2 argumentos. Si hay dos argumentos, el primero debe implementar context.Context.

  • El controlador puede devolver entre 0 y 2 argumentos. Si hay un único valor de retorno, este debe implementar error. Si hay dos valores de retorno, el segundo debe implementar error.

A continuación se enumeran las firmas de controlador válidas. TIn y TOut representan los tipos compatibles con la biblioteca estándar encoding/json. Para obtener más información, consulte func Unmarshal a fin de saber cómo se deserializan estos tipos.

  • func ()
  • func () error
  • func () (TOut, error)
  • func (TIn) error
  • func (TIn) (TOut, error)
  • func (context.Context) error
  • func (context.Context) (TOut, error)
  • func (context.Context, TIn) error
  • func (context.Context, TIn) (TOut, error)

Uso de la versión 2 de AWS SDK for Go en el controlador

A menudo, utilizará las funciones de Lambda para interactuar con otros recursos de AWS o actualizarlos. La forma más sencilla de interactuar con estos recursos es utilizar la versión 2 de AWS SDK for Go.

nota

La versión 1 de AWS SDK for Go está en modo de mantenimiento y está previsto que deje de ser compatible el 31 de julio de 2025. Le recomendamos que utilice únicamente la versión 2 de AWS SDK for Go ahora en adelante.

Para agregar dependencias del SDK a la función, utilice el comando go get para los clientes del SDK específicos que necesite. En el código de ejemplo anterior, utilizamos la biblioteca config y la biblioteca s3. Para agregar estas dependencias, ejecute los siguientes comandos en el directorio que contiene los archivos go.mod y main.go :

go get github.com/aws/aws-sdk-go-v2/config go get github.com/aws/aws-sdk-go-v2/service/s3

A continuación, importe las dependencias en consecuencia en el bloque import de la función:

import ( ... "github.com/aws/aws-sdk-go-v2/config" "github.com/aws/aws-sdk-go-v2/service/s3" )

Al utilizar el SDK en el controlador, configure los clientes con los ajustes correctos. La forma más sencilla de hacerlo es utilizar la cadena de proveedores de credenciales predeterminada. Este ejemplo ilustra una forma de cargar esta configuración:

// Load AWS SDK configuration using the default credential provider chain cfg, err := config.LoadDefaultConfig(ctx) if err != nil { log.Printf("Failed to load AWS SDK config: %v", err) return err }

Después de cargar esta configuración en la variable cfg, puede pasarla a las instancias del cliente. El código de ejemplo crea una instancia de un cliente de Amazon S3 de la siguiente manera:

// Create an S3 client s3Client := s3.NewFromConfig(cfg)

En este ejemplo, inicializamos el cliente de Amazon S3 en la función init() para evitar tener que inicializarlo cada vez que invocamos nuestra función. El problema es que Lambda no tiene acceso al objeto de contexto en la función init(). Como solución alternativa, puede pasar un marcador de posición, como context.TODO(), durante la fase de inicialización. Más adelante, cuando haga una llamada con el cliente, pase el objeto de contexto completo. Esta solución alternativa también se describe en Uso del contexto en las llamadas e inicializaciones de los clientes del AWS SDK.

Después de configurar e inicializar el cliente del SDK, podrá usarlo para interactuar con otros servicios de AWS. El código de ejemplo llama a la API PutObject de Amazon S3 de la siguiente manera:

_, err = s3Client.PutObject(ctx, &s3.PutObjectInput{ Bucket: &bucketName, Key: &key, Body: strings.NewReader(receiptContent), })

Acceso a las variables de entorno

En el código del controlador, puede hacer referencia a cualquier variable de entorno mediante el método os.Getenv(). En este ejemplo, hacemos referencia a la variable de entorno RECEIPT_BUCKET definida mediante la siguiente línea de código:

// Access environment variables bucketName := os.Getenv("RECEIPT_BUCKET") if bucketName == "" { log.Printf("RECEIPT_BUCKET environment variable is not set") return fmt.Errorf("missing required environment variable RECEIPT_BUCKET") }

Uso del estado global

Para evitar la creación de nuevos recursos cada vez que se invoque la función, puede declarar y modificar variables globales fuera del código del controlador de la función de Lambda. Estas variables globales se definen en una instrucción o un bloque var. Además, el controlador puede declarar una función init() que se ejecute durante la fase de inicialización. El método init se comporta del mismo modo en AWS Lambda que en los programas de Go estándar.

Prácticas recomendadas de codificación para las funciones de Lambda en Go

Siga las directrices de la siguiente lista para utilizar las prácticas recomendadas de codificación al crear sus funciones de Lambda:

  • Separe el controlador de Lambda de la lógica del núcleo. Esto le permite probar las distintas unidades de la función con mayor facilidad.

  • Minimice la complejidad de las dependencias. Son preferibles los marcos de trabajo más sencillos, ya que se cargan rápidamente al arrancar el entorno de ejecución.

  • Minimice el tamaño del paquete de implementación de acuerdo con las necesidades de su tiempo de ejecución. Esto reducirá la cantidad de tiempo que tarda el paquete de implementación en descargarse y desempaquetarse antes de la invocación.

  • Reutilice el entorno de ejecución para mejorar el rendimiento de la función. Inicialice los clientes de SDK y las conexiones de base de datos fuera del controlador de funciones y almacene localmente en caché los recursos estáticos en el directorio /tmp. Las invocaciones posteriores procesadas por la misma instancia de su función pueden reutilizar estos recursos. Esto ahorra costes al reducir el tiempo de ejecución de la función.

    Para evitar posibles filtraciones de datos entre las invocaciones, no utilice el entorno de ejecución para almacenar datos de usuario, eventos u otra información con implicaciones de seguridad. Si su función se basa en un estado mutable que no se puede almacenar en la memoria dentro del controlador, considere crear una función independiente o versiones independientes de una función para cada usuario.

  • Utilice una directiva keep-alive para mantener conexiones persistentes. Lambda purga las conexiones inactivas a lo largo del tiempo. Si intenta reutilizar una conexión inactiva al invocar una función, se producirá un error de conexión. Para mantener la conexión persistente, use la directiva keep-alive asociada al tiempo de ejecución. Para ver un ejemplo, consulte Reutilización de conexiones con Keep-Alive en Node.js.

  • Utilice variables de entorno para pasar parámetros operativos a su función. Por ejemplo, si está escribiendo en un bucket de Amazon S3, en lugar de codificar de forma rígida el nombre del bucket, configúrelo como una variable de entorno.

  • Evite utilizar invocaciones recursivas en la función de Lambda, en las que la función se invoca a sí misma o inicia un proceso que puede volver a invocarla. Esto podría producir un volumen no intencionado de invocaciones de la función y costos elevados. Si observa un volumen imprevisto de invocaciones, establezca la simultaneidad reservada de funciones en 0 inmediatamente para limitar todas las invocaciones de la función mientras actualiza el código.

  • No utilice API no documentadas y no públicas en el código de la función de Lambda. Para tiempos de ejecución administrados de AWS Lambda, Lambda aplica periódicamente actualizaciones funcionales y de seguridad a las API internas de Lambda. Estas actualizaciones de las API internas pueden ser incompatibles con versiones anteriores, lo que conlleva consecuencias no deseadas, como errores de invocación si su función depende de estas API no públicas. Consulte la referencia de la API para obtener una lista de las API disponibles públicamente.

  • Escriba el código idempotente. Escribir el código idempotente para las funciones garantiza que los eventos duplicados se gestionen de la misma manera. El código debe validar y gestionar correctamente los eventos duplicados. Para obtener más información, consulte ¿Cómo puedo hacer que mi función de Lambda sea idempotente?.