Skip to content

Instantly share code, notes, and snippets.

@varvay
Last active October 15, 2023 08:39
Spring Boot MVC — Lettuce Command Latency Event Implementation
myapp:
redis:
lettuce:
host: host
port: port
username: username
password: password
ssl:
enabled: false
connection-pool:
min-idle: 0
max-idle: 500
max-total: 500
timeout:
idle-timeout: 10
connection-timeout: 300
wait-timeout: 500
command-timeout: 500
plugins {
java
id("org.springframework.boot") version "3.1.4"
id("io.spring.dependency-management") version "1.1.3"
}
group = "com.myapp"
version = "0.0.1-SNAPSHOT"
java {
sourceCompatibility = JavaVersion.VERSION_17
}
configurations {
compileOnly {
extendsFrom(configurations.annotationProcessor.get())
}
}
repositories {
mavenCentral()
}
dependencies {
implementation("org.springframework.boot:spring-boot-starter-actuator")
implementation("org.springframework.boot:spring-boot-starter-web")
implementation("org.springframework.boot:spring-boot-starter-validation")
implementation("org.apache.commons:commons-lang3")
implementation("org.apache.commons:commons-pool2")
implementation("io.micrometer:micrometer-tracing-bridge-brave")
implementation("io.lettuce:lettuce-core")
implementation("redis.clients:jedis")
implementation("io.micrometer:micrometer-core")
compileOnly("org.projectlombok:lombok")
annotationProcessor("org.projectlombok:lombok")
testImplementation("org.springframework.boot:spring-boot-starter-test")
}
tasks.withType<Test> {
useJUnitPlatform()
}
package com.myproject.redis.controller;
import io.lettuce.core.RedisClient;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.log4j.Log4j2;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.Set;
@Log4j2
@RestController
@RequestMapping("/redis/v1")
@RequiredArgsConstructor
public class Controller {
private final RedisClient redisClient;
@SneakyThrows
@GetMapping("/lettuce")
public ResponseEntity<Set<String>> getLettuceSingle(@RequestParam("key") String key) {
redisClient.getResources()
.eventBus()
.get()
.subscribe(event -> {
log.info("Log Event: {}", event);
});
return ResponseEntity.ofNullable(redisClient.connect().sync().smembers(key));
}
}
package com.myproject.redis.configuration;
import com.myproject.redis.property.LettuceClientProperty;
import io.lettuce.core.RedisClient;
import io.lettuce.core.RedisURI;
import io.lettuce.core.SslVerifyMode;
import io.lettuce.core.api.StatefulRedisConnection;
import io.lettuce.core.api.sync.RedisCommands;
import io.lettuce.core.event.DefaultEventPublisherOptions;
import io.lettuce.core.metrics.MicrometerCommandLatencyRecorder;
import io.lettuce.core.metrics.MicrometerOptions;
import io.lettuce.core.resource.DefaultClientResources;
import io.lettuce.core.support.ConnectionPoolSupport;
import io.micrometer.core.instrument.simple.SimpleMeterRegistry;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.pool2.impl.GenericObjectPool;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.time.Duration;
@Log4j2
@Configuration
@RequiredArgsConstructor
@EnableConfigurationProperties(LettuceClientProperty.class)
public class LettuceClientConfiguration {
private final LettuceClientProperty lettuceClientProperty;
@Bean
public RedisClient redisClient() {
var builder = RedisURI.builder()
.withHost(lettuceClientProperty.host())
.withPort(lettuceClientProperty.port())
.withSsl(lettuceClientProperty.ssl().enabled());
if (StringUtils.isNotBlank(lettuceClientProperty.username()) || StringUtils.isNotBlank(lettuceClientProperty.password())) {
builder.withAuthentication(lettuceClientProperty.username(), lettuceClientProperty.password());
}
if (lettuceClientProperty.ssl().enabled()) {
builder.withVerifyPeer(SslVerifyMode.NONE);
}
var micrometerOptions = MicrometerOptions.builder()
.enable()
.histogram(true)
.localDistinction(true)
.minLatency(Duration.ofMillis(1))
.maxLatency(Duration.ofDays(1))
.build();
var commandLatencyPublisherOptions = DefaultEventPublisherOptions.builder()
.eventEmitInterval(Duration.ofSeconds(1))
.build();
var resources = DefaultClientResources.builder()
.commandLatencyRecorder(
new MicrometerCommandLatencyRecorder(
new SimpleMeterRegistry(),
micrometerOptions
)
)
.commandLatencyPublisherOptions(commandLatencyPublisherOptions)
.build();
return RedisClient.create(resources, builder.build());
}
@Bean
public StatefulRedisConnection<String, String> redisConnection(RedisClient redisClient) {
return redisClient.connect();
}
@Bean
public RedisCommands<String, String> redisCommands(StatefulRedisConnection<String, String> redisConnection) {
return redisConnection.sync();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment