Redis предлагает не только хранилище значений ключей, но и публикацию сообщений о подписке. В этом посте будет описан простой сценарий, использующий Spring Data Redis, — добавление объекта домена сообщений в хранилище с помощью вызова REST, публикация этого сообщения в канал, подписчики этого канала, получающие это сообщение, которые в результате устанавливают отложенный длительный опрос. результаты с сообщением.
Два ключевых классов в Redis публиковать подписываться механизмом является
RedisTemplate класса и
RedisMessageListenerContainer класса.
RedisTemplate содержит
JedisConnectionFactory который содержит сведение о соединении Redis и, а также методы , чтобы манипулировать ключевые магазины значения, существует метод , называемый опубликовать
convertAndSend . Этот метод принимает два аргумента. Первый — это название канала, куда должны быть опубликованы сообщения, а второй — объект, подлежащий отправке.
JedisConnectionFactory который содержит сведение о соединении Redis и, а также методы , чтобы манипулировать ключевые магазины значения, существует метод , называемый опубликовать
convertAndSend . Этот метод принимает два аргумента. Первый — это название канала, куда должны быть опубликованы сообщения, а второй — объект, подлежащий отправке.
В этом примере публикация сообщения выполняется после того, как
сообщение сохраняется через аспект.
@Aspect @Component public class MessageAspect extends AbstractRedisAspect { private static final Logger LOGGER = LoggerFactory .getLogger(MessageAspect.class); @Value("${messaging.redis.channel.messages}") private String channelName; @After("execution(* com.city81.redisPubSub.repository.MessageDao.save(..))") public void interceptMessage(JoinPoint joinPoint) { Message message = (Message) joinPoint.getArgs()[0]; // this publishes the message this.redisTemplate.convertAndSend(channelName, message); } }
RedisMessageListenerContainer , а также проведение
JedisConnectionFactory , держит карту слушателей сообщений , где ключ является сообщением слушателя экземпляр и значение канала. Ссылки экземпляра приемника сообщений класса , который реализует
OnMessage метод
MessageListener интерфейса.
JedisConnectionFactory , держит карту слушателей сообщений , где ключ является сообщением слушателя экземпляр и значение канала. Ссылки экземпляра приемника сообщений класса , который реализует
OnMessage метод
MessageListener интерфейса.
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:p="http://www.springframework.org/schema/p" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.2.xsd" > <!-- for the redis pub sub aop beans --> <aop:aspectj-autoproxy /> <bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory" p:host-name="${messaging.redis.hostname}" p:port="${messaging.redis.port}"/> <bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate" p:connection-factory-ref="jedisConnectionFactory"> </bean> <bean id="messageListener" class="org.springframework.data.redis.listener.adapter.MessageListenerAdapter"> <constructor-arg> <ref bean="messageManager"/> </constructor-arg> </bean> <bean id="redisContainer" class="org.springframework.data.redis.listener.RedisMessageListenerContainer"> <property name="connectionFactory" ref="jedisConnectionFactory"/> <property name="messageListeners"> <map> <entry key-ref="messageListener"> <bean class="org.springframework.data.redis.listener.ChannelTopic"> <constructor-arg value="${messaging.redis.channel.messages}" /> </bean> </entry> </map> </property> </bean> </beans>
Когда сообщение публикуется, те подписчики, которые прослушивают этот канал, получат опубликованное сообщение с помощью
метода onMessage . Опубликованное сообщение содержит сериализованный объект, который был отправлен в теле сообщения Redis, и его необходимо десериализовать и привести к исходному объекту.
public void onMessage( org.springframework.data.redis.connection.Message redisMessage, byte[] pattern) { Message message = (Message) SerializationUtils.deserialize(redisMessage.getBody()); // set the deferred results for the user for (DeferredResult<Message> deferredResult : this.messageDeferredResultList) { deferredResult.setResult(message); } }
Список
DeferredResult заполняется вызовами метода getNewMessage службы REST
. Это, в свою очередь, в
MessageManager создаст
объект DeferredResult , добавит его в список и вернет объект клиенту.
public DeferredResult<Message> getNewMessage() throws Exception { final DeferredResult<Message> deferredResult = new DeferredResult<Message>(deferredResultTimeout); deferredResult.onCompletion(new Runnable() { public void run() { messageDeferredResultList.remove(deferredResult); } }); deferredResult.onTimeout(new Runnable() { public void run() { messageDeferredResultList.remove(deferredResult); } }); messageDeferredResultList.add(deferredResult); return deferredResult; }
GitHub
репо для этого примера содержит два простых HTML — страниц, один из которых начинается запрос длинного опроса , а другое , который добавляет сообщение. Это вызовет веб-сервис REST ниже.
@Controller @RequestMapping("/messages") public class MessageAPIController { @Inject private MessageManager messageManager; // // ADD A MESSAGE // @RequestMapping(value = "/add", method = RequestMethod.POST, produces = "application/json") @ResponseBody public Message addMessage( @RequestParam(required = true) String text) throws Exception { return messageManager.addMessage(text); } // // LONG POLLING // @RequestMapping(value = "/watch", method = RequestMethod.GET, produces = "application/json") @ResponseBody public DeferredResult<Message> getNewMessage() throws Exception { return messageManager.getNewMessage(); } }
Дальнейшее усовершенствование вышеупомянутого, чтобы гарантировать, что сообщения не будут пропущены между длинными запросами на опрос, состояло бы в том, чтобы хранить сообщения в Redis в отсортированном наборе с оценкой, являющейся меткой времени создания сообщения. Затем можно использовать механизм публикации Redis, чтобы сообщить подписчику, что в Redis есть новые сообщения, и затем он может извлечь их на основе времени последнего запроса и вернуть коллекцию сообщений обратно клиенту в объекте DeferredResult.