SpringBoot RabbitMQ - как уменьшить шаблон для многих тем (событий)? - PullRequest
0 голосов
/ 11 февраля 2019

Интересно, есть ли способ уменьшить объем стандартного кода при инициализации многих очередей / привязок RabbitMQ в SpringBoot?

Следуя подходу, основанному на событиях, мое приложение генерирует около 50 типов событий (это будетразделить на несколько небольших приложений позже, но все же).Каждое событие идет на обмен с типом «тема».Некоторые события потребляются другими приложениями, некоторые события дополнительно потребляются тем же приложением, которое их отправляет.

Давайте рассмотрим этот вариант публикации и самостоятельного использования.

В SpringBoot для каждое событие Мне нужно объявить:

  1. имя ключа маршрутизации в конфигурации (например, "event.item.purchased")
  2. имя очереди для использования этого события внутрито же приложение ("queue.event.item.purchased")
  3. соответствующее поле класса свойств конфигурации или переменная itemPurchasedRoutingKey или константа в коде, в котором хранится имя свойства (например, $ {event.item.purchased})
  4. для создания очереди (с именем, содержащим имя события), такой как bean-компонент itemPurchasedQueue
  5. для создания привязки (с именем, содержащим имя события) и имя ключа маршрутизации.например, itemPurchasedBinding, который создается с помощью itemPurchasedQueue.bind (... itemPurchasedRoutingKey)
  6. RabbitListener для события, с аннотацией, содержащей имя очереди (не может быть определено во время выполнения)

Так -6 мест, где «купленный предмет» упоминается в той или иной форме.Количество шаблонного кода просто убивает меня :) Если есть 50 событий, очень легко ошибиться - при добавлении нового события, вы должны помнить, чтобы добавить его в 6 мест.

В идеале, длякаждое событие, которое я хотел бы:

  1. указать ключ маршрутизации в конфигурации.Имя очереди может быть построено на нем путем добавления общего префикса (специфичного для приложения).
  2. использовать некоторую аннотацию или альтернативный RabbitListener, который автоматически объявляет очередь (путем маршрутизации ключ + префикс), связывается с ней и прослушивает события.

Есть ли способ оптимизировать его?Я думал о пользовательских аннотациях, но RabbitListener не любит динамические имена очередей, и Spring Boot не может найти бины для очередей и привязок, если я объявляю их внутри некоторого метода util.Может быть, есть способ объявить все эти вещи в коде, но я не верю в способ Spring, я думаю:)

1 Ответ

0 голосов
/ 15 февраля 2019

Итак, я использовал ручное объявление бина и метод 1 bind () для каждого бина

@Configuration
@EnableConfigurationProperties(RabbitProperties::class)
class RabbitConfiguration(
    private val properties: RabbitProperties,
    private val connectionFactory: ConnectionFactory
) {

    @Bean
    fun admin() = RabbitAdmin(connectionFactory)

    @Bean
    fun exchange() = TopicExchange(properties.template.exchange)

    @Bean
    fun rabbitMessageConverter() = Jackson2JsonMessageConverter(
        jacksonObjectMapper()
            .registerModule(JavaTimeModule())
            .registerModule(Jdk8Module())
            .disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
            .enable(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL)
    )

    @Value("\${okko.rabbit.queue-prefix}")
    lateinit var queuePrefix: String

    fun <T> bind(routingKey: String, listener: (T) -> Mono<Void>): SimpleMessageListenerContainer {
        val queueName = "$queuePrefix.$routingKey"
        val queue = Queue(queueName)
        admin().declareQueue(queue)
        admin().declareBinding(BindingBuilder.bind(queue).to(exchange()).with(routingKey)!!)

        val container = SimpleMessageListenerContainer(connectionFactory)
        container.addQueueNames(queueName)
        container.setMessageListener(MessageListenerAdapter(MessageHandler(listener), rabbitMessageConverter()))
        return container
    }

    internal class MessageHandler<T>(private val listener: (T) -> Mono<Void>) {

        // NOTE: don't change name of this method, rabbit needs it
        fun handleMessage(message: T) {
            listener.invoke(message).subscribeOn(Schedulers.elastic()).subscribe()
        }
    }
}


@Service
@Configuration
class EventConsumerRabbit(
    private val config: RabbitConfiguration,
    private val routingKeys: RabbitEventRoutingKeyConfig
) {

    @Bean
    fun event1() = handle(routingKeys.event1)

    @Bean
    fun event2() = handle(routingKeys.event2)

    ...

    private fun<T> handle(routingKey: String): Mono<Void> = config.bind<T>(routingKey) {
        log.debug("consume rabbit event: $it")
        ... // handle event, return Mono<Void>
    }

    companion object {
        private val log by logger()
    }
}

@Configuration
@ConfigurationProperties("my.rabbit.routing-key.event")
class RabbitEventRoutingKeyConfig {
    lateinit var event1: String
    lateinit var event2: String
    ...
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...