네트워킹 최적화(1세대)
Cloud Run Functions의 단순성을 활용하여 빠르게 코드를 개발하고 서버리스 환경에서 실행할 수 있습니다. 중간 규모에서는 함수 실행 비용이 적으며 코드 최적화의 우선순위가 높아 보이지 않을 수 있습니다. 하지만 배포 규모가 커짐에 따라 코드 최적화가 점점 중요해집니다.
이 문서에서는 함수의 네트워킹을 최적화하는 방법을 설명합니다. 네트워킹 최적화의 장점은 다음과 같습니다.
- 각 함수 호출 시 새로 연결하는 데 사용하는 CPU 시간이 줄어듭니다.
- 연결 또는 DNS 할당량이 부족해질 가능성이 줄어듭니다.
지속적인 연결 유지
이 섹션에서는 함수에서 지속적인 연결을 유지하는 방법의 예를 보여줍니다. 지속적인 연결을 유지하지 못하면 연결 할당량이 빠르게 소진될 수 있습니다.
이 섹션에서 다루는 상황은 다음과 같습니다.
- HTTP/S
- Google API
HTTP/S 요청
아래의 최적화된 코드 스니펫은 함수 호출마다 새 연결을 만드는 대신 지속적인 연결을 유지하는 방법을 보여줍니다.
Google API 액세스
다음 예시에서는 Cloud Pub/Sub를 사용하지만 다른 클라이언트 라이브러리(예: Cloud Natural Language 또는 Cloud Spanner)에서도 같은 방식을 사용할 수 있습니다. 특정 클라이언트 라이브러리의 현재 구현 방식에 따라 성능 개선의 정도가 다를 수 있습니다.
Pub/Sub 클라이언트 객체를 만들면 호출당 연결은 1회, DNS 쿼리는 2회가 발생합니다. 불필요한 연결과 DNS 쿼리를 방지하기 위해 다음 샘플과 같이 전역 범위에서 Pub/Sub 클라이언트 객체를 만듭니다.
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) }
아웃바운드 연결 재설정
함수에서 VPC 및 인터넷으로의 연결 스트림은 기본 인프라가 다시 시작되거나 업데이트될 때 종료될 수 있습니다. 애플리케이션이 장기적으로 지속되는 연결을 재사용하는 경우 끊어진 연결을 재사용하지 않도록 연결을 다시 설정하도록 애플리케이션을 구성하는 것이 좋습니다.
아웃바운드 요청 제한 시간
비활성 상태가 10분간 지속되는 아웃바운드 소켓은 회수할 수 있습니다. 모든 소켓 작업에서 소켓이 10분 더 활성 상태로 유지됩니다.
함수 부하 테스트
함수에서 평균적으로 수행하는 연결 수를 측정하려면 HTTP 함수로 배포하고 성능 테스트 프레임워크를 사용하여 특정 QPS에서 호출합니다. 사용 가능한 방법 중 하나는 Artillery이며 한 줄로 호출할 수 있습니다.
$ artillery quick -d 300 -r 30 URL
이 명령어는 300초 동안 30QPS로 해당 URL을 가져옵니다.
테스트를 수행한 후 Google Cloud 콘솔의 Cloud Run Functions API 할당량 페이지에서 연결 할당량의 사용량을 확인하세요. 사용량이 지속적으로 30 내외라면 호출할 때마다 1번 연결하는 것이고, 30의 배수라면 여러 번 연결하는 것입니다. 코드를 최적화한 후에는 테스트를 시작할 때만 10~30 정도의 적은 연결 수가 표시되어야 합니다.
또한 같은 페이지의 CPU 할당량 플롯에서 최적화 전후의 CPU 비용을 비교할 수 있습니다.