RAM 是云提供商提供的最昂贵的资源之一。因此,将所有缓存数据存储在内存缓存中是有代价的。这就是为什么必须实施旨在不浪费它的技术。
此外,当您的 Spring Boot 应用程序和缓存服务器共存于同一台机器上,共享底层资源时,这几乎是不可避免的。事实上,缓存从应用程序中窃取的 RAM 越少越好。此外,众所周知,序列化的 Java 对象会占用大量空间。因此,通过缓存它们,您的 RAM 可能很容易耗尽空间。
这就是压缩发挥作用的地方!
让我们看看如何在 Kotlin 和 Java中为Spring Boot中的缓存系统添加压缩。
压缩和缓存
通常,在 Spring Boot 中处理缓存时,数据会被序列化,然后存储在缓存中。需要时,数据被搜索、反序列化,最后以原始格式返回给应用程序。
增加一个压缩层,就是将数据序列化后进行压缩,反序列化前先解压。
压缩数据可减少缓存的大小并为您提供两种选择:
- 减少缓存服务器所需的 RAM,为您节省资金。
- 保持缓存大小相同,但允许您保存更多数据。
这两个选项都很棒,但压缩也会带来开销。特别是,压缩和解压缩会带来时间成本,这可能会显着降低缓存的性能优势。这代表了 RAM 和 CPU 之间的权衡,由您来确定这种方法是否适合您的特定情况。
实现压缩逻辑
请记住,上面介绍的方法可以用于Spring Boot 支持的任何缓存提供程序。让我们看看如何在使用Redis缓存时实现它。这可以通过注册一个自定义的类JdkSerializationRedisSerializer作为默认的 Redis 序列化程序来轻松实现。
首先,您需要定义一个有效的 Redis 序列化程序,以实现如上图所述的压缩和解压缩逻辑。您将看到如何使用GZIP,它在 Java 中本机实现,但其他压缩方法也是可能的。此外,Commons IO库将用于保持解压逻辑简单。
如果您是 Maven 用户,请将以下依赖项添加到您项目的构建 POM 中:
<dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> <version>2.9.0</version> </dependency>
|
现在,您拥有了定义自定义 Redis 序列化程序所需的一切。
Java代码
class RedisCacheGZIPSerializer extends JdkSerializationRedisSerializer { @Override public Object deserialize( byte[] bytes ) { return super.deserialize(decompress(bytes)); }
@Override public byte[] serialize( Object o ) { return compress(super.serialize(o)); }
private byte[] compress( byte[] data ) { ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); // compressing the input data using GZIP try (GZIPOutputStream gzipOutputStream = new GZIPOutputStream(byteArrayOutputStream)) { gzipOutputStream.write(data); } catch (IOException e) { throw new SerializationException(e.getMessage()); }
return byteArrayOutputStream.toByteArray(); }
private byte[] decompress( byte[] data ) { ByteArrayOutputStream out = new ByteArrayOutputStream();
try { // decompressing the input data using GZIP IOUtils.copy(new GZIPInputStream(new ByteArrayInputStream(data)), out); } catch (IOException e) { throw new SerializationException(e.getMessage()); }
return out.toByteArray(); } }
|
Kotlin代码
class RedisCacheGZIPSerializer : JdkSerializationRedisSerializer() { override fun deserialize( bytes: ByteArray? ) : Any { return super.deserialize(decompress(bytes)) }
override fun serialize( o: Any? ): ByteArray { return compress(super.serialize(o)) }
private fun compress( data: ByteArray ) : ByteArray { val byteArrayOutputStream = ByteArrayOutputStream()
try { // compressing the input data using GZIP GZIPOutputStream(byteArrayOutputStream).use { gzipOutputStream -> gzipOutputStream.write(data) } } catch (e: IOException) { throw SerializationException(Constants.COMPRESSION_ERROR_MESSAGE, e) }
return byteArrayOutputStream.toByteArray() }
private fun decompress( data: ByteArray? ) : ByteArray { val out = ByteArrayOutputStream()
try { // decompressing the input data using GZIP IOUtils.copy(GZIPInputStream(ByteArrayInputStream(data)), out) } catch (e: IOException) { throw SerializationException(Constants.DECOMPRESSION_ERROR_MESSAGE, e) }
return out.toByteArray() } }
|
其次,需要将刚刚定义的类声明为默认的Redis值序列化程序。这可以通过注册RedisCacheConfiguration主bean来实现:在实现CachingConfigurerSupport自定义类中使用@Configuration注释,如下所示:
@Configuration class CacheConfig extends CachingConfigurerSupport { @Bean @Primary public RedisCacheConfiguration defaultCacheConfig() { RedisCacheGzipSerializer serializerGzip = new RedisCacheGzipSerializer();
return RedisCacheConfiguration .defaultCacheConfig() .serializeValuesWith(SerializationPair.fromSerializer(serializerGzip)); } }
|
kotlin:
@Configuration class CacheConfig : CachingConfigurerSupport() { @Bean @Primary fun defaultCacheConfig( ) : RedisCacheConfiguration? { // registering the custom Redis serializer val serializerGzip = RedisCacheGzipSerializer()
return RedisCacheConfiguration .defaultCacheConfig() .serializeValuesWith(SerializationPair.fromSerializer(serializerGzip)) } }
|