Skip to content

Commit dc1066f

Browse files
authored
Add FastAPI Background Tasks example (#408)
1 parent 3750fa4 commit dc1066f

14 files changed

+600
-0
lines changed

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,7 @@ The Lambda Web Adapter also supports all non-HTTP event triggers, such as SQS, S
167167

168168
- [FastAPI](examples/fastapi)
169169
- [FastAPI in Zip](examples/fastapi-zip)
170+
- [FastAPI with Background Tasks](examples/fastapi-background-tasks)
170171
- [FastAPI with Response Streaming](examples/fastapi-response-streaming)
171172
- [FastAPI with Response Streaming in Zip](examples/fastapi-response-streaming-zip)
172173
- [Flask](examples/flask)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,244 @@
1+
2+
# Created by https://www.gitignore.io/api/osx,linux,python,windows,pycharm,visualstudiocode
3+
4+
### Linux ###
5+
*~
6+
7+
# temporary files which can be created if a process still has a handle open of a deleted file
8+
.fuse_hidden*
9+
10+
# KDE directory preferences
11+
.directory
12+
13+
# Linux trash folder which might appear on any partition or disk
14+
.Trash-*
15+
16+
# .nfs files are created when an open file is removed but is still being accessed
17+
.nfs*
18+
19+
### OSX ###
20+
*.DS_Store
21+
.AppleDouble
22+
.LSOverride
23+
24+
# Icon must end with two \r
25+
Icon
26+
27+
# Thumbnails
28+
._*
29+
30+
# Files that might appear in the root of a volume
31+
.DocumentRevisions-V100
32+
.fseventsd
33+
.Spotlight-V100
34+
.TemporaryItems
35+
.Trashes
36+
.VolumeIcon.icns
37+
.com.apple.timemachine.donotpresent
38+
39+
# Directories potentially created on remote AFP share
40+
.AppleDB
41+
.AppleDesktop
42+
Network Trash Folder
43+
Temporary Items
44+
.apdisk
45+
46+
### PyCharm ###
47+
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
48+
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
49+
50+
# User-specific stuff:
51+
.idea/**/workspace.xml
52+
.idea/**/tasks.xml
53+
.idea/dictionaries
54+
55+
# Sensitive or high-churn files:
56+
.idea/**/dataSources/
57+
.idea/**/dataSources.ids
58+
.idea/**/dataSources.xml
59+
.idea/**/dataSources.local.xml
60+
.idea/**/sqlDataSources.xml
61+
.idea/**/dynamic.xml
62+
.idea/**/uiDesigner.xml
63+
64+
# Gradle:
65+
.idea/**/gradle.xml
66+
.idea/**/libraries
67+
68+
# CMake
69+
cmake-build-debug/
70+
71+
# Mongo Explorer plugin:
72+
.idea/**/mongoSettings.xml
73+
74+
## File-based project format:
75+
*.iws
76+
77+
## Plugin-specific files:
78+
79+
# IntelliJ
80+
/out/
81+
82+
# mpeltonen/sbt-idea plugin
83+
.idea_modules/
84+
85+
# JIRA plugin
86+
atlassian-ide-plugin.xml
87+
88+
# Cursive Clojure plugin
89+
.idea/replstate.xml
90+
91+
# Ruby plugin and RubyMine
92+
/.rakeTasks
93+
94+
# Crashlytics plugin (for Android Studio and IntelliJ)
95+
com_crashlytics_export_strings.xml
96+
crashlytics.properties
97+
crashlytics-build.properties
98+
fabric.properties
99+
100+
### PyCharm Patch ###
101+
# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721
102+
103+
# *.iml
104+
# modules.xml
105+
# .idea/misc.xml
106+
# *.ipr
107+
108+
# Sonarlint plugin
109+
.idea/sonarlint
110+
111+
### Python ###
112+
# Byte-compiled / optimized / DLL files
113+
__pycache__/
114+
*.py[cod]
115+
*$py.class
116+
117+
# C extensions
118+
*.so
119+
120+
# Distribution / packaging
121+
.Python
122+
build/
123+
develop-eggs/
124+
dist/
125+
downloads/
126+
eggs/
127+
.eggs/
128+
lib/
129+
lib64/
130+
parts/
131+
sdist/
132+
var/
133+
wheels/
134+
*.egg-info/
135+
.installed.cfg
136+
*.egg
137+
138+
# PyInstaller
139+
# Usually these files are written by a python script from a template
140+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
141+
*.manifest
142+
*.spec
143+
144+
# Installer logs
145+
pip-log.txt
146+
pip-delete-this-directory.txt
147+
148+
# Unit test / coverage reports
149+
htmlcov/
150+
.tox/
151+
.coverage
152+
.coverage.*
153+
.cache
154+
.pytest_cache/
155+
nosetests.xml
156+
coverage.xml
157+
*.cover
158+
.hypothesis/
159+
160+
# Translations
161+
*.mo
162+
*.pot
163+
164+
# Flask stuff:
165+
instance/
166+
.webassets-cache
167+
168+
# Scrapy stuff:
169+
.scrapy
170+
171+
# Sphinx documentation
172+
docs/_build/
173+
174+
# PyBuilder
175+
target/
176+
177+
# Jupyter Notebook
178+
.ipynb_checkpoints
179+
180+
# pyenv
181+
.python-version
182+
183+
# celery beat schedule file
184+
celerybeat-schedule.*
185+
186+
# SageMath parsed files
187+
*.sage.py
188+
189+
# Environments
190+
.env
191+
.venv
192+
env/
193+
venv/
194+
ENV/
195+
env.bak/
196+
venv.bak/
197+
198+
# Spyder project settings
199+
.spyderproject
200+
.spyproject
201+
202+
# Rope project settings
203+
.ropeproject
204+
205+
# mkdocs documentation
206+
/site
207+
208+
# mypy
209+
.mypy_cache/
210+
211+
### VisualStudioCode ###
212+
.vscode/*
213+
!.vscode/settings.json
214+
!.vscode/tasks.json
215+
!.vscode/launch.json
216+
!.vscode/extensions.json
217+
.history
218+
219+
### Windows ###
220+
# Windows thumbnail cache files
221+
Thumbs.db
222+
ehthumbs.db
223+
ehthumbs_vista.db
224+
225+
# Folder config file
226+
Desktop.ini
227+
228+
# Recycle Bin used on file shares
229+
$RECYCLE.BIN/
230+
231+
# Windows Installer files
232+
*.cab
233+
*.msi
234+
*.msm
235+
*.msp
236+
237+
# Windows shortcuts
238+
*.lnk
239+
240+
# Build folder
241+
242+
*/build/*
243+
244+
# End of https://www.gitignore.io/api/osx,linux,python,windows,pycharm,visualstudiocode
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
# FastAPI Background Task example
2+
3+
A basic FastAPI application example. You can build and test it locally as a typical FastAPI application.
4+
5+
Using AWS Lambda Web Adapter, You can package this web application into Docker image, push to ECR, and deploy to Lambda, ECS/EKS, or EC2.
6+
7+
The application can be deployed in an AWS account using the [Serverless Application Model](https://github.com/awslabs/serverless-application-model). The `template.yaml` file in the root folder contains the application definition.
8+
9+
The top level folder is a typical AWS SAM project. The `app` directory is a FastAPI application with a [Dockerfile](app/Dockerfile).
10+
11+
```dockerfile
12+
FROM public.ecr.aws/docker/library/python:3.12-slim
13+
COPY --from=public.ecr.aws/awsguru/aws-lambda-adapter:0.8.1 /lambda-adapter /opt/extensions/lambda-adapter
14+
ENV PORT=8000
15+
WORKDIR /var/task
16+
COPY requirements.txt ./
17+
RUN python -m pip install -r requirements.txt
18+
COPY *.py ./
19+
CMD exec uvicorn --port=$PORT main:app
20+
```
21+
22+
Line 2 copies lambda web adapter binary into /opt/extensions. This is the change to run the FastAPI application on Lambda.
23+
24+
```dockerfile
25+
COPY --from=public.ecr.aws/awsguru/aws-lambda-adapter:0.8.1 /lambda-adapter /opt/extensions/lambda-adapter
26+
```
27+
28+
## Pre-requisites
29+
30+
The following tools should be installed and configured.
31+
* [AWS CLI](https://aws.amazon.com/cli/)
32+
* [SAM CLI](https://github.com/awslabs/aws-sam-cli)
33+
* [Python](https://www.python.org/)
34+
* [Docker](https://www.docker.com/products/docker-desktop)
35+
36+
37+
## Deploy to Lambda
38+
Navigate to the sample's folder and use the SAM CLI to build a container image
39+
```shell
40+
$ sam build
41+
```
42+
43+
This command compiles the application and prepares a deployment package in the `.aws-sam` sub-directory.
44+
45+
To deploy the application in your AWS account, you can use the SAM CLI's guided deployment process and follow the instructions on the screen
46+
47+
```shell
48+
$ sam deploy --guided
49+
```
50+
Please take note of the container image name.
51+
Once the deployment is completed, the SAM CLI will print out the stack's outputs, including the new application URL. You can use `curl` or a web browser to make a call to the URL
52+
53+
```shell
54+
...
55+
---------------------------------------------------------------------------------------------------------
56+
OutputKey-Description OutputValue
57+
---------------------------------------------------------------------------------------------------------
58+
FastAPIURL - URL for application https://xxxxxxxxxx.execute-api.us-west-2.amazonaws.com/
59+
---------------------------------------------------------------------------------------------------------
60+
...
61+
62+
$ curl https://xxxxxxxxxx.execute-api.us-west-2.amazonaws.com/
63+
```
64+
65+
## Run the docker locally
66+
67+
We can run the same docker image locally, so that we know it can be deployed to ECS Fargate and EKS EC2 without code changes.
68+
69+
```shell
70+
$ docker run -d -p 8000:8000 {ECR Image}
71+
72+
```
73+
74+
Use curl to verify the docker container works.
75+
76+
```shell
77+
$ curl localhost:8000/
78+
```

examples/fastapi-background-tasks/__init__.py

Whitespace-only changes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
FROM public.ecr.aws/docker/library/python:3.12-slim
2+
COPY --from=public.ecr.aws/awsguru/aws-lambda-adapter:0.8.1 /lambda-adapter /opt/extensions/lambda-adapter
3+
ENV PORT=8000
4+
WORKDIR /var/task
5+
COPY requirements.txt ./
6+
RUN python -m pip install -r requirements.txt
7+
COPY *.py ./
8+
CMD exec uvicorn --port=$PORT main:app

examples/fastapi-background-tasks/app/__init__.py

Whitespace-only changes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
from threading import Thread
2+
from queue import Queue
3+
import json
4+
import os
5+
import requests
6+
7+
8+
class BackgroundTaskExtension(Thread):
9+
def __init__(self):
10+
super().__init__()
11+
self.daemon = True
12+
self.queue = Queue()
13+
self.session = requests.Session()
14+
self.start()
15+
16+
def run(self):
17+
# start an internal extension
18+
response = self.session.post(
19+
url=f"http://{os.environ['AWS_LAMBDA_RUNTIME_API']}/2020-01-01/extension/register",
20+
json={'events': ['INVOKE'],},
21+
headers={'Lambda-Extension-Name': 'background-task-extension' }
22+
)
23+
extension_id = response.headers['Lambda-Extension-Identifier']
24+
while True:
25+
response = self.session.get(
26+
url=f"http://{os.environ['AWS_LAMBDA_RUNTIME_API']}/2020-01-01/extension/event/next",
27+
headers={'Lambda-Extension-Identifier': extension_id},
28+
timeout=None
29+
)
30+
event = json.loads(response.text)
31+
if event['eventType'] == 'INVOKE':
32+
while True:
33+
message = self.queue.get()
34+
if message['type'] == 'TASK':
35+
task, args, kwargs = message['task']
36+
task(*args, **kwargs)
37+
if message['type'] == 'DONE':
38+
break
39+
40+
def add_task(self, background_task, *args, **kwargs):
41+
self.queue.put( {"type": "TASK",
42+
"task": (background_task, args, kwargs)} )
43+
44+
def done(self):
45+
self.queue.put( {"type": "DONE"} )

0 commit comments

Comments
 (0)