Недавно один из бизнесов студии объединился с другим бизнесом. Естественно, использовался MQ (Message Queue), поэтому было очевидно, что RabbitMQ будет развернут на сервере.
Мы используем облачную службу, поэтому, конечно, мы развертываем промежуточное программное обеспечение в облачной службе. Служба активирована полностью, но когда приходит время отладки, мы не можем подключиться (говорят, что она напрямую подключена к облачной службе). интранет, но главное, чтобы все коллеги были онлайн) Для локального тестирования)
Я совершенно потерял дар речи. Что мне делать, столкнувшись с этой сценой? Бизнес будет продолжаться, и мы ждем доставки. Вот я и вспомнил стек технологий, который изучил ранее.
Подходит для простых небольших и средних проектов. Если функция простая и количество посещений не большое, можно рассматривать. Если к вашему приложению предъявляются строгие требования к надежности и расширенной функциональности, а также необходимо обрабатывать большие объемы сообщений и сложную маршрутизацию сообщений, то использование специализированной системы очередей сообщений может быть более подходящим.
Сначала убедитесь, что вы правильно настроили зависимости Redis и Lettuce и создали объект LettuceConnectionFactory.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
redis:
host:
port: 6379
password:
lettuce:
pool:
max-active: 1000
max-idle: 1000
min-idle: 0
time-between-eviction-runs: 10s
max-wait: 10000
Создайте объект RedisTemplate и установите LettuceConnectionFactory в качестве его фабрики соединений:
@Bean
public RedisTemplate<String, String> redisTemplate(LettuceConnectionFactory connectionFactory) {
RedisTemplate<String, String> template = new RedisTemplate<>();
template.setConnectionFactory(connectionFactory);
template.setDefaultSerializer(new StringRedisSerializer());
return template;
}
redisTemplate.setDefaultSerializer(new StringRedisSerializer());
Опубликовать сообщение:
redisTemplate.convertAndSend("channel_name", "message_payload");
В приведенном выше коде «channel_name» — это имя канала сообщения, а «message_payload» — это содержимое публикуемого сообщения.
Подпишитесь на новости:
Сначала создайте класс реализации MessageListener для обработки полученных сообщений:
public class MessageListenerImpl implements MessageListener {
@Override
public void onMessage(Message message, byte[] pattern) {
// Обрабатывать полученные сообщения
String channel = new String(message.getChannel());
String payload = new String(message.getBody());
// Выполнять пользовательскую логику
}
}
Создайте объект LettuceMessageListenerAdapter и предоставьте класс реализации MessageListener:
LettuceMessageListenerAdapter listenerAdapter = new LettuceMessageListenerAdapter(new MessageListenerImpl());
listenerAdapter.afterPropertiesSet();
Создайте объект RedisMessageListenerContainer и настройте его LettuceConnectionFactory и адаптер прослушивания:
RedisMessageListenerContainer listenerContainer = new RedisMessageListenerContainer();
listenerContainer.setConnectionFactory(lettuceConnectionFactory);
listenerContainer.addMessageListener(listenerAdapter, new ChannelTopic("Название канала"));
listenerContainer.start();
Выполнив описанные выше шаги, мы создали объект LettuceConnectionFactory для установления соединения с сервером Redis. Затем мы создали класс реализации MessageListener для обработки полученных сообщений. Затем мы создали объект LettuceMessageListenerAdapter и предоставили класс реализации MessageListener. Наконец, мы создаем объект RedisMessageListenerContainer и настраиваем его LettuceConnectionFactory и адаптер прослушивания, а затем запускаем контейнер, чтобы начать прослушивание сообщений на указанном канале.
Преимущество вышеуказанного решения в том, что вы можете четко знать, в какой части прослушиватель отслеживает информацию соответствующего канала. Однако в бизнесе, если для каждого бизнеса и канала соответствующего модуля установлен прослушиватель (мы. предположим, что каждому бизнесу необходимо получить сообщение. Логика выполнения в будущем будет другой) Тогда рабочая нагрузка резко увеличится.
Итак, есть второй способ записи:
/***
* @title MessageManager
* @author SUZE
* @Date 2-17
**/
@Component
public class ReservedMessageManager {
private String ListenerId;
private String UserId;
private String message;
private final RedisTemplate<String, String> redisTemplate;
@Autowired
public ReservedMessageManager(RedisTemplate<String, String> redisTemplate) {
this.redisTemplate = redisTemplate;
subscribeToChannel("reserved");
}
@Resource
private SmsServer smsServer;
public void publishMessage(String channel, reserveMessage message) {
String Message=serialize(message);
redisTemplate.convertAndSend("channel_name", "message_payload");
redisTemplate.convertAndSend(channel, Message);
}
// Событие срабатывает при получении сообщения
private void handleReserveMessage(String channel, reserveMessage reserveMessage) {
if (reserveMessage != null) {
String userId = reserveMessage.getUserId();
String ListenerId=reserveMessage.getListenerId();
String message = reserveMessage.getMessage();
//TODO Обрабатывать полученные сообщениялогика Здесь нам нужно провести проверку сообщения позже. Есть четыре состояния: согласие, отказ и окончание. Состояние мышления отличается от отправки контента
switch (message){
//TODO Сообщение должно быть отправлено обеим сторонам. Поэтому мне нужно отправить две копии Копирайтинг для отправки сообщений
case "wait":
smsServer.sendSms(userId,ListenerId,message);
break;
case "agree":
smsServer.sendSms(userId,ListenerId,message);
break;
case "refuse":
smsServer.sendSms(userId,ListenerId,message);
break;
case "over":
//Здесь нам нужно управлять файловой системой
//Если вы откажетесь Тогда нам нужно следить за этим
smsServer.sendSms(userId,ListenerId,message);
break;
}
//smsServer.sendSms(userId,ListenerId,message);
// Другая логика обработки...
}
}
public void subscribeToChannel(String channel) {
redisTemplate.execute((RedisCallback<Object>) (connection) -> {
connection.subscribe((message, pattern) -> {
String channelName = new String(message.getChannel());
byte[] body = message.getBody();
// Разобрать полученное сообщение
switch (channelName){
case "reserved":
reserveMessage reserveMessage = deserializeMessage(new String(body));
handleReserveMessage(channelName, reserveMessage);
break;
//Есть и другие каналы Например, отказ – это Запретить канал Специально выслушайте причины отказа
}
},канал.getBytes());
вернуть ноль;
});
}
// сериализация
частный ReserveMessage deserializeMessage (тело строки) {
ObjectMapper objectMapper = новый ObjectMapper();
пытаться {
вернуть objectMapper.readValue(body, reserveMessage.class);
} catch (IOException e) {
// Обработка исключений, связанных с сериализацией
e.printStackTrace();
return null;
}
}
// сериализация
public String serialize(reserveMessage reserveMessage) throws SerializationException {
if (reserveMessage == null) {
return null;
}
try {
ObjectMapper objectMapper = new ObjectMapper();
return objectMapper.writeValueAsString(reserveMessage);
} catch (JsonProcessingException e) {
throw new SerializationException("Error serializing object", e);
}
}
}
subscribeToChannel
Метод принимаетchannel
параметр,Используется для указания имени канала для подписки.redisTemplate.execute
метод выполненияRedisдействовать,и пройти вRedisCallback
функция обратного вызова。connection
параметр,Представляет подключение к Redis.connection.subscribe
Способ подписки на канал。Должен Метод принимаетфункция обратного вызоваделатьдляпараметр,используется для Обрабатывать полученные сообщения。message
Получите имя канала и тело сообщения из объекта.。new String(message.getChannel())
Преобразовать имя канала в строковый тип,и хранитьсуществоватьchannelName
в переменной。message.getBody()
Получите представление массива байтов тела сообщения.,и хранитьсуществоватьbody
в переменной。switch
в предложении,В зависимости от имени канала выполняется различная обработка. В этом примере,Обрабатываются только «зарезервированные» каналы.deserializeMessage
метод возвращает тело сообщениясериализациядляreserveMessage
объект,и сохранить егосуществоватьимядляreserveMessage
частьв переменной。handleReserveMessage
метод,Преобразуйте имя канала и обратноесериализацияпозжеreserveMessage
объектделатьдляпараметр Процесс。handleReserveMessage
методиспользуется для Обработка полученных сохраненных сообщенийлогика。Он проверяет тип сообщения,и выполнять различные операции в зависимости от типа. По типу сообщения,этовызовsmsServer.sendSms
методназначенномуuserId
иlistenerId
отправка текстового сообщения。Ошибка: com.fasterxml.jackson.databind.exc.InvalidDefinitionException: невозможно создать экземпляр TopOne.MessageSystem.entity.reserveMessage (не существует создателей, таких как конструктор по умолчанию): невозможно десериализовать из значения объекта (нет делегата или свойства). -основанный Создатель)
В классе ReserveMessage отсутствует конструктор по умолчанию, что не позволяет библиотеке Джексона создавать экземпляры этого класса. В сообщении об ошибке упоминается следующее: «Невозможно создать экземпляр TopOne.MessageSystem.entity.reserveMessage (не существует создателей, таких как конструктор по умолчанию)». Чтобы Джексон мог правильно десериализовать объекты, в класс ReserveMessage необходимо добавить конструктор по умолчанию. Конструктор по умолчанию — это конструктор без параметров, который не требует никаких параметров для создания объекта. В вашем классе ReserveMessage
Это модифицированный класс инкапсуляции:
@Data
public class reserveMessage {
private String UserId;
private String ListenerId;
private String message;
public reserveMessage() {
// конструктор по умолчанию
}
public reserveMessage(String userId, String ListenerId,String message) {
this.UserId = userId;
this.ListenerId = ListenerId;
this.message=message;
}
}
успех
Печать здесь заменяет рассылку СМС в оригинальном бизнесе, что можно расценивать как успех.
На этом все. Спасибо за просмотр.