一:Websocket的使用
websocket使用場景:客戶端與服務端建立長連接,通過傳輸協議完成前後端信息實時共享互通。常用場景有:WEB端簡短聊天室功能;後端完成消息推送功能;
注:websocket連接方式與正常的HTTP連接不同,需要特殊的路徑和前端代碼完成
- maven依賴包:
<!--websocket--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-websocket</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-messaging</artifactId> <version>${spring.version}</version> </dependency> #spring版本: <spring.version>4.3.7.RELEASE</spring.version>
-
springboot中使用websocket
通過核心註解@ServerEndpoint(value="/webSocket")完成前後端的連接完成
- 首先完成ServerEndpointExporter的配置類BEAN:注入ServerEndpointExporter,這個bean會自動註冊使用了@ServerEndpoint註解聲明的Websocket endpoint。要注意,如果使用獨立的servlet容器,而不是直接使用springboot的內置容器,就不要注入ServerEndpointExporter,因爲它將由容器自己提供和管理。
@Configuration
public class WebSocketConfig extends ServerEndpointConfig.Configurator{
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
}
- 然後完成websocket的連接服務類,其中包含了連接的地址配置、連接成功、失敗、單發消息、羣發消息的方法配置,廢話不多說,直接上代碼:
package com.kangce.task.configuration.WebSocketConfig;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.kangce.task.configuration.log.GwsLogger;
import com.kangce.task.entity.PO.BaseMessagePO;
import com.kangce.task.enums.MessageReadFlagEnum;
import com.kangce.task.repository.master.BaseMessageRepositoryMaster;
import com.kangce.task.service.Message.MessageService;
import com.kangce.task.tools.PrimaryKeyGeneratorTool;
import org.springframework.beans.factory.annotation.Autowired;
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.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
/**
* @author hengtao.wu
* @Date 2019/6/20 15:08
**/
//客戶端連接websocket接口地址
@ServerEndpoint(value = "/websocket/{userId}")
@Component
public class WebSocketServer {
/**
*1. 以下注入的bean是一些JDBC的查詢保存類
* 2. 該消息的使用,是通過消息發送的配置表,然後根據配置完成什麼時候推送消息,直接在接口中調用發送消息的方法
* 3. 另外數據庫中保存了所有的消息數據,存爲消息表,表中可以根據需求完成消息的是否發送,是否已讀的參數
* 4. 此處做了消息的已讀未讀,因爲接口中調用此類的消息發送方法是針對在線的用戶完成消息發送,對於未在線的用戶
* 實行用戶登錄之後,對未讀消息進行推送的方式完成。
* 5. 通過一個map來存放所有與服務器建立連接的客戶端,key值用userId完成。
* 6. websocket的連接地址中可以接受參數,通過/{param}表示,方法中通過@PathParam(value="userId")獲取路徑中的參數
*/
private static MessageService messageService;
@Autowired
public void setMessageService(MessageService messageService){
this.messageService = messageService;
}
private static BaseMessageRepositoryMaster baseMessageRepositoryMaster;
@Autowired
public void setBaseMessageRepositoryMaster(BaseMessageRepositoryMaster baseMessageRepositoryMaster){
this.baseMessageRepositoryMaster = baseMessageRepositoryMaster;
}
/**
* 靜態變量,用來記錄當前在線連接數。應該把它設計成線程安全的。
*/
private static int onlineCount = 0;
/**
* 與某個客戶端的連接會話,需要通過它來給客戶端發送數據
*/
private static Map<String,Session> webSocketMap = new HashMap<>();
/**
* 連接建立成功調用的方法*/
@OnOpen
public void onOpen(@PathParam(value="userId") String userId, Session session) {
//將當前登錄對話,以userId爲key值存入
webSocketMap.put(userId,session);
addOnlineCount(); //在線數加1
GwsLogger.info("有新連接加入!當前在線人數爲={},", getOnlineCount());
GwsLogger.info("map長度:" + webSocketMap.size());
//推送 未發送 消息
messageService.sendUnsentMsg(userId);
}
/**
* 連接關閉調用的方法*/
@OnClose
public void onClose(@PathParam(value="userId") String userId) {
webSocketMap.remove(userId);
subOnlineCount(); //在線數減1
GwsLogger.info("有一連接關閉!當前在線人數爲={}", getOnlineCount());
GwsLogger.info("map長度:" + webSocketMap.size());
}
/**
* @param
* @param error
*/
@OnError
public void onError(Throwable error) {
GwsLogger.error("異常", error.getMessage());
}
/**
* 發送未讀消息方法
*
* @param
*/
public void sendUnsentMessage(BaseMessagePO baseMessagePO){
Date date = new Date();
Session session = webSocketMap.get(baseMessagePO.getReceiveUserId());
try {
if(null != session){
JSONArray jsonArray = new JSONArray();
jsonArray = getJSON(baseMessagePO);
session.getBasicRemote().sendText(jsonArray.toJSONString());
}
}catch (Exception e) {
GwsLogger.error("sendMessage異常:", e.getMessage());
}
}
/**
*@Description: 接口調用發送消息
*@Param:
*@return:
*@Author: weishi.wang
*@date: 2019/6/24
*/
public void sendMessage(BaseMessagePO baseMessagePO){
Date date = new Date();
Session session = webSocketMap.get(baseMessagePO.getReceiveUserId());
try {
baseMessagePO.setMessageId(PrimaryKeyGeneratorTool.generateKey32());
baseMessagePO.setSendFlag(MessageReadFlagEnum.UN_READ.getVal());
if(null != session){
JSONArray jsonArray = new JSONArray();
jsonArray = getJSON(baseMessagePO);
session.getBasicRemote().sendText(jsonArray.toJSONString());
}
baseMessagePO.setCreateTime(date);
baseMessagePO.setUpdateTime(date);
baseMessageRepositoryMaster.save(baseMessagePO);
}catch (Exception e) {
GwsLogger.error("sendMessage異常:", e.getMessage());
}
}
/**
* 羣發消息
* @param msg
*/
public void sendToAllUsersMessage(String msg) {
Set<String> set = webSocketMap.keySet();
for (String key : set) {
Session session = webSocketMap.get(key);
try {
session.getBasicRemote().sendText(msg);
} catch (IOException e) {
GwsLogger.error("sendToAllUsersMessage異常:", e.getMessage());
}
}
}
/**
* 羣發某些人消息
* @param userIds
* @param baseMessagePO
*/
public void sendToSomeUsersMessage(String userIds, BaseMessagePO baseMessagePO) {
String[] userIDs = userIds.split(",");
for (int i = 0; i < userIDs.length; i++) {
Session session = webSocketMap.get(userIDs[i]);
baseMessagePO.setSendFlag(MessageReadFlagEnum.UN_READ.getVal());
try {
if(null != session) {
JSONArray jsonArray = new JSONArray();
jsonArray = getJSON(baseMessagePO);
session.getBasicRemote().sendText(jsonArray.toJSONString());
}
} catch (IOException e) {
GwsLogger.error("sendToAllUsersMessage異常:", e.getMessage());
}
}
}
public static synchronized int getOnlineCount() {
return onlineCount;
}
private JSONArray getJSON(BaseMessagePO baseMessagePO) {
JSONArray jsonArray = new JSONArray();
JSONObject jsonObject = new JSONObject();
jsonObject.put("senderUserId", baseMessagePO.getSenderUserId());
jsonObject.put("senderUserName", baseMessagePO.getSenderUserName());
jsonObject.put("messageTitle", baseMessagePO.getMessageTitle());
jsonObject.put("messageContent", baseMessagePO.getMessageContent());
jsonObject.put("projectId", baseMessagePO.getProjectId());
jsonObject.put("projectName", baseMessagePO.getProjectName());
jsonObject.put("taskId", baseMessagePO.getTaskId());
jsonObject.put("taskName", baseMessagePO.getTaskName());
jsonObject.put("createTime", baseMessagePO.getCreateTime());
jsonArray.add(jsonObject);
return jsonArray;
}
public synchronized void addOnlineCount() {
WebSocketServer.onlineCount++;
}
public synchronized void subOnlineCount() {
WebSocketServer.onlineCount--;
}
}
發送消息,已經創建連接成功中的業務邏輯可以根據實際的需求來完成修改。
創建完成後,可以通過http://coolaf.com/tool/chattest這個在線測試工具連接本地的websocket進行測試是否成功。
成功之後,就可以在需要給前端發送消息的業務邏輯中盡情的使用發送消息的方法了,可以通過直接注入該WebSocketServer 類完成調用方法。
二:spring定時調度類:quartz的使用
- pom依賴:
<!--spring定時調度類:quartz依賴-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<!--去除log back的依賴重複-->
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
2. 直接創建定時調度任務類:
@Component
public class SchedulerTask {
/**
* @Description 設置每天0點01分執行一次
*此計劃任務每天執行,推送當項目爲計劃結束日期以及當項目爲臨近點時
* 以及任務/里程碑的當天超時提醒推送
**/
/*@Scheduled(cron = "0 1 0 * * ?")*/
@Scheduled(cron = "0 05 13 * * ?")
private void proErveyDay(){
/**
業務邏輯
*/
}
}
此時,一個定時任務就創建了,啓動服務,該方法就會按照cron表達式完成任務的自動運行。就是這麼簡單。