使用装饰器管理缓存
这个来自python高手飞龙非龙的代码
# -*- coding: utf-8 -*- # # Copyright(c) 2010 poweredsites.org # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import hashlib from datetime import datetime, timedelta from decorator import decorator from tornado.escape import utf8 from tornado.options import options from poweredsites.libs.handler import BaseHandler from poweredsites.db.caches import Page, Cache from poweredsites.db import conn __all__ = ("cache", "page", "mem", "key_gen", "remove") _mem_caches = {} def cache(expire=7200, condition="", key="", anonymous=False): """Decorator which caches the value of a method in a handler or a module. expire: Cache will be expired time from now in seconds. condition: If the result of sql condition has changed, then cache expired. key: The unique key of the cache identify in the DB cache_pre: A method which is defined in self(handler or module), it always be executed before cache or get from cache. cache_condition: A property which is defined in self(handler or module), it is used to construct a complex condition. """ def wrapper(func, self, *args, **kwargs): now = datetime.now() if key: c = key else: c = self.__class__.__name__ + func.__name__ k, handler, cond = key_gen(self, condition, anonymous, c, *args, **kwargs) value = Cache().findby_key(k) new_cond = {} if _valid_cache(value, handler, cond, new_cond, anonymous, now): if new_cond: c = Cache() c.key = k c.condition = new_cond["condition"] c.save(value["_id"]) return value["value"] else: val = func(self, *args, **kwargs) c = Cache() # need key, or save will not work c.key = k c.value = val c.expire = now + timedelta(seconds=expire) c.condition = new_cond.get("condition", "") if value: c.save(value["_id"]) else: c.insert() return val return decorator(wrapper) def page(expire=7200, condition="", key="", anonymous=False): """Decorator which caches a whole page(headers + html) in a handler expire: Cache will be expired time from now in seconds. condition: If the result of sql condition has changed, then cache expired. key: The unique key of the cache identify in the DB """ def wrapper(func, self, *args, **kwargs): now = datetime.now() if key: c = key else: c = self.__class__.__name__ + func.__name__ k, handler, cond = key_gen(self, condition, anonymous, c, *args, **kwargs) value = Cache().findby_key(k) new_cond = {} is_valid = _valid_cache(value, handler, cond, new_cond, anonymous, now) if is_valid and value["status"] in (200, 304) and value["chunk"]: if new_cond: c = Page() c.key = k c.condition = new_cond["condition"] c.save(value["_id"]) # finish request with cache chunk and headers self.set_status(value["status"]) self.set_header("Content-Type", utf8(value["headers"]["Content-Type"])) self.write(utf8("".join(value["chunk"]))) else: func(self, *args, **kwargs) c = Page() c.key = k c.status = self._status_code c.headers = self._headers c.chunk = self._write_buffer c.expire = now + timedelta(seconds=expire) c.condition = new_cond.get("condition", "") if value: c.save(value["_id"]) else: c.insert() return decorator(wrapper) def mem(expire=7200, key=""): """Mem cache to python dict by key""" def wrapper(func, self, *args, **kwargs): now = datetime.now() if key: c = key else: c = self.__class__.__name__ + func.__name__ k, handler, cond = key_gen(self, "", False, c, *args, **kwargs) value = _mem_caches.get(k, None) if _valid_cache(value, handler, cond, [], False, now): return value["value"] else: val = func(self, *args, **kwargs) _mem_caches[k] = {"value":val, "expire":now} return val return decorator(wrapper) def key_gen(self, condition, anonymous, key, *args, **kwargs): code = hashlib.md5() code.update(str(key)) # copy args to avoid sort original args c = list(args[:]) # sort c to avoid generate different key when args is the same # but sequence is different c.sort() c = [str(v) for v in c] code.update("".join(c)) c = ["%s=%s" % (k, v) for k, v in kwargs] c.sort() code.update("".join(c)) if isinstance(self, BaseHandler): handler = self else: handler = getattr(self, "handler") # execute cache_pre before get cache_condition # so we can construct a complex condition. cache_pre = getattr(self, "cache_pre", None) if cache_pre: cache_pre(*args, **kwargs) if not condition: condition = getattr(self, "cache_condition", "") # also update condition to key, so the same func # has diff caches if there condition is diff(cache_condtion) code.update(str(condition)) # cache for every users if anonymous is False if not anonymous and handler.current_user: # add userid= prefix to avoid key conflict # (eg. siteid + userid maybe equal another siteid) code.update(str("userid=%s" % handler.current_user.id)) # page argument as key by default # Todo: add a argument option for key gen like condition page = handler.get_argument("page", "") if page: code.update(str("page=%s" % page)) # cache for different host(the same uri may have different subdomain) code.update(handler.request.host) return code.hexdigest(), handler, condition def remove(key): """Remove a cache's value.""" c = Cache() v = c.findby_key(key) if v: c.remove(v["_id"]) def _valid_cache(value, handler, condition, new_condtion, anonymous, now): if not options.cache_enabled: return False if anonymous and handler.current_user: return False if condition: old_cond = value.get("condition", "") if value else "" rows = conn.mysql.query(condition) new_cond = "" for r in rows: new_cond += str(r) # unify to utf8, the string result return by pymongo is unicode new_cond = utf8(new_cond) old_cond = utf8(old_cond if old_cond else "") if old_cond != new_cond: new_condtion["condition"] = new_cond return False if value: if value["expire"] > now: return True else: return False else: return False
python,go,redis,mongodb,.net,C#,F#,服务器架构
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· [.NET]调用本地 Deepseek 模型
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· .NET Core 托管堆内存泄露/CPU异常的常见思路
· PostgreSQL 和 SQL Server 在统计信息维护中的关键差异
· C++代码改造为UTF-8编码问题的总结
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· 实操Deepseek接入个人知识库
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· Plotly.NET 一个为 .NET 打造的强大开源交互式图表库
· 【.NET】调用本地 Deepseek 模型