Migrar pedidos de saída

Por predefinição, o tempo de execução do Python 2.7 usa o serviço de obtenção de URL para processar pedidos HTTP(S) de saída, mesmo que use as bibliotecas Python urllib, urllib2> ou httplib para emitir esses pedidos. A obtenção de URLs não processa pedidos da biblioteca requests, a menos que os ative explicitamente.

O tempo de execução do Python 3 não precisa de um serviço intermediário para processar pedidos de saída. Se quiser migrar da utilização das APIs URL Fetch, mas ainda precisar de uma funcionalidade semelhante, deve migrar esses pedidos para usar uma biblioteca Python padrão, como a biblioteca requests.

Principais diferenças entre a obtenção de URLs e as bibliotecas Python padrão

  • O limite de tamanho e as quotas para pedidos processados pela obtenção de URLs são diferentes do limite de tamanho e das quotas para pedidos que não são processados pela obtenção de URLs.

  • Com a obtenção de URLs, quando a sua app envia um pedido a outra app do App Engine, a obtenção de URLs adiciona o cabeçalho do pedido X-Appengine-Inbound-Appid para afirmar a identidade da app. A app que recebe o pedido pode usar a identidade para determinar se deve processar o pedido.

    Este cabeçalho só está disponível em pedidos enviados a partir da sua app se esta usar a API URL Fetch. O App Engine remove o cabeçalho se o adicionar a um pedido ou se um terceiro o adicionar.

    Para obter informações sobre a afirmação e a validação da identidade sem usar a obtenção de URLs, consulte o artigo Migrar a identidade da app para tokens de ID OIDC.

    Para ver um exemplo de como usar o cabeçalho do pedido para validar a identidade da app de chamada quando os pedidos são enviados entre apps do App Engine, consulte o exemplo de pedido do App Engine para o App Engine.

  • Pode usar a API URL Fetch para definir um tempo limite predefinido para todos os pedidos. A maioria das bibliotecas do Python 3, como requests e urllib, define o limite de tempo predefinido como None. Por isso, deve atualizar cada pedido que o seu código faz para especificar um limite de tempo.

Vista geral do processo de migração

  1. Se a sua app usar APIs URL Fetch para fazer pedidos, atualize o código para usar uma biblioteca Python padrão. Recomendamos que especifique um limite de tempo para cada pedido.

  2. Teste os seus pedidos de saída no servidor de desenvolvimento local.

  3. Configure a sua app para ignorar a obtenção de URLs quando executada no App Engine.

  4. Implemente a sua app.

Substituir as APIs URL Fetch por uma biblioteca Python

  1. Se ainda não estiver a usar uma biblioteca Python padrão para emitir pedidos de saída, escolha uma biblioteca e adicione-a às dependências da sua app.

    Por exemplo, para usar a biblioteca Requests, crie um ficheiro requirements.txt na mesma pasta que o ficheiro app.yaml e adicione a seguinte linha:

    requests==2.24.0
    

    Para compatibilidade com o Python 2, recomendamos que fixe a biblioteca requests na versão 2.24.0. Quando implementa a sua app, o App Engine transfere todas as dependências definidas no ficheiro requirements.txt.

    Para o desenvolvimento local, recomendamos que instale dependências num ambiente virtual, como o venv.

  2. Pesquise no seu código qualquer utilização do módulo google.appengine.api.urlfetch e atualize o código para usar a sua biblioteca Python.

Fazer pedidos HTTPS simples

O exemplo seguinte mostra como fazer um pedido HTTPS padrão usando a biblioteca requests:

# Copyright 2020 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import logging

from flask import Flask

import requests


app = Flask(__name__)


@app.route("/")
def index():
    url = "http://www.google.com/humans.txt"
    response = requests.get(url)
    response.raise_for_status()
    return response.text


@app.errorhandler(500)
def server_error(e):
    logging.exception("An error occurred during a request.")
    return (
        """
    An internal error occurred: <pre>{}</pre>
    See logs for full stacktrace.
    """.format(
            e
        ),
        500,
    )


if __name__ == "__main__":
    # This is used when running locally.
    app.run(host="127.0.0.1", port=8080, debug=True)

Fazer pedidos HTTPS assíncronos

O exemplo seguinte mostra como fazer um pedido HTTPS assíncrono usando a biblioteca requests:

# Copyright 2020 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import logging
from time import sleep

from flask import Flask
from flask import make_response
from requests_futures.sessions import FuturesSession


TIMEOUT = 10  # Wait this many seconds for background calls to finish
app = Flask(__name__)


@app.route("/")  # Fetch and return remote page asynchronously
def get_async():
    session = FuturesSession()
    url = "http://www.google.com/humans.txt"

    rpc = session.get(url)

    # ... do other things ...

    resp = make_response(rpc.result().text)
    resp.headers["Content-type"] = "text/plain"
    return resp


@app.route("/callback")  # Fetch and return remote pages using callback
def get_callback():
    global response_text
    global counter

    response_text = ""
    counter = 0

    def cb(resp, *args, **kwargs):
        global response_text
        global counter

        if 300 <= resp.status_code < 400:
            return  # ignore intermediate redirection responses

        counter += 1
        response_text += "Response number {} is {} bytes from {}\n".format(
            counter, len(resp.text), resp.url
        )

    session = FuturesSession()
    urls = [
        "https://google.com/",
        "https://www.google.com/humans.txt",
        "https://www.github.com",
        "https://www.travis-ci.org",
    ]

    futures = [session.get(url, hooks={"response": cb}) for url in urls]

    # No wait functionality in requests_futures, so check every second to
    # see if all callbacks are done, up to TIMEOUT seconds
    for elapsed_time in range(TIMEOUT + 1):
        all_done = True
        for future in futures:
            if not future.done():
                all_done = False
                break
        if all_done:
            break
        sleep(1)

    resp = make_response(response_text)
    resp.headers["Content-type"] = "text/plain"
    return resp


@app.errorhandler(500)
def server_error(e):
    logging.exception("An error occurred during a request.")
    return (
        """
    An internal error occurred: <pre>{}</pre>
    See logs for full stacktrace.
    """.format(
            e
        ),
        500,
    )

Testar localmente

Se atualizou algum dos seus pedidos de saída, execute a app no servidor de desenvolvimento local e confirme que os pedidos são bem-sucedidos.

Ignorar obtenção de URL

Para impedir que a obtenção de URLs processe pedidos quando implementa a sua app no App Engine:

  1. No ficheiro app.yaml, defina a variável de ambiente GAE_USE_SOCKETS_HTTPLIB para qualquer valor. O valor pode ser qualquer valor, incluindo uma string vazia. Por exemplo:

    env_variables:
      GAE_USE_SOCKETS_HTTPLIB : ''
    
  2. Se ativou a obtenção de URLs para processar pedidos enviados a partir da biblioteca requests, pode remover os pedidos AppEngineAdapter da sua app.

    Por exemplo, remova requests_toolbelt.adapters.appengine do seu ficheiro appengine_config.py e requests_toolbelt.adapters.appengine.monkeypatch() dos seus ficheiros Python.

Tenha em atenção que, mesmo que ignore a obtenção de URLs, conforme descrito nos passos anteriores, a sua app pode continuar a usar a API URL Fetch diretamente.

Implementar a sua app

Quando tiver tudo pronto para implementar a sua app, deve:

  1. Teste a app no App Engine.

    Veja a página Quotas do App Engine na Google Cloud consola para confirmar que a sua app não está a fazer chamadas à API Url Fetch.

    Veja chamadas API URL Fetch

  2. Se a app for executada sem erros, use a divisão de tráfego para aumentar gradualmente o tráfego para a app atualizada. Monitorize atentamente a app para detetar problemas antes de encaminhar mais tráfego para a app atualizada.