如何缓存Django REST Framework的HTTP流式响应?
缓存Django REST Framework流式HTTP响应的问题
我正在尝试缓存Django REST Framework的HTTP流式响应,思路是继承StreamingHttpResponse子类,在流式传输时把响应块写入临时文件,等流式传输完成并关闭后,调用可调用对象将文件复制到缓存中。
实现代码如下:
from django.http import StreamingHttpResponse from tempfile import TemporaryFile class CachedStreamingHttpResponse(StreamingHttpResponse): def __init__(self, streaming_content=(), *args, **kwargs): self._post_render_callbacks = [] self._buffer = None self.buffered = False super().__init__(streaming_content, *args, **kwargs) def _set_streaming_content(self, value): self._buffer = TemporaryFile() super()._set_streaming_content(value) def post_render(self): self._buffer.seek(0) self.buffered = self._buffer retval = self for post_callback in self._post_render_callbacks: newretval = post_callback(retval) if newretval is not None: retval = newretval def buffer(self, b): self._buffer.write(b) return b @staticmethod def closing_iterator_wrapper(iterable, close): try: yield from iterable finally: close() @property def streaming_content(self): buffered = map(self.buffer, super().streaming_content) return self.closing_iterator_wrapper(buffered, self.post_render) @streaming_content.setter def streaming_content(self, value): self._set_streaming_content(value) def add_post_render_callback(self, callback): """A list of callables to be run after the final chunk is returned. Used to copy the response to cache.""" if self.buffered: callback(self) else: self._post_render_callbacks.append(callback)
我原本计划让缓存框架向该响应传入一个可调用对象,在streaming_content的finally块中调用它,把临时文件复制到S3。但使用上述代码时出现了两个流(一个压缩、一个未压缩),导致响应无法从缓存中返回。
我已经修正了代码中的语法错误,其中一个关键点需要注意:因为重写了streaming_content的getter,必须重新声明和父类完全一致的setter。
注:缓存流式响应几乎总是不合理的。但这些响应由复杂查询、DRF序列化器和视图集生成,我们采用流式传输是为了让网络条件极差的用户更快看到数据。流式传输会在整个过程中占用服务器和客户端资源,可能比不流式传输消耗更多资源;它可能将部分内存消耗转移到数据库(通过游标读取记录)。响应大小最多几MB,通常更小,将被缓存到我们的S3缓存层,Redis成本过高。
内容的提问来源于stack exchange,提问作者Chris
相关产品推荐
相关产品推荐

