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

Guava本地緩存的使用過(guò)程

 更新時(shí)間:2025年01月03日 09:25:54   作者:肥肥肥柯  
文章介紹了使用Guava和Redis實(shí)現(xiàn)二級(jí)緩存的原因,以及如何通過(guò)Guava作為一級(jí)緩存,Redis作為二級(jí)緩存來(lái)減少數(shù)據(jù)庫(kù)壓力,提高緩存的可靠性,同時(shí),通過(guò)一個(gè)具體示例說(shuō)明了如何在微服務(wù)場(chǎng)景中使用Guava和Redis進(jìn)行二級(jí)緩存,最后,總結(jié)了Guava的參數(shù)機(jī)制

Guava和Redis實(shí)現(xiàn)二級(jí)緩存

1、目的

本地緩存為什么不使用hashMap或者concurrentHashMap?

concurrentHahMap和hashMap一樣,都是長(zhǎng)期存在的緩存,除非調(diào)用remove方法,否則緩存中的數(shù)據(jù)無(wú)法主動(dòng)釋放。

僅使用Guava本地緩存會(huì)有什么問(wèn)題?

作為API或者某種功能系統(tǒng)來(lái)用的話,無(wú)論單機(jī)/集群(集群其實(shí)就形成了近乎Guava副本的情況),Guava中的數(shù)據(jù)增長(zhǎng)到后期不可估量的時(shí)候,Guava是支撐不住的;而微服務(wù)情況下沒(méi)法全局緩存,如果數(shù)據(jù)量無(wú)限增長(zhǎng)、不可控的話還是不建議使用。

僅使用Redis緩存會(huì)有什么問(wèn)題?

大數(shù)量的情況下(熱搜)容易引發(fā)緩存雪崩進(jìn)而導(dǎo)致服務(wù)器雪崩。

綜上,結(jié)合Guava、Redis,Guava作為一級(jí)緩存,Redis作為二級(jí)緩存,可以在減少數(shù)據(jù)庫(kù)壓力的基礎(chǔ)上,將“緩存”這道防線做的更加可靠。 

2、二級(jí)緩存場(chǎng)景示例

公司有一款攝像頭,放在了我家經(jīng)常無(wú)人居住的豪宅了,攝像頭包括異常人像報(bào)警、斷電報(bào)警、信號(hào)異常報(bào)警、捕獲畫(huà)面動(dòng)態(tài)報(bào)警等等多種報(bào)警功能類型(跳過(guò)其他設(shè)定,規(guī)定同類型的報(bào)警間隔5秒內(nèi)仍存在則繼續(xù)報(bào)警)。

現(xiàn)在有需求:我可以在平臺(tái)上配置我想要報(bào)警的報(bào)警類型(不然我哪天周末回豪宅了它還一直報(bào)警到平臺(tái)打擾我休息),當(dāng)有我報(bào)警信息過(guò)來(lái)并且是匹配我配置的報(bào)警信息時(shí),這個(gè)這條報(bào)警將推送到我平臺(tái)首頁(yè)。

//這里忽略報(bào)警系統(tǒng)代碼,報(bào)警系統(tǒng)推送報(bào)警消息是通過(guò)RocketMQ實(shí)現(xiàn)
topic: alarm-camera
@Configuration
public class RocketMqConsumer {
    private static Logger logger = LogManager.getLogger(RocketMqConsumer.class);


    public void init() {
        pullAlarm();
        logger.warn("rocketmq拉取告警數(shù)據(jù)成功!");
    }

    /**
     * pullAlarm:拉取告警源數(shù)據(jù)。
     * @author liaokh
     * @since JDK 1.8
     */
    public static void pullAlarm() {
        new Thread() {
            public void run() {
                logger.warn("---------開(kāi)始消費(fèi)報(bào)警broker---------");
                try {
                    // 聲明并初始化一個(gè)consumer
                    DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("rocketmq-consumer-dev-camera" + "-alarm");

                    // 同樣也要設(shè)置NameServer地址
                    consumer.setNamesrvAddr("我的RocketMQ服務(wù)器地址");

                    // 廣播模式 當(dāng) Consumer 使用廣播模式時(shí),每條消息都會(huì)被 Consumer 集群內(nèi)所有的 Consumer 實(shí)例消費(fèi)一次。
                    consumer.setMessageModel(MessageModel.BROADCASTING);

                    // 這里設(shè)置的是一個(gè)consumer的消費(fèi)策略
                    // CONSUME_FROM_LAST_OFFSET 默認(rèn)策略,從該隊(duì)列最尾開(kāi)始消費(fèi),即跳過(guò)歷史消息
                    // CONSUME_FROM_FIRST_OFFSET 從隊(duì)列最開(kāi)始開(kāi)始消費(fèi),即歷史消息(還儲(chǔ)存在broker的)全部消費(fèi)一遍
                    // CONSUME_FROM_TIMESTAMP 從某個(gè)時(shí)間點(diǎn)開(kāi)始消費(fèi),和setConsumeTimestamp()配合使用,默認(rèn)是半個(gè)小時(shí)以前
                    consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_LAST_OFFSET);

                    // 設(shè)置consumer所訂閱的Topic和Tag,*代表全部的Tag
                    consumer.subscribe("alarm-camera", "*");

                    // 設(shè)置一個(gè)Listener,主要進(jìn)行消息的邏輯處理
                    consumer.registerMessageListener(new MessageListenerConcurrently() {

                        @Override
                        public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs,
                                                                        ConsumeConcurrentlyContext context) {

                            for (MessageExt msg : msgs) {
                                try {
                                    String tag = msg.getTags();
                                    String alarmJson = new String(msg.getBody());
                                    logger.warn("收到alarm-camera數(shù)據(jù):tag:" + tag + " alarmJson:" + alarmJson);
                                    CameraAlarmResp resultAlarm = new CameraAlarmResp();
                                    AlarmMQResp alarm = JSON.parseObject(alarmJson, AlarmMQResp.class);
                                    //查看當(dāng)前告警類型是否在該用戶配置的列表中
                                    //根據(jù)攝像頭設(shè)備號(hào)獲取用戶信息
                                    Camera cameraEntity = Utils.getCameraById(alarm.getCameraId());  //這種核心數(shù)據(jù)也可以加載到緩存中
                                    UserAlarm userAlarm = Utils.getUserAlarm(cameraEntity.getUserId());
                                    if (userAlarm == null || StringUtils.isBlank(userAlarm.getAlarmIds())){
                                        logger.error("設(shè)備號(hào)" + alarm.getId() + "的用戶未配置需要推送的告警類型");
                                        return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
                                    }
                                    boolean isReturn = true;
                                    //獲取該用戶告警列表過(guò)濾
                                    String[] userAlarmArr = userAlarm.getAlarmIds().split(",");
                                    for (String s : userAlarmArr) {
                                        if (alarm.getAlarmType().equals(s)){  //說(shuō)明需要推送
                                            isReturn = false;
                                        }
                                    }
                                    if (isReturn){
                                        //匹配則該告警不需要推送,直接消費(fèi)成功
                                        logger.warn("該設(shè)備號(hào)的用戶未配置需要推送的告警類型");
                                        return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
                                    }

                                    WebSocket webSocket = SpringUtil.getBean(WebSocket.class);
                                    //創(chuàng)建業(yè)務(wù)消息信息
                                    JSONObject obj = new JSONObject();
                                    obj.put("cmd", "alarm");//業(yè)務(wù)類型
                                    obj.put("msgId", msg.getMsgId());//消息id
                                    obj.put("msgTxt", JSON.toJSONString(alarm));//消息內(nèi)容
                                    //單個(gè)用戶發(fā)送
                                    webSocket.sendOneMessage(alarm.getUserId(), obj.toJSONString());
                                } catch (Exception e) {
                                    logger.error("請(qǐng)求異常", e);
                                }
                            }
                            // 返回消費(fèi)狀態(tài),消費(fèi)成功
                            return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
                        }
                    });

                    // 調(diào)用start()方法啟動(dòng)consumer
                    consumer.start();
                    logger.warn("rocketmq消費(fèi)者創(chuàng)建成功");
                } catch (Exception e) {
                    logger.error("請(qǐng)求異常", e);
                }
            }
        }.start();
    }
}

該消費(fèi)者將消費(fèi)從報(bào)警系統(tǒng)推送過(guò)來(lái)的報(bào)警信息,如果符合用戶配置的報(bào)警類型,就通過(guò)WebSocket(這里只需要知道websocket是用來(lái)和前端建立長(zhǎng)連接的,如果需要詳細(xì)了解其意義和使用請(qǐng)參考相關(guān)文章)推送到前端。

其實(shí)上面示例有提到,同種類型告警5s內(nèi)如果仍然有報(bào)警將會(huì)5s后推送到平臺(tái),因此,為了避免每條MQ過(guò)來(lái)的時(shí)候,都去數(shù)據(jù)庫(kù)查一次配置表,可能一個(gè)半個(gè)的用戶報(bào)警消息算起來(lái)很少,但是如果這個(gè)攝像頭大賣,涉及到大規(guī)模用戶時(shí),這種MQ將會(huì)變得特別多,每次MQ推送報(bào)警過(guò)來(lái)時(shí)候都要去判斷是否推送,規(guī)模大了這個(gè)查庫(kù)過(guò)程就顯得特別low。

索性將這種配置數(shù)據(jù)存到緩存中,至于僅使用Guava或者僅使用Redis或者像本文一樣結(jié)合使用,又或者根據(jù)項(xiàng)目發(fā)展遞進(jìn)使用,就取決于你自己了。 

@Component
public class Utils {
	private static final Logger logger = LoggerFactory.getLogger(GuavaCacheUtils.class);
	
	/**
	* 獲取用戶告警配置信息
	*/
    public static UserAlarm getUserAlarm(String userId){
		if(StringUtils.isBlank(userId)){
			return null;
		}
		UserAlarm userAlarm = null;
		try {
			userAlarm = GuavaCacheUtils.userAlarmCache.get(userId).orNull();
			if(null == userAlarm){
				GuavaCacheUtils.userAlarmCache.invalidate(userId);  //清除Guava的緩存
                //嘗試從Redis中獲取
                String userAlarmJson = RedisUtils.hget("alarm_camera", userId);
				userAlarm = JSON.parseObject(userAlarmJson, UserAlarm.class);
			}
		} catch (ExecutionException e) {
			logger.error("獲取用戶配置緩存異常",e);
		}
		return userAlarm;
	}

	/**
	* 獲取攝像頭信息
	*/
	public static Camera getCameraById(String cameraId){
		Camera camera= null;
		try {
			camera= GuavaCacheUtils.cameraCache.get(cameraId).orNull();
		} catch (ExecutionException e) {
			logger.error("獲取設(shè)備數(shù)據(jù)異常異常",e);
		}
		return device;
	}
}
/**
 * ClassName:GuavaCacheUtils <br/>
 * @version
 * @since JDK 1.8
 * @see java(jvm)緩存存儲(chǔ)
 */
@Component
public class GuavaCacheUtils {
   private static final Logger logger = LoggerFactory.getLogger(GuavaCacheUtils.class);
	 /**
	 * 用戶告警推送列表緩存
	 *
	 * expireAfterWrite:10分鐘內(nèi)沒(méi)有更新將被回收重新獲取
	 *
	 * load:獲取緩存為空時(shí)執(zhí)行(去數(shù)據(jù)庫(kù)查詢并將結(jié)果放入緩存)
	 */
	public static LoadingCache<String, Optional<UserAlarm>> userAlarmCache = CacheBuilder.newBuilder()
			.expireAfterAccess(10, TimeUnit.MINUTES).build(new CacheLoader<String, Optional<UserAlarm>>() {
				@Override
				public Optional<UserAlarm> load(String userId) throws Exception {
                    UserAlarm userAlarm = SpringUtil.getBean(UserAlarmService.class)
                        .getOne(new LambdaQueryWrapper<UserAlarm>()
									.eq(UserAlarm::getUserId,userId));
					return Optional.fromNullable(userAlarm);
				}
			});

	/**
	 * 攝像頭設(shè)備信息緩存
	 */
	public static LoadingCache<String, Optional<Camera>> cameraCache = CacheBuilder.newBuilder()
			.expireAfterAccess(10, TimeUnit.MINUTES)
			.build(new CacheLoader<String, Optional<Camera>>() {
				@Override
				public Optional<Camera> load(String cameraId) throws Exception {
					String cameraJson = RedisUtils.hget("camera", cameraId);
					Cameracamera= JSON.parseObject(cameraJson, Camera.class);
					return Optional.fromNullable(camera);
				}
			});
}
@Service
public class UserAlarmServiceImpl extends ServiceImpl<UserAlarmMapper, UserAlarm> implements UserAlarmService{
    //新增用戶告警配置
    @Override
    public String insert(UserAlarm userAlarm){
        try{
            this.save(userAlarm);
            //隨即存入Redis
            RedisUtil.hset("alarm_camera",userAlarm.getUserId,userAlarm);
        } catch (Exception e) {
            return "失敗啦";
        }
        return "成功咯";
    }
    
    //修改用戶告警配置
    @Override
    public String update(UserAlarm userAlarm){
        try{
            UpdateWrapper<UserAlarm> wrapper = new UpdateWrapper();
            wrapper.set("alarmType",userAlarm.getAlarmType());
                .eq("user_id",userAlarm.getUserId);
            this.save(userAlarm);
            //隨即更新Redis
            RedisUtil.hset("alarm_camera",userAlarm.getUserId,userAlarm);
        } catch (Exception e) {
            return "失敗啦";
        }
        return "成功咯";
    }
}

3、Guava參數(shù)機(jī)制

#回收機(jī)制

  • expireAfterAccess: 當(dāng)緩存項(xiàng)在指定的時(shí)間段內(nèi)沒(méi)有被讀或?qū)懢蜁?huì)被回收。
  • expireAfterWrite:當(dāng)緩存項(xiàng)在指定的時(shí)間段內(nèi)沒(méi)有更新就會(huì)被回收。
  • refreshAfterWrite:當(dāng)緩存項(xiàng)上一次更新操作之后的多久會(huì)被刷新。

#刷新機(jī)制

  • expireAfterAccess: 設(shè)定時(shí)間內(nèi)沒(méi)有讀緩存才會(huì)reload。
  • expireAfterWrite/refreshAfterWrite:設(shè)定時(shí)間內(nèi)有讀緩存將不影響reload,不論此時(shí)數(shù)據(jù)庫(kù)里的指是否修改了(同時(shí)還讀緩存),時(shí)間到了直接reload。
/**
 * ClassName:GuavaCacheUtils <br/>
 * @version
 * @since JDK 1.8
 * @see java(jvm)緩存存儲(chǔ)
 */
@Component
public class GuavaCacheUtils {
   private static final Logger logger = LoggerFactory.getLogger(GuavaCacheUtils.class);

   /**
    * LoadingCache登錄緩存
    * 鏈?zhǔn)秸{(diào)用
    * removalListener:設(shè)置緩存被移除后的監(jiān)聽(tīng)任務(wù)
    * build:構(gòu)建對(duì)象
    */
   public static LoadingCache<String, Optional<User>> loginCache = CacheBuilder.newBuilder()
         .expireAfterAccess(720, TimeUnit.MINUTES).removalListener(new MyRemovalListener())
         .build(new CacheLoader<String, Optional<User>>() {
            @Override
            public Optional<User> load(String token) throws Exception {
               User user = null;
               try {
                   //到redis中匹配
                  String loginJson = RedisUtils.get(token);
                  user = JSON.parseObject(loginJson, User.class);
               } catch (Exception e) {
                  logger.error("登錄緩存查詢異常", e);
               }
               return Optional.fromNullable(user);
            }
         });

    /**
    * MyRemovalListener自定義緩存移除監(jiān)聽(tīng)器,需要實(shí)現(xiàn)RemovalListener接口并實(shí)現(xiàn)RemovalListener<K,V>接口,K,V為key和value的泛型
    * Optional:主要用于解決空指針異常,簡(jiǎn)潔判空
    * notification.getCause():監(jiān)聽(tīng)到的緩存失效原因
    */
   private static class MyRemovalListener implements RemovalListener<String, Optional<User>> {
      @Override
      public void onRemoval(RemovalNotification<String, Optional<User>> notification) {
         if (notification.getCause().toString().equals("EXPIRED")) {
            String token = notification.getKey();
            RedisUtils.del(0,token);
         }
      }
   }
}

總結(jié)

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • gradle安裝和環(huán)境配置全過(guò)程

    gradle安裝和環(huán)境配置全過(guò)程

    本文介紹了如何安裝和配置Gradle環(huán)境,包括下載Gradle、配置環(huán)境變量、測(cè)試Gradle以及在IntelliJ IDEA中配置Gradle
    2025-01-01
  • java雙色球機(jī)選法程序解析

    java雙色球機(jī)選法程序解析

    這篇文章主要為大家詳細(xì)解析了java雙色球機(jī)選法程序,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-01-01
  • Java反射機(jī)制概念、原理與用法總結(jié)

    Java反射機(jī)制概念、原理與用法總結(jié)

    這篇文章主要介紹了Java反射機(jī)制概念、原理與用法,較為詳細(xì)的分析了java反射機(jī)制的概念、原理,并結(jié)合實(shí)例形式總結(jié)分析了java反射機(jī)制的具體使用方法與相關(guān)注意事項(xiàng),需要的朋友可以參考下
    2017-11-11
  • 詳解Spring Boot使用Maven自定義打包方式

    詳解Spring Boot使用Maven自定義打包方式

    這篇文章主要介紹了Spring Boot使用Maven自定義打包方式,本文通過(guò)多種方式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-12-12
  • java 枚舉類定義靜態(tài)valueOf(java.lang.String)方法的問(wèn)題及解決

    java 枚舉類定義靜態(tài)valueOf(java.lang.String)方法的問(wèn)題及解決

    這篇文章主要介紹了java 枚舉類定義靜態(tài)valueOf(java.lang.String)方法的問(wèn)題及解決,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-09-09
  • RocketMQ消息發(fā)送流程源碼剖析

    RocketMQ消息發(fā)送流程源碼剖析

    這篇文章主要為大家介紹了RocketMQ消息發(fā)送流程源碼剖析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-08-08
  • springboot數(shù)據(jù)庫(kù)密碼加密的配置方法

    springboot數(shù)據(jù)庫(kù)密碼加密的配置方法

    這篇文章主要給大家介紹了關(guān)于springboot數(shù)據(jù)庫(kù)密碼加密的配置方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2021-04-04
  • 聊一聊Java反射

    聊一聊Java反射

    工作中哪些地方比較容易用到反射,這篇文章就為大家介紹了工作中常用到的Java反射,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2016-11-11
  • JAVA8 lambda表達(dá)式權(quán)威教程

    JAVA8 lambda表達(dá)式權(quán)威教程

    本文主要給大家講解Java8中最重要的一個(gè)特征之一lambda表達(dá)式,本文通過(guò)實(shí)例圖文解說(shuō)給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友跟隨小編一起學(xué)習(xí)下吧
    2021-05-05
  • java字符串反轉(zhuǎn)的7種方法

    java字符串反轉(zhuǎn)的7種方法

    本文主要介紹了java字符串反轉(zhuǎn)的7種方法,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-02-02

最新評(píng)論