消息重发中需要注意的问题
由于最近工作中接触了比较多关闭消息推送以及异常重发机制的问题,终于得空总结一下经验
目前接触的消息推送分为两种
主动推送:一般为websocket建立长连接实现,此处网上多有各种实现方式。下面贴出本人结合实际应用场景使用的长连接方式。
websocket服务端代码
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import redis.clients.jedis.Jedis;
import javax.annotation.PostConstruct;
import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
@ServerEndpoint(value = "/websocket/{id}")
@Component
@Slf4j
public class WebSocket {
// 静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。
private static int onlineCount = 0;
// concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。
private static ConcurrentHashMap webSocketSet = new ConcurrentHashMap<>();
// 保存允许建立连接的id
private static List idList = Lists.newArrayList();
private String id = "";
/**
* 这里使用AutoWired注入的bean会出现无法持续保存而出现null的情况。
* 具体原因暂时没有深究,如果有需要时,可以再init初始化方法中手动将临时的beanTmp类存入static常量中即可正常使用该bean类。
* @Autowired
* private RedisCacheUtil redisTmp;
* private static RedisCacheUtil redis;
*
*/
// 与某个客户端的连接会话,需要通过它来给客户端发送数据
private Session session;
public void closeConn(String appId) {
// 关闭连接
try {
WebSocket socket = webSocketSet.get(appId);
if (null != socket) {
if (socket.session.isOpen()) {
socket.session.close();
}
}
} catch (IOException e) {
System.out.println("IO异常");
e.printStackTrace();
}
idList.remove(appId);
}
/**
* 连接/注册时去重
*/
public void conn(String appId) {
// 去重
if (!idList.contains(appId)) {
idList.add(appId);
}
}
/**
* 获取注册在websocket进行连接的id
*/
public static List getIdList() {
return idList;
}
/**
* 初始化方法
* @author caoting
* @date 2019年2月13日
*/
@PostConstruct
public void init() {
try {
/**
* TODO 这里的设计是在项目启动时从DB或者缓存中获取注册了允许建立连接的id
* 然后将获取到的id存入内存--idList
* // 从数据库获取idList
* List ids = wsIdsServiceTmp.selectList(null);
*/
// TODO 初始化时将刚注入的对象进行静态保存
// redis = redisTmp;
} catch (Exception e) {
// TODO 项目启动错误信息
}
}
/**
* 连接启动时查询是否有滞留的新邮件提醒
* @param id
*
* @author caoting
* @throws IOException
* @date 2019年2月28日
*/
private void selectOfflineMail(String id) throws IOException {
// 查询缓存中是否存在离线邮件消息
Jedis jedis = redis.getConnection();
try {
List mails = jedis.lrange(Constant.MAIL_OFFLINE+id, 0, -1);
if (CommomUtil.isNotEmpty(mails)) {
for (String mailuuid : mails) {
String mail = jedis.get(mailuuid);
if (StringUtils.isNotEmpty(mail))
sendToUser(Constant.MESSAGE_MAIL + mail, id);
Thread.sleep(1000);
}
// 发送完成从缓存中移除
jedis.del(Constant.MAIL_OFFLINE+id);
}
} catch (InterruptedException e) {
// TODO Auto-generated c