欧美free性护士vide0shd,老熟女,一区二区三区,久久久久夜夜夜精品国产,久久久久久综合网天天,欧美成人护士h版

首頁綜合 正文
目錄

柚子快報邀請碼778899分享:緩存 Caffeine的使用

柚子快報邀請碼778899分享:緩存 Caffeine的使用

http://yzkb.51969.com/

項目結(jié)構(gòu)圖

?運行反向代理服務(wù)器也就是負(fù)責(zé)反向代理到三個nginx的nginx,該nignx也負(fù)責(zé)前端頁面的跳轉(zhuǎn)。

該nginx的conf為下:

突出位置就是該nginx需要反向代理的其他nginx的IP和端口。

在資源比較有限的時候我們通常不適用上述的機構(gòu),而是用使用Caffeine進行二級緩存,在Cffeine沒有查找到數(shù)據(jù),我們才會去redis中查詢數(shù)據(jù)。

Caffeine是什么?

Caffeine和redis都是內(nèi)存級別的緩存,為什么要使用在這兩緩存作為二級緩存,它們兩有什么區(qū)別呢?

雖然它們都是內(nèi)存級別的緩存,redis是需要單獨部署的,其需要一個單獨的進程,在tomcat訪問redis時需要網(wǎng)絡(luò)通信的開銷,而Caffeine跟我們項目代碼是寫在一起的,它是JVM級別的緩存,用的就是Java中的堆內(nèi)存,無需網(wǎng)絡(luò)的通信的開銷,在Caffeine找不到數(shù)據(jù)后才會去redis中查找。

Caffeine的使用

導(dǎo)入依賴

com.github.ben-manes.caffeine

caffeine

進行測試

import com.github.benmanes.caffeine.cache.Cache;

import com.github.benmanes.caffeine.cache.Caffeine;

import org.junit.jupiter.api.Test;

import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest

public class test {

@Test

public void test1() {

Cache cache = Caffeine.newBuilder()

.initialCapacity(100) //設(shè)置緩存的初始化容量

.maximumSize(1000) //設(shè)置最大的容量

.build();

//向緩存中插入數(shù)據(jù)

cache.put("key1", 123);

//從緩存中取出數(shù)據(jù)

Object value1 = cache.get("key1", key -> 456);

System.out.println(value1);

//獲取沒有的數(shù)據(jù)

Object value2 = cache.get("key2", key -> 789);

System.out.println(value2);

}

}

驅(qū)逐策略(面試點: 使用Caffeine為了防止內(nèi)存溢出,怎么做?)

為了防止一直往內(nèi)存里裝數(shù)值導(dǎo)致占用內(nèi)存,所以Caffeine給我們提供了驅(qū)逐策略。

1.基于容量(設(shè)置緩存的上限)

@Test

public void test2() {

Cache cache = Caffeine.newBuilder()

.initialCapacity(100) //設(shè)置緩存的初始化容量

.maximumSize(1000) //設(shè)置最大的容量

.build();

}

通過設(shè)置最大的容量來控制內(nèi)存,當(dāng)內(nèi)存達到最大時,會將最早存入的數(shù)據(jù)刪除,當(dāng)緩存超出這個容量的時候,會使用Window TinyLfu策略來刪除緩存。

2.基于時間(設(shè)置有效期)

@Test

public void test3() {

Cache cache = Caffeine.newBuilder()

.initialCapacity(100)

.expireAfterWrite(Duration.ofSeconds(10)) //設(shè)置緩存的有效期,此時就是設(shè)置為10s

.build();

}

3.基于引用:設(shè)置數(shù)據(jù)的強引用和弱引用,在內(nèi)存不足的時候jvm會進行垃圾回收,會將弱引用的數(shù)據(jù)進行回收,性能差,不建議使用。

設(shè)置一級緩存?

Caffeine配置(配置到ioc中,后續(xù)提供依賴注入進行使用)

import com.github.benmanes.caffeine.cache.Caffeine;

import com.sl.transport.info.domain.TransportInfoDTO;

import org.springframework.beans.factory.annotation.Value;

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;

/**

* Caffeine緩存配置

*/

@Configuration

public class CaffeineConfig {

//初始化的容量大小

@Value("${caffeine.init}")

private Integer init;

//最大的容量大小

@Value("${caffeine.max}")

private Integer max;

@Bean

public Cache transportInfoCache() {

return Caffeine.newBuilder()

.initialCapacity(init)

.maximumSize(max).build();

}

}

在Controller層中設(shè)置一級緩存

@Resource

private TransportInfoService transportInfoService;

@Resource

private Cache transportInfoCache;

/**

* 根據(jù)運單id查詢運單信息

*

* @param transportOrderId 運單號

* @return 運單信息

*/

@ApiImplicitParams({

@ApiImplicitParam(name = "transportOrderId", value = "運單id")

})

@ApiOperation(value = "查詢", notes = "根據(jù)運單id查詢物流信息")

@GetMapping("{transportOrderId}")

public TransportInfoDTO queryByTransportOrderId(@PathVariable("transportOrderId") String transportOrderId) {

//提供Caffeine先獲取一級緩存,如果沒有緩存就去Mongodb中查數(shù)據(jù)

TransportInfoDTO transportInfoDTO = transportInfoCache.get(transportOrderId, id -> {

TransportInfoEntity transportInfoEntity = transportInfoService.queryByTransportOrderId(transportOrderId);

return BeanUtil.toBean(transportInfoEntity, TransportInfoDTO.class);

});

if(ObjectUtil.isNotEmpty(transportInfoDTO)) {

return transportInfoDTO;

}

throw new SLException(ExceptionEnum.NOT_FOUND);

}

設(shè)置二級緩存(使用springCache進行二級緩存)

配置springCache的配置(redis的配置)

import org.springframework.beans.factory.annotation.Value;

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;

import org.springframework.data.redis.cache.RedisCacheConfiguration;

import org.springframework.data.redis.cache.RedisCacheManager;

import org.springframework.data.redis.core.RedisTemplate;

import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;

import org.springframework.data.redis.serializer.RedisSerializationContext;

import org.springframework.data.redis.serializer.StringRedisSerializer;

import java.time.Duration;

/**

* Redis相關(guān)的配置

*/

@Configuration

public class RedisConfig {

/**

* 存儲的默認(rèn)有效期時間,單位:小時

*/

@Value("${redis.ttl:1}")

private Integer redisTtl;

@Bean

public RedisCacheManager redisCacheManager(RedisTemplate redisTemplate) {

// 默認(rèn)配置

RedisCacheConfiguration defaultCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()

// 設(shè)置key的序列化方式為字符串

.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))

// 設(shè)置value的序列化方式為json格式

.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()))

.disableCachingNullValues() // 不緩存null

.entryTtl(Duration.ofHours(redisTtl)); // 默認(rèn)緩存數(shù)據(jù)保存1小時

// 構(gòu)redis緩存管理器

RedisCacheManager redisCacheManager = RedisCacheManager.RedisCacheManagerBuilder

.fromConnectionFactory(redisTemplate.getConnectionFactory())

.cacheDefaults(defaultCacheConfiguration)

.transactionAware() // 只在事務(wù)成功提交后才會進行緩存的put/evict操作

.build();

return redisCacheManager;

}

}

?在查詢查找的Service上添加對應(yīng)的注解

@Override

//該注解的在作用就是查詢到的數(shù)據(jù)緩存到redis中其key值就為: transport-info::transportOrderId

//注解其中key的值表示key拼接的參數(shù),這里就是第一個參數(shù)

@Cacheable(value = "transport-info", key = "#p0")

public TransportInfoEntity queryByTransportOrderId(String transportOrderId) {

//通過orderId創(chuàng)建查詢條件,查詢物流信息

return mongoTemplate.findOne(

Query.query(Criteria.where("transportOrderId").is(transportOrderId)),

TransportInfoEntity.class

);

}

添加此注解后,會先在redis的緩存中查找數(shù)據(jù),如果有數(shù)據(jù)就直接返回數(shù)據(jù),如果沒有才會提供Mongodb查詢。

當(dāng)然為了保證在數(shù)據(jù)修改后還能保證緩存的準(zhǔn)確性,這里我們需要在修改操作上添加springCache的注解@CachePut。(該注解的作用就是更新緩存的數(shù)據(jù),所以可以在緩存的增刪改時添加該注解)

@Override

@CachePut(value = "transport-info", key = "#p0")

public TransportInfoEntity saveOrUpdate(String transportOrderId, TransportInfoDetail infoDetail) {

//通過orderId創(chuàng)建查詢條件,查詢物流信息是否存在

TransportInfoEntity updateTransportInfoEntity = mongoTemplate.findOne(

Query.query(Criteria.where("transportOrderId").is(transportOrderId)),

TransportInfoEntity.class

);

if(ObjectUtil.isNotEmpty(updateTransportInfoEntity)) {

//如果存在就獲取對應(yīng)的信息,在infoList中添加對應(yīng)的物流信息

updateTransportInfoEntity.getInfoList().add(infoDetail);

} else {

//如果不存在就新建一個document

updateTransportInfoEntity = new TransportInfoEntity();

updateTransportInfoEntity.setTransportOrderId(transportOrderId);

updateTransportInfoEntity.setInfoList(ListUtil.toList(infoDetail));

updateTransportInfoEntity.setCreated(System.currentTimeMillis());

}

//修改物流信息的修改時間

updateTransportInfoEntity.setUpdated(System.currentTimeMillis());

//進行新增或修改操作 id為空時就進行新增,不為空時進行修改操作

return mongoTemplate.save(updateTransportInfoEntity);

}

一級緩存更新的問題

修改后,在一級緩存中的數(shù)據(jù)是不變的,所以為了保證數(shù)據(jù)的準(zhǔn)確性,我們先是想到在進行增刪改的時候用this.transportInfoCache.invalidate(transportOrderId);來清除緩存但是在微服務(wù)的情況小會出現(xiàn)數(shù)據(jù)不一致的情況。(因為一級緩存在微服務(wù)間不是共享的)

@Override

//value和key就是對緩存中key的拼接,這里的key就是transport-info::對應(yīng)的第一個參數(shù)

@CachePut(value = "transport-info", key = "#p0")

public TransportInfoEntity saveOrUpdate(String transportOrderId, TransportInfoDetail infoDetail) {

//通過orderId創(chuàng)建查詢條件,查詢物流信息是否存在

TransportInfoEntity updateTransportInfoEntity = mongoTemplate.findOne(

Query.query(Criteria.where("transportOrderId").is(transportOrderId)),

TransportInfoEntity.class

);

if(ObjectUtil.isNotEmpty(updateTransportInfoEntity)) {

//如果存在就獲取對應(yīng)的信息,在infoList中添加對應(yīng)的物流信息

updateTransportInfoEntity.getInfoList().add(infoDetail);

} else {

//如果不存在就新建一個document

updateTransportInfoEntity = new TransportInfoEntity();

updateTransportInfoEntity.setTransportOrderId(transportOrderId);

updateTransportInfoEntity.setInfoList(ListUtil.toList(infoDetail));

updateTransportInfoEntity.setCreated(System.currentTimeMillis());

}

//修改物流信息的修改時間

updateTransportInfoEntity.setUpdated(System.currentTimeMillis());

//清除緩存中的數(shù)據(jù)

this.transportInfoCache.invalidate(transportOrderId);

//進行新增或修改操作 id為空時就進行新增,不為空時進行修改操作

return mongoTemplate.save(updateTransportInfoEntity);

}

為了解決此問題,我們引入了redis中的發(fā)布與訂閱的功能來解決此問題。

類似mq的機制,在發(fā)送對應(yīng)的key也就是消息,然后訂閱該消息的模塊就會執(zhí)行自定義的操作。

在配置中增加訂閱的配置

import org.springframework.beans.factory.annotation.Value;

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;

import org.springframework.data.redis.cache.RedisCacheConfiguration;

import org.springframework.data.redis.cache.RedisCacheManager;

import org.springframework.data.redis.connection.RedisConnectionFactory;

import org.springframework.data.redis.core.RedisTemplate;

import org.springframework.data.redis.listener.ChannelTopic;

import org.springframework.data.redis.listener.RedisMessageListenerContainer;

import org.springframework.data.redis.listener.adapter.MessageListenerAdapter;

import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;

import org.springframework.data.redis.serializer.RedisSerializationContext;

import org.springframework.data.redis.serializer.StringRedisSerializer;

import java.time.Duration;

/**

* Redis相關(guān)的配置

*/

@Configuration

public class RedisConfig {

/**

* 存儲的默認(rèn)有效期時間,單位:小時

*/

@Value("${redis.ttl:1}")

private Integer redisTtl;

@Bean

public RedisCacheManager redisCacheManager(RedisTemplate redisTemplate) {

// 默認(rèn)配置

RedisCacheConfiguration defaultCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()

// 設(shè)置key的序列化方式為字符串

.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))

// 設(shè)置value的序列化方式為json格式

.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()))

.disableCachingNullValues() // 不緩存null

.entryTtl(Duration.ofHours(redisTtl)); // 默認(rèn)緩存數(shù)據(jù)保存1小時

// 構(gòu)redis緩存管理器

RedisCacheManager redisCacheManager = RedisCacheManager.RedisCacheManagerBuilder

.fromConnectionFactory(redisTemplate.getConnectionFactory())

.cacheDefaults(defaultCacheConfiguration)

.transactionAware() // 只在事務(wù)成功提交后才會進行緩存的put/evict操作

.build();

return redisCacheManager;

}

public static final String CHANNEL_TOPIC = "sl-express-ms-transport-info-caffeine";

/**

* 配置訂閱,用于解決Caffeine一致性的問題

*

* @param connectionFactory 鏈接工廠

* @param listenerAdapter 消息監(jiān)聽器

* @return 消息監(jiān)聽容器

*/

@Bean

public RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory,

MessageListenerAdapter listenerAdapter) {

RedisMessageListenerContainer container = new RedisMessageListenerContainer();

container.setConnectionFactory(connectionFactory);

container.addMessageListener(listenerAdapter, new ChannelTopic(CHANNEL_TOPIC));

return container;

}

}

?編寫RedisMessageListener用于監(jiān)聽消息(監(jiān)聽消息后執(zhí)行的自定義方法),刪除caffeine中的數(shù)據(jù)。(可以理解成監(jiān)聽方法)

import cn.hutool.core.convert.Convert;

import com.github.benmanes.caffeine.cache.Cache;

import com.sl.transport.info.domain.TransportInfoDTO;

import org.springframework.data.redis.connection.Message;

import org.springframework.data.redis.listener.adapter.MessageListenerAdapter;

import org.springframework.stereotype.Component;

import javax.annotation.Resource;

/**

* redis消息監(jiān)聽,解決Caffeine一致性的問題

*/

@Component

public class RedisMessageListener extends MessageListenerAdapter {

@Resource

private Cache transportInfoCache;

@Override

public void onMessage(Message message, byte[] pattern) {

//獲取到消息中的運單id

String transportOrderId = Convert.toStr(message);

//將本jvm中的緩存刪除掉

this.transportInfoCache.invalidate(transportOrderId);

}

}

在增刪改的方法中向?qū)?yīng)的頻道發(fā)送消息。

@Override

//value和key就是對緩存中key的拼接,這里的key就是transport-info::對應(yīng)的第一個參數(shù)

@CachePut(value = "transport-info", key = "#p0")

public TransportInfoEntity saveOrUpdate(String transportOrderId, TransportInfoDetail infoDetail) {

//通過orderId創(chuàng)建查詢條件,查詢物流信息是否存在

TransportInfoEntity updateTransportInfoEntity = mongoTemplate.findOne(

Query.query(Criteria.where("transportOrderId").is(transportOrderId)),

TransportInfoEntity.class

);

if(ObjectUtil.isNotEmpty(updateTransportInfoEntity)) {

//如果存在就獲取對應(yīng)的信息,在infoList中添加對應(yīng)的物流信息

updateTransportInfoEntity.getInfoList().add(infoDetail);

} else {

//如果不存在就新建一個document

updateTransportInfoEntity = new TransportInfoEntity();

updateTransportInfoEntity.setTransportOrderId(transportOrderId);

updateTransportInfoEntity.setInfoList(ListUtil.toList(infoDetail));

updateTransportInfoEntity.setCreated(System.currentTimeMillis());

}

//修改物流信息的修改時間

updateTransportInfoEntity.setUpdated(System.currentTimeMillis());

//清除緩存中的數(shù)據(jù)

this.stringRedisTemplate.convertAndSend(RedisConfig.CHANNEL_TOPIC, transportOrderId);

//進行新增或修改操作 id為空時就進行新增,不為空時進行修改操作

return mongoTemplate.save(updateTransportInfoEntity);

}

最終保證了一級緩存的準(zhǔn)確性。

問: 那redis的這種機制也可以完成mq的一系列操作,為什么微服務(wù)中沒有大量使用呢?

答:redis的發(fā)布訂閱沒有可靠性的處理,沒有像mq那樣的重試機制,所以我們微服務(wù)中沒有大量使用。

柚子快報邀請碼778899分享:緩存 Caffeine的使用

http://yzkb.51969.com/

相關(guān)閱讀

評論可見,查看隱藏內(nèi)容

本文內(nèi)容根據(jù)網(wǎng)絡(luò)資料整理,出于傳遞更多信息之目的,不代表金鑰匙跨境贊同其觀點和立場。

轉(zhuǎn)載請注明,如有侵權(quán),聯(lián)系刪除。

本文鏈接:http://gantiao.com.cn/post/18936786.html

發(fā)布評論

您暫未設(shè)置收款碼

請在主題配置——文章設(shè)置里上傳

掃描二維碼手機訪問

文章目錄