我们在使用Websocket的时候,有时候会用到定时任务+参数传递的问题
在项目中遇到了要使用Websocket服务,其中需求就是,我拿到前端传递某个企业的id并使用websockt建立连接后,再查询该企业的实时信息做一个每5min查询的定时任务,并将查询的值返回给前端。其实听起来不难,但是实际上在使用的时候,遇到了一些大伙可能都会遇到的问题,所以今天将我在websockt上遇到的问题写出来,有更好的方式的大伙可以一起讨论啊。
1,Websocket的配置:
主要目的是为了获取前端传递来的参数,同时要使用ws的服务,必须配置的。例如,我想从Websocket的链接中拿到前端传递的corp_id和equipids的参数,那么此时,我应该在 userProperties这个map集合里放入我想得到的参数,同时,定义FzjkConfigurator 这个类也是为了在后续的配置文件上使用该类,具体配置代码如下:
import org.springframework.context.annotation.Configuration;
import javax.servlet.http.HttpServletRequest;
import javax.websocket.HandshakeResponse;
import javax.websocket.server.HandshakeRequest;
import javax.websocket.server.ServerEndpointConfig;
import java.util.List;
import java.util.Map;
@Configuration
public class FzjkConfigurator extends ServerEndpointConfig.Configurator {
@Override
public void modifyHandshake(ServerEndpointConfig sec, HandshakeRequest request, HandshakeResponse response) {
// 可获取request中的请求参数的集合
Map<String, List<String>> queryParam = request.getParameterMap();
Map<String, Object> userProperties = sec.getUserProperties();
// 以获取get请求中的id参数为例
userProperties.put("corp_id", queryParam.get("corp_id").get(0));
userProperties.put("equipids", queryParam.get("equipids").get(0));
// System.out.println("此时接收到的企业id为:"+queryParam.get("corp_id").get(0));
super.modifyHandshake(sec, request, response);
}
}
1,Websocket的配置:
import com.fasterxml.jackson.databind.ObjectMapper;
import com.sthj.gkjk.wap.config.websocket.FzjkConfigurator;
import com.sthj.gkjk.wap.feign.TdService;
import lombok.SneakyThrows;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.atomic.AtomicInteger;
@ServerEndpoint(value = "/api/websocket/wap/fzjk", configurator = FzjkConfigurator.class)
@Component
public class FzjkSocket extends Endpoint {
private static ConcurrentHashMap<String, String> hashMap = new ConcurrentHashMap<>();
// concurrent包的线程安全Set,用来存放每个客户端对应的Session对象。
private static CopyOnWriteArraySet<Session> sessionSet = new CopyOnWriteArraySet<Session>();
private static final AtomicInteger onlineCount = new AtomicInteger(0);
@PostConstruct
public void init() {
System.out.println("websocket 加载");
}
//最关键的一步,确保可以拿到参数做定时任务
@SneakyThrows
@Override
public void onOpen(Session session, EndpointConfig config) {
sessionSet.add(session);
String corp_id = config.getUserProperties().get("corp_id").toString();
String equipids = config.getUserProperties().get("equipids").toString();
remote = session.getBasicRemote();
//此处我将 session.getId()为了唯一的key值。将 corp_id + "_" + equipids作为value放到hashMap里,这样,我在
//定时任务拿到hashMap的时候,用key值做唯一键处理,这样可以拿到我想要的corp_id和equipids,
//之前,我在这块的时候犯过错, 用的是 hashMap.put(“corp_id”, corp_id + "_" + equipids);
//上述这样会导致,其实corp_id这个key值一直在被刷新,这样会导致定任务里拿到的参数不是我们想要的参数
hashMap.put(session.getId(), corp_id + "_" + equipids);
int cnt = onlineCount.incrementAndGet(); // 在线数加1
}
public static void sendMessage(String json, Session session) throws Exception {
//推送 根据具体的明确业务写
System.out.println(session.getId());
session.getBasicRemote().sendText(json);
}
@OnMessage
public void onMessage(String message) {
/* 从前端接收message消息 */
}
public static ConcurrentHashMap<String, String> getHashMap() {
return hashMap;
}
/**
* 连接建立成功调用的方法
*/
@OnOpen
public void onOpen(Session session) {
sessionSet.add(session);
int cnt = onlineCount.incrementAndGet(); // 在线数加1
System.out.println("有人已建立连接,当前连接总数=" + cnt);
}
//在定时任务里会调用改方法去给前端传递结果
public static void sendBatchInfo(Map<String, String> jsonMap) throws Exception {
for (Session session : sessionSet) {
if (session.isOpen()) {
String json = jsonMap.get(session.getId());
sendMessage(json, session);
}
}
}
@OnClose
public void onClose(Session session) {
sessionSet.remove(session);
hashMap.remove(session.getId());
int cnt = onlineCount.incrementAndGet(); // 在线数加1
System.out.println("有人已断开连接,当前连接数为=" + cnt);
}
@OnError
public void onError(Session session, Throwable error) {
System.out.println("socket连接异常!");
error.printStackTrace();
}
}
定时任务怎么写
我这次写的定时任务是用@Scheduled注解的方式
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
@Component
public class FzjkTask {
@Scheduled(cron = "0 1/5 * * * ?") // 从第1分钟开始,每5分钟一次
public void FzjkTask1() throws Exception {
Map<String, String> resultMap = new HashMap<>();
//
ConcurrentHashMap<String, String> hashMap = FzjkSocket.getHashMap();
for (String s : hashMap.keySet()) {
//这个datas就是对于的session.getId()对于的value值corp_id + "_" + equipids
String[] datas = hashMap.get(s).split("_");
String corp_id=datas[0];
String equids=datas[1]
//接下来就可以用拿到的corp_id和equipids做对应的业务处理
//为了保证我们传递出去的值是我们想要的结果
//我们还是用key和value唯一对应传递
resultMap.put(s, JSON.toJSONString("此处为我们做业务处理的结果"));
}
//最后利用webscoket进行传值
FzjkSocket.sendBatchInfo(resultMap);
}
}