from socketify import App import redis import aiohttp import asyncio from helpers.twolevel_cache import TwoLevelCache # create redis poll + connections redis_pool = redis.ConnectionPool(host="localhost", port=6379, db=0) redis_connection = redis.Redis(connection_pool=redis_pool) # 2 LEVEL CACHE (Redis to share among workers, Memory to be much faster) # cache in memory is 30s, cache in redis is 60s duration cache = TwoLevelCache(redis_connection, 30, 60) ### # Model ### async def get_pokemon(number): async with aiohttp.ClientSession() as session: async with session.get( f"https://pokeapi.co/api/v2/pokemon/{number}" ) as response: pokemon = await response.text() # cache only works with strings/bytes # we will not change nothing here so no needs to parse json return pokemon.encode("utf-8") async def get_original_pokemons(): async with aiohttp.ClientSession() as session: async with session.get( f"https://pokeapi.co/api/v2/pokemon?limit=151" ) as response: # cache only works with strings/bytes # we will not change nothing here so no needs to parse json pokemons = await response.text() return pokemons.encode("utf-8") ### # Routes ### def list_original_pokemons(res, req): # check cache for faster response value = cache.get("original_pokemons") if value != None: return res.end(value) # get asynchronous from Model async def get_originals(): value = await cache.run_once("original_pokemons", 5, get_original_pokemons) res.cork_end(value) res.run_async(get_originals()) def list_pokemon(res, req): # get needed parameters try: number = int(req.get_parameter(0)) except: # invalid number return req.set_yield(1) # check cache for faster response cache_key = f"pokemon-{number}" value = cache.get(cache_key) if value != None: return res.end(value) # get asynchronous from Model async def find_pokemon(number, res): # sync with redis lock to run only once # if more than 1 worker/request try to do this request, only one will call the Model and the others will get from cache value = await cache.run_once(cache_key, 5, get_pokemon, number) res.cork_end(value) res.run_async(find_pokemon(number, res)) ### # Here i decided to use an sync first and async only if needs, but you can use async directly see ./async.py ### app = App() app.get("/", list_original_pokemons) app.get("/:number", list_pokemon) app.any("/*", lambda res, _: res.write_status(404).end("Not Found")) app.listen( 3000, lambda config: print("Listening on port http://localhost:%d now\n" % config.port), ) app.run()