Otimize a rede (1.ª geração)
A simplicidade das funções do Cloud Run permite-lhe desenvolver rapidamente código e executá-lo num ambiente sem servidor. A uma escala moderada, o custo de execução das funções é baixo e a otimização do código pode não parecer uma prioridade elevada. No entanto, à medida que a implementação aumenta, a otimização do código torna-se cada vez mais importante.
Este documento descreve como otimizar a rede para as suas funções. Seguem-se algumas das vantagens da otimização da rede:
- Reduzir o tempo da CPU gasto no estabelecimento de novas ligações em cada chamada de função.
- Reduzir a probabilidade de ficar sem ligação ou quotas de DNS.
Mantenha ligações persistentes
Esta secção dá exemplos de como manter ligações persistentes numa função. Se não o fizer, pode esgotar rapidamente as quotas de ligações.
Esta secção aborda os seguintes cenários:
- HTTP/S
- APIs Google
Pedidos HTTP/S
O seguinte fragmento do código otimizado mostra como manter ligações persistentes em vez de criar uma nova ligação em cada invocação de função:
Node.js
Python
Go
PHP
Recomendamos que use a estrutura HTTP PHP Guzzle para enviar pedidos HTTP, uma vez que processa as ligações persistentes automaticamente.
Aceder às APIs Google
O exemplo seguinte usa o Cloud Pub/Sub, mas esta abordagem também funciona para outras bibliotecas cliente; por exemplo, o Cloud Natural Language ou o Cloud Spanner. Tenha em atenção que as melhorias no desempenho podem depender da implementação atual de bibliotecas de cliente específicas.
A criação de um objeto cliente do Pub/Sub resulta numa ligação e em duas consultas DNS por invocação. Para evitar ligações e consultas DNS desnecessárias, crie o objeto cliente do Pub/Sub no âmbito global, conforme mostrado no seguinte exemplo:
Node.js
Python
Go
// 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" "log" "net/http" "os" "sync" "cloud.google.com/go/pubsub" "github.com/GoogleCloudPlatform/functions-framework-go/functions" ) // client is a global Pub/Sub client, initialized once per instance. var client *pubsub.Client var once sync.Once // createClient creates the global pubsub Client func createClient() { // GOOGLE_CLOUD_PROJECT is a user-set environment variable. var projectID = os.Getenv("GOOGLE_CLOUD_PROJECT") // 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) } } func init() { // register http function functions.HTTP("PublishMessage", PublishMessage) } type publishRequest struct { Topic stringjson:"topic"
Message stringjson:"message"
} // PublishMessage publishes a message to Pub/Sub. PublishMessage only works // with topics that already exist. func PublishMessage(w http.ResponseWriter, r *http.Request) { // use of sync.Once ensures client is only created once. once.Do(createClient) // Parse the request body to get the topic name and message. p := publishRequest{} if err := json.NewDecoder(r.Body).Decode(&p); err != nil { log.Printf("json.NewDecoder: %v", err) http.Error(w, "Error parsing request", http.StatusBadRequest) return } if p.Topic == "" || p.Message == "" { s := "missing 'topic' or 'message' parameter" log.Println(s) http.Error(w, s, http.StatusBadRequest) return } m := &pubsub.Message{ Data: []byte(p.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, "Message published: %v", id) }
Reposicionamentos de ligações de saída
As streams de ligação da sua função à VPC e à Internet podem ser ocasionalmente terminadas e substituídas quando a infraestrutura subjacente é reiniciada ou atualizada. Se a sua aplicação reutilizar ligações de longa duração, recomendamos que a configure para restabelecer as ligações de forma a evitar a reutilização de uma ligação inativa.
Limites de tempo de pedidos de saída
Os soquetes de saída podem ser recuperados após 10 minutos de inatividade. Qualquer operação de soquete mantém o soquete ativo durante mais 10 minutos.
Teste de carregamento da sua função
Para medir quantas ligações a sua função realiza em média, implemente-a como uma função HTTP e use uma framework de teste de desempenho para a invocar a um determinado QPS. Uma possível escolha é o Artillery, que pode invocar com uma única linha:
$ artillery quick -d 300 -r 30 URL
Este comando obtém o URL fornecido a 30 CPS durante 300 segundos.
Após realizar o teste, verifique a utilização da quota de ligação na página de quotas da API Cloud Run Functions na Google Cloud consola. Se a utilização for consistentemente cerca de 30 (ou o seu múltiplo), está a estabelecer uma (ou várias) ligações em cada invocação. Depois de otimizar o código, deve ver algumas (10 a 30) ligações apenas no início do teste.
Também pode comparar o custo da CPU antes e depois da otimização no gráfico da quota da CPU na mesma página.