我是写Linux后端的(golang、c++、py),后端缓存算法通常是指的是内存里面的lru、或diskqueue,都是独立使用。 很少有用内存lru与disklru结合的场景需求。近段时间研究android开发,里面有一些设计思想值得后端学习。
写这篇文章的原因:
看到了android开发里面的一个片段
于是在画板里面手绘下图:
为了简化测试,用Python编程语言实现
import tkinter as tk
from tkinter import ttk, messagebox
from PIL import Image, ImageTk, ImageOps
import requests
from io import BytesIO
import threading
import queue
from functools import lru_cache
from diskcache import Cache
import os# 配置缓存
CACHE_DIR = "image_cache"
os.makedirs(CACHE_DIR, exist_ok=True)
disk_cache = Cache(CACHE_DIR) # 磁盘缓存(自动管理容量)@lru_cache(maxsize=5) # LRU缓存(仅记录URL)
def get_from_lru(url):passclass ImageLoader:def __init__(self):self.queue = queue.Queue()self.thread = threading.Thread(target=self._worker, daemon=True)self.thread.start()def load(self, url, callback):self.queue.put((url, callback))def _worker(self):while True:url, callback = self.queue.get()data = Nonecache_type = "error"# 检查LRUif get_from_lru.cache_info().currsize > 0:data = disk_cache.get(url)if data:cache_type = "lru"# 检查磁盘if not data:data = disk_cache.get(url)if data:cache_type = "disk"get_from_lru(url) # 更新LRU标记# 网络加载if not data:try:res = requests.get(url, timeout=10)res.raise_for_status()data = res.contentcache_type = "network"disk_cache.set(url, data) # 自动处理容量限制get_from_lru(url)except Exception as e:callback(None, cache_type)continue# 返回结果try:img = Image.open(BytesIO(data))callback(img, cache_type)except:callback(None, "error")class ImageViewerApp:def __init__(self, root):self.root = rootself.root.title("图片查看器")self.urls = [f"https://picsum.photos/seed/img{i}/800/600" for i in range(1, 11)]self.current = 0self.loader = ImageLoader()self._create_widgets()def _create_widgets(self):frame = ttk.Frame(self.root, padding=10)frame.pack(fill=tk.BOTH, expand=True)# 图片显示区域self.img_label = ttk.Label(frame)self.img_label.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)# 导航按钮btn_frame = ttk.Frame(frame)btn_frame.pack(fill=tk.X, pady=5)self.prev_btn = ttk.Button(btn_frame, text="◀ 上一张", command=self.prev_image)self.prev_btn.pack(side=tk.LEFT, padx=5)self.next_btn = ttk.Button(btn_frame, text="下一张 ▶", command=self.next_image)self.next_btn.pack(side=tk.RIGHT, padx=5)# 缓存状态self.status_label = ttk.Label(frame, text="缓存状态: LRU(0/5), 磁盘(0/8)")self.status_label.pack(fill=tk.X, pady=2)# 加载指示器self.loading = ttk.Label(self.img_label, text="加载中...", font=("SimHei", 12))def _load_image(self, index):self.current = indexurl = self.urls[index]self.status_label.config(text="加载中...")self.loading.place(relx=0.5, rely=0.5, anchor="center")self.prev_btn.config(state=tk.DISABLED)self.next_btn.config(state=tk.DISABLED)self.loader.load(url, self._on_loaded)def _on_loaded(self, img, cache_type):self.root.after(0, lambda: self._update_display(img, cache_type))def _update_display(self, img, cache_type):self.loading.place_forget()self.prev_btn.config(state=tk.NORMAL)self.next_btn.config(state=tk.NORMAL)if img:# 调整图片大小max_w = self.img_label.winfo_width() - 20max_h = self.img_label.winfo_height() - 20img = ImageOps.contain(img, (max_w or 500, max_h or 400))self.photo = ImageTk.PhotoImage(img)self.img_label.config(image=self.photo)# 更新缓存状态lru = get_from_lru.cache_info().currsizedisk = len(disk_cache)self.status_label.config(text=f"缓存状态: LRU({lru}/5) [{cache_type.upper()}], 磁盘({disk}/8)")else:messagebox.showerror("错误", "无法加载图片")def prev_image(self):self._load_image((self.current - 1) % 10)def next_image(self):self._load_image((self.current + 1) % 10)if __name__ == "__main__":root = tk.Tk()root.geometry("800x600")app = ImageViewerApp(root)app._load_image(0)root.mainloop()
测试效果:
经过缓存的图片从内存或文件加载,速度快了很多。 用空间换时间_