1.推送的信息全部都是模拟的所以不存在ORM的操作
要模拟推送的实体类
思路是创建连接时就创建线程,并使用定时线程池不断像session中进行推送信息
package com.hua.queerdemo.domain.response;
public class UserInfoSendResponse {
private Long userId;
private String name;
private String age;
private String address;
private Integer fraction;
public Integer getFraction() {
return fraction;
}
public void setFraction(Integer fraction) {
this.fraction = fraction;
}
public Long getUserId() {
return userId;
}
public void setUserId(Long userId) {
this.userId = userId;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
websocket配置类
package com.hua.queerdemo.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
@Configuration
public class WebSocketConfig {
//这个bean的注册,用于扫描带有@ServerEndpoint的注解成为websocket ,如果你使用外置的tomcat就不需要该配置文件
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
}
websocket类
package com.hua.queerdemo.utils;
import com.hua.queerdemo.service.WebSocketService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledFuture;
@Slf4j
@ServerEndpoint(value = "/websocket/{id}/{modId}")
@Component
public class WebSocketUtils {
private static WebSocketService webSocketService;
@Autowired
public void setWebSocketService(WebSocketService webSocketService) {
WebSocketUtils.webSocketService = webSocketService;
}
//连接用户
private Session session;
//用户ID
private Long id;
//存放每个用户的websocketUtils
private static ConcurrentHashMap<Long,WebSocketUtils> websocketHashMap = new ConcurrentHashMap<Long,WebSocketUtils>();
@OnOpen
public synchronized void onOpen(Session session, @PathParam("id") Long id,@PathParam("modId") Long modId){
log.debug("创建连接");
System.out.println("创建连接");
this.session=session;
this.id=id;
websocketHashMap.put(this.id,this);
log.debug(session.getId()+"连接成功");
//线程执行
webSocketService.get(session,this.id,modId);
}
@OnClose
public void onClose(@PathParam("id") Long id){
System.out.println("清除线程");
ConcurrentHashMap<Long, ScheduledFuture> scheduledFutureConcurrentHashMap = webSocketService.getScheduledFutureConcurrentHashMap();
//清除用户的登录记录
if (null!=websocketHashMap.get(id)){
websocketHashMap.remove(id);
}
//结束线程 GC回收机制会自动清理资源
if (null!=scheduledFutureConcurrentHashMap.get(this.id)){
scheduledFutureConcurrentHashMap.get(this.id).cancel(false);
scheduledFutureConcurrentHashMap.remove(this.id);
}
}
@OnMessage
public void onMessage(String message,Session session){
System.out.println("收到的消息");
}
@OnError
public void onError(Session session, Throwable error){
System.out.println("发生错误");
System.out.println("清除线程");
ConcurrentHashMap<Long, ScheduledFuture> scheduledFutureConcurrentHashMap = webSocketService.getScheduledFutureConcurrentHashMap();
//清除用户的登录记录
if (null!=websocketHashMap.get(id)){
websocketHashMap.remove(id);
}
//结束线程 GC回收机制会自动清理资源
if (null!=scheduledFutureConcurrentHashMap.get(this.id)){
scheduledFutureConcurrentHashMap.get(this.id).cancel(false);
scheduledFutureConcurrentHashMap.remove(this.id);
}
}
public void sendMessage(String message) throws IOException {
this.session.getBasicRemote().sendText(message);
}
}
webSocket业务类
package com.hua.queerdemo.service.impl;
import com.alibaba.fastjson.JSON;
import com.hua.queerdemo.domain.response.UserInfoSendResponse;
import com.hua.queerdemo.service.WebSocketService;
import lombok.extern.slf4j.Slf4j;
import org.quartz.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.scheduling.annotation.*;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.stereotype.Service;
import javax.websocket.Session;
import java.io.IOException;
import java.util.Random;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledFuture;
@Slf4j
@EnableScheduling
@EnableAsync(proxyTargetClass = true)
@Service
public class WebSocketServiceImpl implements WebSocketService, ApplicationRunner {
@Autowired
private ThreadPoolTaskScheduler threadPoolTaskScheduler;
@Autowired
Scheduler scheduled;
UserInfoSendResponse userInfoOne = new UserInfoSendResponse();
UserInfoSendResponse userInfoTwo = new UserInfoSendResponse();
UserInfoSendResponse userInfoThree = new UserInfoSendResponse();
//初始化三条模拟数据
@Override
public void run(ApplicationArguments args) throws Exception {
userInfoOne.setUserId(1L);
userInfoOne.setName("张山");
userInfoOne.setAge("18");
userInfoOne.setAddress("北京西安");
userInfoOne.setFraction(20);
userInfoTwo.setUserId(2L);
userInfoTwo.setName("小明");
userInfoTwo.setAge("18");
userInfoTwo.setAddress("火葬场");
userInfoOne.setFraction(30);
userInfoThree.setUserId(3L);
userInfoThree.setName("马鞍山");
userInfoThree.setAge("18");
userInfoThree.setAddress("龙归");
userInfoOne.setFraction(40);
}
//模拟三条数据随机更新数值
@Scheduled(cron = "0/1 * * * * ?")
public void updateInfo(){
userInfoOne.setFraction((int) (Math.random()*100+1));
userInfoTwo.setFraction((int) (Math.random()*100+1));
userInfoThree.setFraction((int) (Math.random()*100+1));
}
private static ConcurrentHashMap<Long, ScheduledFuture> scheduledFutureConcurrentHashMap = new ConcurrentHashMap<Long,ScheduledFuture>();
Integer test = 0 ;
public ConcurrentHashMap<Long, ScheduledFuture> getScheduledFutureConcurrentHashMap() {
return scheduledFutureConcurrentHashMap;
}
@Async("asyncServiceExecutor")
public void get(Session session, Long id,Long modId){
System.out.println("开启线程"+Thread.currentThread().getName());
String cron = "0/1 * * * * ?";
//在该线程下执行定时任务
threadPoolTaskScheduler.setPoolSize(1);
//判断之前是否是相同的用户,是则清除之前的线程
if (null!=scheduledFutureConcurrentHashMap.get(id)) {
System.out.println("清理之前存在的线程");
scheduledFutureConcurrentHashMap.get(id).cancel(false);
scheduledFutureConcurrentHashMap.remove(id);
}
Thread thread = new Thread(() -> {
System.out.println("id:" + id + "的线程");
System.out.println("查询对应数据" + Thread.currentThread().getName());
String userInfoJson = null;
switch (modId.intValue()){
case 1: userInfoJson = JSON.toJSONString(userInfoOne); break;
case 2: userInfoJson = JSON.toJSONString(userInfoTwo); break;
case 3: userInfoJson = JSON.toJSONString(userInfoThree); break;
}
try {
session.getBasicRemote().sendText(userInfoJson);
} catch (IOException e) {
e.printStackTrace();
}
});
ScheduledFuture<?> schedule = threadPoolTaskScheduler.schedule(thread, new CronTrigger(cron));
scheduledFutureConcurrentHashMap.put(id,schedule);
}
}
线程池配置信息
package com.hua.queerdemo.config;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;
@Configuration
@EnableAsync
@Slf4j
public class ExecutorConfig {
@Value("${async.executor.thread.core_pool_size}")
private int corePoolSize;
@Value("${async.executor.thread.max_pool_size}")
private int maxPoolSize;
@Value("${async.executor.thread.queue_capacity}")
private int queueCapacity;
@Value("${async.executor.thread.name.prefix}")
private String namePrefix;
@Bean(name = "taskScheduler")
public ThreadPoolTaskScheduler taskScheduler(){
ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
scheduler.setPoolSize(10);
scheduler.setThreadNamePrefix("task-");
scheduler.setAwaitTerminationSeconds(600);
scheduler.setWaitForTasksToCompleteOnShutdown(true);
return scheduler;
}
@Bean(name = "asyncServiceExecutor")
public Executor asyncServiceExecutor() {
log.info("start asyncServiceExecutor");
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
//配置核心线程数
executor.setCorePoolSize(corePoolSize);
//配置最大线程数
executor.setMaxPoolSize(maxPoolSize);
//配置队列大小
executor.setQueueCapacity(queueCapacity);
//配置线程池中的线程的名称前缀
executor.setThreadNamePrefix(namePrefix);
// rejection-policy:当pool已经达到max size的时候,如何处理新任务
// CALLER_RUNS:不在新线程中执行任务,而是有调用者所在的线程来执行
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
//执行初始化
executor.initialize();
return executor;
}
}
properties
# 异步线程配置
# 配置核心线程数
async.executor.thread.core_pool_size = 5
# 配置最大线程数
async.executor.thread.max_pool_size = 5
# 配置队列大小
async.executor.thread.queue_capacity = 99999
# 配置线程池中的线程的名称前缀
async.executor.thread.name.prefix = wulababa
controller
package com.hua.queerdemo.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
@Controller
public class UserController {
@RequestMapping("/toLogin")
public String toLogin(){
return "/login";
}
@RequestMapping("/mLogin")
public String login(Long modId,Long userId,String username,String password, Model model){
model.addAttribute("userId",userId);
model.addAttribute("modId",modId);
return "/websocket";
}
}
login页面
登录页中的用户名和密码实际是无效数据,真正做出登录判断的是用户ID
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<script src="/webjars/jquery/3.4.1/jquery.js"></script>
<body>
<form action="/mLogin" method="post">
选择要登录的用户ID:
<select name="userId" id="userId">
<option>1</option>
<option>2</option>
<option>3</option>
<option>4</option>
</select>
<br>
选择要实时观看的modId:
<select name="modId" id="modId">
<option>1</option>
<option>2</option>
<option>3</option>
</select>
</div>
<br>
<!-- <input id="userId" name="userId">-->
用户名:<input id="username" name="username" >
密码:<input id="password" name="password">
<input type="submit" value="登录">
</form>
</body>
</html>
websocket展示页面
<!DOCTYPE html>
<html lang="en">
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<script src="/webjars/jquery/3.4.1/jquery.js"></script>
<script>
window.onload=function(){
if ('WebSocket' in window) {
console.log("浏览器支持Websocket")
var id = $("#id").text();
var modId = $("#modId").text();
alert(id)
websocket = new WebSocket("ws://127.0.0.1:8080/websocket/"+id+"/"+modId);
} else {
console.log('当前浏览器 Not support websocket')
}
//连接发生错误的回调方法
websocket.onerror = function() {
alert("WebSocket连接发生错误")
};
//连接成功建立的回调方法
websocket.onopen = function() {
console.log("WebSocket连接成功")
}
//接收到消息的回调方法
websocket.onmessage = function(event) {
var parse = JSON.parse(event.data);
$("#userId").text(parse.userId);
$("#name").text(parse.name)
$("#age").text(parse.age)
$("#address").text(parse.address)
$("#fraction").text(parse.fraction)
var size = parse.size;
parse.facingMode
// alert("这是后台推送的消息:"+event.data);
}
//连接关闭的回调方法
websocket.onclose = function() {
}
//监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。
window.onbeforeunload = function() {
closeWebSocket();
}
//关闭WebSocket连接
function closeWebSocket() {
websocket.close();
}
}
</script>
<body>
<div>欢迎登录,测试Demo</div>
目前的用户是:<div id="id" th:text="${userId}"></div>
目前的modId是:<div id="modId" th:text="${modId}"></div>
目前的游戏类型是:<div th:text="${modId}"></div>
<table border="1px solid black">
<tr>
<th>用户ID</th>
<th>名称</th>
<th>年龄</th>
<th>地址</th>
<th>成绩</th>
</tr>
<tr>
<td id="userId">1</td>
<td id="name">1</td>
<td id="age">1</td>
<td id="address">1</td>
<td id="fraction">1</td>
</tr>
</table>
</body>
</html>
/**
* 注释
*/