Spring WebFlux + Redis

1. redis-reactive, embedded redis ์˜์กด์„ฑ ์ถ”๊ฐ€

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-data-redis-reactive'
	implementation 'it.ozimov:embedded-redis:0.7.2'
    ...
}

2. redis์˜ ๋ฌธ์ž์—ด์˜ key value๋ฅผ ์‚ฌ์šฉํ•˜๋„๋ก Bean ๋“ฑ๋ก

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.redis.connection.ReactiveRedisConnectionFactory;
import org.springframework.data.redis.core.ReactiveRedisOperations;
import org.springframework.data.redis.core.ReactiveRedisTemplate;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

@Configuration
public class BasicRedisConfig {

    @Primary
    @Bean
    ReactiveRedisOperations<String, String> redisOperations(ReactiveRedisConnectionFactory factory) {
        RedisSerializer<String> serializer = new StringRedisSerializer();
        RedisSerializationContext<String, String> serializationContext = RedisSerializationContext
                .<String, String>newSerializationContext()
                .key(serializer)
                .value(serializer)
                .hashKey(serializer)
                .hashValue(serializer)
                .build();

        return new ReactiveRedisTemplate<>(factory, serializationContext);
    }
}

3. test ๋ฐ ๋กœ์ปฌ test๋ฅผ ์œ„ํ•œ embedded redis ์„ค์ •

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import redis.embedded.RedisServer;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

@Configuration
public class EmbeddedRedisConfig {

    @Value("${spring.redis.port}")
    private int redisPort;

    private RedisServer redisServer;

    @PostConstruct
    public void redisServer() {
        redisServer = new RedisServer(redisPort);
        redisServer.start();
    }

    @PreDestroy
    public void stopRedis() {
        redisServer.stop();
    }
}

4. handler ์ž‘์„ฑ

- loadData(): 10๋งŒ๊ฐœ์˜ ๋ฐ์ดํ„ฐ ๋กœ๋“œ ๋ฉ”์„œ๋“œ

- findReactorList(): ๋น„๋™๊ธฐ, ๋…ผ๋ธ”๋กœํ‚น ๋ฐฉ์‹์˜ ๋ฉ”์„œ๋“œ

- findNormalList(): ๋™๊ธฐ, ๋ธ”๋กœํ‚น ๋ฐฉ์‹์˜ ๋ฉ”์„œ๋“œ

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

@Component
@RequiredArgsConstructor
public class BasicHandler {

    private final ReactiveRedisConnectionFactory factory;
    private final ReactiveRedisOperations<String, String> reactiveRedisOperations;
    private final RedisTemplate<String , String> stringStringRedisTemplate;
    private static final AtomicInteger count = new AtomicInteger(0);

    public void loadData() {
        List<String> data = new ArrayList<>();
        IntStream.range(0, 100000).forEach(i -> data.add(UUID.randomUUID().toString()));

        Flux<String> stringFlux = Flux.fromIterable(data);

        factory.getReactiveConnection()
                .serverCommands()
                .flushAll()
                .thenMany(stringFlux.flatMap(uid -> reactiveRedisOperations.opsForValue()
                        .set(String.valueOf(count.getAndAdd(1)), uid)))
                .subscribe();
    }

    public Flux<String> findReactorList() {
        return reactiveRedisOperations
                .keys("*")
                .flatMap(key -> reactiveRedisOperations.opsForValue().get(key));
    }

    public Flux<String> findNormalList() {
        return Flux.fromIterable(Objects.requireNonNull(stringStringRedisTemplate.keys("*"))
                .stream()
                .map(key -> stringStringRedisTemplate.opsForValue().get(key))
                .collect(Collectors.toList()));
    }
}

5. router ๋“ฑ๋ก

- ๋“ฑ๋ก์‹œ bean์ด๋ฆ„์ด ๋‹ค๋ฅธ router์™€ ๊ฒน์น˜์ง€ ์•Š๊ฒŒ ์กฐ์‹ฌํ•ด์•ผํ•จ. ๋™์ผ ์ด๋ฆ„์˜ bean 2๊ฐœ ๋งŒ๋“ค๋ ค๊ณ ํ•˜๋ฉด์„œ ์‹คํŒจํ•˜๊ฒŒ ๋จ

import com.webflux.study.handler.BasicHandler;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.RouterFunctions;
import org.springframework.web.reactive.function.server.ServerResponse;

@Configuration
@RequiredArgsConstructor
public class BasicRouter {

    private final BasicHandler basicHandler;

    @Bean
    public RouterFunction<ServerResponse> basicRoute() {
        return RouterFunctions.route()
                .GET("/reactive-list", serverRequest -> ServerResponse.ok().contentType(MediaType.TEXT_EVENT_STREAM)
                        .body(basicHandler.findReactorList(), String.class))
                .GET("/normal-list", serverRequest -> ServerResponse.ok().contentType(MediaType.TEXT_EVENT_STREAM)
                        .body(basicHandler.findNormalList(), String.class))
                .GET("/load", serverRequest -> { basicHandler.loadData(); return ServerResponse.ok()
                        .body(BodyInserters.fromValue("Load Data Completed")); })
                .build();
    }
}

์ถœ์ฒ˜ : https://wellbell.tistory.com/238

Last updated