企業(yè)級應(yīng)用主要作用是信息處理,當(dāng)需要讀取數(shù)據(jù)時(shí),由于受限于數(shù)據(jù)庫的訪問效率,導(dǎo)致整體系統(tǒng)性能偏低。
應(yīng)用程序直接與數(shù)據(jù)庫打交道,訪問效率低
為了改善上述現(xiàn)象,開發(fā)者通常會(huì)在應(yīng)用程序與數(shù)據(jù)庫之間建立一種臨時(shí)的數(shù)據(jù)存儲(chǔ)機(jī)制,該區(qū)域中的數(shù)據(jù)在內(nèi)存中保存,讀寫速度較快,可以有效解決數(shù)據(jù)庫訪問效率低下的問題。這一塊臨時(shí)存儲(chǔ)數(shù)據(jù)的區(qū)域就是緩存。
使用緩存后,應(yīng)用程序與緩存打交道,緩存與數(shù)據(jù)庫打交道,數(shù)據(jù)訪問效率提高
緩存是什么?緩存是一種介于數(shù)據(jù)永久存儲(chǔ)介質(zhì)與應(yīng)用程序之間的數(shù)據(jù)臨時(shí)存儲(chǔ)介質(zhì),使用緩存可以有效的減少低速數(shù)據(jù)讀取過程的次數(shù)(例如磁盤IO),提高系統(tǒng)性能。此外緩存不僅可以用于提高永久性存儲(chǔ)介質(zhì)的數(shù)據(jù)讀取效率,還可以提供臨時(shí)的數(shù)據(jù)存儲(chǔ)空間。而springboot提供了對市面上幾乎所有的緩存技術(shù)進(jìn)行整合的方案,下面就一起開啟springboot整合緩存之旅。
SpringBoot內(nèi)置緩存解決方案
springboot技術(shù)提供有內(nèi)置的緩存解決方案,可以幫助開發(fā)者快速開啟緩存技術(shù),并使用緩存技術(shù)進(jìn)行數(shù)據(jù)的快速操作,例如讀取緩存數(shù)據(jù)和寫入數(shù)據(jù)到緩存。
步驟①:導(dǎo)入springboot提供的緩存技術(shù)對應(yīng)的starter
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
步驟②:啟用緩存,在引導(dǎo)類上方標(biāo)注注解@EnableCaching配置springboot程序中可以使用緩存
@SpringBootApplication
//開啟緩存功能
@EnableCaching
public class Springboot19CacheApplication {
public static void main(String[] args) {
SpringApplication.run(Springboot19CacheApplication.class, args);
}
}
在業(yè)務(wù)方法上面使用注解@Cacheable聲明當(dāng)前方法的返回值放入緩存中,其中要指定緩存的存儲(chǔ)位置,以及緩存中保存當(dāng)前方法返回值對應(yīng)的名稱。上例中value屬性描述緩存的存儲(chǔ)位置,可以理解為是一個(gè)存儲(chǔ)空間名,key屬性描述了緩存中保存數(shù)據(jù)的名稱,使用#id讀取形參中的id值作為緩存名稱。
使用@Cacheable注解后,執(zhí)行當(dāng)前操作,如果發(fā)現(xiàn)對應(yīng)名稱在緩存中沒有數(shù)據(jù),就正常讀取數(shù)據(jù),然后放入緩存;如果對應(yīng)名稱在緩存中有數(shù)據(jù),就終止當(dāng)前業(yè)務(wù)方法執(zhí)行,直接返回緩存中的數(shù)據(jù)。
手機(jī)驗(yàn)證碼案例
為了便于下面演示各種各樣的緩存技術(shù),我們創(chuàng)建一個(gè)手機(jī)驗(yàn)證碼的案例環(huán)境,模擬使用緩存保存手機(jī)驗(yàn)證碼的過程。
手機(jī)驗(yàn)證碼案例需求如下:
- 輸入手機(jī)號獲取驗(yàn)證碼,組織文檔以短信形式發(fā)送給用戶(頁面模擬)
- 輸入手機(jī)號和驗(yàn)證碼驗(yàn)證結(jié)果
為了描述上述操作,我們制作兩個(gè)表現(xiàn)層接口,一個(gè)用來模擬發(fā)送短信的過程,其實(shí)就是根據(jù)用戶提供的手機(jī)號生成一個(gè)驗(yàn)證碼,然后放入緩存,另一個(gè)用來模擬驗(yàn)證碼校驗(yàn)的過程,其實(shí)就是使用傳入的手機(jī)號和驗(yàn)證碼進(jìn)行匹配,并返回最終匹配結(jié)果。下面直接制作本案例的模擬代碼,先以上例中springboot提供的內(nèi)置緩存技術(shù)來完成當(dāng)前案例的制作。
步驟①:導(dǎo)入springboot提供的緩存技術(shù)對應(yīng)的starter
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
步驟②:啟用緩存,在引導(dǎo)類上方標(biāo)注注解@EnableCaching配置springboot程序中可以使用緩存
@SpringBootApplication
//開啟緩存功能
@EnableCaching
public class Springboot19CacheApplication {
public static void main(String[] args) {
SpringApplication.run(Springboot19CacheApplication.class, args);
}
}
步驟③:定義驗(yàn)證碼對應(yīng)的實(shí)體類,封裝手機(jī)號與驗(yàn)證碼兩個(gè)屬性
@Data
public class SMSCode {
private String tele;
private String code;
}
步驟④:定義驗(yàn)證碼功能的業(yè)務(wù)層接口與實(shí)現(xiàn)類
public interface SMSCodeService {
public String sendCodeToSMS(String tele);
public boolean checkCode(SMSCode smsCode);
}
@Service
public class SMSCodeServiceImpl implements SMSCodeService {
@Autowired
private CodeUtils codeUtils;
@CachePut(value = "smsCode", key = "#tele")
public String sendCodeToSMS(String tele) {
String code = codeUtils.generator(tele);
return code;
}
public boolean checkCode(SMSCode smsCode) {
//取出內(nèi)存中的驗(yàn)證碼與傳遞過來的驗(yàn)證碼比對,如果相同,返回true
String code = smsCode.getCode();
String cacheCode = codeUtils.get(smsCode.getTele());
return code.equals(cacheCode);
}
}
獲取驗(yàn)證碼后,當(dāng)驗(yàn)證碼失效時(shí)必須重新獲取驗(yàn)證碼,因此在獲取驗(yàn)證碼的功能上不能使用@Cacheable注解,@Cacheable注解是緩存中沒有值則放入值,緩存中有值則取值。此處的功能僅僅是生成驗(yàn)證碼并放入緩存,并不具有從緩存中取值的功能,因此不能使用@Cacheable注解,應(yīng)該使用僅具有向緩存中保存數(shù)據(jù)的功能,使用@CachePut注解即可。
對于校驗(yàn)驗(yàn)證碼的功能建議放入工具類中進(jìn)行。
步驟⑤:定義驗(yàn)證碼的生成策略與根據(jù)手機(jī)號讀取驗(yàn)證碼的功能
@Component
public class CodeUtils {
private String [] patch = {"000000","00000","0000","000","00","0",""};
public String generator(String tele){
int hash = tele.hashCode();
int encryption = 20206666;
long result = hash ^ encryption;
long nowTime = System.currentTimeMillis();
result = result ^ nowTime;
long code = result % 1000000;
code = code < 0 ? -code : code;
String codeStr = code + "";
int len = codeStr.length();
return patch[len] + codeStr;
}
@Cacheable(value = "smsCode",key="#tele")
public String get(String tele){
return null;
}
}
步驟⑥:定義驗(yàn)證碼功能的web層接口,一個(gè)方法用于提供手機(jī)號獲取驗(yàn)證碼,一個(gè)方法用于提供手機(jī)號和驗(yàn)證碼進(jìn)行校驗(yàn)
@RestController
@RequestMapping("/sms")
public class SMSCodeController {
@Autowired
private SMSCodeService smsCodeService;
@GetMapping
public String getCode(String tele){
String code = smsCodeService.sendCodeToSMS(tele);
return code;
}
@PostMapping
public boolean checkCode(SMSCode smsCode){
return smsCodeService.checkCode(smsCode);
}
}
SpringBoot整合Ehcache緩存
手機(jī)驗(yàn)證碼的案例已經(jīng)完成了,下面就開始springboot整合各種各樣的緩存技術(shù),第一個(gè)整合Ehcache技術(shù)。Ehcache是一種緩存技術(shù),使用springboot整合Ehcache其實(shí)就是變更一下緩存技術(shù)的實(shí)現(xiàn)方式,話不多說,直接開整
步驟①:導(dǎo)入Ehcache的坐標(biāo)
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache</artifactId>
</dependency>
此處為什么不是導(dǎo)入Ehcache的starter,而是導(dǎo)入技術(shù)坐標(biāo)呢?其實(shí)springboot整合緩存技術(shù)做的是通用格式,不管你整合哪種緩存技術(shù),只是實(shí)現(xiàn)變化了,操作方式一樣。這也體現(xiàn)出springboot技術(shù)的優(yōu)點(diǎn),統(tǒng)一同類技術(shù)的整合方式。
步驟②:配置緩存技術(shù)實(shí)現(xiàn)使用Ehcache
spring:
cache:
type: ehcache
ehcache:
config: ehcache.xml
配置緩存的類型type為ehcache,此處需要說明一下,當(dāng)前springboot可以整合的緩存技術(shù)中包含有ehcach,所以可以這樣書寫。其實(shí)這個(gè)type不可以隨便寫的,不是隨便寫一個(gè)名稱就可以整合的。
由于ehcache的配置有獨(dú)立的配置文件格式,因此還需要指定ehcache的配置文件,以便于讀取相應(yīng)配置
<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
updateCheck="false">
<diskStore path="D:\ehcache" />
<!--默認(rèn)緩存策略 -->
<!-- external:是否永久存在,設(shè)置為true則不會(huì)被清除,此時(shí)與timeout沖突,通常設(shè)置為false-->
<!-- diskPersistent:是否啟用磁盤持久化-->
<!-- maxElementsInMemory:最大緩存數(shù)量-->
<!-- overflowToDisk:超過最大緩存數(shù)量是否持久化到磁盤-->
<!-- timeToIdleSeconds:最大不活動(dòng)間隔,設(shè)置過長緩存容易溢出,設(shè)置過短無效果,可用于記錄時(shí)效性數(shù)據(jù),例如驗(yàn)證碼-->
<!-- timeToLiveSeconds:最大存活時(shí)間-->
<!-- memoryStoreEvictionPolicy:緩存清除策略-->
<defaultCache
eternal="false"
diskPersistent="false"
maxElementsInMemory="1000"
overflowToDisk="false"
timeToIdleSeconds="60"
timeToLiveSeconds="60"
memoryStoreEvictionPolicy="LRU" />
<cache
name="smsCode"
eternal="false"
diskPersistent="false"
maxElementsInMemory="1000"
overflowToDisk="false"
timeToIdleSeconds="10"
timeToLiveSeconds="10"
memoryStoreEvictionPolicy="LRU" />
</ehcache>
注意前面的案例中,設(shè)置了數(shù)據(jù)保存的位置是smsCode
@CachePut(value = "smsCode", key = "#tele")
public String sendCodeToSMS(String tele) {
String code = codeUtils.generator(tele);
return code;
}
這個(gè)設(shè)定需要保障ehcache中有一個(gè)緩存空間名稱叫做smsCode的配置,前后要統(tǒng)一。在企業(yè)開發(fā)過程中,通過設(shè)置不同名稱的cache來設(shè)定不同的緩存策略,應(yīng)用于不同的緩存數(shù)據(jù)。
到這里springboot整合Ehcache就做完了,可以發(fā)現(xiàn)一點(diǎn),原始代碼沒有任何修改,僅僅是加了一組配置就可以變更緩存供應(yīng)商了,這也是springboot提供了統(tǒng)一的緩存操作接口的優(yōu)勢,變更實(shí)現(xiàn)并不影響原始代碼的書寫。
SpringBoot整合Redis緩存
上節(jié)使用Ehcache替換了springboot內(nèi)置的緩存技術(shù),其實(shí)springboot支持的緩存技術(shù)還很多,下面使用redis技術(shù)作為緩存解決方案來實(shí)現(xiàn)手機(jī)驗(yàn)證碼案例。
比對使用Ehcache的過程,加坐標(biāo),改緩存實(shí)現(xiàn)類型為ehcache,做Ehcache的配置。如果還成redis做緩存呢?一模一樣,加坐標(biāo),改緩存實(shí)現(xiàn)類型為redis,做redis的配置。差別之處只有一點(diǎn),redis的配置可以在yml文件中直接進(jìn)行配置,無需制作獨(dú)立的配置文件。
步驟①:導(dǎo)入redis的坐標(biāo)
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
步驟②:配置緩存技術(shù)實(shí)現(xiàn)使用redis
spring:
redis:
host: localhost
port: 6379
cache:
type: redis
如果需要對redis作為緩存進(jìn)行配置,注意不是對原始的redis進(jìn)行配置,而是配置redis作為緩存使用相關(guān)的配置,隸屬于spring.cache.redis節(jié)點(diǎn)下,注意不要寫錯(cuò)位置了。
spring:
redis:
host: localhost
port: 6379
cache:
type: redis
redis:
use-key-prefix: false
key-prefix: sms_
cache-null-values: false
time-to-live: 10s
SpringBoot整合Memcached緩存
目前我們已經(jīng)掌握了3種緩存解決方案的配置形式,分別是springboot內(nèi)置緩存,ehcache和redis,本節(jié)研究一下國內(nèi)比較流行的一款緩存memcached。
按照之前的套路,其實(shí)變更緩存并不繁瑣,但是springboot并沒有支持使用memcached作為其緩存解決方案,也就是說在type屬性中沒有memcached的配置選項(xiàng),這里就需要更變一下處理方式了。在整合之前先安裝memcached。
安裝
windows版安裝包下載地址:https://www.runoob.com/memcached/window-install-memcached.html
下載的安裝包是解壓縮就能使用的zip文件,解壓縮完畢后會(huì)得到如下文件
可執(zhí)行文件只有一個(gè)memcached.exe,使用該文件可以將memcached作為系統(tǒng)服務(wù)啟動(dòng),執(zhí)行此文件時(shí)會(huì)出現(xiàn)報(bào)錯(cuò)信息,如下:
此處出現(xiàn)問題的原因是注冊系統(tǒng)服務(wù)時(shí)需要使用管理員權(quán)限,當(dāng)前賬號權(quán)限不足導(dǎo)致安裝服務(wù)失敗,切換管理員賬號權(quán)限啟動(dòng)命令行
然后再次執(zhí)行安裝服務(wù)的命令即可,如下:
memcached.exe -d install
服務(wù)安裝完畢后可以使用命令啟動(dòng)和停止服務(wù),如下:
memcached.exe -d start # 啟動(dòng)服務(wù)
memcached.exe -d stop # 停止服務(wù)
也可以在任務(wù)管理器中進(jìn)行服務(wù)狀態(tài)的切換
變更緩存為memcached
由于memcached未被springboot收錄為緩存解決方案,因此使用memcached需要通過手工硬編碼的方式來使用,于是前面的套路都不適用了,需要自己寫了。
memcached目前提供有三種客戶端技術(shù),分別是Memcached Client for Java、SpyMemcached和Xmemcached,其中性能指標(biāo)各方面最好的客戶端是Xmemcached,本次整合就使用這個(gè)作為客戶端實(shí)現(xiàn)技術(shù)了。下面開始使用Xmemcached
步驟①:導(dǎo)入xmemcached的坐標(biāo)
<dependency>
<groupId>com.googlecode.xmemcached</groupId>
<artifactId>xmemcached</artifactId>
<version>2.4.7</version>
</dependency>
步驟②:配置memcached,制作memcached的配置類
@Configuration
public class XMemcachedConfig {
@Bean
public MemcachedClient getMemcachedClient() throws IOException {
MemcachedClientBuilder memcachedClientBuilder = new XMemcachedClientBuilder("localhost:11211");
MemcachedClient memcachedClient = memcachedClientBuilder.build();
return memcachedClient;
}
}
memcached默認(rèn)對外服務(wù)端口11211。
步驟③:使用xmemcached客戶端操作緩存,注入MemcachedClient對象
@Service
public class SMSCodeServiceImpl implements SMSCodeService {
@Autowired
private CodeUtils codeUtils;
@Autowired
private MemcachedClient memcachedClient;
public String sendCodeToSMS(String tele) {
String code = codeUtils.generator(tele);
try {
memcachedClient.set(tele,10,code);
} catch (Exception e) {
e.printStackTrace();
}
return code;
}
public boolean checkCode(SMSCode smsCode) {
String code = null;
try {
code = memcachedClient.get(smsCode.getTele()).toString();
} catch (Exception e) {
e.printStackTrace();
}
return smsCode.getCode().equals(code);
}
}
設(shè)置值到緩存中使用set操作,取值使用get操作,其實(shí)更符合我們開發(fā)者的習(xí)慣。
上述代碼中對于服務(wù)器的配置使用硬編碼寫死到了代碼中,將此數(shù)據(jù)提取出來,做成獨(dú)立的配置屬性。
定義配置屬性
以下過程采用前期學(xué)習(xí)的屬性配置方式進(jìn)行,當(dāng)前操作有助于理解原理篇中的很多知識。
- 定義配置類,加載必要的配置屬性,讀取配置文件中memcached節(jié)點(diǎn)信息
@Component
@ConfigurationProperties(prefix = "memcached")
@Data
public class XMemcachedProperties {
private String servers;
private int poolSize;
private long opTimeout;
}
- 定義memcached節(jié)點(diǎn)信息
memcached:
servers: localhost:11211
poolSize: 10
opTimeout: 3000
- 在memcached配置類中加載信息
@Configuration
public class XMemcachedConfig {
@Autowired
private XMemcachedProperties props;
@Bean
public MemcachedClient getMemcachedClient() throws IOException {
MemcachedClientBuilder memcachedClientBuilder = new XMemcachedClientBuilder(props.getServers());
memcachedClientBuilder.setConnectionPoolSize(props.getPoolSize());
memcachedClientBuilder.setOpTimeout(props.getOpTimeout());
MemcachedClient memcachedClient = memcachedClientBuilder.build();
return memcachedClient;
}
}
思考
到這里已經(jīng)完成了三種緩存的整合,其中redis和mongodb需要安裝獨(dú)立的服務(wù)器,連接時(shí)需要輸入對應(yīng)的服務(wù)器地址,這種是遠(yuǎn)程緩存,Ehcache是一個(gè)典型的內(nèi)存級緩存,因?yàn)樗裁匆膊挥冒惭b,啟動(dòng)后導(dǎo)入jar包就有緩存功能了。這個(gè)時(shí)候就要問了,能不能這兩種緩存一起用呢?咱們下節(jié)再說。
SpringBoot整合jetcache緩存
目前我們使用的緩存都是要么A要么B,能不能AB一起用呢?這一節(jié)就解決這個(gè)問題。springboot針對緩存的整合僅僅停留在用緩存上面,如果緩存自身不支持同時(shí)支持AB一起用,springboot也沒辦法,所以要想解決AB緩存一起用的問題,就必須找一款緩存能夠支持AB兩種緩存一起用,有這種緩存嗎?還真有,阿里出品,jetcache。
jetcache嚴(yán)格意義上來說,并不是一個(gè)緩存解決方案,只能說他算是一個(gè)緩存框架,然后把別的緩存放到j(luò)etcache中管理,這樣就可以支持AB緩存一起用了。并且jetcache參考了springboot整合緩存的思想,整體技術(shù)使用方式和springboot的緩存解決方案思想非常類似。下面咱們就先把jetcache用起來,然后再說它里面的一些小的功能。
做之前要先明確一下,jetcache并不是隨便拿兩個(gè)緩存都能拼到一起去的。目前jetcache支持的緩存方案本地緩存支持兩種,遠(yuǎn)程緩存支持兩種,分別如下:
- 本地緩存(Local)
- LinkedHashMap
- Caffeine
- 遠(yuǎn)程緩存(Remote)
- Redis
- Tair
其實(shí)也有人問我,為什么jetcache只支持2+2這么4款緩存呢?阿里研發(fā)這個(gè)技術(shù)其實(shí)主要是為了滿足自身的使用需要。最初肯定只有1+1種,逐步變化成2+2種。下面就以LinkedHashMap+Redis的方案實(shí)現(xiàn)本地與遠(yuǎn)程緩存方案同時(shí)使用。
純遠(yuǎn)程方案
步驟①:導(dǎo)入springboot整合jetcache對應(yīng)的坐標(biāo)starter,當(dāng)前坐標(biāo)默認(rèn)使用的遠(yuǎn)程方案是redis
<dependency>
<groupId>com.alicp.jetcache</groupId>
<artifactId>jetcache-starter-redis</artifactId>
<version>2.6.2</version>
</dependency>
步驟②:遠(yuǎn)程方案基本配置
jetcache:
remote:
default:
type: redis
host: localhost
port: 6379
poolConfig:
maxTotal: 50
其中poolConfig是必配項(xiàng),否則會(huì)報(bào)錯(cuò)
步驟③:啟用緩存,在引導(dǎo)類上方標(biāo)注注解@EnableCreateCacheAnnotation配置springboot程序中可以使用注解的形式創(chuàng)建緩存
@SpringBootApplication
//jetcache啟用緩存的主開關(guān)
@EnableCreateCacheAnnotation
public class Springboot20JetCacheApplication {
public static void main(String[] args) {
SpringApplication.run(Springboot20JetCacheApplication.class, args);
}
}
步驟④:創(chuàng)建緩存對象Cache,并使用注解@CreateCache標(biāo)記當(dāng)前緩存的信息,然后使用Cache對象的API操作緩存,put寫緩存,get讀緩存。
@Service
public class SMSCodeServiceImpl implements SMSCodeService {
@Autowired
private CodeUtils codeUtils;
@CreateCache(name="jetCache_",expire = 10,timeUnit = TimeUnit.SECONDS)
private Cache<String ,String> jetCache;
public String sendCodeToSMS(String tele) {
String code = codeUtils.generator(tele);
jetCache.put(tele,code);
return code;
}
public boolean checkCode(SMSCode smsCode) {
String code = jetCache.get(smsCode.getTele());
return smsCode.getCode().equals(code);
}
}
通過上述jetcache使用遠(yuǎn)程方案連接redis可以看出,jetcache操作緩存時(shí)的接口操作更符合開發(fā)者習(xí)慣,使用緩存就先獲取緩存對象Cache,放數(shù)據(jù)進(jìn)去就是put,取數(shù)據(jù)出來就是get,更加簡單易懂。并且jetcache操作緩存時(shí),可以為某個(gè)緩存對象設(shè)置過期時(shí)間,將同類型的數(shù)據(jù)放入緩存中,方便有效周期的管理。
上述方案中使用的是配置中定義的default緩存,其實(shí)這個(gè)default是個(gè)名字,可以隨便寫,也可以隨便加。例如再添加一種緩存解決方案,參照如下配置進(jìn)行:
jetcache:
remote:
default:
type: redis
host: localhost
port: 6379
poolConfig:
maxTotal: 50
sms:
type: redis
host: localhost
port: 6379
poolConfig:
maxTotal: 50
如果想使用名稱是sms的緩存,需要再創(chuàng)建緩存時(shí)指定參數(shù)area,聲明使用對應(yīng)緩存即可
@Service
public class SMSCodeServiceImpl implements SMSCodeService {
@Autowired
private CodeUtils codeUtils;
@CreateCache(area="sms",name="jetCache_",expire = 10,timeUnit = TimeUnit.SECONDS)
private Cache<String ,String> jetCache;
public String sendCodeToSMS(String tele) {
String code = codeUtils.generator(tele);
jetCache.put(tele,code);
return code;
}
public boolean checkCode(SMSCode smsCode) {
String code = jetCache.get(smsCode.getTele());
return smsCode.getCode().equals(code);
}
}
純本地方案
遠(yuǎn)程方案中,配置中使用remote表示遠(yuǎn)程,換成local就是本地,只不過類型不一樣而已。
步驟①:導(dǎo)入springboot整合jetcache對應(yīng)的坐標(biāo)starter
<dependency>
<groupId>com.alicp.jetcache</groupId>
<artifactId>jetcache-starter-redis</artifactId>
<version>2.6.2</version>
</dependency>
步驟②:本地緩存基本配置
jetcache:
local:
default:
type: linkedhashmap
keyConvertor: fastjson
為了加速數(shù)據(jù)獲取時(shí)key的匹配速度,jetcache要求指定key的類型轉(zhuǎn)換器。簡單說就是,如果你給了一個(gè)Object作為key的話,我先用key的類型轉(zhuǎn)換器給轉(zhuǎn)換成字符串,然后再保存。等到獲取數(shù)據(jù)時(shí),仍然是先使用給定的Object轉(zhuǎn)換成字符串,然后根據(jù)字符串匹配。由于jetcache是阿里的技術(shù),這里推薦key的類型轉(zhuǎn)換器使用阿里的fastjson。
步驟③:啟用緩存
@SpringBootApplication
//jetcache啟用緩存的主開關(guān)
@EnableCreateCacheAnnotation
public class Springboot20JetCacheApplication {
public static void main(String[] args) {
SpringApplication.run(Springboot20JetCacheApplication.class, args);
}
}
步驟④:創(chuàng)建緩存對象Cache時(shí),標(biāo)注當(dāng)前使用本地緩存
@Service
public class SMSCodeServiceImpl implements SMSCodeService {
@CreateCache(name="jetCache_",expire = 1000,timeUnit = TimeUnit.SECONDS,cacheType = CacheType.LOCAL)
private Cache<String ,String> jetCache;
public String sendCodeToSMS(String tele) {
String code = codeUtils.generator(tele);
jetCache.put(tele,code);
return code;
}
public boolean checkCode(SMSCode smsCode) {
String code = jetCache.get(smsCode.getTele());
return smsCode.getCode().equals(code);
}
}
cacheType控制當(dāng)前緩存使用本地緩存還是遠(yuǎn)程緩存,配置cacheType=CacheType.LOCAL即使用本地緩存。
本地+遠(yuǎn)程方案
本地和遠(yuǎn)程方法都有了,兩種方案一起使用如何配置呢?其實(shí)就是將兩種配置合并到一起就可以了。
jetcache:
local:
default:
type: linkedhashmap
keyConvertor: fastjson
remote:
default:
type: redis
host: localhost
port: 6379
poolConfig:
maxTotal: 50
sms:
type: redis
host: localhost
port: 6379
poolConfig:
maxTotal: 50
在創(chuàng)建緩存的時(shí)候,配置cacheType為BOTH即則本地緩存與遠(yuǎn)程緩存同時(shí)使用。
@Service
public class SMSCodeServiceImpl implements SMSCodeService {
@CreateCache(name="jetCache_",expire = 1000,timeUnit = TimeUnit.SECONDS,cacheType = CacheType.BOTH)
private Cache<String ,String> jetCache;
}
cacheType如果不進(jìn)行配置,默認(rèn)值是REMOTE,即僅使用遠(yuǎn)程緩存方案。關(guān)于jetcache的配置,參考以下信息
屬性 | 默認(rèn)值 | 說明 |
---|---|---|
jetcache.statIntervalMinutes | 0 | 統(tǒng)計(jì)間隔,0表示不統(tǒng)計(jì) |
jetcache.hiddenPackages | 無 | 自動(dòng)生成name時(shí),隱藏指定的包名前綴 |
jetcache.[local|remote].${area}.type | 無 | 緩存類型,本地支持linkedhashmap、caffeine,遠(yuǎn)程支持redis、tair |
jetcache.[local|remote].${area}.keyConvertor | 無 | key轉(zhuǎn)換器,當(dāng)前僅支持fastjson |
jetcache.[local|remote].${area}.valueEncoder | java | 僅remote類型的緩存需要指定,可選java和kryo |
jetcache.[local|remote].${area}.valueDecoder | java | 僅remote類型的緩存需要指定,可選java和kryo |
jetcache.[local|remote].${area}.limit | 100 | 僅local類型的緩存需要指定,緩存實(shí)例最大元素?cái)?shù) |
jetcache.[local|remote].${area}.expireAfterWriteInMillis | 無窮大 | 默認(rèn)過期時(shí)間,毫秒單位 |
jetcache.local.${area}.expireAfterAccessInMillis | 0 | 僅local類型的緩存有效,毫秒單位,最大不活動(dòng)間隔 |
以上方案僅支持手工控制緩存,但是springcache方案中的方法緩存特別好用,給一個(gè)方法添加一個(gè)注解,方法就會(huì)自動(dòng)使用緩存。jetcache也提供了對應(yīng)的功能,即方法緩存。
方法緩存
jetcache提供了方法緩存方案,只不過名稱變更了而已。在對應(yīng)的操作接口上方使用注解@Cached即可
步驟①:導(dǎo)入springboot整合jetcache對應(yīng)的坐標(biāo)starter
<dependency>
<groupId>com.alicp.jetcache</groupId>
<artifactId>jetcache-starter-redis</artifactId>
<version>2.6.2</version>
</dependency>
步驟②:配置緩存
jetcache:
local:
default:
type: linkedhashmap
keyConvertor: fastjson
remote:
default:
type: redis
host: localhost
port: 6379
keyConvertor: fastjson
valueEncode: java
valueDecode: java
poolConfig:
maxTotal: 50
sms:
type: redis
host: localhost
port: 6379
poolConfig:
maxTotal: 50
由于redis緩存中不支持保存對象,因此需要對redis設(shè)置當(dāng)Object類型數(shù)據(jù)進(jìn)入到redis中時(shí)如何進(jìn)行類型轉(zhuǎn)換。需要配置keyConvertor表示key的類型轉(zhuǎn)換方式,同時(shí)標(biāo)注value的轉(zhuǎn)換類型方式,值進(jìn)入redis時(shí)是java類型,標(biāo)注valueEncode為java,值從redis中讀取時(shí)轉(zhuǎn)換成java,標(biāo)注valueDecode為java。
注意,為了實(shí)現(xiàn)Object類型的值進(jìn)出redis,需要保障進(jìn)出redis的Object類型的數(shù)據(jù)必須實(shí)現(xiàn)序列化接口。
@Data
public class Book implements Serializable {
private Integer id;
private String type;
private String name;
private String description;
}
步驟③:啟用緩存時(shí)開啟方法緩存功能,并配置basePackages,說明在哪些包中開啟方法緩存
@SpringBootApplication
//jetcache啟用緩存的主開關(guān)
@EnableCreateCacheAnnotation
//開啟方法注解緩存
@EnableMethodCache(basePackages = "com.itheima")
public class Springboot20JetCacheApplication {
public static void main(String[] args) {
SpringApplication.run(Springboot20JetCacheApplication.class, args);
}
}
步驟④:使用注解@Cached標(biāo)注當(dāng)前方法使用緩存
@Service
public class BookServiceImpl implements BookService {
@Autowired
private BookDao bookDao;
@Override
@Cached(name="book_",key="#id",expire = 3600,cacheType = CacheType.REMOTE)
public Book getById(Integer id) {
return bookDao.selectById(id);
}
}
遠(yuǎn)程方案的數(shù)據(jù)同步
由于遠(yuǎn)程方案中redis保存的數(shù)據(jù)可以被多個(gè)客戶端共享,這就存在了數(shù)據(jù)同步問題。jetcache提供了3個(gè)注解解決此問題,分別在更新、刪除操作時(shí)同步緩存數(shù)據(jù),和讀取緩存時(shí)定時(shí)刷新數(shù)據(jù)
更新緩存
@CacheUpdate(name="book_",key="#book.id",value="#book")
public boolean update(Book book) {
return bookDao.updateById(book) > 0;
}
刪除緩存
@CacheInvalidate(name="book_",key = "#id")
public boolean delete(Integer id) {
return bookDao.deleteById(id) > 0;
}
定時(shí)刷新緩存
@Cached(name="book_",key="#id",expire = 3600,cacheType = CacheType.REMOTE)
@CacheRefresh(refresh = 5)
public Book getById(Integer id) {
return bookDao.selectById(id);
}
數(shù)據(jù)報(bào)表
jetcache還提供有簡單的數(shù)據(jù)報(bào)表功能,幫助開發(fā)者快速查看緩存命中信息,只需要添加一個(gè)配置即可
jetcache:
statIntervalMinutes: 1
設(shè)置后,每1分鐘在控制臺(tái)輸出緩存數(shù)據(jù)命中信息
[DefaultExecutor] c.alicp.jetcache.support.StatInfoLogger : jetcache stat from 2022-02-28 09:32:15,892 to 2022-02-28 09:33:00,003
cache | qps| rate| get| hit| fail| expire| avgLoadTime| maxLoadTime
---------+-------+-------+------+-------+-------+---------+--------------+--------------
book_ | 0.66| 75.86%| 29| 22| 0| 0| 28.0| 188
---------+-------+-------+------+-------+-------+---------+--------------+--------------
思考
jetcache解決了前期使用緩存方案單一的問題,但是仍然不能靈活的選擇緩存進(jìn)行搭配使用,是否存在一種技術(shù)可以靈活的搭配各種各樣的緩存使用呢?有,咱們下一節(jié)再講。
SpringBoot整合j2cache
jetcache可以在限定范圍內(nèi)構(gòu)建多級緩存,但是靈活性不足,不能隨意搭配緩存,本節(jié)介紹一種可以隨意搭配緩存解決方案的緩存整合框架,j2cache。下面就來講解如何使用這種緩存框架,以Ehcache與redis整合為例:
步驟①:導(dǎo)入j2cache、redis、ehcache坐標(biāo)
<dependency>
<groupId>net.oschina.j2cache</groupId>
<artifactId>j2cache-core</artifactId>
<version>2.8.4-release</version>
</dependency>
<dependency>
<groupId>net.oschina.j2cache</groupId>
<artifactId>j2cache-spring-boot2-starter</artifactId>
<version>2.8.0-release</version>
</dependency>
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache</artifactId>
</dependency>
j2cache的starter中默認(rèn)包含了redis坐標(biāo),官方推薦使用redis作為二級緩存,因此此處無需導(dǎo)入redis坐標(biāo)
步驟②:配置一級與二級緩存,并配置一二級緩存間數(shù)據(jù)傳遞方式,配置書寫在名稱為j2cache.properties的文件中。如果使用ehcache還需要單獨(dú)添加ehcache的配置文件
# 1級緩存
j2cache.L1.provider_class = ehcache
ehcache.configXml = ehcache.xml
# 2級緩存
j2cache.L2.provider_class = net.oschina.j2cache.cache.support.redis.SpringRedisProvider
j2cache.L2.config_section = redis
redis.hosts = localhost:6379
# 1級緩存中的數(shù)據(jù)如何到達(dá)二級緩存
j2cache.broadcast = net.oschina.j2cache.cache.support.redis.SpringRedisPubSubPolicy
此處配置不能亂配置,需要參照官方給出的配置說明進(jìn)行。例如1級供應(yīng)商選擇ehcache,供應(yīng)商名稱僅僅是一個(gè)ehcache,但是2級供應(yīng)商選擇redis時(shí)要寫專用的Spring整合Redis的供應(yīng)商類名SpringRedisProvider,而且這個(gè)名稱并不是所有的redis包中能提供的,也不是spring包中提供的。因此配置j2cache必須參照官方文檔配置,而且還要去找專用的整合包,導(dǎo)入對應(yīng)坐標(biāo)才可以使用。
一級與二級緩存最重要的一個(gè)配置就是兩者之間的數(shù)據(jù)溝通方式,此類配置也不是隨意配置的,并且不同的緩存解決方案提供的數(shù)據(jù)溝通方式差異化很大,需要查詢官方文檔進(jìn)行設(shè)置。
步驟③:使用緩存
@Service
public class SMSCodeServiceImpl implements SMSCodeService {
@Autowired
private CodeUtils codeUtils;
@Autowired
private CacheChannel cacheChannel;
public String sendCodeToSMS(String tele) {
String code = codeUtils.generator(tele);
cacheChannel.set("sms",tele,code);
return code;
}
public boolean checkCode(SMSCode smsCode) {
String code = cacheChannel.get("sms",smsCode.getTele()).asString();
return smsCode.getCode().equals(code);
}
}
j2cache的使用和jetcache比較類似,但是無需開啟使用的開關(guān),直接定義緩存對象即可使用,緩存對象名CacheChannel。文章來源:http://www.zghlxwxcb.cn/news/detail-450222.html
j2cache的使用不復(fù)雜,配置是j2cache的核心,畢竟是一個(gè)整合型的緩存框架。緩存相關(guān)的配置過多,可以查閱j2cache-core核心包中的j2cache.properties文件中的說明。如下:文章來源地址http://www.zghlxwxcb.cn/news/detail-450222.html
#J2Cache configuration
#########################################
# Cache Broadcast Method
# values:
# jgroups -> use jgroups's multicast
# redis -> use redis publish/subscribe mechanism (using jedis)
# lettuce -> use redis publish/subscribe mechanism (using lettuce, Recommend)
# rabbitmq -> use RabbitMQ publisher/consumer mechanism
# rocketmq -> use RocketMQ publisher/consumer mechanism
# none -> don't notify the other nodes in cluster
# xx.xxxx.xxxx.Xxxxx your own cache broadcast policy classname that implement net.oschina.j2cache.cluster.ClusterPolicy
#########################################
j2cache.broadcast = redis
# jgroups properties
jgroups.channel.name = j2cache
jgroups.configXml = /network.xml
# RabbitMQ properties
rabbitmq.exchange = j2cache
rabbitmq.host = localhost
rabbitmq.port = 5672
rabbitmq.username = guest
rabbitmq.password = guest
# RocketMQ properties
rocketmq.name = j2cache
rocketmq.topic = j2cache
# use ; to split multi hosts
rocketmq.hosts = 127.0.0.1:9876
#########################################
# Level 1&2 provider
# values:
# none -> disable this level cache
# ehcache -> use ehcache2 as level 1 cache
# ehcache3 -> use ehcache3 as level 1 cache
# caffeine -> use caffeine as level 1 cache(only in memory)
# redis -> use redis as level 2 cache (using jedis)
# lettuce -> use redis as level 2 cache (using lettuce)
# readonly-redis -> use redis as level 2 cache ,but never write data to it. if use this provider, you must uncomment `j2cache.L2.config_section` to make the redis configurations available.
# memcached -> use memcached as level 2 cache (xmemcached),
# [classname] -> use custom provider
#########################################
j2cache.L1.provider_class = caffeine
j2cache.L2.provider_class = redis
# When L2 provider isn't `redis`, using `L2.config_section = redis` to read redis configurations
# j2cache.L2.config_section = redis
# Enable/Disable ttl in redis cache data (if disabled, the object in redis will never expire, default:true)
# NOTICE: redis hash mode (redis.storage = hash) do not support this feature)
j2cache.sync_ttl_to_redis = true
# Whether to cache null objects by default (default false)
j2cache.default_cache_null_object = true
#########################################
# Cache Serialization Provider
# values:
# fst -> using fast-serialization (recommend)
# kryo -> using kryo serialization
# json -> using fst's json serialization (testing)
# fastjson -> using fastjson serialization (embed non-static class not support)
# java -> java standard
# fse -> using fse serialization
# [classname implements Serializer]
#########################################
j2cache.serialization = json
#json.map.person = net.oschina.j2cache.demo.Person
#########################################
# Ehcache configuration
#########################################
# ehcache.configXml = /ehcache.xml
# ehcache3.configXml = /ehcache3.xml
# ehcache3.defaultHeapSize = 1000
#########################################
# Caffeine configuration
# caffeine.region.[name] = size, xxxx[s|m|h|d]
#
#########################################
caffeine.properties = /caffeine.properties
#########################################
# Redis connection configuration
#########################################
#########################################
# Redis Cluster Mode
#
# single -> single redis server
# sentinel -> master-slaves servers
# cluster -> cluster servers (數(shù)據(jù)庫配置無效,使用 database = 0)
# sharded -> sharded servers (密碼、數(shù)據(jù)庫必須在 hosts 中指定,且連接池配置無效 ; redis://user:password@127.0.0.1:6379/0)
#
#########################################
redis.mode = single
#redis storage mode (generic|hash)
redis.storage = generic
## redis pub/sub channel name
redis.channel = j2cache
## redis pub/sub server (using redis.hosts when empty)
redis.channel.host =
#cluster name just for sharded
redis.cluster_name = j2cache
## redis cache namespace optional, default[empty]
redis.namespace =
## redis command scan parameter count, default[1000]
#redis.scanCount = 1000
## connection
# Separate multiple redis nodes with commas, such as 192.168.0.10:6379,192.168.0.11:6379,192.168.0.12:6379
redis.hosts = 127.0.0.1:6379
redis.timeout = 2000
redis.password =
redis.database = 0
redis.ssl = false
## redis pool properties
redis.maxTotal = 100
redis.maxIdle = 10
redis.maxWaitMillis = 5000
redis.minEvictableIdleTimeMillis = 60000
redis.minIdle = 1
redis.numTestsPerEvictionRun = 10
redis.lifo = false
redis.softMinEvictableIdleTimeMillis = 10
redis.testOnBorrow = true
redis.testOnReturn = false
redis.testWhileIdle = true
redis.timeBetweenEvictionRunsMillis = 300000
redis.blockWhenExhausted = false
redis.jmxEnabled = false
#########################################
# Lettuce scheme
#
# redis -> single redis server
# rediss -> single redis server with ssl
# redis-sentinel -> redis sentinel
# redis-cluster -> cluster servers
#
#########################################
#########################################
# Lettuce Mode
#
# single -> single redis server
# sentinel -> master-slaves servers
# cluster -> cluster servers (數(shù)據(jù)庫配置無效,使用 database = 0)
# sharded -> sharded servers (密碼、數(shù)據(jù)庫必須在 hosts 中指定,且連接池配置無效 ; redis://user:password@127.0.0.1:6379/0)
#
#########################################
## redis command scan parameter count, default[1000]
#lettuce.scanCount = 1000
lettuce.mode = single
lettuce.namespace =
lettuce.storage = hash
lettuce.channel = j2cache
lettuce.scheme = redis
lettuce.hosts = 127.0.0.1:6379
lettuce.password =
lettuce.database = 0
lettuce.sentinelMasterId =
lettuce.maxTotal = 100
lettuce.maxIdle = 10
lettuce.minIdle = 10
# timeout in milliseconds
lettuce.timeout = 10000
# redis cluster topology refresh interval in milliseconds
lettuce.clusterTopologyRefresh = 3000
#########################################
# memcached server configurations
# refer to https://gitee.com/mirrors/XMemcached
#########################################
memcached.servers = 127.0.0.1:11211
memcached.username =
memcached.password =
memcached.connectionPoolSize = 10
memcached.connectTimeout = 1000
memcached.failureMode = false
memcached.healSessionInterval = 1000
memcached.maxQueuedNoReplyOperations = 100
memcached.opTimeout = 100
memcached.sanitizeKeys = false
到了這里,關(guān)于SpringBoot【開發(fā)實(shí)用篇】---- 整合第三方技術(shù)(緩存)的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!