SpringBoot+Websocket 打造实时聊天、实时监听JVM负载

WebSocket

介绍

  WebSocket协议是基于TCP的一种新的网络协议。它实现了浏览器与服务器全双工(full-duplex)通信——允许服务器主动发送信息给客户端

使用场景

  • 弹幕
  • 网页聊天系统
  • 实时监控
  • 股票行情推送​

单播

  点对点,私信私聊方式

广播

  游戏公告,发布订阅

多播(也叫组播)

  多人聊天,发布订阅

socketjs

介绍

  1. 是一个浏览器JavaScript库,提供了一个类似WebSocket的对象。
  2. 提供了一个连贯的跨浏览器的JavaScriptAPI,在浏览器和Web服务器之间创建了一个低延迟,全双工,跨域的通信通道
  3. 在底层SockJS首先尝试使用本地WebSocket。如果失败了,它可以使用各种浏览器特定的传输协议,并通过类似WebSocket的抽象方式呈现它们
  4. SockJS旨在适用于所有现代浏览器和不支持WebSocket协议的环境。

git地址

https://github.com/sockjs/sockjs-client

stompjs

介绍

  STOMP Simple (or Streaming) Text Orientated Messaging Protocol 它定义了可互操作的连线格式,以便任何可用的STOMP客户端都可以与任何STOMP消息代理进行通信,以在语言和平台之间提供简单而广泛的消息互操作性(归纳一句话:是一个简单的面向文本的消息传递协议。)

官网

http://jmesnil.net/stomp-websocket/doc/

https://stomp-js.github.io/stomp-websocket/codo/class/Client.html

 

SpringBoot第一个案例(游戏公告通知)

项目结构

添加依赖

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-websocket</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.24</version>
        </dependency>

WebSocket配置类

package com.ybchen.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;

/**
 * websocket配置类
 *
 * @author: chenyanbin 2022-07-02 21:21
 */
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
    /**
     * 注册端点,发布或者订阅消息的时候,需要连接此端点
     *
     * @param registry
     */
    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry
                //添加端点
                .addEndpoint("/endpoint-websocket")
                //设置允许访问的源(所有)
                .setAllowedOrigins("*")
                //开始sockjs支持
                .withSockJS();
    }

    @Override
    public void configureMessageBroker(MessageBrokerRegistry registry) {
        //设置消息代理(服务端推送给客户端的路径前缀)
        registry.enableSimpleBroker("/topic", "/chat");
        //设置消息发布订阅的头(客户端发送数据给服务器的一个前缀)
        registry.setApplicationDestinationPrefixes("/app");
    }
}

Model

package com.ybchen.model;

import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;

import java.util.Date;

/**
 * @author: chenyanbin 2022-07-02 21:14
 */
@Data
public class InMessage {
    /**
     * 从哪里来
     */
    private String from;

    /**
     * 到哪里去
     */
    private String to;

    /**
     * 内容
     */
    private String content;

    /**
     * 时间
     */
    @JsonFormat(locale = "zh", timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
    private Date time;
}
package com.ybchen.model;

import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;

import java.util.Date;

/**
 * @author: chenyanbin 2022-07-02 21:16
 */
@Data
public class OutMessage {

    /**
     * 从哪里来
     */
    private String from;

    /**
     * 内容
     */
    private String content;

    /**
     * 时间
     */
    @JsonFormat(locale = "zh", timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
    private Date time;
}

Controller

package com.ybchen.controller.v1;

import com.ybchen.model.InMessage;
import com.ybchen.model.OutMessage;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.handler.annotation.SendTo;
import org.springframework.stereotype.Controller;

/**
 * 游戏公告-控制层
 *
 * @author: chenyanbin 2022-07-02 21:17
 */
@Controller
@Slf4j
public class GameInfoController {

    //消息路由,别人发送的消息会到这里来
    @MessageMapping("/v1/chat")
    //发送哪里去
    @SendTo("/topic/game_chat")
    public OutMessage gameInfo(InMessage message) {
        log.info("接收信息:{}", message);
        OutMessage outMessage = new OutMessage();
        BeanUtils.copyProperties(message, outMessage);
        return outMessage;
    }
}

游戏公告演示

前端页面

<!DOCTYPE HTML>
<html>
<head>
    <title>游戏公告客户端</title>
     <script src="https://cdn.jsdelivr.net/npm/sockjs-client@1/dist/sockjs.min.js"></script>
     <script src="https://cdn.bootcdn.net/ajax/libs/stomp.js/2.3.3/stomp.js"></script>
</head>

 
<body>
内容
<input  id="text" type="text" style="width:500px"/>
<button onclick="connect()">建立连接</button>
<button onclick="disconnect()">释放连接</button>
<div id="message"></div>
</body>
 
<script type="text/javascript">
   var stompClient = null;

//建立连接
function connect() {
    var socket = new SockJS('http://127.0.0.1:9999/endpoint-websocket'); //连接上端点(基站)
    stompClient = Stomp.over(socket);           //用stom进行包装,规范协议
    stompClient.connect({}, function (frame) {  
        console.log('Connected: ' + frame);
        stompClient.subscribe('/topic/game_chat', function (result) {
            console.info("result="+result)
            showContent(JSON.parse(result.body));
        });
    });
    alert("连接成功");
}

//显示内容
function showContent(body) {
     console.log(body);
    document.getElementById('message').innerHTML += body.content+","+body.time + '<br/>';
}

//释放连接
function disconnect(){
 if (stompClient !== null) {
        stompClient.disconnect();
        alert("释放连接成功");
    }else{
        alert("释放连接失败");
    }
}
</script>
</html>
客户端
<!DOCTYPE HTML>
<html>
<head>
    <title>游戏公告服务端</title>
     <script src="https://cdn.jsdelivr.net/npm/sockjs-client@1/dist/sockjs.min.js"></script>
     <script src="https://cdn.bootcdn.net/ajax/libs/stomp.js/2.3.3/stomp.js"></script>
</head>

 
<body>
内容
<input  id="text" type="text" style="width:500px"/>
<button onclick="send()">发送消息</button>
<button onclick="connect()">建立连接</button>
<button onclick="disconnect()">释放连接</button>
<div id="message"></div>
</body>
 
<script type="text/javascript">
   var stompClient = null;

//建立连接
function connect() {
    var socket = new SockJS('http://127.0.0.1:9999/endpoint-websocket'); //连接上端点(基站)
    stompClient = Stomp.over(socket);           //用stom进行包装,规范协议
    stompClient.connect({}, function (frame) {  
        console.log('Connected: ' + frame);
        stompClient.subscribe('/topic/game_chat', function (result) {
            console.info("result="+result)
            showContent(JSON.parse(result.body));
        });
    });
    alert("连接成功");
}

//显示内容
function showContent(body) {
    console.log(body);
    document.getElementById('message').innerHTML += body.content+","+body.time + '<br/>';
}

//发送消息
function send() {
    var message = document.getElementById('text').value;
    stompClient.send("/app/v1/chat", {}, JSON.stringify({'content':message }));
}

//释放连接
function disconnect(){
 if (stompClient !== null) {
        stompClient.disconnect();
        alert("释放连接成功");
    }else{
        alert("释放连接失败");
    }
}
</script>
</html>
服务端

WebSocket推送方式的区别

  • sendTo,不常用,固定发送给指定的订阅者
  • SimpMessagingTemplate,灵活,支持多种发送方式

项目结构

Controller

package com.ybchen.controller.v2;

import com.ybchen.model.InMessage;
import com.ybchen.service.WebSocketService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.stereotype.Controller;

/**
 * 游戏公告-控制层
 *
 * @author: chenyanbin 2022-07-02 21:17
 */
@Controller
@Slf4j
public class GameInfoV2Controller {
    @Autowired
    WebSocketService webSocketService;

    //消息路由,别人发送的消息会到这里来
    @MessageMapping("/v2/chat")
    public void gameInfo(InMessage message) {
        log.info("接收信息:{}", message);
        webSocketService.sendTopicMessage(message);
    }
}

Service

package com.ybchen.service;

import com.ybchen.model.InMessage;
import com.ybchen.model.OutMessage;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.stereotype.Service;

import java.util.Date;

/**
 * 简单消息模板,用来推送消息
 *
 * @author: chenyanbin 2022-07-03 17:04
 */
@Service
public class WebSocketService {
    @Autowired
    private SimpMessagingTemplate template;


    public void sendTopicMessage(InMessage message) {
        OutMessage outMessage = new OutMessage();
        BeanUtils.copyProperties(message, outMessage);
        outMessage.setTime(new Date());
        for (int i = 0; i < 10; i++) {
            outMessage.setTime(new Date());
            try {
                Thread.sleep(300);
            } catch (InterruptedException e) {
            }
            //发送消息
            template.convertAndSend("/topic/game_rank", outMessage);
        }
    }
}

前端页面

<!DOCTYPE HTML>
<html>
<head>
    <title>游戏公告客户端</title>
     <script src="https://cdn.jsdelivr.net/npm/sockjs-client@1/dist/sockjs.min.js"></script>
     <script src="https://cdn.bootcdn.net/ajax/libs/stomp.js/2.3.3/stomp.js"></script>
</head>

 
<body>
内容
<input  id="text" type="text" style="width:500px"/>
<button onclick="connect()">建立连接</button>
<button onclick="disconnect()">释放连接</button>
<div id="message"></div>
</body>
 
<script type="text/javascript">
   var stompClient = null;

//建立连接
function connect() {
    var socket = new SockJS('http://127.0.0.1:9999/endpoint-websocket'); //连接上端点(基站)
    stompClient = Stomp.over(socket);           //用stom进行包装,规范协议
    stompClient.connect({}, function (frame) {  
        console.log('Connected: ' + frame);
        stompClient.subscribe('/topic/game_rank', function (result) {
            console.info("result="+result)
            showContent(JSON.parse(result.body));
        });
    });
    alert("连接成功");
}

//显示内容
function showContent(body) {
     console.log(body);
    document.getElementById('message').innerHTML += body.content+","+body.time + '<br/>';
}

//释放连接
function disconnect(){
 if (stompClient !== null) {
        stompClient.disconnect();
        alert("释放连接成功");
    }else{
        alert("释放连接失败");
    }
}
</script>
</html>
客户端
<!DOCTYPE HTML>
<html>
<head>
    <title>游戏公告服务端</title>
     <script src="https://cdn.jsdelivr.net/npm/sockjs-client@1/dist/sockjs.min.js"></script>
     <script src="https://cdn.bootcdn.net/ajax/libs/stomp.js/2.3.3/stomp.js"></script>
</head>

 
<body>
内容
<input  id="text" type="text" style="width:500px"/>
<button onclick="send()">发送消息</button>
<button onclick="connect()">建立连接</button>
<button onclick="disconnect()">释放连接</button>
<div id="message"></div>
</body>
 
<script type="text/javascript">
   var stompClient = null;

//建立连接
function connect() {
    var socket = new SockJS('http://127.0.0.1:9999/endpoint-websocket'); //连接上端点(基站)
    stompClient = Stomp.over(socket);           //用stom进行包装,规范协议
    stompClient.connect({}, function (frame) {  
        console.log('Connected: ' + frame);
        stompClient.subscribe('/topic/game_rank', function (result) {
            console.info("result="+result)
            showContent(JSON.parse(result.body));
        });
    });
    alert("连接成功");
}

//显示内容
function showContent(body) {
    console.log(body);
    document.getElementById('message').innerHTML += body.content+","+body.time + '<br/>';
}

//发送消息
function send() {
    var message = document.getElementById('text').value;
    stompClient.send("/app/v2/chat", {}, JSON.stringify({'content':message }));
}

//释放连接
function disconnect(){
 if (stompClient !== null) {
        stompClient.disconnect();
        alert("释放连接成功");
    }else{
        alert("释放连接失败");
    }
}
</script>
</html>
服务端

演示

WebSocket 4类监听器

  • SessionSubscribeEvent 订阅事件
  • SessionUnsubscribeEvent取消订阅事件
  • SessionDisconnectEvent 断开连接事件
  • SessionDisconnectEvent 建立连接事件
package com.ybchen.listener;

import org.springframework.context.ApplicationListener;
import org.springframework.messaging.Message;
import org.springframework.messaging.simp.SimpMessageType;
import org.springframework.messaging.simp.stomp.StompHeaderAccessor;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.messaging.SessionConnectEvent;

/**
 * 连接事件监听器
 * @author: chenyanbin 2022-07-03 17:35
 */
@Component
public class ConnectEventListener implements ApplicationListener<SessionConnectEvent> {

    @Override
    public void onApplicationEvent(SessionConnectEvent event) {
        Message<byte[]> message = event.getMessage();
        //消息头响应器
        StompHeaderAccessor headerAccessor = StompHeaderAccessor.wrap(message);
        //消息类型
        SimpMessageType messageType = headerAccessor.getCommand().getMessageType();
        System.err.println("【ConnectEventListener监听器事件】消息类型:"+messageType);
    }
}
package com.ybchen.listener;

import org.springframework.context.ApplicationListener;
import org.springframework.messaging.Message;
import org.springframework.messaging.simp.SimpMessageType;
import org.springframework.messaging.simp.stomp.StompHeaderAccessor;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.messaging.SessionConnectEvent;

/**
 * 释放连接事件监听器
 * @author: chenyanbin 2022-07-03 17:35
 */
@Component
public class DisconnectEventListener implements ApplicationListener<SessionConnectEvent> {

    @Override
    public void onApplicationEvent(SessionConnectEvent event) {
        Message<byte[]> message = event.getMessage();
        //消息头响应器
        StompHeaderAccessor headerAccessor = StompHeaderAccessor.wrap(message);
        //消息类型
        SimpMessageType messageType = headerAccessor.getCommand().getMessageType();
        System.err.println("【DisconnectEventListener监听器事件】消息类型:"+messageType);
    }
}
package com.ybchen.listener;

import org.springframework.context.ApplicationListener;
import org.springframework.messaging.Message;
import org.springframework.messaging.simp.SimpMessageType;
import org.springframework.messaging.simp.stomp.StompHeaderAccessor;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.messaging.SessionSubscribeEvent;

/**
 * 订阅事件监听器
 * @author: chenyanbin 2022-07-03 17:35
 */
@Component
public class SubscribeEventListener implements ApplicationListener<SessionSubscribeEvent> {
    @Override
    public void onApplicationEvent(SessionSubscribeEvent event) {
        Message<byte[]> message = event.getMessage();
        //消息头响应器
        StompHeaderAccessor headerAccessor = StompHeaderAccessor.wrap(message);
        //消息类型
        SimpMessageType messageType = headerAccessor.getCommand().getMessageType();
        System.err.println("【SubscribeEventListener监听器事件】消息类型:"+messageType);
    }
}

 

演示

点对点简单版单人聊天

controller

package com.ybchen.controller.v3;

import com.ybchen.model.InMessage;
import com.ybchen.service.WebSocketService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.stereotype.Controller;

/**
 * 简单版单人聊天
 *
 * @author: chenyanbin 2022-07-03 17:59
 */
@Controller
public class V3ChatRoomController {
    @Autowired
    WebSocketService webSocketService;

    @MessageMapping("/v3/single/chat")
    public void singleChat(InMessage message) {
        webSocketService.sendChatMessage(message);
    }
}

service

package com.ybchen.service;

import com.ybchen.model.InMessage;
import com.ybchen.model.OutMessage;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.stereotype.Service;

import java.util.Date;

/**
 * 简单消息模板,用来推送消息
 *
 * @author: chenyanbin 2022-07-03 17:04
 */
@Service
public class WebSocketService {
    @Autowired
    private SimpMessagingTemplate template;


    /**
     * 发送消息
     *
     * @param destination 目的地
     * @param message     消息内容
     */
    public void sendTopicMessage(String destination, InMessage message) {
        OutMessage outMessage = new OutMessage();
        BeanUtils.copyProperties(message, outMessage);
        outMessage.setTime(new Date());
        for (int i = 0; i < 10; i++) {
            outMessage.setTime(new Date());
            try {
                Thread.sleep(300);
            } catch (InterruptedException e) {
            }
            //发送消息
            template.convertAndSend(destination, outMessage);
        }
    }

    /**
     * 发送聊天消息
     *
     * @param message
     */
    public void sendChatMessage(InMessage message) {
        OutMessage outMessage = new OutMessage();
        BeanUtils.copyProperties(message, outMessage);
        outMessage.setTime(new Date());
        outMessage.setContent(message.getFrom()+" 发送:"+message.getContent());
        //发送消息
        template.convertAndSend("/chat/single/" + message.getTo(), outMessage);
    }
}

前端页面

<!DOCTYPE HTML>
<html>
<head>
    <title>简单版单人聊天</title>
     <script src="https://cdn.jsdelivr.net/npm/sockjs-client@1/dist/sockjs.min.js"></script>
     <script src="https://cdn.bootcdn.net/ajax/libs/stomp.js/2.3.3/stomp.js"></script>
</head>

 
<body>
    <input type="text" id="from" class="form-control" placeholder="我是">
    <input type="text" id="to" class="form-control" placeholder="发送给谁">
内容
<input  id="text" type="text" style="width:500px"/>
<button onclick="send()">发送消息</button>
<button onclick="connect()">建立连接</button>
<button onclick="disconnect()">释放连接</button>
<div id="message"></div>
</body>
 
<script type="text/javascript">
   var stompClient = null;

//建立连接
function connect() {
    var from = document.getElementById('from').value;
    console.log("from="+from)
    var socket = new SockJS('http://127.0.0.1:9999/endpoint-websocket'); //连接上端点(基站)
    stompClient = Stomp.over(socket);           //用stom进行包装,规范协议
    stompClient.connect({}, function (frame) {  
        console.log('Connected: ' + frame);
        stompClient.subscribe('/chat/single/'+from, function (result) {
            console.info("result="+result)
            showContent(JSON.parse(result.body));
        });
    });
    alert("连接成功");
}

//显示内容
function showContent(body) {
    console.log(body);
    document.getElementById('message').innerHTML += body.content+","+body.time + '<br/>';
}

//发送消息
function send() {
    var message = document.getElementById('text').value;
    var from = document.getElementById('from').value;
    var to = document.getElementById('to').value;
    stompClient.send("/app/v3/single/chat", {}, JSON.stringify({'content':message,'to':to,'from':from }));
}

//释放连接
function disconnect(){
 if (stompClient !== null) {
        stompClient.disconnect();
        alert("释放连接成功");
    }else{
        alert("释放连接失败");
    }
}
</script>
</html>
简单版单人聊天

演示

实时监控服务器JVM负载

controller

package com.ybchen.controller.v4;

import com.ybchen.service.WebSocketService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Controller;

/**
 * 实时推送服务器的JVM负载
 * @author: chenyanbin 2022-07-03 18:26
 */
@Controller
public class V4ServiceInfoController {
    @Autowired
    private WebSocketService webSocketService;

    @MessageMapping("/v4/schedule/push")
    //3秒一次
    @Scheduled(fixedDelay = 3000)
    public void sendServiceInfo(){
        webSocketService.sendServiceInfo();
    }
}

service

package com.ybchen.service;

import com.ybchen.model.InMessage;
import com.ybchen.model.OutMessage;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.stereotype.Service;

import java.util.Date;

/**
 * 简单消息模板,用来推送消息
 *
 * @author: chenyanbin 2022-07-03 17:04
 */
@Service
public class WebSocketService {
    @Autowired
    private SimpMessagingTemplate template;


    /**
     * 发送消息
     *
     * @param destination 目的地
     * @param message     消息内容
     */
    public void sendTopicMessage(String destination, InMessage message) {
        OutMessage outMessage = new OutMessage();
        BeanUtils.copyProperties(message, outMessage);
        outMessage.setTime(new Date());
        for (int i = 0; i < 10; i++) {
            outMessage.setTime(new Date());
            try {
                Thread.sleep(300);
            } catch (InterruptedException e) {
            }
            //发送消息
            template.convertAndSend(destination, outMessage);
        }
    }

    /**
     * 发送聊天消息
     *
     * @param message
     */
    public void sendChatMessage(InMessage message) {
        OutMessage outMessage = new OutMessage();
        BeanUtils.copyProperties(message, outMessage);
        outMessage.setTime(new Date());
        outMessage.setContent(message.getFrom() + " 发送:" + message.getContent());
        //发送消息
        template.convertAndSend("/chat/single/" + message.getTo(), outMessage);
    }

    /**
     * 推送服务器JVM负载信息
     */
    public void sendServiceInfo() {
        //系统核数
        int processors = Runtime.getRuntime().availableProcessors();
        //空闲内存
        long freeMemory = Runtime.getRuntime().freeMemory();
        //最大内存
        long maxMemory = Runtime.getRuntime().maxMemory();
        String message = "服务器可用处理器:" + processors+",虚拟机空闲内存大小:"+freeMemory+",最大内存:"+maxMemory;
        OutMessage outMessage = new OutMessage();
        outMessage.setTime(new Date());
        outMessage.setContent(message);
        //发送消息
        template.convertAndSend("/topic/service_info", outMessage);
    }
}

前端页面

<!DOCTYPE HTML>
<html>
<head>
    <title>服务器JVM负载</title>
     <script src="https://cdn.jsdelivr.net/npm/sockjs-client@1/dist/sockjs.min.js"></script>
     <script src="https://cdn.bootcdn.net/ajax/libs/stomp.js/2.3.3/stomp.js"></script>
</head>

 
<body>
内容
<input  id="text" type="text" style="width:500px"/>
<button onclick="connect()">建立连接</button>
<button onclick="disconnect()">释放连接</button>
<div id="message"></div>
</body>
 
<script type="text/javascript">
   var stompClient = null;

//建立连接
function connect() {
    var socket = new SockJS('http://127.0.0.1:9999/endpoint-websocket'); //连接上端点(基站)
    stompClient = Stomp.over(socket);           //用stom进行包装,规范协议
    stompClient.connect({}, function (frame) {  
        console.log('Connected: ' + frame);
        stompClient.subscribe('/topic/service_info', function (result) {
            console.info("result="+result)
            showContent(JSON.parse(result.body));
        });
    });
    alert("连接成功");
}

//显示内容
function showContent(body) {
     console.log(body);
    document.getElementById('message').innerHTML += body.content+","+body.time + '<br/>';
}

//释放连接
function disconnect(){
 if (stompClient !== null) {
        stompClient.disconnect();
        alert("释放连接成功");
    }else{
        alert("释放连接失败");
    }
}
</script>
</html>
服务器JVM负载

演示

案例源码

链接: https://pan.baidu.com/s/18QplqZL7fMyVA7w7PjRKrA?pwd=ecj5 提取码: ecj5 

nginx代理websocket

http {

    map $http_upgrade $connection_upgrade {
        default upgrade;
        '' close;
    }

    upstream websocket {
        ip_hash;   #使用ip固定转发到后端服务器
        server localhost:3100;  
        server localhost:3101;
        server localhost:3102;
    }


    server {
        listen 8020;
        location / {
            proxy_pass http://websocket;
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection $connection_upgrade; #      声明支持websocket
        }
    }
}

 

posted @ 2022-07-03 20:11  陈彦斌  阅读(1153)  评论(0)    收藏  举报