El entorno de ejecución de Go

El entorno de ejecución de Go para las funciones de Cloud Functions se basa en la versión de Go 1.11. A fin de obtener instrucciones sobre cómo preparar tu máquina local para la programación con Go, consulta Configura un entorno de programación de Go.

Para comenzar con Go en Cloud Functions, consulta la guía de inicio rápido.

Selecciona el entorno de ejecución

Puedes seleccionar el entorno de ejecución de Go para tu función durante la implementación.

gcloud

Si usas la herramienta de línea de comandos de gcloud, puedes especificar el entorno de ejecución mediante el parámetro --runtime. Por ejemplo:

gcloud functions deploy FUNCTION_NAME --runtime go111 FLAGS...

FLAGS... se refiere a los argumentos que se pasaron durante la primera implementación de tu función. Para obtener más información en cuanto a los argumentos obligatorios y los opcionales, consulta Implementa con la herramienta de gcloud.

Console

Si usas GCP Console, puedes seleccionar el entorno de ejecución cuando creas e implementas una función.

  1. En GCP Console, ve a la página Descripción general de Cloud Functions.

    Ir a la página de descripción general de Cloud Functions

    Asegúrate de que el proyecto para el que habilitaste Cloud Functions esté seleccionado.

  2. Haz clic en Crear función.

  3. En Entorno de ejecución, selecciona Go 1.11.

Entorno de ejecución

El entorno de ejecución incluye el tiempo de ejecución, el sistema operativo, los paquetes y una biblioteca que invoca tu función.

Imagen base

Para el entorno de ejecución de Go, Cloud Functions usa un entorno de ejecución basado en Ubuntu 18.04. Las actualizaciones al entorno de ejecución de lenguaje se realizan automáticamente (a menos que se notifique algo distinto) y, además, incluyen cambios en la definición de la imagen base.

Funciones de HTTP

Las funciones de HTTP se activan mediante una solicitud HTTP. Estas funciones usan la interfaz http.HandlerFunc estándar. Consulta Escribe funciones de HTTP para obtener más información.

Funciones en segundo plano

Las funciones en segundo plano se activan mediante eventos como una actualización a un depósito de Cloud Storage. Las funciones en segundo plano toman un objeto context.Context y una struct como argumentos.

// Package helloworld provides a set of Cloud Function samples.
package helloworld

import (
	"context"
	"fmt"
	"log"
	"time"

	"cloud.google.com/go/functions/metadata"
)

// GCSEvent is the payload of a GCS event. Please refer to the docs for
// additional information regarding GCS events.
type GCSEvent struct {
	Bucket         string    `json:"bucket"`
	Name           string    `json:"name"`
	Metageneration string    `json:"metageneration"`
	ResourceState  string    `json:"resourceState"`
	TimeCreated    time.Time `json:"timeCreated"`
	Updated        time.Time `json:"updated"`
}

// HelloGCSInfo prints information about a GCS event.
func HelloGCSInfo(ctx context.Context, e GCSEvent) error {
	meta, err := metadata.FromContext(ctx)
	if err != nil {
		return fmt.Errorf("metadata.FromContext: %v", err)
	}
	log.Printf("Event ID: %v\n", meta.EventID)
	log.Printf("Event type: %v\n", meta.EventType)
	log.Printf("Bucket: %v\n", e.Bucket)
	log.Printf("File: %v\n", e.Name)
	log.Printf("Metageneration: %v\n", e.Metageneration)
	log.Printf("Created: %v\n", e.TimeCreated)
	log.Printf("Updated: %v\n", e.Updated)
	return nil
}

context.Context es un objeto estándar de Go que también contiene metadatos sobre el evento que activó la función. Puedes acceder a estos metadatos mediante el paquete cloud.google.com/go/functions/metadata, como en el ejemplo anterior. Si usas este paquete, recuerda copiarlo o incluirlo en tu archivo go.mod.

El segundo argumento es una struct cuyo tipo lo defines tú. Cuando tu función en segundo plano se activa mediante un evento, la carga útil de este evento se deserializará con json.Unmarshal() en la struct de acuerdo con la definición del tipo que proporciones.

Definiciones de evento/estructura

Cuando implementas tu función en segundo plano, seleccionas el evento de activación. Cuando se envía un evento a tu función, este tendrá una estructura específica. La definición de struct que proporciones en el código debe corresponder a la estructura del tipo de evento seleccionado. La estructura para cada evento se documenta en la página del activador del evento correspondiente.

Ten en cuenta que la struct no es obligatoria para definir cada campo contenido en la carga útil. Te recomendamos que, por ejemplo, solo uses los campos Bucket o Data en tu función, en cuyo caso solo necesitarás definir esos campos en tu struct. También puedes volver a nombrar el campo (por ejemplo cuando el nombre del campo JSON contiene un guión bajo) con etiquetas JSON en ese campo.

Actualizaciones al entorno de ejecución

Este entorno de ejecución recibe actualizaciones automáticas a la versión de Go. Por ejemplo, las funciones de Cloud Functions se compilan actualmente con la cadena de herramientas estándar de Go 1.11. Periódicamente, aplicamos parches a la cadena de herramientas de Go a medida que la comunidad de lenguaje las pone a disposición de los usuarios.

De manera similar, Cloud Functions podría aplicar actualizaciones a otros aspectos del entorno de ejecución, como el sistema operativo, los paquetes o incluso otros elementos del entorno. Estas actualizaciones ayudan a mantener tus funciones protegidas.

Estructura del código fuente

Tu función debe estar en un paquete de Go en la raíz de tu proyecto. Esta no puede estar en package main. Los subpaquetes solo se admiten cuando se usan módulos de Go.

Por ejemplo, las siguientes configuraciones de código fuente son válidas:

  • Un paquete en la raíz de tu proyecto que exporta una o más funciones:

    .
    └── function.go
    
  • Un paquete en la raíz de tu proyecto que importa código desde un subpaquete y exporta una o más funciones.

    .
    ├── function.go
    ├── go.mod
    └── shared/
        └── shared.go
    
  • Un paquete en la raíz de tu proyecto con un subdirectorio que define un package main.

    .
    ├── cmd/
    |   └── main.go
    └── function.go
    

Especifica dependencias

Cloud Functions en Go debe proporcionar todas sus dependencias a través de los módulos de Go con un archivo go.mod o un directorio vendor. Tu función no puede especificar dependencias con módulos de Go y un directorio vendor al mismo tiempo.

Módulos de Go

Cloud Functions admite la funcionalidad experimental de Módulos, que te permite especificar las dependencias en un archivo go.mod en la raíz de tu proyecto. Cuando implementas la función, las dependencias especificadas en el archivo go.mod se recuperarán y compilarán automáticamente.

El comportamiento de los módulos de Go difiere según si desarrollas funciones dentro o fuera de GOPATH. Si deseas determinar si te encuentras dentro de GOPATH, sigue estos pasos:

  1. Navega al directorio de tu proyecto.

  2. Encuentra tu GOPATH mediante la ejecución del comando:

    go env GOPATH
    

    Este da como resultado una línea similar a la siguiente:

    GOPATH=YOUR_GOPATH
    
  3. Encuentra tu directorio de trabajo actual con solo ejecutar lo siguiente:

    pwd
    

Si tu directorio de trabajo comienza con YOUR_GOPATH, estás dentro de GOPATH. En este caso, genera tu archivo go.mod con solo ejecutar los siguientes tres comandos:

export GO111MODULE=on
go mod init
go mod tidy

Si tu directorio de trabajo no comienza con YOUR_GOPATH, estás fuera de GOPATH. En este caso, genera tu archivo go.mod con solo ejecutar los siguientes comandos:

go mod init MODULE
go mod tidy

En el ejemplo anterior, MODULE es el nombre de tu módulo. Por ejemplo, el nombre del módulo podría ser example.com/myproject. El comando go automáticamente detecta el nombre del módulo cuando estás dentro de GOPATH.

Después de haber creado un archivo go.mod, puedes usar el comando go get para recuperar dependencias y automáticamente agregarlas a tu proyecto. Por ejemplo:

go get DEPENDENCY

En el ejemplo anterior, DEPENDENCY es una dependencia que te recomendamos que agregues a tu función. Por ejemplo, el comando go get cloud.google.com/go/storage agrega la biblioteca cliente de Cloud Storage a tu función.

Directorio de proveedor

Cloud Functions también te permite incluir tus dependencias a través del directorio vendor. La mayoría de las veces, los directorios vendor se mantienen con un administrador de dependencias. Puedes usar el administrador de dependencias que prefieras. Por ejemplo, puedes usar la funcionalidad de módulos de Go para crear un directorio de vendor desde tu archivo go.mod.

Si tienes un archivo go.mod y un directorio vendor, se ignorará el directorio vendor cuando implementes tu función. Puedes usar un archivo .gcloudignore para no tener que descargar los archivos go.mod y go.sum, en cuyo caso, se respetarán los contenidos de tu directorio vendor:

  1. Crea un archivo .gcloudignore en la raíz del directorio de tu proyecto:

    touch .gcloudignore
    
  2. Agrega go.* al archivo .gcloudignore para garantizar que los archivos go.mod y go.sum no se suban cuando implementes la función:

    echo go.* >> .gcloudignore
    
  3. Crea un directorio vendor con los contenidos de tu archivo go.mod mediante la ejecución del siguiente comando:

    go mod vendor
    

Dependencias privadas

Si las dependencias de tu función se alojan en un repositorio que no es accesible de manera pública, debes usar un directorio vendor para recuperar tus dependencias antes de implementar la función. Si planeas usar un archivo go.mod, consulta las instrucciones anteriores para evitar conflictos entre el archivo go.mod y el directorio vendor.

Inicialización única

Es posible que tus funciones necesiten realizar una inicialización única, como crear clientes de API y configurar el acceso a bases de datos. Hay muchas formas de hacerlo:

  • Usa una función func init() para inicializar los valores cuando una instancia nueva de tu función se inicia. Ten en cuenta que el código en la función func init() se ejecuta antes de que tu función reciba su primera solicitud.

  • Usa la función sync.Once.Do() para ejecutar código una vez por instancia de Cloud Functions. Hacer esto resulta útil en los casos en los que solo quieres inicializar a pedido y no cuando la instancia de la función se inicia primero. Por ejemplo, es posible que solo necesites inicializar un cliente de base de datos en ciertas circunstancias.

context.Context

El paquete context de Go define el tipo de Context, que lleva las fechas límite, los indicadores de cancelación y otros valores de alcance de la solicitud en todos los límites de API y entre procesos.

Los clientes de API que sobreviven a la invocación de una función específica deben inicializarse con un valor context.Context global, como uno creado por la función context.Background(). Para optimizar el rendimiento, podrías inicializar un cliente en alcance global. Es posible que este cliente y su contexto se conserven durante el ciclo de vida de una instancia de función determinada.

Solo debes usar el contexto de invocación de función para objetos u operaciones que caen dentro del ciclo de vida de una invocación de función específica. El contexto de invocación podría cancelarse en cualquier punto después de que tu función termina de ejecutarse, lo que significa que cualquier cliente inicializado con este contexto podría cerrarse. El contexto de invocación de funciones es accesible a través del argumento de la función: generalmente r.Context() para funciones de HTTP y ctx para funciones en segundo plano.

Consulta el siguiente código para ver un ejemplo con un cliente de Cloud Pub/Sub:

// Package contexttip is an example of how to use Pub/Sub and context.Context in
// a Cloud Function.
package contexttip

import (
	"context"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"

	"cloud.google.com/go/pubsub"
)

// projectID is set from the GOOGLE_CLOUD_PROJECT environment variable, which is
// automatically set by the Cloud Functions runtime.
var projectID = os.Getenv("GOOGLE_CLOUD_PROJECT")

// client is a global Pub/Sub client, initialized once per instance.
var client *pubsub.Client

func init() {
	// err is pre-declared to avoid shadowing client.
	var err error

	// client is initialized with context.Background() because it should
	// persist between function invocations.
	client, err = pubsub.NewClient(context.Background(), projectID)
	if err != nil {
		log.Fatalf("pubsub.NewClient: %v", err)
	}
}

type publishRequest struct {
	Topic string `json:"topic"`
}

// PublishMessage publishes a message to Pub/Sub. PublishMessage only works
// with topics that already exist.
func PublishMessage(w http.ResponseWriter, r *http.Request) {
	// Read the request body.
	data, err := ioutil.ReadAll(r.Body)
	if err != nil {
		log.Printf("ioutil.ReadAll: %v", err)
		http.Error(w, "Error reading request", http.StatusBadRequest)
		return
	}

	// Parse the request body to get the topic name.
	p := publishRequest{}
	if err := json.Unmarshal(data, &p); err != nil {
		log.Printf("json.Unmarshal: %v", err)
		http.Error(w, "Error parsing request", http.StatusBadRequest)
		return
	}

	m := &pubsub.Message{
		Data: []byte("Test message"),
	}
	// Publish and Get use r.Context() because they are only needed for this
	// function invocation. If this were a background function, they would use
	// the ctx passed as an argument.
	id, err := client.Topic(p.Topic).Publish(r.Context(), m).Get(r.Context())
	if err != nil {
		log.Printf("topic(%s).Publish.Get: %v", p.Topic, err)
		http.Error(w, "Error publishing message", http.StatusInternalServerError)
		return
	}
	fmt.Fprintf(w, "Published msg: %v", id)
}
¿Te ha resultado útil esta página? Enviar comentarios:

Enviar comentarios sobre...

Documentación de Cloud Functions