Ağ iletişimini optimize etme

Cloud Functions'ın basitliği, kodları hızla geliştirmenizi ve sunucusuz bir ortamda çalıştırmanızı sağlar. Orta düzeyde, işlevleri çalıştırmanın maliyeti düşüktür ve kodunuzu optimize etmek yüksek bir öncelik gibi görünmeyebilir. Ancak dağıtımınız genişledikçe kodunuzu optimize etmek de giderek daha önemli hale gelir.

Bu dokümanda, işlevleriniz için ağ iletişiminin nasıl optimize edileceği açıklanmaktadır. Ağ iletişimini optimize etmenin avantajlarından bazıları şunlardır:

  • Her işlev çağrısında yeni bağlantılar kurmak için harcanan CPU süresini azaltın.
  • Bağlantı veya DNS kotalarının tükenmesi olasılığını azaltın.

Kalıcı Bağlantıları Koruma

Bu bölümde, bir işlevdeki kalıcı bağlantıların nasıl korunacağıyla ilgili örnekler yer almaktadır. Aksi takdirde bağlantı kotalarının hızla tükenmesine neden olabilir.

Bu bölümde aşağıdaki senaryolar ele alınmıştır:

  • HTTP/S
  • Google API'leri

HTTP/S İstekleri

Aşağıdaki optimize edilmiş kod snippet'i, her işlev çağrısında yeni bir bağlantı oluşturmak yerine kalıcı bağlantıların nasıl korunacağını gösterir:

Node.js

const http = require('http');
const functions = require('firebase-functions');

// Setting the `keepAlive` option to `true` keeps
// connections open between function invocations
const agent = new http.Agent({keepAlive: true});

exports.function = functions.https.onRequest((request, response) => {
    req = http.request({
        host: '',
        port: 80,
        path: '',
        method: 'GET',
        agent: agent, // Holds the connection open after the first invocation
    }, res => {
        let rawData = '';
        res.setEncoding('utf8');
        res.on('data', chunk => { rawData += chunk; });
        res.on('end', () => {
            response.status(200).send(`Data: ${rawData}`);
        });
    });
    req.on('error', e => {
        response.status(500).send(`Error: ${e.message}`);
    });
    req.end();
});

Python

from firebase_functions import https_fn
import requests

# Create a global HTTP session (which provides connection pooling)
session = requests.Session()

@https_fn.on_request()
def connection_pooling(request):

    # The URL to send the request to
    url = "http://example.com"

    # Process the request
    response = session.get(url)
    response.raise_for_status()
    return https_fn.Response("Success!")
    

Bu HTTP işlevi, HTTP isteklerinde bulunmak için bir bağlantı havuzu kullanır. Bir istek nesnesini (flask.Request) alır ve yanıt metnini veya make_response kullanılarak Response nesnesine dönüştürülebilen değer gruplarını döndürür.

Google API'lerine erişme

Aşağıdaki örnekte Cloud Pub/Sub kullanılsa da bu yaklaşım Cloud Natural Language veya Cloud Spanner gibi diğer istemci kitaplıklarında da işe yarar. Performans iyileştirmelerinin, belirli istemci kitaplıklarının mevcut uygulamasına bağlı olabileceğini unutmayın.

Pub/Sub istemci nesnesi oluşturulduğunda çağrı başına bir bağlantı ve iki DNS sorgusu oluşturulur. Gereksiz bağlantıları ve DNS sorgularını önlemek için Pub/Sub istemci nesnesini aşağıdaki örnekte gösterildiği gibi global kapsamda oluşturun:

Düğüm.js

const PubSub = require('@google-cloud/pubsub');
const functions = require('firebase-functions');
const pubsub = PubSub();

exports.function = functions.https.onRequest((req, res) => {
    const topic = pubsub.topic('');

    topic.publish('Test message', err => {
        if (err) {
            res.status(500).send(`Error publishing the message: ${err}`);
        } else {
            res.status(200).send('1 message published');
        }
    });
});

Python

import os

from firebase_functions import https_fn
from google.cloud import pubsub_v1

# from firebase_functions import https_fn
# Create a global Pub/Sub client to avoid unneeded network activity
pubsub = pubsub_v1.PublisherClient()

@https_fn.on_request()
def gcp_api_call(request):

    project = os.getenv("GCP_PROJECT")
    request_json = request.get_json()

    topic_name = request_json["topic"]
    topic_path = pubsub.topic_path(project, topic_name)

    # Process the request
    data = b"Test message"
    pubsub.publish(topic_path, data=data)

    return https_fn.Response("1 message published")
    

Bu HTTP işlevi, işlev çağrısı başına gereken bağlantı sayısını azaltmak için önbelleğe alınmış bir istemci kitaplığı örneği kullanır. Bir istek nesnesini (flask.Request) alır ve yanıt metnini veya make_response kullanılarak Response nesnesine dönüştürülebilen değer gruplarını döndürür.

GCP_PROJECT ortam değişkeni, Python 3.7 çalışma zamanında otomatik olarak ayarlanır. Daha sonraki çalışma zamanlarında bunu işlev dağıtımında belirttiğinizden emin olun. Ortam değişkenlerini yapılandırma bölümüne bakın.

İşlevinizin Yük Testi

İşlevinizin ortalama kaç bağlantı gösterdiğini ölçmek için işlevi HTTP işlevi olarak dağıtın ve belirli QPS'de çağırmak için performans testi çerçevesi kullanın. Olası seçeneklerden biri, tek bir satırla çağırabileceğiniz Artillery'dir:

$ artillery quick -d 300 -r 30 URL

Bu komut, belirtilen URL'yi 300 saniye boyunca 30 QPS'de getirir.

Testi gerçekleştirdikten sonra bağlantı kotanızın kullanımını Cloud Console'daki Cloud Functions API kota sayfasından kontrol edin. Kullanım sürekli olarak 30 (veya birden fazla) civarındaysa her çağrıda bir (veya daha fazla) bağlantı oluşturursunuz. Kodunuzu optimize ettikten sonra, testin başında birkaç (10-30) bağlantının gerçekleştiğini görebilirsiniz.

Ayrıca aynı sayfadaki CPU kota grafiğinde optimizasyondan önceki ve sonraki CPU maliyetini karşılaştırabilirsiniz.