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,>
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 elMappingJackson2MessageConverter
. 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. ElClassMapper
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 cargaMyPayload
al tipo MIMEapplication/json
de la siguiente manera:Una vez que haya configurado
ClassMapper
, puede usarlo en su activador de servicio especificando el tipo MIME de la clase de carga. Por ejemplo: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 elRedisPublishingMessageHandler
para usar el conversor de mensajesClassMapper
. Puede configurar el conversor de mensajesClassMapper
de la siguiente manera:Luego, puede usar
mappingJackson2MessageConverter
en suRedisInboundChannelAdapter
yRedisPublishingMessageHandler
como el conversor de mensajes. Por ejemplo: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.