package com.example.demo.common;
import java.util.concurrent.CountDownLatch;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.listener.PatternTopic;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
import org.springframework.data.redis.listener.adapter.MessageListenerAdapter;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
@Configuration
public class RedisMessageListener {
private static final String TOPIC = "TOPIC";
/**
* redis消息监听容器
* @param redisConnectionFactory redis连接工厂
* @param messageListenerAdapter redis消息监听适配器
* @return
*/
@Bean
public RedisMessageListenerContainer container(RedisConnectionFactory redisConnectionFactory,
MessageListenerAdapter messageListenerAdapter) {
RedisMessageListenerContainer container = new RedisMessageListenerContainer();
container.setConnectionFactory(redisConnectionFactory);
container.addMessageListener(messageListenerAdapter, new PatternTopic(TOPIC));
return container;
}
/**
* @param receiver 适配器处理器(defaultListenerMethod 默认的监听器处理方法)
* @param stringRedisSerializer
* @return
*/
@Bean
public MessageListenerAdapter adapter(ReceiverRedisMessage receiver, StringRedisSerializer stringRedisSerializer) {
//绑定监听方法 messageReceive
MessageListenerAdapter messageListenerAdapter = new MessageListenerAdapter(
receiver, "messageReceive");
//设置序列化
messageListenerAdapter.setStringSerializer(stringRedisSerializer);
GenericJackson2JsonRedisSerializer genericJackson2JsonRedisSerializer = new GenericJackson2JsonRedisSerializer();
messageListenerAdapter.setSerializer(genericJackson2JsonRedisSerializer);
return messageListenerAdapter;
}
@Bean
public ReceiverRedisMessage receiver(CountDownLatch latch) {
return new ReceiverRedisMessage(latch);
}
@Bean
public CountDownLatch latch() {
return new CountDownLatch(1);
}
@Bean
public StringRedisSerializer stringRedisSerializer(){
return new StringRedisSerializer();
}
}
package com.example.demo.common;
import java.util.concurrent.CountDownLatch;
import lombok.extern.slf4j.Slf4j;
/**
* @author wh
* @date: 2019/9/26 0026 11:26
* @desc:
*/
@Slf4j
public class ReceiverRedisMessage {
private CountDownLatch latch;
public ReceiverRedisMessage(CountDownLatch latch) {this.latch = latch;}
public void messageReceive(String jsonString) {
//具体消息处理
try {
log.info("处理消息:{}",jsonString);
}finally {
latch.countDown();
}
}
}
package com.example.demo;
import com.alibaba.fastjson.JSON;
import com.example.demo.common.RedisMessageService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@SpringBootTest
public class RedisMessageApplicationTests {
@Autowired
private StringRedisTemplate redisTemplate;
@Test
public void contextLoads() {
redisTemplate.convertAndSend("TOPIC",JSON.toJSONString("发送消息"));
}
}
import com.ngc.common.web.http.HttpResponseEntity;
import java.util.Iterator;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.context.request.async.DeferredResult;
@Slf4j
public class MessageContainer {
/**
* 用户DeferredResult绑定
*/
private static ConcurrentHashMap<String, DeferredResult<HttpResponseEntity>> userDeferredResultMap = new ConcurrentHashMap<>();
/**
* 缓存需要推送消息DeferredResult
*
* @param key 用户Id
* @param value DeferredResult
*/
public static void put(String key, DeferredResult<HttpResponseEntity> value) {
if (StringUtils.isBlank(key) || Objects.isNull(value)) {
return;
}
log.info("异步消息新增=={}", key);
userDeferredResultMap.put(key, value);
}
/**
* 移除
*/
public static void remove(String key, DeferredResult<HttpResponseEntity> deferredResult) {
if (StringUtils.isBlank(key)) {
return;
}
log.info("异步消息移除=={}", key);
if (userDeferredResultMap.containsKey(key) &&
userDeferredResultMap.containsValue(deferredResult)) {
//移除
boolean remove = userDeferredResultMap.remove(key, deferredResult);
if (remove == true) {
log.info("异步消息移除成功=={}", key);
} else {
log.info("异步消息移除失败=={}", key);
}
}
}
/**
* 设置返回结果
*/
public static void setResult(HttpResponseEntity result) {
if (Objects.isNull(result)) {
return;
}
Iterator<Entry<String, DeferredResult<HttpResponseEntity>>> iterator = userDeferredResultMap
.entrySet().iterator();
//循环遍历,setResult,并移除已经推送过
while (iterator.hasNext()) {
Entry<String, DeferredResult<HttpResponseEntity>> next = iterator.next();
log.info("异步消息发送消息=={}", next.getKey());
DeferredResult<HttpResponseEntity> deferredResult = next.getValue();
deferredResult.setResult(result);
iterator.remove();
}
}
}
import com.ngc.bs.common.listener.MessageContainer;
import com.ngc.common.web.http.HttpResponseCodeEnum;
import com.ngc.common.web.http.HttpResponseEntity;
import java.util.Objects;
import javax.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.jeecgframework.core.util.ResourceUtil;
import org.jeecgframework.web.system.pojo.base.TSUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.async.DeferredResult;
@Slf4j
@RestController
@RequestMapping("/message")
public class MessageController {
@Autowired
private JdbcTemplate jdbcTemplate;
/**
* 工作台消息
*/
@ResponseBody
@RequestMapping(value = "/workbench")
public DeferredResult<HttpResponseEntity> workench(HttpServletRequest request) {
log.info("轮询接口进入....");
//https://blog.csdn.net/yzf913214/article/details/53915378
request.setAttribute("org.apache.catalina.ASYNC_SUPPORTED", true);
DeferredResult<HttpResponseEntity> deferredResult = new DeferredResult<>(30000L,
HttpResponseEntity.error(HttpResponseCodeEnum.NOT_FOUND.getCode(), "no message")
);
//检查用户登录信息,如果未登录直接返回,不进行消息订阅
TSUser user = ResourceUtil.getSessionUser();
if (Objects.isNull(user)) {
deferredResult.setResult(HttpResponseEntity.error("请重新登录"));
return deferredResult;
}
String key = user.getId() + "_" + System.currentTimeMillis();
//异步完成时调用,为了防止超时,网络故障,增加第二套保证
deferredResult.onCompletion(() -> MessageContainer.remove(key, deferredResult));
MessageContainer.put(key, deferredResult);
// log.info("轮询接口结束返回....deferredResult.getResult():"+deferredResult.getResult()+" deferredResult.hasResult(): "+deferredResult.hasResult()+"deferredResult.setErrorResult(\"erro\") "+deferredResult.setErrorResult("erro"));
return deferredResult;
}
}
1.在使用注解时使用@RequestMapping不要使用@GetMapping
2.Spring boot 项目使用t摩擦他启动
3. 配置参数 如下 或者使用实例request设置
<async-supported>trueasync-supported>
1 不建议多个长轮询 chrome最多六个线程处理 长轮询最好只存在一个即可
2.在使用注解时使用@RequestMapping不要使用@GetMapping
3.Spring boot 项目使用t摩擦他启动
4. 配置参数 如下 或者使用实例request设置