从架构角度结合分布式缓存和本地缓存
<blockquote> <p>在工作中,有时候会碰到这样一种情况,一个产品本身就是以一个单体应用去开发设计的,但架不住规模的变化,需要调整为多节点集群部署。</p> <p>那碰到这种既需要单节点部署,又需要多节点分布式部署的场景,缓存业务如何进行自适应调整</p> </blockquote> <h1 id="需求分析">需求分析</h1> <p>面对这种场景,糙一点的方式无非是搞两套实现,一套是单节点部署,采用本地缓存,另一套采用分布式缓存,适用于多节点集群部署。</p> <p>站在设计的角度上这很合理,但不够优雅。。。</p> <p>愿景:把本地缓存和分布式缓存结合起来,提供统一的 API 去处理,在具体的场景中自适应具体的实现。</p> <p>优点:</p> <ul> <li>统一实现,不必考虑部署场景</li> <li>底层实现更方便替换</li> <li>提供额外特性 <ul> <li>超时过期</li> <li>缓存空值</li> <li>初始化清理</li> <li>…</li> </ul> </li> <li>…根据设计自行扩展</li> </ul> <h1 id="设计">设计</h1> <ol> <li> <p>定义缓存操作接口</p> <p>两个接口,分别应对于Map接口和常量接口</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 </pre></td><td class="rouge-code"><pre><span class="kd">public</span> <span class="kd">interface</span> <span class="nc">AdaptingCache</span><span class="o"><</span><span class="no">K</span><span class="o">,</span> <span class="no">V</span><span class="o">></span> <span class="o">{</span> <span class="cm">/** * 获取缓存名称 * * @return 缓存名称 */</span> <span class="nc">String</span> <span class="nf">getName</span><span class="o">();</span> <span class="cm">/** * 获取底层具体实现 * * @return 底层具体实现,Redis为RedisCache */</span> <span class="nc">Cache</span> <span class="nf">getNativeCache</span><span class="o">();</span> <span class="cm">/** * 获取缓存值 * * @param key 缓存key * @return 缓存值 */</span> <span class="no">V</span> <span class="nf">get</span><span class="o">(</span><span class="no">K</span> <span class="n">key</span><span class="o">);</span> <span class="cm">/** * 获取缓存值 * * @param key 缓存key * @param valueLoader 不存在则加载,并保存到缓存中 * @return 缓存值 */</span> <span class="no">V</span> <span class="nf">get</span><span class="o">(</span><span class="no">K</span> <span class="n">key</span><span class="o">,</span> <span class="nc">Callable</span><span class="o"><</span><span class="no">V</span><span class="o">></span> <span class="n">valueLoader</span><span class="o">);</span> <span class="cm">/** * 添加缓存 * * @param key 缓存key * @param value 缓存值 */</span> <span class="kt">void</span> <span class="nf">put</span><span class="o">(</span><span class="no">K</span> <span class="n">key</span><span class="o">,</span> <span class="nd">@Nullable</span> <span class="no">V</span> <span class="n">value</span><span class="o">);</span> <span class="cm">/** * 添加缓存并返回 * * @param key 缓存key * @param value 缓存值 * @return 缓存值 */</span> <span class="k">default</span> <span class="no">V</span> <span class="nf">putIfAbsent</span><span class="o">(</span><span class="no">K</span> <span class="n">key</span><span class="o">,</span> <span class="nd">@Nullable</span> <span class="no">V</span> <span class="n">value</span><span class="o">)</span> <span class="o">{</span> <span class="no">V</span> <span class="n">existingValue</span> <span class="o">=</span> <span class="n">get</span><span class="o">(</span><span class="n">key</span><span class="o">);</span> <span class="k">if</span> <span class="o">(</span><span class="n">existingValue</span> <span class="o">==</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span> <span class="n">put</span><span class="o">(</span><span class="n">key</span><span class="o">,</span> <span class="n">value</span><span class="o">);</span> <span class="o">}</span> <span class="k">return</span> <span class="n">existingValue</span><span class="o">;</span> <span class="o">}</span> <span class="cm">/** * 移除缓存 * * @param key 缓存key */</span> <span class="kt">void</span> <span class="nf">evict</span><span class="o">(</span><span class="no">K</span> <span class="n">key</span><span class="o">);</span> <span class="cm">/** * 清理全部缓存 */</span> <span class="kt">void</span> <span class="nf">clear</span><span class="o">();</span> <span class="o">}</span> </pre></td></tr></tbody></table></code></pre></div> </div> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 </pre></td><td class="rouge-code"><pre><span class="kd">public</span> <span class="kd">interface</span> <span class="nc">AdaptingValueCache</span><span class="o"><</span><span class="no">V</span><span class="o">></span> <span class="o">{</span> <span class="cm">/** * 获取缓存名称 * * @return 缓存名称 */</span> <span class="nc">String</span> <span class="nf">getName</span><span class="o">();</span> <span class="cm">/** * 获取缓存值 * * @return 缓存值 */</span> <span class="no">V</span> <span class="nf">get</span><span class="o">();</span> <span class="cm">/** * 获取缓存值 * * @param valueLoader 若不存在则添加至缓存中 * @return 缓存值 */</span> <span class="no">V</span> <span class="nf">get</span><span class="o">(</span><span class="nc">Callable</span><span class="o"><</span><span class="no">V</span><span class="o">></span> <span class="n">valueLoader</span><span class="o">);</span> <span class="cm">/** * 添加缓存值 * * @param value */</span> <span class="kt">void</span> <span class="nf">put</span><span class="o">(</span><span class="nd">@Nullable</span> <span class="no">V</span> <span class="n">value</span><span class="o">);</span> <span class="cm">/** * 添加缓存值,并返回 * * @param value 缓存值 * @return 缓存值 */</span> <span class="k">default</span> <span class="no">V</span> <span class="nf">putIfAbsent</span><span class="o">(</span><span class="nd">@Nullable</span> <span class="no">V</span> <span class="n">value</span><span class="o">)</span> <span class="o">{</span> <span class="no">V</span> <span class="n">existingValue</span> <span class="o">=</span> <span class="n">get</span><span class="o">();</span> <span class="k">if</span> <span class="o">(</span><span class="n">existingValue</span> <span class="o">==</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span> <span class="n">put</span><span class="o">(</span><span class="n">value</span><span class="o">);</span> <span class="o">}</span> <span class="k">return</span> <span class="n">existingValue</span><span class="o">;</span> <span class="o">}</span> <span class="cm">/** * 清理缓存 */</span> <span class="kt">void</span> <span class="nf">clear</span><span class="o">();</span> <span class="o">}</span> </pre></td></tr></tbody></table></code></pre></div> </div> </li> <li> <p>抽象桥接 Spring cache</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 </pre></td><td class="rouge-code"><pre><span class="kd">public</span> <span class="kd">abstract</span> <span class="kd">class</span> <span class="nc">AbstractAdaptingCache</span><span class="o"><</span><span class="no">K</span><span class="o">,</span> <span class="no">V</span><span class="o">></span> <span class="kd">implements</span> <span class="nc">AdaptingCache</span><span class="o"><</span><span class="no">K</span><span class="o">,</span> <span class="no">V</span><span class="o">></span> <span class="o">{</span> <span class="kd">protected</span> <span class="nc">Cache</span> <span class="n">adapter</span><span class="o">;</span> <span class="kd">public</span> <span class="nf">AbstractAdaptingCache</span><span class="o">(</span><span class="nc">Cache</span> <span class="n">adapter</span><span class="o">)</span> <span class="o">{</span> <span class="k">this</span><span class="o">.</span><span class="na">adapter</span> <span class="o">=</span> <span class="n">adapter</span><span class="o">;</span> <span class="o">}</span> <span class="nd">@Override</span> <span class="kd">public</span> <span class="nc">String</span> <span class="nf">getName</span><span class="o">()</span> <span class="o">{</span> <span class="k">return</span> <span class="k">this</span><span class="o">.</span><span class="na">adapter</span><span class="o">.</span><span class="na">getName</span><span class="o">();</span> <span class="o">}</span> <span class="nd">@Override</span> <span class="kd">public</span> <span class="nc">Cache</span> <span class="nf">getNativeCache</span><span class="o">()</span> <span class="o">{</span> <span class="k">return</span> <span class="k">this</span><span class="o">.</span><span class="na">adapter</span><span class="o">;</span> <span class="o">}</span> <span class="nd">@SuppressWarnings</span><span class="o">(</span><span class="s">"unchecked"</span><span class="o">)</span> <span class="nd">@Override</span> <span class="kd">public</span> <span class="no">V</span> <span class="nf">get</span><span class="o">(</span><span class="no">K</span> <span class="n">key</span><span class="o">)</span> <span class="o">{</span> <span class="nc">Cache</span><span class="o">.</span><span class="na">ValueWrapper</span> <span class="n">valueWrapper</span> <span class="o">=</span> <span class="k">this</span><span class="o">.</span><span class="na">adapter</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="n">key</span><span class="o">);</span> <span class="k">if</span> <span class="o">(</span><span class="n">valueWrapper</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span> <span class="k">return</span> <span class="o">(</span><span class="no">V</span><span class="o">)</span><span class="n">valueWrapper</span><span class="o">.</span><span class="na">get</span><span class="o">();</span> <span class="o">}</span> <span class="k">return</span> <span class="kc">null</span><span class="o">;</span> <span class="o">}</span> <span class="nd">@Override</span> <span class="kd">public</span> <span class="no">V</span> <span class="nf">get</span><span class="o">(</span><span class="no">K</span> <span class="n">key</span><span class="o">,</span> <span class="nc">Callable</span><span class="o"><</span><span class="no">V</span><span class="o">></span> <span class="n">valueLoader</span><span class="o">)</span> <span class="o">{</span> <span class="k">return</span> <span class="k">this</span><span class="o">.</span><span class="na">adapter</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="n">key</span><span class="o">,</span> <span class="n">valueLoader</span><span class="o">);</span> <span class="o">}</span> <span class="nd">@Override</span> <span class="kd">public</span> <span class="kt">void</span> <span class="nf">put</span><span class="o">(</span><span class="no">K</span> <span class="n">key</span><span class="o">,</span> <span class="no">V</span> <span class="n">value</span><span class="o">)</span> <span class="o">{</span> <span class="k">this</span><span class="o">.</span><span class="na">adapter</span><span class="o">.</span><span class="na">put</span><span class="o">(</span><span class="n">key</span><span class="o">,</span> <span class="n">value</span><span class="o">);</span> <span class="o">}</span> <span class="nd">@Override</span> <span class="kd">public</span> <span class="kt">void</span> <span class="nf">evict</span><span class="o">(</span><span class="no">K</span> <span class="n">key</span><span class="o">)</span> <span class="o">{</span> <span class="k">this</span><span class="o">.</span><span class="na">adapter</span><span class="o">.</span><span class="na">evict</span><span class="o">(</span><span class="n">key</span><span class="o">);</span> <span class="o">}</span> <span class="nd">@Override</span> <span class="kd">public</span> <span class="kt">void</span> <span class="nf">clear</span><span class="o">()</span> <span class="o">{</span> <span class="k">this</span><span class="o">.</span><span class="na">adapter</span><span class="o">.</span><span class="na">clear</span><span class="o">();</span> <span class="o">}</span> <span class="o">}</span> </pre></td></tr></tbody></table></code></pre></div> </div> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 </pre></td><td class="rouge-code"><pre><span class="kd">public</span> <span class="kd">abstract</span> <span class="kd">class</span> <span class="nc">AbstractAdaptingValueCache</span><span class="o"><</span><span class="no">V</span><span class="o">></span> <span class="kd">implements</span> <span class="nc">AdaptingValueCache</span><span class="o"><</span><span class="no">V</span><span class="o">></span> <span class="o">{</span> <span class="kd">protected</span> <span class="nc">Cache</span> <span class="n">adapter</span><span class="o">;</span> <span class="kd">public</span> <span class="nf">AbstractAdaptingValueCache</span><span class="o">(</span><span class="nc">Cache</span> <span class="n">adapter</span><span class="o">)</span> <span class="o">{</span> <span class="k">this</span><span class="o">.</span><span class="na">adapter</span> <span class="o">=</span> <span class="n">adapter</span><span class="o">;</span> <span class="o">}</span> <span class="nd">@Override</span> <span class="kd">public</span> <span class="nc">String</span> <span class="nf">getName</span><span class="o">()</span> <span class="o">{</span> <span class="k">return</span> <span class="k">this</span><span class="o">.</span><span class="na">adapter</span><span class="o">.</span><span class="na">getName</span><span class="o">();</span> <span class="o">}</span> <span class="nd">@SuppressWarnings</span><span class="o">(</span><span class="s">"unchecked"</span><span class="o">)</span> <span class="nd">@Override</span> <span class="kd">public</span> <span class="no">V</span> <span class="nf">get</span><span class="o">()</span> <span class="o">{</span> <span class="nc">Cache</span><span class="o">.</span><span class="na">ValueWrapper</span> <span class="n">valueWrapper</span> <span class="o">=</span> <span class="k">this</span><span class="o">.</span><span class="na">adapter</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="k">this</span><span class="o">.</span><span class="na">getName</span><span class="o">());</span> <span class="k">if</span> <span class="o">(</span><span class="n">valueWrapper</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span> <span class="k">return</span> <span class="o">(</span><span class="no">V</span><span class="o">)</span><span class="n">valueWrapper</span><span class="o">.</span><span class="na">get</span><span class="o">();</span> <span class="o">}</span> <span class="k">return</span> <span class="kc">null</span><span class="o">;</span> <span class="o">}</span> <span class="nd">@Override</span> <span class="kd">public</span> <span class="no">V</span> <span class="nf">get</span><span class="o">(</span><span class="nc">Callable</span><span class="o"><</span><span class="no">V</span><span class="o">></span> <span class="n">valueLoader</span><span class="o">)</span> <span class="o">{</span> <span class="k">return</span> <span class="k">this</span><span class="o">.</span><span class="na">adapter</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="k">this</span><span class="o">.</span><span class="na">getName</span><span class="o">(),</span> <span class="n">valueLoader</span><span class="o">);</span> <span class="o">}</span> <span class="nd">@Override</span> <span class="kd">public</span> <span class="kt">void</span> <span class="nf">put</span><span class="o">(</span><span class="no">V</span> <span class="n">value</span><span class="o">)</span> <span class="o">{</span> <span class="k">this</span><span class="o">.</span><span class="na">adapter</span><span class="o">.</span><span class="na">put</span><span class="o">(</span><span class="k">this</span><span class="o">.</span><span class="na">getName</span><span class="o">(),</span> <span class="n">value</span><span class="o">);</span> <span class="o">}</span> <span class="nd">@Override</span> <span class="kd">public</span> <span class="kt">void</span> <span class="nf">clear</span><span class="o">()</span> <span class="o">{</span> <span class="k">this</span><span class="o">.</span><span class="na">adapter</span><span class="o">.</span><span class="na">clear</span><span class="o">();</span> <span class="o">}</span> <span class="o">}</span> </pre></td></tr></tbody></table></code></pre></div> </div> </li> <li> <p>定义缓存提供者接口,用于提供底层实现</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1 2 3 4 5 6 7 8 9 10 11 </pre></td><td class="rouge-code"><pre><span class="kd">public</span> <span class="kd">interface</span> <span class="nc">CacheProvider</span> <span class="o">{</span> <span class="cm">/** * 获取缓存 * * @param cacheBuilder 缓存构建器 * @return 缓存 */</span> <span class="o"><</span><span class="no">K</span><span class="o">,</span> <span class="no">V</span><span class="o">></span> <span class="nc">Cache</span> <span class="nf">getCache</span><span class="o">(</span><span class="nc">CacheBuilder</span><span class="o"><</span><span class="no">K</span><span class="o">,</span> <span class="no">V</span><span class="o">></span> <span class="n">cacheBuilder</span><span class="o">);</span> <span class="o">}</span> </pre></td></tr></tbody></table></code></pre></div> </div> </li> <li> <p>缓存提供者具体实现,根据配置开启指定实现(本地缓存/分布式缓存)</p> <ul> <li> <p>本地缓存,采用 Caffeign</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 </pre></td><td class="rouge-code"><pre><span class="kd">public</span> <span class="kd">class</span> <span class="nc">MemoryCacheProvider</span> <span class="kd">implements</span> <span class="nc">CacheProvider</span> <span class="o">{</span> <span class="kd">private</span> <span class="kd">final</span> <span class="nc">VillaCacheProperties</span> <span class="n">cacheProperties</span><span class="o">;</span> <span class="nd">@Override</span> <span class="kd">public</span> <span class="o"><</span><span class="no">K</span><span class="o">,</span> <span class="no">V</span><span class="o">></span> <span class="nc">Cache</span> <span class="nf">getCache</span><span class="o">(</span><span class="nc">CacheBuilder</span><span class="o"><</span><span class="no">K</span><span class="o">,</span> <span class="no">V</span><span class="o">></span> <span class="n">cacheBuilder</span><span class="o">)</span> <span class="o">{</span> <span class="nc">Caffeine</span><span class="o"><</span><span class="nc">Object</span><span class="o">,</span> <span class="nc">Object</span><span class="o">></span> <span class="n">newedBuilder</span> <span class="o">=</span> <span class="nc">Caffeine</span><span class="o">.</span><span class="na">newBuilder</span><span class="o">();</span> <span class="nc">String</span> <span class="n">name</span> <span class="o">=</span> <span class="n">cacheBuilder</span><span class="o">.</span><span class="na">getName</span><span class="o">();</span> <span class="kt">boolean</span> <span class="n">isConfig</span> <span class="o">=</span> <span class="n">cacheProperties</span><span class="o">.</span><span class="na">getNameConfigs</span><span class="o">().</span><span class="na">containsKey</span><span class="o">(</span><span class="n">name</span><span class="o">);</span> <span class="kt">boolean</span> <span class="n">cacheNullValues</span> <span class="o">=</span> <span class="n">cacheBuilder</span><span class="o">.</span><span class="na">isCacheNullValues</span><span class="o">();</span> <span class="nc">Duration</span> <span class="n">ttl</span> <span class="o">=</span> <span class="n">cacheBuilder</span><span class="o">.</span><span class="na">getTtl</span><span class="o">();</span> <span class="k">if</span> <span class="o">(</span><span class="n">isConfig</span><span class="o">)</span> <span class="o">{</span> <span class="nc">VillaCacheProperties</span><span class="o">.</span><span class="na">NameConfig</span> <span class="n">nameConfig</span> <span class="o">=</span> <span class="n">cacheProperties</span><span class="o">.</span><span class="na">getNameConfigs</span><span class="o">().</span><span class="na">get</span><span class="o">(</span><span class="n">name</span><span class="o">);</span> <span class="n">ttl</span> <span class="o">=</span> <span class="n">nameConfig</span><span class="o">.</span><span class="na">getTtl</span><span class="o">();</span> <span class="n">cacheNullValues</span> <span class="o">=</span> <span class="n">nameConfig</span><span class="o">.</span><span class="na">isCacheNullValues</span><span class="o">();</span> <span class="n">cacheBuilder</span><span class="o">.</span><span class="na">initializeClear</span><span class="o">(</span><span class="n">nameConfig</span><span class="o">.</span><span class="na">isInitializeClear</span><span class="o">());</span> <span class="o">}</span> <span class="k">if</span> <span class="o">(</span><span class="n">ttl</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span> <span class="n">newedBuilder</span><span class="o">.</span><span class="na">expireAfterWrite</span><span class="o">(</span><span class="n">ttl</span><span class="o">);</span> <span class="o">}</span> <span class="n">com</span><span class="o">.</span><span class="na">github</span><span class="o">.</span><span class="na">benmanes</span><span class="o">.</span><span class="na">caffeine</span><span class="o">.</span><span class="na">cache</span><span class="o">.</span><span class="na">Cache</span><span class="o"><</span><span class="nc">Object</span><span class="o">,</span> <span class="nc">Object</span><span class="o">></span> <span class="n">cache</span> <span class="o">=</span> <span class="n">newedBuilder</span><span class="o">.</span><span class="na">build</span><span class="o">();</span> <span class="k">return</span> <span class="k">new</span> <span class="nf">CaffeineCache</span><span class="o">(</span><span class="n">name</span><span class="o">,</span> <span class="n">cache</span><span class="o">,</span> <span class="n">cacheNullValues</span><span class="o">);</span> <span class="o">}</span> <span class="o">}</span> </pre></td></tr></tbody></table></code></pre></div> </div> </li> <li> <p>分布式缓存,采用 Redis</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 </pre></td><td class="rouge-code"><pre><span class="kd">public</span> <span class="kd">class</span> <span class="nc">RedisCacheProvider</span> <span class="kd">implements</span> <span class="nc">CacheProvider</span> <span class="o">{</span> <span class="kd">private</span> <span class="kd">final</span> <span class="nc">RedisConnectionFactory</span> <span class="n">redisConnectionFactory</span><span class="o">;</span> <span class="kd">private</span> <span class="kd">final</span> <span class="nc">CacheProperties</span> <span class="n">cacheProperties</span><span class="o">;</span> <span class="kd">private</span> <span class="kd">final</span> <span class="nc">ResourceLoader</span> <span class="n">resourceLoader</span><span class="o">;</span> <span class="kd">private</span> <span class="kd">final</span> <span class="nc">VillaCacheProperties</span> <span class="n">villaCacheProperties</span><span class="o">;</span> <span class="nd">@Override</span> <span class="kd">public</span> <span class="o"><</span><span class="no">K</span><span class="o">,</span> <span class="no">V</span><span class="o">></span> <span class="nc">Cache</span> <span class="nf">getCache</span><span class="o">(</span><span class="nc">CacheBuilder</span><span class="o"><</span><span class="no">K</span><span class="o">,</span> <span class="no">V</span><span class="o">></span> <span class="n">cacheBuilder</span><span class="o">)</span> <span class="o">{</span> <span class="nc">String</span> <span class="n">name</span> <span class="o">=</span> <span class="n">cacheBuilder</span><span class="o">.</span><span class="na">getName</span><span class="o">();</span> <span class="nc">Duration</span> <span class="n">ttl</span> <span class="o">=</span> <span class="n">cacheBuilder</span><span class="o">.</span><span class="na">getTtl</span><span class="o">();</span> <span class="kt">boolean</span> <span class="n">cacheNullValues</span> <span class="o">=</span> <span class="n">cacheBuilder</span><span class="o">.</span><span class="na">isCacheNullValues</span><span class="o">();</span> <span class="kt">boolean</span> <span class="n">isConfig</span> <span class="o">=</span> <span class="n">villaCacheProperties</span><span class="o">.</span><span class="na">getNameConfigs</span><span class="o">().</span><span class="na">containsKey</span><span class="o">(</span><span class="n">name</span><span class="o">);</span> <span class="k">if</span> <span class="o">(</span><span class="n">isConfig</span><span class="o">)</span> <span class="o">{</span> <span class="nc">VillaCacheProperties</span><span class="o">.</span><span class="na">NameConfig</span> <span class="n">nameConfig</span> <span class="o">=</span> <span class="n">villaCacheProperties</span><span class="o">.</span><span class="na">getNameConfigs</span><span class="o">().</span><span class="na">get</span><span class="o">(</span><span class="n">name</span><span class="o">);</span> <span class="n">ttl</span> <span class="o">=</span> <span class="n">nameConfig</span><span class="o">.</span><span class="na">getTtl</span><span class="o">();</span> <span class="n">cacheNullValues</span> <span class="o">=</span> <span class="n">nameConfig</span><span class="o">.</span><span class="na">isCacheNullValues</span><span class="o">();</span> <span class="n">cacheBuilder</span><span class="o">.</span><span class="na">initializeClear</span><span class="o">(</span><span class="n">nameConfig</span><span class="o">.</span><span class="na">isInitializeClear</span><span class="o">());</span> <span class="o">}</span> <span class="nc">RedisCacheConfiguration</span> <span class="n">redisCacheConfiguration</span> <span class="o">=</span> <span class="k">this</span><span class="o">.</span><span class="na">createConfiguration</span><span class="o">(</span><span class="n">cacheProperties</span><span class="o">,</span> <span class="n">resourceLoader</span><span class="o">.</span><span class="na">getClassLoader</span><span class="o">());</span> <span class="k">if</span> <span class="o">(!</span><span class="n">cacheNullValues</span><span class="o">)</span> <span class="o">{</span> <span class="n">redisCacheConfiguration</span> <span class="o">=</span> <span class="n">redisCacheConfiguration</span><span class="o">.</span><span class="na">disableCachingNullValues</span><span class="o">();</span> <span class="o">}</span> <span class="k">if</span> <span class="o">(</span><span class="n">ttl</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span> <span class="n">redisCacheConfiguration</span> <span class="o">=</span> <span class="n">redisCacheConfiguration</span><span class="o">.</span><span class="na">entryTtl</span><span class="o">(</span><span class="n">ttl</span><span class="o">);</span> <span class="o">}</span> <span class="k">return</span> <span class="k">this</span><span class="o">.</span><span class="na">createRedisCache</span><span class="o">(</span><span class="n">name</span><span class="o">,</span> <span class="n">redisCacheConfiguration</span><span class="o">);</span> <span class="o">}</span> <span class="kd">protected</span> <span class="nc">RedisCache</span> <span class="nf">createRedisCache</span><span class="o">(</span><span class="nc">String</span> <span class="n">name</span><span class="o">,</span> <span class="nd">@Nullable</span> <span class="nc">RedisCacheConfiguration</span> <span class="n">cacheConfig</span><span class="o">)</span> <span class="o">{</span> <span class="nc">RedisCacheManager</span><span class="o">.</span><span class="na">RedisCacheManagerBuilder</span> <span class="n">builder</span> <span class="o">=</span> <span class="nc">RedisCacheManager</span><span class="o">.</span><span class="na">builder</span><span class="o">(</span><span class="n">redisConnectionFactory</span><span class="o">);</span> <span class="k">if</span> <span class="o">(</span><span class="n">cacheConfig</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span> <span class="n">builder</span><span class="o">.</span><span class="na">cacheDefaults</span><span class="o">(</span><span class="n">cacheConfig</span><span class="o">);</span> <span class="o">}</span> <span class="k">if</span> <span class="o">(</span><span class="n">cacheProperties</span><span class="o">.</span><span class="na">getRedis</span><span class="o">().</span><span class="na">isEnableStatistics</span><span class="o">())</span> <span class="o">{</span> <span class="n">builder</span><span class="o">.</span><span class="na">enableStatistics</span><span class="o">();</span> <span class="o">}</span> <span class="k">return</span> <span class="o">(</span><span class="nc">RedisCache</span><span class="o">)</span><span class="n">builder</span><span class="o">.</span><span class="na">build</span><span class="o">().</span><span class="na">getCache</span><span class="o">(</span><span class="n">name</span><span class="o">);</span> <span class="o">}</span> <span class="kd">private</span> <span class="n">org</span><span class="o">.</span><span class="na">springframework</span><span class="o">.</span><span class="na">data</span><span class="o">.</span><span class="na">redis</span><span class="o">.</span><span class="na">cache</span><span class="o">.</span><span class="na">RedisCacheConfiguration</span> <span class="nf">createConfiguration</span><span class="o">(</span> <span class="nc">CacheProperties</span> <span class="n">cacheProperties</span><span class="o">,</span> <span class="nc">ClassLoader</span> <span class="n">classLoader</span><span class="o">)</span> <span class="o">{</span> <span class="nc">CacheProperties</span><span class="o">.</span><span class="na">Redis</span> <span class="n">redisProperties</span> <span class="o">=</span> <span class="n">cacheProperties</span><span class="o">.</span><span class="na">getRedis</span><span class="o">();</span> <span class="n">org</span><span class="o">.</span><span class="na">springframework</span><span class="o">.</span><span class="na">data</span><span class="o">.</span><span class="na">redis</span><span class="o">.</span><span class="na">cache</span><span class="o">.</span><span class="na">RedisCacheConfiguration</span> <span class="n">config</span> <span class="o">=</span> <span class="n">org</span><span class="o">.</span><span class="na">springframework</span><span class="o">.</span><span class="na">data</span><span class="o">.</span><span class="na">redis</span><span class="o">.</span><span class="na">cache</span><span class="o">.</span><span class="na">RedisCacheConfiguration</span> <span class="o">.</span><span class="na">defaultCacheConfig</span><span class="o">();</span> <span class="n">config</span> <span class="o">=</span> <span class="n">config</span> <span class="o">.</span><span class="na">serializeValuesWith</span><span class="o">(</span><span class="nc">RedisSerializationContext</span><span class="o">.</span><span class="na">SerializationPair</span><span class="o">.</span><span class="na">fromSerializer</span><span class="o">(</span><span class="k">new</span> <span class="nc">JdkSerializationRedisSerializer</span><span class="o">(</span><span class="n">classLoader</span><span class="o">)));</span> <span class="k">if</span> <span class="o">(</span><span class="n">redisProperties</span><span class="o">.</span><span class="na">getTimeToLive</span><span class="o">()</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span> <span class="n">config</span> <span class="o">=</span> <span class="n">config</span><span class="o">.</span><span class="na">entryTtl</span><span class="o">(</span><span class="n">redisProperties</span><span class="o">.</span><span class="na">getTimeToLive</span><span class="o">());</span> <span class="o">}</span> <span class="k">if</span> <span class="o">(</span><span class="n">redisProperties</span><span class="o">.</span><span class="na">getKeyPrefix</span><span class="o">()</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span> <span class="n">config</span> <span class="o">=</span> <span class="n">config</span><span class="o">.</span><span class="na">prefixCacheNameWith</span><span class="o">(</span><span class="n">redisProperties</span><span class="o">.</span><span class="na">getKeyPrefix</span><span class="o">());</span> <span class="o">}</span> <span class="k">if</span> <span class="o">(!</span><span class="n">redisProperties</span><span class="o">.</span><span class="na">isCacheNullValues</span><span class="o">())</span> <span class="o">{</span> <span class="n">config</span> <span class="o">=</span> <span class="n">config</span><span class="o">.</span><span class="na">disableCachingNullValues</span><span class="o">();</span> <span class="o">}</span> <span class="k">if</span> <span class="o">(!</span><span class="n">redisProperties</span><span class="o">.</span><span class="na">isUseKeyPrefix</span><span class="o">())</span> <span class="o">{</span> <span class="n">config</span> <span class="o">=</span> <span class="n">config</span><span class="o">.</span><span class="na">disableKeyPrefix</span><span class="o">();</span> <span class="o">}</span> <span class="k">return</span> <span class="n">config</span><span class="o">;</span> <span class="o">}</span> <span class="o">}</span> </pre></td></tr></tbody></table></code></pre></div> </div> </li> </ul> </li> <li> <p>定义构建器,用于构建缓存</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 </pre></td><td class="rouge-code"><pre><span class="kd">public</span> <span class="kd">class</span> <span class="nc">CacheBuilder</span><span class="o"><</span><span class="no">K</span><span class="o">,</span> <span class="no">V</span><span class="o">></span> <span class="o">{</span> <span class="kd">private</span> <span class="nc">String</span> <span class="n">name</span><span class="o">;</span> <span class="kd">private</span> <span class="nc">Duration</span> <span class="n">ttl</span><span class="o">;</span> <span class="kd">private</span> <span class="kt">boolean</span> <span class="n">cacheNullValues</span> <span class="o">=</span> <span class="kc">true</span><span class="o">;</span> <span class="kd">private</span> <span class="kt">boolean</span> <span class="n">initializeClear</span> <span class="o">=</span> <span class="kc">false</span><span class="o">;</span> <span class="kd">public</span> <span class="kd">static</span> <span class="nc">CacheBuilder</span><span class="o"><</span><span class="nc">Object</span><span class="o">,</span> <span class="nc">Object</span><span class="o">></span> <span class="nf">newBuilder</span><span class="o">()</span> <span class="o">{</span> <span class="k">return</span> <span class="k">new</span> <span class="nc">CacheBuilder</span><span class="o"><>();</span> <span class="o">}</span> <span class="cm">/** * 缓存名称 * * @param name 缓存名称 * @return this */</span> <span class="kd">public</span> <span class="nc">CacheBuilder</span><span class="o"><</span><span class="no">K</span><span class="o">,</span> <span class="no">V</span><span class="o">></span> <span class="nf">name</span><span class="o">(</span><span class="nc">String</span> <span class="n">name</span><span class="o">)</span> <span class="o">{</span> <span class="k">this</span><span class="o">.</span><span class="na">name</span> <span class="o">=</span> <span class="n">name</span><span class="o">;</span> <span class="k">return</span> <span class="k">this</span><span class="o">;</span> <span class="o">}</span> <span class="cm">/** * 初始化时清理数据 * * @return this */</span> <span class="kd">public</span> <span class="nc">CacheBuilder</span><span class="o"><</span><span class="no">K</span><span class="o">,</span> <span class="no">V</span><span class="o">></span> <span class="nf">initializeClear</span><span class="o">(</span><span class="kt">boolean</span> <span class="n">initializeClear</span><span class="o">)</span> <span class="o">{</span> <span class="k">this</span><span class="o">.</span><span class="na">initializeClear</span> <span class="o">=</span> <span class="n">initializeClear</span><span class="o">;</span> <span class="k">return</span> <span class="k">this</span><span class="o">;</span> <span class="o">}</span> <span class="cm">/** * 设置超时时间,超时自动剔除 * * @param ttl 超时时间 * @return this */</span> <span class="kd">public</span> <span class="nc">CacheBuilder</span><span class="o"><</span><span class="no">K</span><span class="o">,</span> <span class="no">V</span><span class="o">></span> <span class="nf">ttl</span><span class="o">(</span><span class="nc">Duration</span> <span class="n">ttl</span><span class="o">)</span> <span class="o">{</span> <span class="k">this</span><span class="o">.</span><span class="na">ttl</span> <span class="o">=</span> <span class="n">ttl</span><span class="o">;</span> <span class="k">return</span> <span class="k">this</span><span class="o">;</span> <span class="o">}</span> <span class="cm">/** * 缓存null值 * * @param cacheNullValues 是否允许缓存空值 * @return this */</span> <span class="kd">public</span> <span class="nc">CacheBuilder</span><span class="o"><</span><span class="no">K</span><span class="o">,</span> <span class="no">V</span><span class="o">></span> <span class="nf">cacheNullValues</span><span class="o">(</span><span class="kt">boolean</span> <span class="n">cacheNullValues</span><span class="o">)</span> <span class="o">{</span> <span class="k">this</span><span class="o">.</span><span class="na">cacheNullValues</span> <span class="o">=</span> <span class="n">cacheNullValues</span><span class="o">;</span> <span class="k">return</span> <span class="k">this</span><span class="o">;</span> <span class="o">}</span> <span class="cm">/** * 构建缓存对象,Map结构 * * @param cacheProvider 缓存提供者 * @return 缓存对象,Map结构 */</span> <span class="kd">public</span> <span class="o"><</span><span class="no">K1</span> <span class="kd">extends</span> <span class="no">K</span><span class="o">,</span> <span class="no">V1</span> <span class="kd">extends</span> <span class="no">V</span><span class="o">></span> <span class="nc">AdaptingCache</span><span class="o"><</span><span class="no">K1</span><span class="o">,</span> <span class="no">V1</span><span class="o">></span> <span class="nf">build</span><span class="o">(</span><span class="nc">CacheProvider</span> <span class="n">cacheProvider</span><span class="o">)</span> <span class="o">{</span> <span class="nc">Assert</span><span class="o">.</span><span class="na">state</span><span class="o">(</span><span class="n">name</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">,</span> <span class="s">"CacheBuild name must not be null."</span><span class="o">);</span> <span class="nc">AdaptingCache</span><span class="o"><</span><span class="no">K1</span><span class="o">,</span> <span class="no">V1</span><span class="o">></span> <span class="n">cache</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">AbstractAdaptingCache</span><span class="o"><</span><span class="no">K1</span><span class="o">,</span> <span class="no">V1</span><span class="o">>(</span><span class="n">cacheProvider</span><span class="o">.</span><span class="na">getCache</span><span class="o">(</span><span class="k">this</span><span class="o">))</span> <span class="o">{</span> <span class="o">};</span> <span class="k">if</span> <span class="o">(</span><span class="nc">BooleanUtils</span><span class="o">.</span><span class="na">isTrue</span><span class="o">(</span><span class="n">initializeClear</span><span class="o">))</span> <span class="o">{</span> <span class="n">cache</span><span class="o">.</span><span class="na">clear</span><span class="o">();</span> <span class="o">}</span> <span class="k">return</span> <span class="n">cache</span><span class="o">;</span> <span class="o">}</span> <span class="cm">/** * 构建缓存对象,Value结构 * * @param cacheProvider 缓存提供者 * @return 缓存对象,Value结构 */</span> <span class="kd">public</span> <span class="o"><</span><span class="no">V1</span> <span class="kd">extends</span> <span class="no">V</span><span class="o">></span> <span class="nc">AdaptingValueCache</span><span class="o"><</span><span class="no">V1</span><span class="o">></span> <span class="nf">buildValue</span><span class="o">(</span><span class="nc">CacheProvider</span> <span class="n">cacheProvider</span><span class="o">)</span> <span class="o">{</span> <span class="nc">Assert</span><span class="o">.</span><span class="na">state</span><span class="o">(</span><span class="n">name</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">,</span> <span class="s">"CacheBuild name must not be null."</span><span class="o">);</span> <span class="nc">AdaptingValueCache</span><span class="o"><</span><span class="no">V1</span><span class="o">></span> <span class="n">valueCache</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">AbstractAdaptingValueCache</span><span class="o"><</span><span class="no">V1</span><span class="o">>(</span><span class="n">cacheProvider</span><span class="o">.</span><span class="na">getCache</span><span class="o">(</span><span class="k">this</span><span class="o">))</span> <span class="o">{</span> <span class="o">};</span> <span class="k">if</span> <span class="o">(</span><span class="nc">BooleanUtils</span><span class="o">.</span><span class="na">isTrue</span><span class="o">(</span><span class="n">initializeClear</span><span class="o">))</span> <span class="o">{</span> <span class="n">valueCache</span><span class="o">.</span><span class="na">clear</span><span class="o">();</span> <span class="o">}</span> <span class="k">return</span> <span class="n">valueCache</span><span class="o">;</span> <span class="o">}</span> <span class="cm">/** * 获取缓存名称 * * @return 缓存名称 */</span> <span class="kd">public</span> <span class="nc">String</span> <span class="nf">getName</span><span class="o">()</span> <span class="o">{</span> <span class="k">return</span> <span class="n">name</span><span class="o">;</span> <span class="o">}</span> <span class="cm">/** * 获取缓存超时时间 * * @return 超时时间 */</span> <span class="kd">public</span> <span class="nc">Duration</span> <span class="nf">getTtl</span><span class="o">()</span> <span class="o">{</span> <span class="k">return</span> <span class="n">ttl</span><span class="o">;</span> <span class="o">}</span> <span class="cm">/** * 是否允许缓存空值 * * @return 允许则为true */</span> <span class="kd">public</span> <span class="kt">boolean</span> <span class="nf">isCacheNullValues</span><span class="o">()</span> <span class="o">{</span> <span class="k">return</span> <span class="n">cacheNullValues</span><span class="o">;</span> <span class="o">}</span> <span class="o">}</span> </pre></td></tr></tbody></table></code></pre></div> </div> </li> </ol> <h1 id="示例">示例</h1> <p><strong>改造前</strong></p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1 2 3 4 5 6 7 </pre></td><td class="rouge-code"><pre><span class="kd">class</span> <span class="nc">Main</span><span class="o">{</span> <span class="kd">private</span> <span class="nc">Map</span><span class="o"><</span><span class="nc">String</span><span class="o">,</span><span class="nc">String</span><span class="o">></span> <span class="n">cache</span><span class="o">;</span> <span class="kd">private</span> <span class="nc">String</span><span class="o">[]</span> <span class="n">values</span><span class="o">;</span> <span class="o">}</span> </pre></td></tr></tbody></table></code></pre></div></div> <p><strong>改造后</strong></p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1 2 3 4 5 6 7 8 9 10 11 12 </pre></td><td class="rouge-code"><pre><span class="kd">class</span> <span class="nc">Main</span><span class="o">{</span> <span class="kd">private</span> <span class="nc">AdaptingCache</span><span class="o"><</span><span class="nc">String</span><span class="o">,</span><span class="nc">String</span><span class="o">></span> <span class="n">cache</span><span class="o">;</span> <span class="kd">private</span> <span class="nc">AdaptingValueCache</span><span class="o"><</span><span class="nc">String</span><span class="o">[]></span> <span class="n">values</span><span class="o">;</span> <span class="kd">public</span> <span class="nf">Main</span><span class="o">(</span><span class="nc">CacheProvider</span> <span class="n">cacheProvider</span><span class="o">){</span> <span class="n">cache</span> <span class="o">=</span> <span class="nc">CacheBuilder</span><span class="o">.</span><span class="na">newBuilder</span><span class="o">().</span><span class="na">name</span><span class="o">(</span><span class="s">"cache"</span><span class="o">).</span><span class="na">build</span><span class="o">(</span><span class="n">cacheProvider</span><span class="o">);</span> <span class="n">values</span> <span class="o">=</span><span class="nc">CacheBuilder</span><span class="o">.</span><span class="na">newBuilder</span><span class="o">().</span><span class="na">name</span><span class="o">(</span><span class="s">"values"</span><span class="o">).</span><span class="na">buildValue</span><span class="o">(</span><span class="n">cacheProvider</span><span class="o">);</span> <span class="o">}</span> <span class="o">}</span> </pre></td></tr></tbody></table></code></pre></div></div> <h1 id="拓展">拓展</h1> <p>架构设计没有统一的标准,每个人都有自己的设计理念。</p> <p>但奔向的目标是一致的,让产品更方便的使用、让后期维护和拓展更加容易…</p> <p>如果你有什么新的想法,评论区见</p>