1. 程式人生 > >使用spring boot +WebSocket實現(後臺主動)訊息推送

使用spring boot +WebSocket實現(後臺主動)訊息推送

前言:使用此webscoket務必確保生產環境能相容/支援!使用此webscoket務必確保生產環境能相容/支援!使用此webscoket務必確保生產環境能相容/支援!主要是tomcat的相容與支援。

有個需求:APP使用者產生某個操作,需要讓後臺管理系統部分人員感知(表現為一個頁面訊息)。

最早版本是後臺管理系統輪訓,每隔一段時間輪訓一次,由於訊息重要,每隔幾秒就查一次。這樣做明顯很不雅!會消耗大量資源,並且大部分請求是沒有用的(查不到資料進來),很藍瘦。

後來,想著用訊息推送的方式來處理這個邏輯。使用者在app產生了目標操作,即產生一個訊息,推送給後臺管理系統的對應使用者。

然後我就找各種資料,一開始同事推薦dwz,後來發現不太適用於目前的專案(也許能實現只是我不知道如何實現)。

後來瞭解到WebSocket,網上看了很多文件都是類似聊天室的場景,有些不同。在此,我主要側重介紹下 伺服器主動推送,由服務端來觸發。

WebSocket 主要能實現的場景:

1、網頁聊天室

2、伺服器訊息實時通知

WebSocket 使用方法應該有很多,在次介紹下使用  tomcat8+h5 環境下的實現。

ps:我自己的測試環境是tomcat7這樣寫是不行的。wang115032337《https://blog.csdn.net/wang115032337》這位朋友在他的環境下,tomcat7/8都可以用本文章的寫法,只不過需要去除WebSocketConfig類(有文章表示tomcat7和8對websocket的支援是不同的,本人未深入瞭解)

話不多說,直接上程式碼,想深入瞭解WebSocket 的請查閱相關介紹。

1.pom

	<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-websocket</artifactId>
        </dependency>
2.使用@ServerEndpoint創立websocket endpoint( wang115032337這位朋友在他的環境下加入@ServerEndpoint類會報錯,直接刪除了仍可用
@Configuration
public class WebSocketConfig {
    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }

}
3.具體實現類可自己選擇url要不要帶引數
package com.star.manager.service;

import java.io.IOException;
import java.util.concurrent.CopyOnWriteArraySet;

import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;

import lombok.extern.slf4j.Slf4j;

import org.springframework.stereotype.Component;
@Slf4j
//@ServerEndpoint("/websocket/{user}")
@ServerEndpoint(value = "/websocket")
@Component
public class WebSocketServer {
    //靜態變數,用來記錄當前線上連線數。應該把它設計成執行緒安全的。
    private static int onlineCount = 0;
    //concurrent包的執行緒安全Set,用來存放每個客戶端對應的MyWebSocket物件。
   private static CopyOnWriteArraySet<WebSocketServer> webSocketSet = new CopyOnWriteArraySet<WebSocketServer>();

    //與某個客戶端的連線會話,需要通過它來給客戶端傳送資料
    private Session session;

    /**
     * 連線建立成功呼叫的方法*/
    @OnOpen
    public void onOpen(Session session) {
        this.session = session;
        webSocketSet.add(this);     //加入set中
        addOnlineCount();           //線上數加1
        log.info("有新連線加入!當前線上人數為" + getOnlineCount());
        try {
        	 sendMessage("連線成功");
        } catch (IOException e) {
            log.error("websocket IO異常");
        }
    }
	//	//連線開啟時執行
	//	@OnOpen
	//	public void onOpen(@PathParam("user") String user, Session session) {
	//		currentUser = user;
	//		System.out.println("Connected ... " + session.getId());
	//	}

    /**
     * 連線關閉呼叫的方法
     */
    @OnClose
    public void onClose() {
        webSocketSet.remove(this);  //從set中刪除
        subOnlineCount();           //線上數減1
        log.info("有一連線關閉!當前線上人數為" + getOnlineCount());
    }

    /**
     * 收到客戶端訊息後呼叫的方法
     *
     * @param message 客戶端傳送過來的訊息*/
    @OnMessage
    public void onMessage(String message, Session session) {
    	log.info("來自客戶端的訊息:" + message);

        //群發訊息
        for (WebSocketServer item : webSocketSet) {
            try {
                item.sendMessage(message);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

	/**
	 * 
	 * @param session
	 * @param error
	 */
    @OnError
    public void onError(Session session, Throwable error) {
        log.error("發生錯誤");
        error.printStackTrace();
    }


    public void sendMessage(String message) throws IOException {
        this.session.getBasicRemote().sendText(message);
    }


    /**
     * 群發自定義訊息
     * */
    public static void sendInfo(String message) throws IOException {
    	log.info(message);
        for (WebSocketServer item : webSocketSet) {
            try {
                item.sendMessage(message);
            } catch (IOException e) {
                continue;
            }
        }
    }

    public static synchronized int getOnlineCount() {
        return onlineCount;
    }

    public static synchronized void addOnlineCount() {
        WebSocketServer.onlineCount++;
    }

    public static synchronized void subOnlineCount() {
        WebSocketServer.onlineCount--;
    }
}

產生一個訊息:產生訊息場景有多種,http(s),定時任務,mq等,這貼一個httpq請求的controller程式碼

	 @RequestMapping(value="/pushVideoListToWeb",method=RequestMethod.POST,consumes = "application/json")
	 public @ResponseBody Map<String,Object> pushVideoListToWeb(@RequestBody Map<String,Object> param) {
		 Map<String,Object> result =new HashMap<String,Object>();
		 
		 try {
			 WebSocketServer.sendInfo("有新客戶呼入,sltAccountId:"+CommonUtils.getValue(param, "sltAccountId"));
			 result.put("operationResult", true);
		 }catch (IOException e) {
			 result.put("operationResult", true);
		 }
		 return result;
	 }


重要的地方我都加粗了,主要是這段,使用這個方法,可以實現伺服器主動推送。
    public void sendMessage(String message) throws IOException {
        this.session.getBasicRemote().sendText(message);
    }

4.js(html就不寫了,隨便找個能觸發這個js的就可以)

//socket = new WebSocket("ws://localhost:9094/starManager/websocket/張三");
		var socket;
		if(typeof(WebSocket) == "undefined") {
			console.log("您的瀏覽器不支援WebSocket");
		}else{
			console.log("您的瀏覽器支援WebSocket");
			
			//實現化WebSocket物件,指定要連線的伺服器地址與埠  建立連線
			//socket = new WebSocket("ws://localhost:9094/starManager/websocket/張三")
			socket = new WebSocket("ws://localhost:9094/starManager/websocket");
			//開啟事件
			socket.onopen = function() {
				console.log("Socket 已開啟");
				//socket.send("這是來自客戶端的訊息" + location.href + new Date());
			};
			//獲得訊息事件
			socket.onmessage = function(msg) {
				console.log(msg.data);
				//發現訊息進入    調後臺獲取
				getCallingList();
			};
			//關閉事件
			socket.onclose = function() {
				console.log("Socket已關閉");
			};
			//發生了錯誤事件
			socket.onerror = function() {
				alert("Socket發生了錯誤");
			}
			 $(window).unload(function(){
				  socket.close();
				});

//                            		$("#btnSend").click(function() {
//                            			socket.send("這是來自客戶端的訊息" + location.href + new Date());
//                            		});
//
//                            		$("#btnClose").click(function() {
//                            			socket.close();
//                            		});
		}
                                

簡單說說:

通過前端程式碼 

socket = new WebSocket("ws://localhost:9094/starManager/websocket");

其中,starManager是工程名,/webscoket是訪問路徑名

建立連線,前端呼叫scoket.open() 會使後臺在靜態成員變數webSocketSet裡面增加一個元素,相當於一個快取。後臺服務呼叫sendMessage

(指定某個使用者,定向)或sendInfo(遍歷webSocketSet逐個傳送,類似群發)方法,即可向已登入的客戶端推送訊息。

程式碼就這麼多。我的用這些程式碼就跑的起來。做的時候出現過頁面報404等錯誤,如果也是spring boot+h5,仔細核對下和我程式碼有無區別,加配置 路徑是有ok,問題應該不大。

如果你恰好也有可以用WebSocket實現的類似場景,希望對你有幫助。如有寫的不對或不夠好的地方,歡迎指正。

相關推薦

使用spring boot +WebSocket實現後臺主動訊息

前言:使用此webscoket務必確保生產環境能相容/支援!使用此webscoket務必確保生產環境能相容/支援!使用此webscoket務必確保生產環境能相容/支援!主要是tomcat的相容與支援。有個需求:APP使用者產生某個操作,需要讓後臺管理系統部分人員感知(表現為一

resin4.0.44+websocket 實現私信功能服務端訊息

最近專案開發中,碰到一個新的開發需求——私信功能。 專案要求:類似微博中傳送私信功能,給對方傳送一條私信訊息,如果對方線上就立馬接受到訊息提示,並顯示到頁面上。如果對方不線上,則下次登入以後,顯示訊息提示。 技術選擇:websocket也是目前比較流行的接收

Spring Boot入門系列十八整合mybatis,使用註解的方式實現增刪改查

之前介紹了Spring Boot 整合mybatis 使用xml配置的方式實現增刪改查,還介紹了自定義mapper 實現複雜多表關聯查詢。雖然目前 mybatis 使用xml 配置的方式 已經極大減輕了配置的複雜度,支援 generator 外掛 根據表結構自動生成實體類、配置檔案和dao層程式碼,減輕很大一

Spring Boot入門系列十七整合Mybatis,建立自定義mapper 實現多表關聯查詢!

之前講了Springboot整合Mybatis,介紹瞭如何自動生成pojo實體類、mapper類和對應的mapper.xml 檔案,並實現最基本的增刪改查功能。mybatis 外掛自動生成的mapper 實現了大部分基本、通用的方法,如:insert、update、delete、select 等大概20個左右

Spring Boot入門系列十九整合mybatis,使用註解實現動態Sql、引數傳遞等常用操作!

前面介紹了Spring Boot 整合mybatis 使用註解的方式實現資料庫操作,介紹瞭如何自動生成註解版的mapper 和pojo類。 接下來介紹使用mybatis 常用註解以及如何傳引數等資料庫操作中的常用操作。 其實,mybatis 註解方式 和 XML配置方式兩者的使用基本上相同,只有在構建 SQL

spring-boot-starter-actuator健康監控配置和使用

frame maven git 追蹤 包括 屬性 per dump zookeepe 在生產環境中,需要實時或定期監控服務的可用性。Spring Boot的actuator(健康監控)功能提供了很多監控所需的接口,可以對應用系統進行配置查看、相關功能統計等。 集成:

Java框架spring Boot學習筆記十四:log4j介紹

inf alt 技術分享 images 使用 image 詳細 配置文件 -128 功能 日誌功能,通過log4j可以看到程序運行過程的詳細信息。 使用 導入log4j的jar包 復制log4j的配置文件,復制到src下面         3.設置日誌級別    

spring boot系統學習知識點筆記

調試接口 .com tco map aid 結果 ota http 而且 一、http的註解配置   1、@SpringBootAplication=@SpringBootConfiguration(其實就是個@Configuration)+@EnableAutoConfi

spring boot 郵件傳送帶附件

首先開啟QQ郵箱的POP.SMTP伺服器,獲取授權碼。 設定-->賬戶-->POP3/IMAP/SMTP/Exchange/CardDAV/CalDAV服務   pom.xml需要載入三個jar,可以在這個網站裡下載:https://mvnrepository.com/

spring boot 整合 freemark簡單結構

一、建立Mean 專案  這個就不多說了 二、我的spring boot demo 結構 如下: 三、主要的配置檔案(application.properties ;pom.xml ; log4j2.xml) (1、)application.properties 檔案

Spring Boot 自動配置auto-configurtion 揭祕

本章,我們為你揭祕Spring Boot自動配置(Auto Configuration)執行機制,談到auto-configuration,肯定離不開@EnableAutoConfiguration註解。 package org.springframework.

Spring Boot 學習筆記十二——單元測試

依賴關係 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test&

Spring boot Mybatis 整合完整版

個人開源專案 springboot+mybatis+thymeleaf+docker構建的個人站點開源專案(集成了個人主頁、個人作品、個人部落格) 推薦開源專案 開源的springboot介面文件元件swagger2 更多幹貨 SpringBoot

spring-boot前世今生簡單介紹

序 本文主要講述spring boot的由來,即其它誕生的背景,初衷,現狀,及對未來的展望。 背景 在很早的年代,J2EE還是java企業級應用的王者規範,EJB風行其道。後來有一個叫Rod Johnson的音樂學博士,寫了本《Expert One on one J

spring boot整合hessian十二

1.首先新增hessian依賴 <dependency> <groupId>com.caucho</groupId> <artifactId>hessian</artifact

Spring Boot配置neo4j簡單版

圖資料庫的開發已經變得越來越流行,與springBoot結合也是很正常,在很早前就已經發布了相關Lib,但是當前網上的Spring Boot整合neo4j都極其複雜,也導致了本人在開發過程中耗費很長時間,顧寫下這個文章幫助大眾理順neo4j整合。只需要簡單六步,順序按照編寫

Spring Boot自動配置Auto-Configuration,@EnableAutoConfiguration,Spring Beans和依賴注入

自動配置(Auto-Configuration) 自動配置(auto-configuration)是Spring Boot最重要的特性之一,因為該特性會根據應用的classpath(這個主要是根據maven pom.xml決定),annotations和其他的

使用Intellij中的Spring Initializr來快速構建Spring Boot/Cloud工程十五

在之前的所有Spring Boot和Spring Cloud相關博文中,都會涉及Spring Boot工程的建立。而建立的方式多種多樣,我們可以通過Maven來手工構建或是通過腳手架等方式快速搭建,也可以通過《Spring Boot快速入門》一文中提到的SPRING INITIALIZR頁面工具來建立,相信每

spring boot + webSocket 實現簡單會話與線上人數統計的demo

webSocket推送是常用於生產專案的模組,在我們部門做的一個彙報演示的demo中遇到了webSocket的一些問題。 自己下來看看了看webSocket的東西,結合spring boot 做了一個簡單的demo; 介紹的部分大家可以參考眾多的帖子,度娘 http://w

Spring boot +Mybatis 實戰完整版

個人開源專案 更多幹貨 正題 本專案使用的環境: 開發工具:Intellij IDEA 2017.1.3 springboot: 1.5.6 jdk:1.8.0_161 maven:3.3.9 額外功能 PageHelper 分頁外掛 myb