es.davy.ai

Preguntas y respuestas de programación confiables

¿Tienes una pregunta?

Si tienes alguna pregunta, puedes hacerla a continuación o ingresar lo que estás buscando.

Migración de integración de Spring a Redis desde RabbitMQ para compartir eventos de aplicación.

Estamos migrando de RabbitMQ a Redis en nuestras aplicaciones de microservicio.

Aquí está nuestro activador de servicio:

@ServiceActivator(inputChannel = ApplicationEventChannelNames.REMOTE_CHANNEL)
public void handleApplicationEvent(@Header(value = ApplicationEventHeaders.APPLICATION_EVENT) final ApplicationEvent event,
                                   @Payload Object message) {
...
}

Inicialmente tuvimos un problema en el que estábamos perdiendo eventos de aplicación en SimpleMessageConverter. Lo solucionamos implementando un CustomRedisMessageConverter y colocando el evento de aplicación en el payload en el método fromMessage y recuperándolo del payload y creando nuevos encabezados de mensajes con el evento de aplicación en el método toMessage.

@Override
public Object fromMessage(Message<?> message, Class<?> targetClass) {
    if (message.getHeaders().get(ApplicationEventHeaders.APPLICATION_EVENT) != null) {

        Map<string, object=""> map = new HashMap<>();

        map.put("headers", ((ApplicationEvent) message.getHeaders().get(ApplicationEventHeaders.APPLICATION_EVENT)).getName());
        map.put("payload", message.getPayload());

        GenericMessage<><string, object="">> msg = new GenericMessage<>(map, message.getHeaders());

        return super.fromMessage(msg, targetClass);
    }
    return super.fromMessage(message, targetClass);
}

@Override
public Message<?> toMessage(Object payload, MessageHeaders headers) {

    try {
        final Map<string,></string,> message = new ObjectMapper().readValue((String) payload, new TypeReference<><string,></string,>>() {});

        if (message.get("headers") != null) {
            final Map<string, object=""> messageHeaders = new HashMap<>(headers);

            messageHeaders.put(ApplicationEventHeaders.APPLICATION_EVENT, new ApplicationEvent((String) message.get("headers")));

            return super.toMessage(message.get("payload"), new MessageHeaders(messageHeaders));
        }
    } catch (JsonProcessingException exception) {
        /* Intentionally left blank */
    }

    return super.toMessage(payload, headers);
}

¿Hay alguna mejor manera de hacer esto?

Por último, el payload en el activador de servicio viene como un LinkedHashMap, pero queremos que sea un objeto. Con RabbitMQ esto se manejaba automáticamente. ¿Hay alguna forma de hacer lo mismo en Redis? ¿O usamos encabezados para realizar un seguimiento del tipo de payload y convertirlos manualmente en un objeto?

ACTUALIZACIÓN – Configuración de Redis:

“`
@Bean
public RedisInboundChannelAdapter applicationEventInboundChannelAdapter(@Value(value = "${com.xxx.xxx.xxx.integration.spring.topic}") String topic,
MessageChannel applicationEventRemoteChannel,
RedisConnectionFactory connectionFactory) {

<pre><code>final RedisInboundChannelAdapter inboundChannelAdapter = new RedisInboundChannelAdapter(connectionFactory);
inboundChannelAdapter.setTopics(topic);
inboundChannelAdapter.setOutputChannel(applicationEventRemoteChannel);
inboundChannelAdapter.setErrorChannel(errorChannel());
inboundChannelAdapter.setMessageConverter(new CustomRedisMessageConverter());

return inboundChannelAdapter;
</code></pre>

}

@ServiceActivator(inputChannel = "errorChannel")
public void processError(MessageHandlingException exception) {

<pre><code>try {

logger.error(
"Could not process {}, got exception: {}",
exception.getFailedMessage().getPayload(),
exception.getMessage());

logger.error(
ExceptionUtils.readStackTrace(exception));

} catch (Throwable throwable) {

logger.error(
"Got {} during processing with message: {}",
MessageHandlingException.class.getSimpleName(),
exception);
}
</code></pre>

}

@Bean
@ServiceActivator(inputChannel = ApplicationEventChannelNames.LOCAL_CHANNEL)
public RedisPublishingMessageHandler redisPublishingMessageHandler(@Value(value = "${com.xxx.xxx.xxx.integration.spring.topic}") String topic,
RedisConnectionFactory redisConnectionFactory) {

<pre><code>final RedisPublishingMessageHandler redisPublishingMessageHandler = new RedisPublishingMessageHandler(redisConnectionFactory);

redisPublishingMessageHandler.setTopic(topic);
redisPublishingMessageHandler.setSerializer(new Jackson2JsonRedisSerializer<>(String.class));
redisPublishingMessageHandler.setMessageConverter(new CusomRedisMessageConverter());

return redisPublishingMessageHandler;
</code></pre>

}

@Bean
public MessageChannel errorChannel() {
return new DirectChannel();
}
“`</string,></string,></string,>

Tags:  

Answer

  1. Avatar for davy.ai

    En cuanto a su primera pregunta, su enfoque de implementar un conversor de mensajes personalizado para manejar las necesidades específicas de su aplicación es válido. Puede ser la mejor opción para su caso de uso y puede que no haya una mejor manera de lograr el mismo resultado.

    Sin embargo, puede considerar usar un conversor de mensajes diferente, como el JsonMessageConverter o el MappingJackson2MessageConverter. Estos conversores de mensajes pueden serializar y deserializar automáticamente mensajes hacia y desde JSON, lo que puede hacer que sea más fácil trabajar con eventos y cargas de la aplicación.

    En cuanto a su segunda pregunta, puede usar la interfaz ClassMapper en Spring Integration para convertir automáticamente los datos de carga a un tipo de objeto específico. El ClassMapper asigna una clase de carga a un tipo MIME, que puede usarse para convertir automáticamente los datos de carga al objeto apropiado.

    Para usar ClassMapper, debe configurarlo como un bean en el contexto de su aplicación y luego especificar el tipo MIME para cada clase de carga que desee asignar. Por ejemplo, puede asignar una clase de carga MyPayload al tipo MIME application/json de la siguiente manera:

    @Bean
    public ClassMapper classMapper() {
        DefaultClassMapper classMapper = new DefaultClassMapper();
        Map<string,></string,><?>> classMappings = new HashMap<>();
        classMappings.put("application/json", MyPayload.class);
        classMapper.setIdClassMapping(classMappings);
        return classMapper;
    }
    

    Una vez que haya configurado ClassMapper, puede usarlo en su activador de servicio especificando el tipo MIME de la clase de carga. Por ejemplo:

    @ServiceActivator(inputChannel = "myInputChannel")
    public void handleMessage(Message<mypayload> message) {
        MyPayload payload = message.getPayload();
        // ...
    }
    

    Con esta configuración, Spring Integration convertirá automáticamente la carga del mensaje a MyPayload según el tipo MIME especificado.

    Por último, debe configurar el RedisInboundChannelAdapter y el RedisPublishingMessageHandler para usar el conversor de mensajes ClassMapper. Puede configurar el conversor de mensajes ClassMapper de la siguiente manera:

    @Bean
    public MappingJackson2MessageConverter mappingJackson2MessageConverter() {
        MappingJackson2MessageConverter messageConverter = new MappingJackson2MessageConverter();
        messageConverter.setClassMapper(classMapper());
        return messageConverter;
    }
    

    Luego, puede usar mappingJackson2MessageConverter en su RedisInboundChannelAdapter y RedisPublishingMessageHandler como el conversor de mensajes. Por ejemplo:

    @Bean
    public RedisInboundChannelAdapter redisInboundChannelAdapter(RedisConnectionFactory connectionFactory, MessageChannel myInputChannel) {
        RedisInboundChannelAdapter adapter = new RedisInboundChannelAdapter(connectionFactory);
        adapter.setOutputChannel(myInputChannel);
        adapter.setSerializer(mappingJackson2MessageConverter());
        adapter.setTopics("myTopic");
        return adapter;
    }
    
    @Bean
    public RedisPublishingMessageHandler redisPublishingMessageHandler(RedisConnectionFactory connectionFactory) {
        RedisPublishingMessageHandler handler = new RedisPublishingMessageHandler(connectionFactory);
        handler.setTopic("myTopic");
        handler.setSerializer(mappingJackson2MessageConverter());
        return handler;
    }
    

    Con esta configuración, Spring Integration utilizará ClassMapper para convertir automáticamente los datos de carga al tipo de objeto apropiado según el tipo MIME.

Comments are closed.