我是寫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()
測試效果:
經過緩存的圖片從內存或文件加載,速度快了很多。 用空間換時間_