微服務架構實戰(五):Springboot2.x集成Guava實現緩存限流秒殺

簡介

該項目主要利用Spring boot2.x + Guava 實現數據緩存,並使用RateLimiter做秒殺限流示例。

  • Guava是一種基於開源的Java庫,其中包含谷歌正在由他們很多項目使用的很多核心庫。這個庫是為了方便編碼,並減少編碼錯誤。這個庫提供用於集合,緩存,支持原語,併發性,常見註解,字符串處理,I/O和驗證的實用方法。
  • Guava - RateLimiter使用的是一種叫令牌桶的流控算法,RateLimiter會按照一定的頻率往桶裡扔令牌,線程拿到令牌才能執行。
  • 源碼地址
  • GitHub:https://github.com/yundianzixun/spring-boot-starter-guava
  • 我們社區:https://100boot.cn

小工具一枚,歡迎使用和Star支持,如使用過程中碰到問題,可以提出Issue,我會盡力完善該Starter

微服務架構實戰(五):Springboot2.x集成Guava實現緩存限流秒殺

版本基礎

  • Spring Boot:2.0.4
  • Guava:19.0

操作步驟

第一步:添加maven依賴

 <dependency>
<groupid>org.springframework.boot/<groupid>
<artifactid>spring-boot-starter-cache/<artifactid>
/<dependency>
<dependency>
<groupid>com.google.guava/<groupid>
<artifactid>guava/<artifactid>
<version>19.0/<version>
/<dependency>

第二步:增加GuavaCacheConfig 配置

GuavaCacheConfig.java

package com.itunion.guava.config;
import com.google.common.cache.CacheBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.guava.GuavaCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import java.util.concurrent.TimeUnit;
/**
* Created by lin on 19/3/14.
*/
@EnableConfigurationProperties(GuavaProperties.class)
@EnableCaching
@Configuration
public class GuavaCacheConfig {
@Autowired

private GuavaProperties guavaProperties;
@Bean
public CacheBuilder<object> cacheBuilder() {
long maximumSize = guavaProperties.getMaximumSize();
long duration = guavaProperties.getExpireAfterAccessDuration();
if (maximumSize <= 0) {
maximumSize = 1024;
}
if (duration <= 0) {
duration = 5;
}
return CacheBuilder.newBuilder()
.maximumSize(maximumSize)
.expireAfterWrite(duration, TimeUnit.SECONDS);
}
/**
* expireAfterAccess: 當緩存項在指定的時間段內沒有被讀或寫就會被回收。
* expireAfterWrite:當緩存項在指定的時間段內沒有更新就會被回收,如果我們認為緩存數據在一段時間後數據不再可用,那麼可以使用該種策略。
* refreshAfterWrite:當緩存項上一次更新操作之後的多久會被刷新。
* @return
*/
@DependsOn({"cacheBuilder"})
@Bean
public CacheManager cacheManager(CacheBuilder<object> cacheBuilder) {
GuavaCacheManager cacheManager = new GuavaCacheManager();
cacheManager.setCacheBuilder(cacheBuilder);
return cacheManager;
}
}
/<object>/<object>

備註:duration 緩存項在指定的5秒鐘內有效,超過試卷進行回收操作,具體時間根據業務配置;

第三步:增加GuavaProperties配置

GuavaProperties.java

package com.itunion.guava.config;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* Created by lin on 19/3/14.
*/
@ConfigurationProperties(prefix = "guava.cache.config")
public class GuavaProperties {
private long maximumSize;
private long maximumWeight;
private long expireAfterWriteDuration;
private long expireAfterAccessDuration;
private long refreshDuration;
private int initialCapacity;
private int concurrencyLevel;
public long getMaximumSize() {
return maximumSize;
}
public void setMaximumSize(long maximumSize) {
this.maximumSize = maximumSize;
}
public long getMaximumWeight() {
return maximumWeight;
}
public void setMaximumWeight(long maximumWeight) {
this.maximumWeight = maximumWeight;
}
public long getExpireAfterWriteDuration() {
return expireAfterWriteDuration;
}
public void setExpireAfterWriteDuration(long expireAfterWriteDuration) {
this.expireAfterWriteDuration = expireAfterWriteDuration;
}
public long getExpireAfterAccessDuration() {
return expireAfterAccessDuration;
}
public void setExpireAfterAccessDuration(long expireAfterAccessDuration) {
this.expireAfterAccessDuration = expireAfterAccessDuration;
}
public long getRefreshDuration() {
return refreshDuration;
}
public void setRefreshDuration(long refreshDuration) {
this.refreshDuration = refreshDuration;
}
public int getInitialCapacity() {
return initialCapacity;
}
public void setInitialCapacity(int initialCapacity) {
this.initialCapacity = initialCapacity;

}
public int getConcurrencyLevel() {
return concurrencyLevel;
}
public void setConcurrencyLevel(int concurrencyLevel) {
this.concurrencyLevel = concurrencyLevel;
}
}

第三步:增加測試Guava緩存服務

CacheGuavaServiceImpl.java

@Cacheable(value = "guavacache")
@Override
public Map<string> getUserCache() {
new Thread().start();
while(true){
try {
Map<string> userMap = new HashMap<>();
userMap.put("name","IT實戰聯盟");
userMap.put("url","https://100boot.cn");
userMap.put("address","上海");
Thread.sleep(5000);
return userMap;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
/<string>/<string>

備註:在請求數據的時候 為了達到大數據的效果,這裡設置了睡眠5秒的線程,5秒後返回結果

第四步:訪問服務並命中guava緩存

CacheController.java

/**
* 查詢緩存
*/
@ApiOperation(value = "getByCache", notes = "查詢緩存")

@RequestMapping(value = "getByCache", method = RequestMethod.GET)
@ResponseBody
public String getByCache() {
Long startTime = System.currentTimeMillis();
Map<string> map = cacheGuavaService.getUserCache();
Long endTime = System.currentTimeMillis();
System.out.println("耗時: " + (endTime - startTime));
System.out.println(map.toString());
return map.toString();
}
/<string>

第五步:測試效果

微服務架構實戰(五):Springboot2.x集成Guava實現緩存限流秒殺

如上圖所示:第一次請求耗費了5秒,接下來5秒鐘內是直接命中guava從緩存裡面獲取的數據。5秒鐘後進行了回收,再一次請求了服務並存儲到guava中。

第六步:創建RateLimiter令牌限流服務

微服務架構實戰(五):Springboot2.x集成Guava實現緩存限流秒殺

GuavaRateLimiterServiceImpl.java

 /**
* 每秒鐘只發出2個令牌,拿到令牌的請求才可以進入下一個業務

*/
private RateLimiter seckillRateLimiter = RateLimiter.create(2);
@Override
public boolean tryAcquireSeckill() {
return seckillRateLimiter.tryAcquire();
}
這裡為了能夠方便測試只設置了2個令牌。
CacheGuavaServiceImpl.java
@Override
@Transactional
public String executeSeckill() {
// 驗證是否被限流器限制,如果沒有,則繼續往下執行業務
if(guavaRateLimiterService.tryAcquireSeckill()){
return "哈哈哈,你沒有限制住我!啦啦啦啦!";
}else {
//被限流器限制
return "嗚嗚嗚,竟然限制我!!!";
}
}

在使用RateLimiter令牌時,進行驗證是否被限流器限制,如果沒有則繼續執行下面業務,如果被限制則直接返回不繼續執行。

第六步:調用RateLimiter令牌限流服務

微服務架構實戰(五):Springboot2.x集成Guava實現緩存限流秒殺

CacheController.java

/**

* 測試限流器

*/

@ApiOperation(value = "getRateLimiter", notes = "測試限流器")

@RequestMapping(value = "getRateLimiter", method = RequestMethod.GET)

@ResponseBody

public String getRateLimiter() {

String str = cacheGuavaService.executeSeckill();

System.out.println(str+","+new Date());

return str;

}

第七步:RateLimiter令牌限流服務測試

微服務架構實戰(五):Springboot2.x集成Guava實現緩存限流秒殺

備註:由於手速原因可以看到每秒超過令牌個數的直接返回“嗚嗚嗚,竟然限制我!!!”,說明有效果。

貢獻者

  • IT實戰聯盟-Line
  • IT實戰聯盟-咖啡

往期回顧

微服務架構實戰(五):Springboot2.x集成Guava實現緩存限流秒殺


分享到:


相關文章: