이전에는 지연된 패키지 google.appengine.ext.deferred가 Python 2의 웹 앱 프레임워크에 의존했습니다. 웹 앱 프레임워크가 Python 3용 App Engine 서비스 SDK에서 삭제되었으므로 Python 2 앱을 Python 3으로 업그레이드할 때 몇 가지 항목을 변경해야 합니다.
Deferred API 사용 설정
Python 3용 Deferred API를 사용 설정하기 위해 더 이상 app.yaml 파일에서 builtins.deferred를 on으로 설정할 필요가 없습니다. 대신 API를 사용 설정하려면 호출에서 use_deferred=True를 wrap_wsgi_app()으로 전달해야 합니다.
앱에서 기본 /_ah/queue/deferred 엔드포인트를 사용하는 경우 Python 3에서 deferred.defer()를 사용하면 Python 2와 동일하게 유지됩니다.
앱에서 지연된 태스크 실행에 커스텀 URL을 사용하는 경우 Python 2용 deferred 모듈의 TaskHandler 클래스가 이 API의 Python 3 버전에서 삭제되었으므로 몇 가지 항목을 변경해야 합니다.
지연된 작업을 실행할 수 있도록 커스텀 URL을 설정하려면 앱이 deferred.Handler 클래스(Python 2에서는 deferred.TaskHandler)에서 post 또는 run_from_request 메서드를 재정의하고 WSGI 요청 매개변수가 포함된 사전을 나타내는 environ 매개변수를 전달하면 됩니다. 그런 다음 Python 3 샘플과 같이 커스텀 엔드포인트에서 post 메서드를 호출할 수 있습니다.
요청 라우팅 및 environ 사전 액세스와 같은 Python 3 Deferred API의 엔드 투 엔드 사용량은 앱에서 마이그레이션하는 웹 프레임워크에 따라 달라집니다. 다음 섹션에서 Python 2 예시와 Python 3 예시의 코드 변경사항을 비교합니다.
Python 3 예시
다음 예시에서는 Flask 앱 및 Django 앱에서 기본 엔드포인트와 커스텀 엔드포인트를 사용하여 지연된 태스크를 실행하는 방법을 보여줍니다.
Flask
importosfromflaskimportFlask,requestfromgoogle.appengine.apiimportwrap_wsgi_appfromgoogle.appengine.extimportdeferredfromgoogle.appengine.extimportndbmy_key=os.environ.get("GAE_VERSION","Missing")app=Flask(__name__)app.wsgi_app=wrap_wsgi_app(app.wsgi_app,use_deferred=True)classCounter(ndb.Model):count=ndb.IntegerProperty(indexed=False)defdo_something_later(key,amount):entity=Counter.get_or_insert(key,count=0)entity.count+=amountentity.put()@app.route("/counter/increment")defincrement_counter():# Use default URL and queue name, no task name, execute ASAP.deferred.defer(do_something_later,my_key,10)# Use default URL and queue name, no task name, execute after 1 minute.deferred.defer(do_something_later,my_key,10,_countdown=60)# Providing non-default task queue argumentsdeferred.defer(do_something_later,my_key,10,_url="/custom/path",_countdown=120)return"Deferred counter increment."@app.route("/counter/get")defview_counter():counter=Counter.get_or_insert(my_key,count=0)returnstr(counter.count)@app.route("/custom/path",methods=["POST"])defcustom_deferred():print("Executing deferred task.")# request.environ contains the WSGI `environ` dictionary (See PEP 0333)returndeferred.Handler().post(request.environ)
Django
importosfromdjango.confimportsettingsfromdjango.core.wsgiimportget_wsgi_applicationfromdjango.httpimportHttpResponsefromdjango.urlsimportpathfromgoogle.appengine.apiimportwrap_wsgi_appfromgoogle.appengine.extimportdeferredfromgoogle.appengine.extimportndbmy_key=os.environ.get("GAE_VERSION","Missing")classCounter(ndb.Model):count=ndb.IntegerProperty(indexed=False)defdo_something_later(key,amount):entity=Counter.get_or_insert(key,count=0)entity.count+=amountentity.put()defincrement_counter(request):# Use default URL and queue name, no task name, execute ASAP.deferred.defer(do_something_later,my_key,10)# Use default URL and queue name, no task name, execute after 1 minute.deferred.defer(do_something_later,my_key,10,_countdown=60)# Providing non-default task queue argumentsdeferred.defer(do_something_later,my_key,10,_url="/custom/path",_countdown=120)returnHttpResponse("Deferred counter increment.")defview_counter(request):counter=Counter.get_or_insert(my_key,count=0)returnHttpResponse(str(counter.count))defcustom_deferred(request):print("Executing deferred task.")# request.environ contains the WSGI `environ` dictionary (See PEP 0333)response,status,headers=deferred.Handler().post(request.environ)returnHttpResponse(response,status=status.value)urlpatterns=(path("counter/get",view_counter,name="view_counter"),path("counter/increment",increment_counter,name="increment_counter"),path("custom/path",custom_deferred,name="custom_deferred"),)settings.configure(DEBUG=True,SECRET_KEY="thisisthesecretkey",ROOT_URLCONF=__name__,MIDDLEWARE_CLASSES=("django.middleware.common.CommonMiddleware","django.middleware.csrf.CsrfViewMiddleware","django.middleware.clickjacking.XFrameOptionsMiddleware",),ALLOWED_HOSTS=["*"],)app=wrap_wsgi_app(get_wsgi_application(),use_deferred=True)
프레임워크 제외
importosimportrefromgoogle.appengine.apiimportwrap_wsgi_appfromgoogle.appengine.extimportdeferredfromgoogle.appengine.extimportndbmy_key=os.environ.get("GAE_VERSION","Missing")classCounter(ndb.Model):count=ndb.IntegerProperty(indexed=False)defdo_something_later(key,amount):entity=Counter.get_or_insert(key,count=0)entity.count+=amountentity.put()defIncrementCounter(environ,start_response):# Use default URL and queue name, no task name, execute ASAP.deferred.defer(do_something_later,my_key,10)# Use default URL and queue name, no task name, execute after 1 minute.deferred.defer(do_something_later,my_key,10,_countdown=60)# Providing non-default task queue argumentsdeferred.defer(do_something_later,my_key,10,_url="/custom/path",_countdown=120)start_response("200 OK",[("Content-Type","text/html")])return[b"Deferred counter increment."]defViewCounter(environ,start_response):counter=Counter.get_or_insert(my_key,count=0)start_response("200 OK",[("Content-Type","text/html")])return[str(counter.count).encode("utf-8")]classCustomDeferredHandler(deferred.Handler):"""Deferred task handler that adds additional logic."""defpost(self,environ):print("Executing deferred task.")returnsuper().post(environ)routes={"counter/increment":IncrementCounter,"counter/get":ViewCounter,"custom/path":CustomDeferredHandler(),}classWSGIApplication:def__call__(self,environ,start_response):path=environ.get("PATH_INFO","").lstrip("/")forregex,handlerinroutes.items():match=re.search(regex,path)ifmatchisnotNone:returnhandler(environ,start_response)start_response("404 Not Found",[("Content-Type","text/plain")])return[b"Not found"]app=wrap_wsgi_app(WSGIApplication(),use_deferred=True)
[[["이해하기 쉬움","easyToUnderstand","thumb-up"],["문제가 해결됨","solvedMyProblem","thumb-up"],["기타","otherUp","thumb-up"]],[["이해하기 어려움","hardToUnderstand","thumb-down"],["잘못된 정보 또는 샘플 코드","incorrectInformationOrSampleCode","thumb-down"],["필요한 정보/샘플이 없음","missingTheInformationSamplesINeed","thumb-down"],["번역 문제","translationIssue","thumb-down"],["기타","otherDown","thumb-down"]],["최종 업데이트: 2025-03-26(UTC)"],[[["The Deferred API for Python 3 on Google App Engine no longer requires setting `builtins.deferred` in `app.yaml`; instead, you must use `use_deferred=True` when calling `wrap_wsgi_app()`."],["The default behavior of the Deferred API in Python 3 remains consistent with Python 2, using the `/_ah/queue/deferred` URL and the default queue, but keep in mind that Cloud Tasks will differ."],["When using the local development server for testing, you need to set `DEFERRED_USE_CROSS_COMPATIBLE_PICKLE_PROTOCOL: 'True'` in your `app.yaml` to make sure the Deferred API works."],["Custom URLs for deferred task execution in Python 3 require using the `deferred.Handler` class's `post` or `run_from_request` methods, as the `TaskHandler` from Python 2 is no longer available."],["The way a Python 3 App uses the Deferred API, including its handling of requests and accessing the `environ` dictionary, is based on the web framework that is being migrated to, as shown in the provided examples with Flask, Django and without a framework."]]],[]]