欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Springboot?多級(jí)緩存設(shè)計(jì)與實(shí)現(xiàn)方案

 更新時(shí)間:2024年02月27日 14:54:54   作者:牽著貓散步的鼠鼠  
多級(jí)緩存是提升高并發(fā)系統(tǒng)性能的關(guān)鍵策略之一,它不僅能夠減少系統(tǒng)的響應(yīng)時(shí)間,提高用戶體驗(yàn),還能有效降低后端系統(tǒng)的負(fù)載,防止系統(tǒng)過(guò)載,這篇文章主要介紹了Springboot?多級(jí)緩存設(shè)計(jì)與實(shí)現(xiàn),需要的朋友可以參考下

對(duì)于高并發(fā)系統(tǒng)來(lái)說(shuō),有三個(gè)重要的機(jī)制來(lái)保障其高效運(yùn)行,它們分別是:緩存、限流和熔斷。而緩存是排在最前面也是高并發(fā)系統(tǒng)之所以高效運(yùn)行的關(guān)鍵手段,那么問(wèn)題來(lái)了:緩存只使用 Redis 就夠了嗎?

冗余設(shè)計(jì)理念

當(dāng)然不是,不要把所有雞蛋放到一個(gè)籃子里,成熟的系統(tǒng)在關(guān)鍵功能實(shí)現(xiàn)時(shí)一定會(huì)考慮冗余設(shè)計(jì),注意這里的冗余設(shè)計(jì)不是貶義詞。

冗余設(shè)計(jì)是在系統(tǒng)或設(shè)備完成任務(wù)起關(guān)鍵作用的地方,增加一套以上完成相同功能的功能通道(or 系統(tǒng))、工作元件或部件,以保證當(dāng)該部分出現(xiàn)故障時(shí),系統(tǒng)或設(shè)備仍能正常工作,以減少系統(tǒng)或者設(shè)備的故障概率,提高系統(tǒng)可靠性。

例如,飛機(jī)的設(shè)計(jì),飛機(jī)正常運(yùn)行只需要兩個(gè)發(fā)動(dòng)機(jī),但在每臺(tái)飛機(jī)的設(shè)計(jì)中可能至少會(huì)設(shè)計(jì)四個(gè)發(fā)動(dòng)機(jī),這就有冗余設(shè)計(jì)的典型使用場(chǎng)景,這樣設(shè)計(jì)的目的是為了保證極端情況下,如果有一個(gè)或兩個(gè)發(fā)動(dòng)機(jī)出現(xiàn)故障,不會(huì)因?yàn)槟硞€(gè)發(fā)動(dòng)機(jī)的故障而引起重大的安全事故。

多級(jí)緩存概述

緩存功能的設(shè)計(jì)也是一樣,我們?cè)诟卟l(fā)系統(tǒng)中通常會(huì)使用多級(jí)緩存來(lái)保證其高效運(yùn)行,其中的多級(jí)緩存就包含以下這些:

  • 瀏覽器緩存:它的實(shí)現(xiàn)主要依靠 HTTP 協(xié)議中的緩存機(jī)制,當(dāng)瀏覽器第一次請(qǐng)求一個(gè)資源時(shí),服務(wù)器會(huì)將該資源的相關(guān)緩存規(guī)則(如 Cache-Control、Expires 等)一同返回給客戶端,瀏覽器會(huì)根據(jù)這些規(guī)則來(lái)判斷是否需要緩存該資源以及該資源的有效期。
  • Nginx 緩存:在 Nginx 中配置中開(kāi)啟緩存功能。
  • 分布式緩存:所有系統(tǒng)調(diào)用的中間件都是分布式緩存,如 Redis、MemCached 等。
  • 本地緩存:JVM 層面,單系統(tǒng)運(yùn)行期間在內(nèi)存中產(chǎn)生的緩存,例如 Caffeine、Google Guava 等。

以下是它們的具體使用。

開(kāi)啟瀏覽器緩存

在 Java Web應(yīng)用中,實(shí)現(xiàn)瀏覽器緩存可以使用 HttpServletResponse 對(duì)象來(lái)設(shè)置與緩存相關(guān)的響應(yīng)頭,以開(kāi)啟瀏覽器的緩存功能,它的具體實(shí)現(xiàn)分為以下幾步。

① 配置 Cache-Control

Cache-Control 是 HTTP/1.1 中用于控制緩存策略的主要方式。它可以設(shè)置多個(gè)指令,如 max-age(定義資源的最大存活時(shí)間,單位秒)、no-cache(要求重新驗(yàn)證)、public(指示可以被任何緩存區(qū)緩存)、private(只能被單個(gè)用戶私有緩存存儲(chǔ))等,設(shè)置如下:

response.setHeader("Cache-Control", "max-age=3600, public"); // 緩存一小時(shí)

② 配置 Expires

設(shè)置一個(gè)絕對(duì)的過(guò)期時(shí)間,超過(guò)這個(gè)時(shí)間點(diǎn)后瀏覽器將不再使用緩存的內(nèi)容而向服務(wù)器請(qǐng)求新的資源,設(shè)置如下:

response.setDateHeader("Expires", System.currentTimeMillis() + 3600 * 1000); // 緩存一小時(shí)

③ 配置 ETag

ETag(實(shí)體標(biāo)簽)一種驗(yàn)證機(jī)制,它為每個(gè)版本的資源生成一個(gè)唯一標(biāo)識(shí)符。當(dāng)客戶端發(fā)起請(qǐng)求時(shí),會(huì)攜帶上先前接收到的 ETag,服務(wù)器根據(jù) ETag 判斷資源是否已更新,若未更新則返回 304 Not Modified 狀態(tài)碼,通知瀏覽器繼續(xù)使用本地緩存,設(shè)置如下:

String etag = generateETagForContent(); // 根據(jù)內(nèi)容生成ETagresponse.setHeader("ETag", etag);

④ 配置 Last-Modified

指定資源最后修改的時(shí)間戳,瀏覽器下次請(qǐng)求時(shí)會(huì)帶上 If-Modified-Since 頭,服務(wù)器對(duì)比時(shí)間戳決定是否返回新內(nèi)容或發(fā)送 304 狀態(tài)碼,設(shè)置如下:

long lastModifiedDate = getLastModifiedDate();
response.setDateHeader("Last-Modified", lastModifiedDate);

整體配置

在 Spring Web 框架中,可以通過(guò) HttpServletResponse 對(duì)象來(lái)設(shè)置這些頭信息。例如,在過(guò)濾器中設(shè)置響應(yīng)頭以啟用緩存:

public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
       throws IOException, ServletException {
   HttpServletResponse httpResponse = (HttpServletResponse) response;
   // 設(shè)置緩存策略
   httpResponse.setHeader("Cache-Control", "max-age=3600");
   // 其他響應(yīng)頭設(shè)置...
   chain.doFilter(request, response);
}

以上就是在 Java Web 應(yīng)用程序中利用 HTTP 協(xié)議特性控制瀏覽器緩存的基本方法。

開(kāi)啟 Nginx 緩存

Nginx 中開(kāi)啟緩存的配置總共有以下 5 步。

① 定義緩存配置

在 Nginx 配置中定義一個(gè)緩存路徑和配置,通過(guò) proxy_cache_path 指令完成,例如,以下配置:

proxy_cache_path /path/to/cache levels=1:2 keys_zone=my_cache:10m max_size=10g inactive=60m use_temp_path=off;

其中:

  • /path/to/cache:這是緩存文件的存放路徑。
  • levels=1:2:定義緩存目錄的層級(jí)結(jié)構(gòu)。
  • keys_zone=my_cache:10m:定義一個(gè)名為 my_cache 的共享內(nèi)存區(qū)域,大小為 10MB。
  • max_size=10g:設(shè)置緩存的最大大小為 10GB。
  • inactive=60m:如果在 60 分鐘內(nèi)沒(méi)有被訪問(wèn),緩存將被清理。
  • use_temp_path=off:避免在文件系統(tǒng)中進(jìn)行不必要的數(shù)據(jù)拷貝。

② 啟用緩存

在 server 或 location 塊中,使用 proxy_cache 指令來(lái)啟用緩存,并指定要使用的 keys zone,例如,以下配置:

server {  
    ...  
    location / {  
        proxy_cache my_cache;  
        ...  
    }  
}

③ 設(shè)置緩存有效期

使用 proxy_cache_valid 指令來(lái)設(shè)置哪些響應(yīng)碼的緩存時(shí)間,例如,以下配置:

location / {  
    proxy_cache my_cache;  
    proxy_cache_valid 200 304 12h;  
    proxy_cache_valid any 1m;  
    ...  
}

④ 配置反向代理

確保你已經(jīng)配置了反向代理,以便 Nginx 可以將請(qǐng)求轉(zhuǎn)發(fā)到后端服務(wù)器。例如,以下配置:

location / {  
    proxy_pass http://backend_server;  
    ...  
}

⑤ 重新加載配置

保存并關(guān)閉 Nginx 配置文件后,使用 nginx -s reload 命令重新加載配置,使更改生效。

Redis+Caffeine實(shí)現(xiàn)應(yīng)用層二級(jí)緩存

在SpringBoot中實(shí)現(xiàn)多級(jí)緩存需要解決兩個(gè)關(guān)鍵問(wèn)題:緩存數(shù)據(jù)的讀取順序和數(shù)據(jù)的一致性。以下是實(shí)現(xiàn)多級(jí)緩存的步驟:

導(dǎo)入依賴:

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-cache</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
    <dependency>
        <groupId>com.github.ben-manes.caffeine</groupId>
        <artifactId>caffeine</artifactId>
    </dependency>
    <!-- 其他依賴 -->
</dependencies>

編寫(xiě)redis相關(guān)配置:

spring:
  redis:
    host: 127.0.0.1
    port: 6379
    password:
    jedis:
      pool:
        max-active: 8
        max-wait: -1
        max-idle: 500
        min-idle: 0
    lettuce:
      shutdown-timeout: 0

本地緩存配置類

/**
 * 本地緩存Caffeine配置類
 */
@Configuration
public class LocalCacheConfiguration {
    @Bean("localCacheManager")
    public Cache<String, Object> localCacheManager() {
        return Caffeine.newBuilder()
                //寫(xiě)入或者更新5s后,緩存過(guò)期并失效, 實(shí)際項(xiàng)目中肯定不會(huì)那么短時(shí)間就過(guò)期,根據(jù)具體情況設(shè)置即可
                .expireAfterWrite(5, TimeUnit.SECONDS)
                // 初始的緩存空間大小
                .initialCapacity(50)
                // 緩存的最大條數(shù),通過(guò) Window TinyLfu算法控制整個(gè)緩存大小
                .maximumSize(500)
            	//打開(kāi)數(shù)據(jù)收集功能
                .recordStats()
                .build();
    }
}

Redis客戶端配置類:

@Configuration
public class RedisConfig {
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        //關(guān)聯(lián)
        template.setConnectionFactory(factory);
        //設(shè)置key的序列化方式
//        template.setKeySerializer();
        //設(shè)置value的序列化方式
//        template.setValueSerializer();
        return template;
    }
}

編寫(xiě)測(cè)試用的服務(wù)類接口:

public interface UserService {
    void add(User user);
    User getById(String id);
    User update(User user);
    void deleteById(String id);
}

編寫(xiě)測(cè)試用的服務(wù)類:

這里本地緩存也可以用注解式緩存來(lái)實(shí)現(xiàn),這里就不細(xì)寫(xiě)啦~

import com.alibaba.fastjson.JSON;
import com.github.benmanes.caffeine.cache.Cache;
import com.wsh.springboot_caffeine.entity.User;
import com.wsh.springboot_caffeine.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
@Service
public class UserServiceImpl implements UserService {
    /**
     * 模擬數(shù)據(jù)庫(kù)存儲(chǔ)數(shù)據(jù)
     */
    private static HashMap<String, User> userMap = new HashMap<>();
    private final RedisTemplate<String, Object> redisTemplate;
    private final Cache<String, Object> caffeineCache;
    @Autowired
    public UserServiceImpl(RedisTemplate<String, Object> redisTemplate,
                           @Qualifier("localCacheManager") Cache<String, Object> caffeineCache) {
        this.redisTemplate = redisTemplate;
        this.caffeineCache = caffeineCache;
    }
    static {
        userMap.put("1", new User("1", "zhangsan"));
        userMap.put("2", new User("2", "lisi"));
        userMap.put("3", new User("3", "wangwu"));
        userMap.put("4", new User("4", "zhaoliu"));
    }
    @Override
    public void add(User user) {
        // 1.保存Caffeine緩存
        caffeineCache.put(user.getId(), user);
        // 2.保存redis緩存
        redisTemplate.opsForValue().set(user.getId(), JSON.toJSONString(user), 20, TimeUnit.SECONDS);
        // 3.保存數(shù)據(jù)庫(kù)(模擬)
        userMap.put(user.getId(), user);
    }
    @Override
    public User getById(String id) {
        // 1.先從Caffeine緩存中讀取
        Object o = caffeineCache.getIfPresent(id);
        if (Objects.nonNull(o)) {
            System.out.println("從Caffeine中查詢到數(shù)據(jù)...");
            return (User) o;
        }
        // 2.如果緩存中不存在,則從Redis緩存中查找
        String jsonString = (String) redisTemplate.opsForValue().get(id);
        User user = JSON.parseObject(jsonString, User.class);
        if (Objects.nonNull(user)) {
            System.out.println("從Redis中查詢到數(shù)據(jù)...");
            // 保存Caffeine緩存
            caffeineCache.put(user.getId(), user);
            return user;
        }
        // 3.如果Redis緩存中不存在,則從數(shù)據(jù)庫(kù)中查詢
        user = userMap.get(id);
        if (Objects.nonNull(user)) {
            // 保存Caffeine緩存
            caffeineCache.put(user.getId(), user);
            // 保存Redis緩存,20s后過(guò)期
            redisTemplate.opsForValue().set(user.getId(), JSON.toJSONString(user), 20, TimeUnit.SECONDS);
        }
        System.out.println("從數(shù)據(jù)庫(kù)中查詢到數(shù)據(jù)...");
        return user;
    }
    @Override
    public User update(User user) {
        User oldUser = userMap.get(user.getId());
        oldUser.setName(user.getName());
        // 1.更新數(shù)據(jù)庫(kù)
        userMap.put(oldUser.getId(), oldUser);
        // 2.更新Caffeine緩存
        caffeineCache.put(oldUser.getId(), oldUser);
        // 3.更新Redis數(shù)據(jù)庫(kù)
        redisTemplate.opsForValue().set(oldUser.getId(), JSON.toJSONString(oldUser), 20, TimeUnit.SECONDS);
        return oldUser;
    }
    @Override
    public void deleteById(String id) {
        // 1.刪除數(shù)據(jù)庫(kù)
        userMap.remove(id);
        // 2.刪除Caffeine緩存
        caffeineCache.invalidate(id);
        // 3.刪除Redis緩存
        redisTemplate.delete(id);
    }
}

總結(jié)

多級(jí)緩存是提升高并發(fā)系統(tǒng)性能的關(guān)鍵策略之一。它不僅能夠減少系統(tǒng)的響應(yīng)時(shí)間,提高用戶體驗(yàn),還能有效降低后端系統(tǒng)的負(fù)載,防止系統(tǒng)過(guò)載。在實(shí)際應(yīng)用中,開(kāi)發(fā)者應(yīng)根據(jù)系統(tǒng)的具體需求和資源情況,靈活設(shè)計(jì)和調(diào)整多級(jí)緩存策略,以達(dá)到最佳的性能表現(xiàn)。大部分情況下我們使用redis作為緩存是可以滿足需求的,加入本地緩存后雖然帶來(lái)了部分性能提升,但是存在數(shù)據(jù)一致性的問(wèn)題,一定程度上添加了維護(hù)難度。

到此這篇關(guān)于Springboot 多級(jí)緩存設(shè)計(jì)與實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)Springboot 多級(jí)緩存內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java遞歸實(shí)現(xiàn)字符串全排列與全組合

    Java遞歸實(shí)現(xiàn)字符串全排列與全組合

    這篇文章主要為大家詳細(xì)介紹了Java遞歸實(shí)現(xiàn)字符串全排列與全組合,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-02-02
  • 詳解Nacos配置中心的實(shí)現(xiàn)

    詳解Nacos配置中心的實(shí)現(xiàn)

    Spring Cloud Alibaba 是阿里巴巴提供的一站式微服務(wù)開(kāi)發(fā)解決方案。而 Nacos 作為 Spring Cloud Alibaba 的核心組件之一,提供了兩個(gè)非常重要的功能:注冊(cè)中心和配置中心,我們今天來(lái)了解和實(shí)現(xiàn)一下二者
    2022-08-08
  • Mybatis中的自定義映射resultMap

    Mybatis中的自定義映射resultMap

    在MyBatis中,自定義映射resultMap可以讓你精確控制如何將數(shù)據(jù)庫(kù)返回的結(jié)果集映射到Java對(duì)象上,本文給介紹了Mybatis之自定義映射resultMap,需要的朋友可以參考下
    2024-03-03
  • 如何利用Java輸出鏈表中倒數(shù)第k個(gè)結(jié)點(diǎn)

    如何利用Java輸出鏈表中倒數(shù)第k個(gè)結(jié)點(diǎn)

    這篇文章主要給大家介紹了關(guān)于如何利用Java輸出鏈表中倒數(shù)第k個(gè)結(jié)點(diǎn)的相關(guān)資料,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用java具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2021-12-12
  • RabbitMQ中的Channel和Exchange詳解

    RabbitMQ中的Channel和Exchange詳解

    這篇文章主要介紹了RabbitMQ中的Channel和Exchange詳解,創(chuàng)建和銷毀TCP連接很耗時(shí),打開(kāi)太多TCP連接,耗操作系統(tǒng)資源,并發(fā)量大到一定程度,系統(tǒng)的吞吐量會(huì)降低,使用一個(gè)connection多channel的方式,可以提升連接的利用率,需要的朋友可以參考下
    2023-08-08
  • 線程池FutureTask異步執(zhí)行多任務(wù)實(shí)現(xiàn)詳解

    線程池FutureTask異步執(zhí)行多任務(wù)實(shí)現(xiàn)詳解

    這篇文章主要為大家介紹了線程池FutureTask異步執(zhí)行多任務(wù)實(shí)現(xiàn)詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-11-11
  • SpringBoot實(shí)現(xiàn)緩存組件配置動(dòng)態(tài)切換的步驟詳解

    SpringBoot實(shí)現(xiàn)緩存組件配置動(dòng)態(tài)切換的步驟詳解

    現(xiàn)在有多個(gè)springboot項(xiàng)目,但是不同的項(xiàng)目中使用的緩存組件是不一樣的,有的項(xiàng)目使用redis,有的項(xiàng)目使用ctgcache,現(xiàn)在需要用同一套代碼通過(guò)配置開(kāi)關(guān),在不同的項(xiàng)目中切換這兩種緩存,本文介紹了SpringBoot實(shí)現(xiàn)緩存組件配置動(dòng)態(tài)切換的步驟,需要的朋友可以參考下
    2024-07-07
  • 淺談list.removeAll()刪除失敗的原因及解決

    淺談list.removeAll()刪除失敗的原因及解決

    這篇文章主要介紹了淺談list.removeAll()刪除失敗的原因及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2020-12-12
  • java基于quasar實(shí)現(xiàn)協(xié)程池的方法示例

    java基于quasar實(shí)現(xiàn)協(xié)程池的方法示例

    本文主要介紹了java基于quasar實(shí)現(xiàn)協(xié)程池的方法示例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧<BR>
    2022-06-06
  • Java中實(shí)現(xiàn)獲取路徑的方法匯總

    Java中實(shí)現(xiàn)獲取路徑的方法匯總

    本文給大家匯總分享的是Java中實(shí)現(xiàn)獲取路徑的方法,非常的簡(jiǎn)單實(shí)用,需要的小伙伴可以參考下。
    2015-03-03

最新評(píng)論