SpringBoot與Caffeine整合,解決微服務間高頻調用的性能瓶頸
作者:Java知識日歷
在微服務架構中,數據的一致性和響應時間是非常重要的。由于每個微服務通常獨立部署和運行,頻繁的數據庫查詢會導致較高的延遲和資源消耗。通過引入緩存機制,可以顯著減少數據庫負載,提高系統的整體性能和響應速度。
在微服務架構中,數據的一致性和響應時間是非常重要的。由于每個微服務通常獨立部署和運行,頻繁的數據庫查詢會導致較高的延遲和資源消耗。通過引入緩存機制,可以顯著減少數據庫負載,提高系統的整體性能和響應速度。
哪些公司在使用Caffeine?
- Google 內部使用 Caffeine 作為其內部服務的一部分,特別是在需要高性能緩存的場景中。
- GitHub 在其應用程序中使用 Caffeine 來緩存常用數據,提升網站的加載速度和響應效率。
- PayPal 使用 Caffeine 來處理高并發請求,并通過緩存減少對數據庫的依賴,提高系統的整體穩定性。
- Uber 在其后端服務中使用 Caffeine 來緩存頻繁訪問的數據,從而減輕數據庫負載并加快響應時間。
- Twitter 在其微服務架構中使用 Caffeine 來緩存熱點數據,確保實時數據的快速訪問。
- LinkedIn 利用 Caffeine 來優化其推薦系統和其他高流量服務,以減少延遲并提高用戶體驗。
- Netflix 使用 Caffeine 來提高其微服務架構中的性能,特別是在需要快速數據訪問的地方。
在微服務中使用Caffeine的好處
- 減少數據庫負載:
- 緩存熱點數據,減少對數據庫的直接訪問次數。
- 降低數據庫壓力,提升數據庫性能。
- 提高響應速度:
- 將常用的數據存儲在內存中,提供更快的讀取速度。
- 減少網絡延遲,提升用戶體驗。
- 簡化系統架構:
- 不需要依賴外部緩存系統(如Redis或Memcached),減少了系統的復雜性。
- 輕量級且易于集成,適合小型到中型規模的應用程序。
- 監控和調優:
- 內置統計功能,可以實時監控緩存的命中率、加載時間和驅逐情況。
- 根據監控數據進行調優,優化緩存策略和配置。
- 支持多種緩存策略:
- 根據業務需求選擇合適的緩存淘汰策略(如LRU、LFU、W-TinyLFU等)。
- 靈活應對不同的緩存場景。
代碼實操
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
</dependency>
配置Caffeine緩存管理器
創建一個配置類來配置Caffeine緩存管理器:
import com.github.benmanes.caffeine.cache.Caffeine;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.caffeine.CaffeineCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.concurrent.TimeUnit;
@Configuration
@EnableCaching
publicclass CacheConfig {
@Bean
public CacheManager cacheManager() {
CaffeineCacheManager caffeineCacheManager = new CaffeineCacheManager("data");
caffeineCacheManager.setCaffeine(caffeineCacheBuilder());
return caffeineCacheManager;
}
Caffeine<Object, Object> caffeineCacheBuilder() {
return Caffeine.newBuilder()
.recordStats() // 記錄統計信息
.expireAfterWrite(60, TimeUnit.MINUTES) // 緩存過期時間
.maximumSize(100); // 最大緩存條目數
}
}
創建服務并啟用緩存
創建一個服務類,并在方法上使用@Cacheable
注解來啟用緩存:
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
@Slf4j
@Service
publicclass DataService {
@Autowired
private DataRepository dataRepository;
@Cacheable(value = "data", key = "#id")
public String getDataById(String id) throws InterruptedException {
log.info("Fetching data for ID: {}", id);
// Simulate a slow service call by sleeping for 2 seconds
Thread.sleep(2000);
return dataRepository.findById(id).orElseThrow(() -> new RuntimeException("Data not found"));
}
}
Controller
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@Slf4j
@RestController
@RequestMapping("/api/data")
publicclass DataController {
@Autowired
private DataService dataService;
@GetMapping("/{id}")
public String getData(@PathVariable String id) throws InterruptedException {
log.info("Received request for data with ID: {}", id);
return dataService.getDataById(id);
}
}
創建數據存儲
不想寫代碼,我們隨手寫一個簡單的內存數據存儲吧,意思意思就行了,因為他不是重點!
import org.springframework.stereotype.Repository;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
@Repository
publicclass DataRepository {
privatefinal Map<String, String> dataStore = new HashMap<>();
public DataRepository() {
dataStore.put("1", "Data for ID 1");
dataStore.put("2", "Data for ID 2");
dataStore.put("3", "Data for ID 3");
}
public Optional<String> findById(String id) {
return Optional.ofNullable(dataStore.get(id));
}
}
啟動類
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
測試
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;
publicclass ApiPerformanceTest {
public static void main(String[] args) {
String apiUrl = "http://localhost:8080/api/data/1";
HttpClient client = HttpClient.newHttpClient();
for (int i = 0; i < 5; i++) {
try {
long startTime = System.currentTimeMillis();
HttpRequest request = HttpRequest.newBuilder()
.uri(new URI(apiUrl))
.timeout(Duration.ofMinutes(1))
.GET()
.build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
long endTime = System.currentTimeMillis();
long responseTime = endTime - startTime;
System.out.println("Request " + (i + 1) + ": Response Time = " + responseTime + " ms");
System.out.println("Response Body: " + response.body());
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
log
Request 1: Response Time = 2005 ms
Response Body: Data for ID 1
Request 2: Response Time = 19 ms
Response Body: Data for ID 1
Request 3: Response Time = 17 ms
Response Body: Data for ID 1
Request 4: Response Time = 16 ms
Response Body: Data for ID 1
Request 5: Response Time = 18 ms
Response Body: Data for ID 1
- 第一次請求: 大約需要2秒(2005毫秒),因為我們在服務中模擬了一個慢速的服務調用。
- 后續請求: 幾乎立即返回(大約10-20毫秒),這是因為Caffeine緩存生效了。
責任編輯:武曉燕
來源:
Java知識日歷