SpringBoot与Caffeine整合,解决微服务间高频调用的性能瓶颈

开发 前端
在微服务架构中,数据的一致性和响应时间是非常重要的。由于每个微服务通常独立部署和运行,频繁的数据库查询会导致较高的延迟和资源消耗。通过引入缓存机制,可以显著减少数据库负载,提高系统的整体性能和响应速度。

在微服务架构中,数据的一致性和响应时间是非常重要的。由于每个微服务通常独立部署和运行,频繁的数据库查询会导致较高的延迟和资源消耗。通过引入缓存机制,可以显著减少数据库负载,提高系统的整体性能和响应速度。

哪些公司在使用Caffeine?

  • Google 内部使用 Caffeine 作为其内部服务的一部分,特别是在需要高性能缓存的场景中。
  • GitHub 在其应用程序中使用 Caffeine 来缓存常用数据,提升网站的加载速度和响应效率。
  • PayPal 使用 Caffeine 来处理高并发请求,并通过缓存减少对数据库的依赖,提高系统的整体稳定性。
  • Uber 在其后端服务中使用 Caffeine 来缓存频繁访问的数据,从而减轻数据库负载并加快响应时间。
  • Twitter 在其微服务架构中使用 Caffeine 来缓存热点数据,确保实时数据的快速访问。
  • LinkedIn 利用 Caffeine 来优化其推荐系统和其他高流量服务,以减少延迟并提高用户体验。
  • Netflix 使用 Caffeine 来提高其微服务架构中的性能,特别是在需要快速数据访问的地方。

在微服务中使用Caffeine的好处

  1. 减少数据库负载:
  • 缓存热点数据,减少对数据库的直接访问次数。
  • 降低数据库压力,提升数据库性能。
  1. 提高响应速度:
  • 将常用的数据存储在内存中,提供更快的读取速度。
  • 减少网络延迟,提升用户体验。
  1. 简化系统架构:
  • 不需要依赖外部缓存系统(如Redis或Memcached),减少了系统的复杂性。
  • 轻量级且易于集成,适合小型到中型规模的应用程序。
  1. 监控和调优:
  • 内置统计功能,可以实时监控缓存的命中率、加载时间和驱逐情况。
  • 根据监控数据进行调优,优化缓存策略和配置。
  1. 支持多种缓存策略:
  • 根据业务需求选择合适的缓存淘汰策略(如LRU、LFU、W-TinyLFU等)。
  • 灵活应对不同的缓存场景。

代码实操

<dependency>
        <groupId>com.github.ben-manes.caffeine</groupId>
        <artifactId>caffeine</artifactId>
    </dependency>
  • 1.
  • 2.
  • 3.
  • 4.

配置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); // 最大缓存条目数
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.

创建服务并启用缓存

创建一个服务类,并在方法上使用@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"));
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.

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);
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.

创建数据存储

不想写代码,我们随手写一个简单的内存数据存储吧,意思意思就行了,因为他不是重点!

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));
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.

启动类

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);
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.

测试

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();
            }
        }
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.

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
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 第一次请求: 大约需要2秒(2005毫秒),因为我们在服务中模拟了一个慢速的服务调用。
  • 后续请求: 几乎立即返回(大约10-20毫秒),这是因为Caffeine缓存生效了。
责任编辑:武晓燕 来源: Java知识日历
相关推荐

2017-02-15 09:40:38

JavaScript分析解决

2020-09-09 10:00:41

JavaScript前端瓶颈

2024-11-25 07:00:00

2022-05-16 08:07:15

微服务容器通信

2011-11-03 10:45:09

京东性能瓶颈

2024-09-14 09:21:55

Spring微服务服务间调用

2010-07-21 09:33:09

VMware View

2025-03-06 08:37:01

2024-10-07 09:00:58

2021-11-04 08:04:49

缓存CaffeineSpringBoot

2025-03-06 10:59:24

2017-10-17 11:39:40

微服务路由解决方案

2011-04-28 11:05:27

Windows 7

2020-12-07 06:30:34

Redis性能命令

2024-07-02 10:58:53

2025-02-12 08:52:44

2020-11-11 10:00:13

NAT性能内核

2017-02-21 13:16:49

微服务RPC技术

2016-09-18 23:40:38

微服务实时性能分析
点赞
收藏

51CTO技术栈公众号