Add new exercise

This commit is contained in:
Manuel Vergara 2024-06-16 18:00:16 +02:00
parent d7c1cee2a8
commit 71d0a3c4d8
8 changed files with 358 additions and 0 deletions

View File

@ -0,0 +1,90 @@
README.md
.git
.gitignore
.gitattributes
# CI
.codeclimate.yml
.travis.yml
.taskcluster.yml
# Docker
docker-compose.yml
Dockerfile
.docker
.dockerignore
# Byte-compiled / optimized / DLL files
**/__pycache__/
**/*.py[cod]
# C extensions
*.so
# Distribution / packaging
.Python
env/
build/
develop-eggs/
dist/
downloads/
eggs/
lib/
lib64/
parts/
sdist/
var/
*.egg-info/
.installed.cfg
*.egg
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.coverage
.cache
nosetests.xml
coverage.xml
# Translations
*.mo
*.pot
# Django stuff:
*.log
# Sphinx documentation
docs/_build/
# PyBuilder
target/
# Virtual environment
# .env
.venv/
venv/
# PyCharm
.idea
# Python mode for VIM
.ropeproject
**/.ropeproject
# Vim swap files
**/*.swp
# VS Code
.vscode/

View File

@ -0,0 +1,12 @@
FROM python:3.12-alpine AS builder
WORKDIR /app
COPY . .
RUN pip install --no-cache-dir -r requirements.txt
EXPOSE 5000
CMD ["python", "app.py"]

View File

@ -0,0 +1,188 @@
# Crear una API Caching con Redis, Flask y Docker
<!-- Artículo original: https://dev.to/vjanz/implement-api-caching-with-redis-flask-and-docker-step-by-step-5h01 -->
![](https://res.cloudinary.com/practicaldev/image/fetch/s--MflWnlWv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/4350/1%2AgEpkD_3NMTxK-w96c_QBBA.png)
## Prueba 1: Sin Redis
Primero vamos a hacer una prueba de la aplicación sin redis.
Vamos al directorio donde queremos trabajar, creamos un entorno virtual y lo activamos:
```bash
python3 -m venv venv
source venv/bin/activate
```
Ahora instalamos las dependencias en nuestro entorno:
```bash
(venv) pip install Flask redis flask_caching requests
```
Y guardamos estas dependencias en un archivo `requirements.txt`:
```bash
(venv) pip freeze > requirements.txt
```
Vamos a crear un archivo `app.py` con el siguiente contenido:
```python
import requests
from flask import Flask, request, jsonify
app = Flask(__name__)
@app.route("/universities")
def get_universities():
API_URL = "http://universities.hipolabs.com/search?country="
search = request.args.get('country')
r = requests.get(f"{API_URL}{search}")
return jsonify(r.json())
if __name__ == "__main__":
app.run(debug=True, host="0.0.0.0", port=5000)
```
Ahora vamos a ejecutar la aplicación:
```bash
export FLASK_APP=app.py
export FLASK_ENV=development
flask run
```
Y si nos vamos a postman podremos comprobar cuanto tarda en responder la petición:
![](https://res.cloudinary.com/practicaldev/image/fetch/s--cFeIXNRd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2898/1%2A9o8_94EDMwGO-BWWKnshBA.png)
## Prueba 2: Dockerizar nuestra aplicación
Vamos a dockerizar nuestra aplicación, para ello vamos a crear un archivo `Dockerfile` con el siguiente contenido:
```Dockerfile
FROM python:3.12-alpine AS builder
WORKDIR /app
COPY . .
RUN pip install --no-cache-dir -r requirements.txt
EXPOSE 5000
CMD ["python", "app.py"]
```
Vamos a crear también un archivo `docker-compose.yaml` con el siguiente contenido:
```yaml
version: '3.8'
services:
api:
container_name: app-python-flask-with-redis
build: .
env_file:
- .env
ports:
- '5000:5000'
depends_on:
- redis
redis:
image: redis:7.0-alpine
container_name: redis-python
ports:
- '6379:6379'
```
Incluimos también el contenedor de Redis. Lanzamos nuestra aplicación con el comando:
```bash
docker-compose up -d --build
```
Si vemos `docker ps` veremos que tenemos dos contenedores corriendo. También podemos revisar los logs del contenedor de la aplicación con `docker logs app-python-flask-with-redis`.
Comprobamos que nuestra aplicación sigue funcionando en docker igual que lo hacía en local.
## Prueba 3: Añadir Redis a nuestra aplicación
Ahora vamos a añadir Redis a nuestra aplicación. Vamos a modificar el archivo `app.py` para que use Redis:
```python
import requests
from flask import Flask, jsonify, request
from flask_caching import Cache
app = Flask(__name__)
app.config.from_object('config.BaseConfig')
cache = Cache(app)
@app.route("/universities")
@cache.cached(timeout=30, query_string=True)
def get_universities():
API_URL = "http://universities.hipolabs.com/search?country="
search = request.args.get('country')
r = requests.get(f"{API_URL}{search}")
return jsonify(r.json())
if __name__ == '__main__':
app.run(host='0.0.0.0')
```
Y vamos a crear un archivo `config.py` con el siguiente contenido:
```python
import os
class BaseConfig(object):
CACHE_TYPE = os.environ['CACHE_TYPE']
CACHE_REDIS_HOST = os.environ['CACHE_REDIS_HOST']
CACHE_REDIS_PORT = os.environ['CACHE_REDIS_PORT']
CACHE_REDIS_DB = os.environ['CACHE_REDIS_DB']
CACHE_REDIS_URL = os.environ['CACHE_REDIS_URL']
CACHE_DEFAULT_TIMEOUT = os.environ['CACHE_DEFAULT_TIMEOUT']
```
Este fichero recoge las variables de entorno que vamos a usar en nuestra aplicación. Vamos a crear un archivo `.env` con el siguiente contenido:
```bash
# .e
CACHE_TYPE=redis
CACHE_REDIS_HOST=redis
CACHE_REDIS_PORT=6379
CACHE_REDIS_DB=0
CACHE_REDIS_URL=redis://redis:6379/0
CACHE_DEFAULT_TIMEOUT=300
```
Al finalizar la práctica, tendremos esta estructura:
```
.
├── app.py
├── config.py
├── docker-compose.yaml
├── Dockerfile
├── .env
└── requirements.txt
```
Volvemos a lanzar nuestra aplicación con `docker-compose up -d --build` y comprobamos que todo sigue funcionando correctamente.
Volvemos a lanzar la misma petición desde postman y comprobamos que la respuesta es mucho más rápida que antes:
![](https://res.cloudinary.com/practicaldev/image/fetch/s--mHDsWyzq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2922/1%2Au4a8OUBQu2gBzvc6iL5nsw.png)
Podemos probar con otros países, la primera vez tardará más porque no estará en caché, pero las siguientes veces será mucho más rápido.
Esta es la magia de Redis, una base de datos en memoria que nos permite almacenar datos en caché y acelerar nuestras aplicaciones 🚀

View File

@ -0,0 +1,21 @@
import requests
from flask import Flask, jsonify, request
from flask_caching import Cache
app = Flask(__name__)
app.config.from_object('config.BaseConfig')
cache = Cache(app)
@app.route("/universities")
@cache.cached(timeout=30, query_string=True)
def get_universities():
API_URL = "http://universities.hipolabs.com/search?country="
search = request.args.get('country')
r = requests.get(f"{API_URL}{search}")
return jsonify(r.json())
if __name__ == '__main__':
app.run(host='0.0.0.0')

View File

@ -0,0 +1,11 @@
import os
class BaseConfig(object):
CACHE_TYPE = os.environ['CACHE_TYPE']
CACHE_REDIS_HOST = os.environ['CACHE_REDIS_HOST']
CACHE_REDIS_PORT = os.environ['CACHE_REDIS_PORT']
CACHE_REDIS_DB = os.environ['CACHE_REDIS_DB']
CACHE_REDIS_URL = os.environ['CACHE_REDIS_URL']
CACHE_DEFAULT_TIMEOUT = os.environ['CACHE_DEFAULT_TIMEOUT']

View File

@ -0,0 +1,19 @@
version: '3.8'
services:
api:
container_name: app-python-flask-with-redis
build: .
env_file:
- .env
ports:
- '5000:5000'
depends_on:
- redis
redis:
image: redis:7.0-alpine
container_name: redis-python
ports:
- '6379:6379'

View File

@ -0,0 +1,16 @@
async-timeout==4.0.3
blinker==1.8.2
cachelib==0.9.0
certifi==2024.6.2
charset-normalizer==3.3.2
click==8.1.7
Flask==3.0.3
Flask-Caching==2.3.0
idna==3.7
itsdangerous==2.2.0
Jinja2==3.1.4
MarkupSafe==2.1.5
redis==5.0.5
requests==2.32.3
urllib3==2.2.1
Werkzeug==3.0.3

View File

@ -12,3 +12,4 @@ Aquí iré dejando scripts y ejercicios que se me ocurran, con lo que no hay un
| [Descifrador wargame](./02_scripts_descifrador_wargame/) | Script descifrador de código al estilo wargame | intermedio | | [Descifrador wargame](./02_scripts_descifrador_wargame/) | Script descifrador de código al estilo wargame | intermedio |
| [Clima España](./03_clima/) | Script conectado a API AEMET | intermedio | | [Clima España](./03_clima/) | Script conectado a API AEMET | intermedio |
| [acortador de enlaces](./04_acortador_url/) | Script para acortar enlaces y redirigirlos con app Flask | intermedio | | [acortador de enlaces](./04_acortador_url/) | Script para acortar enlaces y redirigirlos con app Flask | intermedio |
| [API Caching](./05_redis_flask_docker) | App Flask con caché en Redis y con una bbdd | intermedio |