首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >SpringBoot RabbitMQ -如何减少许多主题(事件)的样板?

SpringBoot RabbitMQ -如何减少许多主题(事件)的样板?
EN

Stack Overflow用户
提问于 2019-02-11 16:13:37
回答 1查看 133关注 0票数 0

我想知道在初始化许多RabbitMQ队列/绑定时,是否有一种方法可以减少SpringBoot中样板代码的数量?

按照事件驱动的方法,我的应用程序产生了大约50种类型的事件(稍后会分成几个较小的应用程序,但仍然如此)。每个事件都以“主题”类型进行交换。有些事件正在被其他应用程序所消耗,有些事件则是由发送它们的同一个应用程序额外消耗的。

让我们考虑一下出版和自我消费的情况。

在SpringBoot for 中,我需要声明每个事件

  1. 在配置中路由密钥名(如"event.item.purchased")
  2. 在同一个应用程序("queue.event.item.purchased")中使用该事件的队列名
  3. 匹配配置属性类字段或保持属性名称的代码中的变量itemPurchasedRoutingKey或常量(如${event.item.purchased})
  4. 用于创建队列的bean (具有事件名称的名称),如itemPurchasedQueue
  5. 用于绑定创建(带有以事件名称为特征的名称)和路由密钥名的bean。类似于用itemPurchasedQueue.bind(...itemPurchasedRoutingKey)构造的itemPurchasedBinding
  6. 事件的RabbitListener,带有包含队列名称的注释(不能在运行时定义)

SO-6以一种或另一种形式提及“购买物品”的场所。如果有50个事件,很容易出错--当添加新事件时,您需要记住将它添加到6个位置。

理想情况下,对于每一项活动,我想:

  1. 在配置中指定路由键。队列名可以通过附加公共前缀(特定于应用程序)来构建。
  2. 使用一些注释或替代的RabbitListener来自动声明队列(通过路由键+前缀)、绑定到它并侦听事件。

有没有优化它的方法?我考虑了自定义注释,但是RabbitListener不喜欢动态队列名,如果我在某些util方法中声明队列和绑定,那么spring引导就找不到bean。也许有一种方法可以在代码中声明所有这些东西,但我相信这不是Spring的方法:)

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2019-02-15 13:16:07

因此,我最终使用了手动bean声明,并对每个bean使用了一个bind()方法

代码语言:javascript
复制
@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
    ...
}
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/54634670

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档