Como migrar solicitações de saída

Por padrão, o ambiente de execução do Python 2.7 usa o serviço de busca de URL para processar solicitações HTTP(S) de saída, mesmo se você usar as bibliotecas Python urllib, urllib2 ou httplib para emitir essas solicitações. A busca de URL não processa solicitações da biblioteca requests a menos que você a ative explicitamente.

O ambiente de execução do Python 3 não precisa de um serviço intermediário para processar solicitações de saída. Se você quiser migrar das APIs URL Fetch, mas ainda precisar de funcionalidade semelhante, migre essas solicitações para usar uma biblioteca Python padrão, como o requests biblioteca.

Principais diferenças entre a busca de URL e as bibliotecas Python padrão

  • As classes tamanho limite e cotas para solicitações processadas pela busca de URL são diferentes dessas mesmas classes para solicitações que não são processadas pela busca de URL.

  • Com a busca de URL, quando o app envia uma solicitação para outro app do App Engine, a busca de URL adiciona o cabeçalho de solicitação X-Appengine-Inbound-Appid para confirmar a identidade do app. O app que recebe a solicitação pode usar a identidade para determinar se precisa processá-la.

    Esse cabeçalho só estará disponível em solicitações enviadas do app se usar a busca de URL. O cabeçalho será removido pelo App Engine se você ou um terceiro adicioná-lo a uma solicitação.

    Para saber informações sobre como declarar e verificar a identidade sem usar a busca de URL, consulte Como migrar a identidade de app para tokens de ID do OIDC.

    Para ver um exemplo de como usar o cabeçalho de solicitação para verificar a identidade do aplicativo de chamada quando as solicitações são enviadas entre aplicativos do App Engine, consulte a Amostra de solicitação do App Engine para o App Engine.

  • É possível usar a busca de URL para definir um tempo limite padrão para todas as solicitações. A maioria das bibliotecas do Python 3, como requests e urllib, tem o tempo limite padrão definido como None. Por isso, atualize cada solicitação que seu código faz para especificar um tempo limite.

Visão geral do processo de migração

  1. Se seu app usa APIs de busca de URL para fazer solicitações, atualize seu código para usar uma biblioteca Python padrão. Recomendamos que você especifique um tempo limite para cada solicitação.

  2. Teste as solicitações de saída no servidor de desenvolvimento local.

  3. Configure seu app para ignorar a busca de URL quando executado no App Engine.

  4. Implante o app.

Como substituir APIs de busca de URL por uma biblioteca Python

  1. Se você ainda não estiver usando uma biblioteca Python padrão para emitir solicitações de saída, escolha uma biblioteca e adicione-a às dependências do app.

    Por exemplo, para usar a biblioteca de solicitações, crie um arquivo requirements.txt na mesma pasta do arquivo app.yaml e adicione a seguinte linha:

    requests==2.24.0
    

    Para compatibilidade com Python 2, recomendamos fixar a biblioteca requests na versão 2.24.0. Quando você implanta o app, o App Engine faz o download de todas as dependências definidas no arquivo requirements.txt.

    Para desenvolvimento local, recomendamos que você instale as dependências em um ambiente virtual como venv.

  2. Pesquise seu código para qualquer uso do módulo google.appengine.api.urlfetch e o atualize para usar a biblioteca Python.

Como fazer solicitações HTTPS simples

No exemplo a seguir, mostramos como fazer uma solicitação 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)

Como fazer solicitações HTTPS assíncronas

No exemplo a seguir, mostramos como fazer uma solicitação HTTPS assíncrona 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, make_response

from requests_futures.sessions import FuturesSession
from time import sleep

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

Como testar localmente

Se você atualizou qualquer solicitação de saída, execute o app do no servidor de desenvolvimento local e confirme se as solicitações foram bem-sucedidas.

Como ignorar a busca de URL

Para impedir que a busca de URL processe solicitações ao implantar o app no App Engine:

  1. No seu arquivo app.yaml, defina a variável de ambiente GAE_USE_SOCKETS_HTTPLIB como qualquer valor. O valor pode ser qualquer valor (inclusive uma string vazia). Exemplo:

    env_variables:
      GAE_USE_SOCKETS_HTTPLIB : ''
    
  2. Se você ativou a busca de URL para processar solicitações enviadas da biblioteca requests, remova as solicitações AppEngineAdapter do seu app.

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

Mesmo que você ignore a busca de URL conforme descrito nas etapas anteriores, seu app ainda poderá usar a API de busca de URL.

Implantar o app

Quando estiver pronto para implantar o aplicativo, você deverá:

  1. Teste o aplicativo no App Engine.

    Acesse a página "Cotas" do App Engine no Console do Google Cloud para confirmar que seu app não está fazendo chamadas à API de busca de URL.

    Visualizar chamadas da API de busca de URL

  2. Se o aplicativo for executado sem erros, use a divisão de tráfego para aumentar gradualmente o tráfego de seu aplicativo atualizado. Monitore de perto o aplicativo em busca de problemas antes de direcionar mais tráfego para o aplicativo atualizado.