Com a ajuda dos desenvolvedores do OpenResty, percebi que o set_XYZ
(ou seja, set
, set_md5
) são avaliados durante a fase rewrite
do nginx, enquanto o $request_body
/ $echo_request_body
estão disponíveis somente através da fase content
.
Portanto, ao avaliar set_md5 $digest $request_body;
, a variável $request_body
está vazia, o que explica o resultado constante da MD5.
Por fim, implementei a geração real de chaves de cache em meu próprio aplicativo de API (veja o exemplo abaixo) e acessei-o com access_by_lua
bloquear.
O bloco é executado na fase access
, antes de srcache_fetch
e srcache_store
são avaliados (eles avaliam em post-access
e output-filter
, respectivamente).
Implementá-lo em minha própria API permitiu maior controle sobre a lógica de geração de chave de cache, o que seria difícil de fazer com o nginx sozinho (já que eu não queria me tornar um programador lua completo).
Por exemplo, eu queria poder deterministicamente armazenar em cache as solicitações POST
com corpos Json. A serialização de Json não é determinística, pois as chaves podem estar em qualquer ordem. Na minha API, eu classifico as chaves para que a chave de cache gerada seja constante para os mesmos dados.
Além disso, simplificou a manipulação do $request_body
, já que o sub-tema emitido pela lua apenas o encaminha para a API, independentemente do status de buffer de fase ou de disco.
A configuração final parece
location /api {
# proxy_pass ...
# Force normal responses (no deflate, etc.) See https://github.com/openresty/srcache-nginx-module#srcache_ignore_content_encoding
proxy_set_header Accept-Encoding "";
set $cache_key "";
access_by_lua_block {
local res = ngx.location.capture('/generate-key' .. ngx.var.request_uri, {
method=ngx.HTTP_POST,
-- forwards the entire request body,
-- regardless of disk buffering!
always_forward_body=true,
args=ngx.var.args
})
if res then
ngx.var.cache_key = res.body
end
}
# ... srcache options ...
}
location /generate-key {
# proxy_pass ...
}
A API de geração de chave de exemplo é a seguinte:
import flask
import json
import hashlib
import urllib
app = flask.Flask(__name__)
@app.route('/generate', defaults={'path': ''}, methods=['POST'])
@app.route('/generate/<path:path>', methods=['POST'])
def generate_cache_key(path):
return urllib.quote('{}_{}_{}'.format(path,
digest(stable_body()),
digest(stable_json_dumps(flask.request.args))))
def stable_body():
if flask.request.json:
return stable_json_dumps(flask.request.json)
return flask.request.data
def stable_json_dumps(data):
return json.dumps(data, sort_keys=True)
def digest(data):
return hashlib.md5(data).hexdigest()
if __name__ == '__main__':
app.run()