【详解】Memcached与Spring提供的cache接口整合

举报
皮牙子抓饭 发表于 2025/07/05 21:46:45 2025/07/05
【摘要】 Memcached与Spring提供的Cache接口整合在现代Web应用开发中,缓存技术是提高系统性能和响应速度的重要手段之一。Memcached作为一个高性能的分布式内存对象缓存系统,在许多大型网站和应用中被广泛使用。而Spring框架作为Java企业级开发中的主流框架,提供了强大的缓存抽象层——​​@Cacheable​​、​​@CachePut​​、​​@CacheEvict​​等注解...

Memcached与Spring提供的Cache接口整合

在现代Web应用开发中,缓存技术是提高系统性能和响应速度的重要手段之一。Memcached作为一个高性能的分布式内存对象缓存系统,在许多大型网站和应用中被广泛使用。而Spring框架作为Java企业级开发中的主流框架,提供了强大的缓存抽象层——​​@Cacheable​​、​​@CachePut​​、​​@CacheEvict​​等注解,使得开发者可以非常方便地集成各种缓存解决方案。

本文将介绍如何在Spring Boot项目中整合Memcached,利用Spring的缓存注解实现数据的高效缓存管理。

1. 环境准备

1.1 安装Memcached

首先,确保你的环境中已经安装了Memcached服务。可以在官网上下载适合你操作系统的安装包,或者使用包管理器进行安装。例如,在Ubuntu上可以通过以下命令安装:

sudo apt-get update
sudo apt-get install memcached

启动Memcached服务:

sudo service memcached start

1.2 添加依赖

在Spring Boot项目中,需要添加对Memcached的支持。通过Maven或Gradle添加相应的依赖。这里以Maven为例:

<dependencies>
    <!-- Spring Boot Cache Starter -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-cache</artifactId>
    </dependency>
    <!-- XMemcached Client -->
    <dependency>
        <groupId>com.google.code.simple-xml</groupId>
        <artifactId>simple-xmemcached</artifactId>
        <version>4.0.6</version>
    </dependency>
</dependencies>

2. 配置Spring Boot

在​​application.properties​​文件中配置Memcached的相关参数:

# Memcached服务器地址
spring.cache.memcached.servers=localhost:11211
# 缓存默认超时时间(毫秒)
spring.cache.memcached.expiration=3600
# 是否开启压缩
spring.cache.memcached.compression-threshold=10240

3. 集成Memcached

为了使Spring能够识别并使用Memcached,需要创建一个配置类来配置Memcached客户端。

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import net.rubyeye.xmemcached.MemcachedClient;
import net.rubyeye.xmemcached.XMemcachedClientBuilder;

@Configuration
public class CacheConfig {

    @Value("${spring.cache.memcached.servers}")
    private String servers;

    @Value("${spring.cache.memcached.expiration}")
    private int expiration;

    @Value("${spring.cache.memcached.compression-threshold}")
    private int compressionThreshold;

    @Bean
    public MemcachedClient memcachedClient() throws Exception {
        XMemcachedClientBuilder builder = new XMemcachedClientBuilder(AddrUtil.getAddresses(servers));
        builder.setConnectionPoolSize(5);
        builder.setOpTimeout(3000);
        builder.setSessionLocator(new KetamaNodeLocator());
        return builder.build();
    }
}

4. 使用缓存注解

在业务逻辑中使用Spring的缓存注解来管理数据的读取和存储。

4.1 ​​@Cacheable​

当调用方法时,如果缓存中存在该数据,则直接从缓存中读取;如果不存在,则执行方法,并将结果放入缓存中。

@Service
public class UserService {

    @Autowired
    private UserRepository userRepository;

    @Cacheable(value = "users", key = "#id")
    public User getUserById(Long id) {
        return userRepository.findById(id).orElse(null);
    }
}

4.2 ​​@CachePut​

无论缓存中是否存在,都会先执行方法,然后将结果放入缓存中。

@Service
public class UserService {

    @Autowired
    private UserRepository userRepository;

    @CachePut(value = "users", key = "#user.id")
    public User updateUser(User user) {
        return userRepository.save(user);
    }
}

4.3 ​​@CacheEvict​

用于清除缓存中的数据。

@Service
public class UserService {

    @CacheEvict(value = "users", key = "#id")
    public void deleteUser(Long id) {
        userRepository.deleteById(id);
    }
}

5. 测试缓存功能

可以通过单元测试或直接运行应用程序来验证缓存功能是否按预期工作。确保缓存命中率高,且数据一致性得到保证。

通过上述步骤,我们成功地在Spring Boot应用中集成了Memcached,并利用Spring的缓存注解实现了高效的数据缓存管理。这种整合不仅提高了应用的性能,也简化了缓存逻辑的实现。下面是一个将Memcached与Spring Cache接口整合的实际应用场景示例。我们将使用Spring Boot来简化配置和开发过程。

1. 添加依赖

首先,在​​pom.xml​​中添加必要的依赖:

<dependencies>
    <!-- Spring Boot Web Starter -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <!-- Spring Boot Cache Starter -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-cache</artifactId>
    </dependency>

    <!-- XMemcached Client -->
    <dependency>
        <groupId>cloud.alipay</groupId>
        <artifactId>xmemcached</artifactId>
        <version>2.6.5</version>
    </dependency>

    <!-- Spring Cache Abstraction for XMemcached -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>

    <!-- Optional: Lombok for reducing boilerplate code -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <scope>provided</scope>
    </dependency>
</dependencies>

2. 配置Memcached

在​​application.properties​​中配置Memcached服务器地址:

# Memcached configuration
spring.cache.type=generic
xmemcached.server=localhost:11211
xmemcached.connectionPoolSize=5

3. 创建Memcached配置类

创建一个配置类来配置Memcached客户端:

import net.rubyeye.xmemcached.MemcachedClient;
import net.rubyeye.xmemcached.XMemcachedClientBuilder;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.io.IOException;
import java.util.concurrent.Executors;

@Configuration
public class MemcachedConfig {

    @Value("${xmemcached.server}")
    private String server;

    @Value("${xmemcached.connectionPoolSize}")
    private int connectionPoolSize;

    @Bean
    public MemcachedClient memcachedClient() throws IOException {
        XMemcachedClientBuilder builder = new XMemcachedClientBuilder(server.split(","));
        builder.setConnectionPoolSize(connectionPoolSize);
        builder.setCommandFactory(net.rubyeye.xmemcached.command.TextCommandFactory.getInstance());
        return builder.build();
    }
}

4. 创建自定义CacheManager

创建一个自定义的​​CacheManager​​来集成Memcached:

import net.rubyeye.xmemcached.MemcachedClient;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.cache.support.SimpleValueWrapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.Collections;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

@Configuration
public class CacheConfig {

    @Bean
    public CacheManager cacheManager(MemcachedClient memcachedClient) {
        return new CacheManager() {
            private final Map<String, Cache> cacheMap = new ConcurrentHashMap<>();

            @Override
            public Cache getCache(String name) {
                return cacheMap.computeIfAbsent(name, k -> new MemcachedCache(memcachedClient, name));
            }

            @Override
            public Collection<String> getCacheNames() {
                return Collections.unmodifiableSet(cacheMap.keySet());
            }
        };
    }

    private static class MemcachedCache implements Cache {
        private final MemcachedClient memcachedClient;
        private final String name;

        public MemcachedCache(MemcachedClient memcachedClient, String name) {
            this.memcachedClient = memcachedClient;
            this.name = name;
        }

        @Override
        public String getName() {
            return name;
        }

        @Override
        public Object getNativeCache() {
            return memcachedClient;
        }

        @Override
        public ValueWrapper get(Object key) {
            try {
                Object value = memcachedClient.get(generateKey(key));
                return (value != null) ? new SimpleValueWrapper(value) : null;
            } catch (Exception e) {
                throw new RuntimeException("Error retrieving value from Memcached", e);
            }
        }

        @Override
        public <T> T get(Object key, Class<T> type) {
            try {
                return (T) memcachedClient.get(generateKey(key));
            } catch (Exception e) {
                throw new RuntimeException("Error retrieving value from Memcached", e);
            }
        }

        @Override
        public <T> T get(Object key, Callable<T> valueLoader) {
            try {
                Object value = memcachedClient.get(generateKey(key));
                if (value == null) {
                    value = valueLoader.call();
                    memcachedClient.set(generateKey(key), 0, value);
                }
                return (T) value;
            } catch (Exception e) {
                throw new RuntimeException("Error retrieving value from Memcached", e);
            }
        }

        @Override
        public void put(Object key, Object value) {
            try {
                memcachedClient.set(generateKey(key), 0, value);
            } catch (Exception e) {
                throw new RuntimeException("Error storing value in Memcached", e);
            }
        }

        @Override
        public void evict(Object key) {
            try {
                memcachedClient.delete(generateKey(key));
            } catch (Exception e) {
                throw new RuntimeException("Error deleting value from Memcached", e);
            }
        }

        @Override
        public void clear() {
            try {
                memcachedClient.flushAll();
            } catch (Exception e) {
                throw new RuntimeException("Error flushing Memcached", e);
            }
        }

        private String generateKey(Object key) {
            return name + ":" + key.toString();
        }
    }
}

5. 使用缓存注解

在服务类中使用Spring Cache注解来缓存方法的结果:

import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

@Service
public class UserService {

    @Cacheable(value = "users", key = "#userId")
    public User getUserById(String userId) {
        // 模拟从数据库中获取用户信息
        System.out.println("Fetching user from database: " + userId);
        return new User(userId, "User" + userId);
    }
}

class User {
    private String id;
    private String name;

    public User(String id, String name) {
        this.id = id;
        this.name = name;
    }

    // Getters and setters
    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

6. 测试

创建一个控制器来测试缓存功能:

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class UserController {

    private final UserService userService;

    public UserController(UserService userService) {
        this.userService = userService;
    }

    @GetMapping("/user/{id}")
    public User getUser(@PathVariable String id) {
        return userService.getUserById(id);
    }
}

7. 运行应用

启动Spring Boot应用,并访问​​http://localhost:8080/user/1​​。第一次访问时,控制台会输出“Fetching user from database: 1”。再次访问时,控制台不会输出该信息,说明数据已经从Memcached缓存中获取。

在Spring框架中,可以通过整合Memcached来实现应用的缓存功能,从而提高应用的性能和响应速度。下面详细介绍如何在Spring应用中整合Memcached,并通过Spring Cache抽象来使用它。

1. 添加依赖

首先,需要在项目的​​pom.xml​​(对于Maven项目)或​​build.gradle​​(对于Gradle项目)中添加必要的依赖。

Maven
<dependencies>
    <!-- Spring Cache Abstraction -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-cache</artifactId>
    </dependency>
    
    <!-- XMemcached Client -->
    <dependency>
        <groupId>com.google.code.simple-spring-memcached</groupId>
        <artifactId>xmemcached-provider</artifactId>
        <version>4.0.0</version>
    </dependency>
    
    <!-- Simple Spring Memcached -->
    <dependency>
        <groupId>com.google.code.simple-spring-memcached</groupId>
        <artifactId>simple-spring-memcached</artifactId>
        <version>4.0.0</version>
    </dependency>
</dependencies>
Gradle
dependencies {
    // Spring Cache Abstraction
    implementation 'org.springframework.boot:spring-boot-starter-cache'
    
    // XMemcached Client
    implementation 'com.google.code.simple-spring-memcached:xmemcached-provider:4.0.0'
    
    // Simple Spring Memcached
    implementation 'com.google.code.simple-spring-memcached:simple-spring-memcached:4.0.0'
}

2. 配置Memcached

在​​application.properties​​或​​application.yml​​中配置Memcached服务器的地址和端口。

application.properties
# Memcached server configuration
spring.cache.type=generic
ssm.memcached.servers=localhost:11211
ssm.memcached.connectionPoolSize=5
application.yml
spring:
  cache:
    type: generic
ssm:
  memcached:
    servers: localhost:11211
    connectionPoolSize: 5

3. 配置Spring Cache

创建一个配置类来设置Memcached作为缓存提供者。

import com.google.code.ssm.CacheFactory;
import com.google.code.ssm.providers.xmemcached.MemcachedClientFactoryImpl;
import com.google.code.ssm.spring.SSMCache;
import com.google.code.ssm.spring.SSMCacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
@EnableCaching
public class CacheConfig {

    @Value("${ssm.memcached.servers}")
    private String servers;

    @Value("${ssm.memcached.connectionPoolSize}")
    private int connectionPoolSize;

    @Bean
    public CacheFactory cacheFactory() {
        CacheFactory cacheFactory = new CacheFactory();
        cacheFactory.setAddressProvider(new StaticAddressProvider(servers));
        cacheFactory.setClientFactory(new MemcachedClientFactoryImpl());
        cacheFactory.setConnectionPoolSize(connectionPoolSize);
        return cacheFactory;
    }

    @Bean
    public SSMCacheManager ssmCacheManager(CacheFactory cacheFactory) {
        SSMCacheManager cacheManager = new SSMCacheManager();
        SSMCache defaultCache = new SSMCache(cacheFactory, "default", 60 * 60, false, 0);
        cacheManager.setCaches(Arrays.asList(defaultCache));
        return cacheManager;
    }
}

4. 使用缓存注解

在需要缓存的方法上使用Spring Cache的注解。

import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

@Service
public class UserService {

    @Cacheable(value = "users", key = "#userId")
    public User getUserById(String userId) {
        // 模拟从数据库中获取用户信息
        System.out.println("Fetching user from database: " + userId);
        return new User(userId, "User Name");
    }
}

5. 测试缓存功能

创建一个测试类来验证缓存是否生效。

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
public class UserServiceTest {

    @Autowired
    private UserService userService;

    @Test
    public void testGetUserById() {
        User user1 = userService.getUserById("1");
        User user2 = userService.getUserById("1");
        
        // 第二次调用应该从缓存中获取数据,不会打印“Fetching user from database”
        System.out.println(user1);
        System.out.println(user2);
    }
}

总结

通过上述步骤,你可以在Spring Boot应用中成功整合Memcached,并使用Spring Cache抽象来简化缓存的使用。这种方式不仅提高了应用的性能,还使得缓存逻辑更加清晰和易于管理。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。