Python2.7下如何跨多进程清除lru_cache缓存?
跨Twisted多进程清除lru_cache的实现方案
首先明确:Python标准库的lru_cache是进程本地缓存——每个Twisted启动的进程都有独立的内存空间,缓存数据完全隔离,单个进程调用cache_clear()只能清除自身的缓存,没法直接操作其他进程的缓存。要实现跨进程清除,必须通过「触发每个进程自行执行清除操作」的方式,下面是几个可行的落地方案:
方案1:基于UDP广播的IPC触发
给每个Twisted进程添加一个UDP监听,当Django端点被调用时,发送广播指令触发所有进程执行缓存清除。
步骤1:封装带注册的lru_cache装饰器
先替换标准库的lru_cache,把所有缓存函数统一注册到全局列表,方便后续批量清除:
from functools import lru_cache as std_lru_cache # 全局存储所有带lru_cache的函数 cached_functions = [] def lru_cache(maxsize=None): def decorator(func): wrapped_func = std_lru_cache(maxsize)(func) cached_functions.append(wrapped_func) return wrapped_func return decorator
之后所有需要缓存的函数都用@lru_cache()代替标准的@functools.lru_cache()。
步骤2:给Twisted进程添加UDP监听
每个Twisted进程启动时加载UDP协议,监听特定端口接收清除指令:
import os from twisted.internet.protocol import DatagramProtocol from twisted.internet import reactor class CacheClearProtocol(DatagramProtocol): def datagramReceived(self, datagram, address): # 验证指令合法性 if datagram.strip() == b"CLEAR_LRU_CACHE": print(f"Clearing cache in process {os.getpid()}") for func in cached_functions: func.cache_clear() # 启动UDP监听(本地端口9999) reactor.listenUDP(9999, CacheClearProtocol())
步骤3:实现Django清除端点
在Django视图中发送UDP广播,通知所有进程清除缓存:
import socket from django.http import HttpResponse def clear_lru_cache_endpoint(request): # 创建UDP套接字并启用广播 sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) # 发送清除指令到本地广播地址的9999端口 sock.sendto(b"CLEAR_LRU_CACHE", ("<broadcast>", 9999)) sock.close() return HttpResponse("缓存清除指令已发送至所有进程")
注意:如果进程分布在不同机器,需要调整广播地址或改用多播。
方案2:基于共享存储的定时检查
用Redis/共享文件等外部存储存一个「清除标记」,每个Twisted进程定时检查标记,触发清除操作。适合不想维护IPC通道的场景。
步骤1:Django端点设置清除标记(以Redis为例)
import redis from django.http import HttpResponse # 初始化Redis连接(根据项目配置调整) redis_client = redis.Redis(host='localhost', port=6379, db=0) def clear_lru_cache_endpoint(request): # 设置清除标记 redis_client.set("lru_cache_clear_flag", "1") return HttpResponse("缓存清除标记已设置")
步骤2:Twisted进程添加定时检查任务
import os from twisted.internet import task import redis redis_client = redis.Redis(host='localhost', port=6379, db=0) cached_functions = [] # 同方案1的全局注册列表 def check_and_clear_cache(): # 检查标记是否存在 if redis_client.get("lru_cache_clear_flag") == b"1": print(f"Clearing cache in process {os.getpid()}") for func in cached_functions: func.cache_clear() # 清除标记,避免重复执行 redis_client.set("lru_cache_clear_flag", "0") # 每1秒检查一次(可根据需求调整间隔) cache_check_loop = task.LoopingCall(check_and_clear_cache) cache_check_loop.start(1)
缺点:缓存清除有延迟,适合对实时性要求不高的场景。
方案3:基于系统信号的触发
给每个Twisted进程注册自定义信号处理,Django端点找到所有Twisted进程PID并发送信号,触发缓存清除。
步骤1:Twisted进程注册信号处理
import os import signal cached_functions = [] # 同方案1的全局注册列表 def handle_cache_clear(signum, frame): print(f"Received clear signal in process {os.getpid()}") for func in cached_functions: func.cache_clear() # 注册SIGUSR1信号(可选用其他未被占用的信号) signal.signal(signal.SIGUSR1, handle_cache_clear)
步骤2:Django端点发送信号到所有Twisted进程
需要安装psutil库(Python2.7需安装psutil<5.0版本,因为高版本不再支持Python2.7):
import psutil import signal from django.http import HttpResponse def clear_lru_cache_endpoint(request): # 遍历所有进程,识别Twisted进程 for proc in psutil.process_iter(['name', 'cmdline']): try: # 根据进程名或命令行判断(需替换为你的Twisted进程标识) if 'twisted' in proc.name() or 'your_twisted_entry.py' in proc.cmdline(): proc.send_signal(signal.SIGUSR1) except (psutil.NoSuchProcess, psutil.AccessDenied): continue return HttpResponse("缓存清除信号已发送至所有Twisted进程")
优点:实时性好,缺点需要进程识别准确,且需足够权限发送信号。
关键注意事项
- 必须用自定义的
lru_cache装饰器替换所有标准库的调用,否则无法批量收集缓存函数。 - 所有方案的核心逻辑都是「让每个进程自行执行
cache_clear()」,因为跨进程无法直接操作对方内存中的缓存。
内容的提问来源于stack exchange,提问作者Giordano
相关产品推荐
相关产品推荐

