springboot2.2.X手册:5分钟用Netty搭建高性能异步WebSocket服务
yuyutoo 2024-10-12 01:40 15 浏览 0 评论
溪云阁:专注编程教学,架构,JAVA,Python,微服务,机器学习等领域,欢迎关注,一起学习。
断更快两个月了,6月份工作忙到飞起,7月份家里又有事,已经累到躺下就想睡觉的程度了。
现在我们做WebSocket服务,很多时候都是会整合Netty作为服务器,但是有个问题,就是发现网上的整合起来,比较繁琐,各种配置,各种对应,最关键是千篇一律的网文,看得好辛苦了,今天咱们来介绍一个开源的组件,帮你快速搭建基于Netty的WebSocket服务,让你更加轻松,更加专注于业务开发。
组件介绍
netty-websocket-spring-boot-starter是基于Netty服务器来做的WebSocket服务器,不需要配置Netty服务器信息,只需要配置Webscoket的注解就行,目前用起来还是很方便的。
加载包体
<?xml version="1.0"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.boots</groupId>
<artifactId>boots</artifactId>
<version>1.1.0.RELEASE</version>
</parent>
<groupId>boots.weboscket</groupId>
<artifactId>boots-weboscket</artifactId>
<version>2.0.0.RELEASE</version>
<name>boots-weboscket</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<!-- 公共组件:swagger服务+入参出参+统一异常拦截 -->
<dependency>
<groupId>com.boots</groupId>
<artifactId>module-boots-api</artifactId>
<version>2.0.0.RELEASE</version>
</dependency>
<!-- netty工具类 -->
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
</dependency>
<!-- netty-websocket整合工具类 -->
<dependency>
<groupId>org.yeauty</groupId>
<artifactId>netty-websocket-spring-boot-starter</artifactId>
<version>0.9.5</version>
</dependency>
</dependencies>
</project>
配置文件
######配置基本信息######
##配置应用名称
spring.application.name: boots-websocket
##配置时间格式,为了避免精度丢失,全部换成字符串
spring.jackson.timeZone: GMT+8
spring.jackson.dateFormat: yyyy-MM-dd HH:mm:ss
spring.jackson.generator.writeNumbersAsStrings: true
单个推送后端代码
/**
* All rights Reserved, Designed By 林溪
* Copyright: Copyright(C) 2016-2020
* Company 溪云阁 .
*/
package com.boots.websocket.websocket;
import org.yeauty.annotation.OnClose;
import org.yeauty.annotation.OnError;
import org.yeauty.annotation.OnMessage;
import org.yeauty.annotation.OnOpen;
import org.yeauty.annotation.ServerEndpoint;
import org.yeauty.pojo.Session;
import com.module.boots.exception.CommonRuntimeException;
import io.netty.handler.codec.http.HttpHeaders;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
/**
* 单个推送服务
* @author:溪云阁
* @date:2020年8月4日
*/
@Slf4j
@ServerEndpoint(path = "/SingleSocket", host = "127.0.0.1", port = "8900")
public class SingleSocket {
/**
* 新建WebSocket的时候,执行该方法
* @author 溪云阁
* @param session
* @param headers void
*/
@OnOpen
@SneakyThrows(CommonRuntimeException.class)
public void onOpen(Session session, HttpHeaders headers) {
log.info("WebSocket服务连接成功");
}
/**
* 关闭WebSocket的时候,执行该方法
* @author 溪云阁
* @param session void
*/
@OnClose
@SneakyThrows(CommonRuntimeException.class)
public void onClose(Session session) {
log.info("WebSocket服务关闭成功");
}
/**
* WebSocket发生异常的时候,执行该方法
* @author 溪云阁
* @param session
* @param th void
*/
@OnError
public void onError(Session session, Throwable th) {
log.error("{}", th.fillInStackTrace());
th.printStackTrace();
}
/**
* WebSocket接收到的消息为字符串的时候,指定该方法
* @author 溪云阁
* @param session
* @param msg void
*/
@OnMessage
@SneakyThrows(CommonRuntimeException.class)
public void OnMessage(Session session, String msg) {
log.info("接收到的信息:{}", msg);
session.sendText(msg);
}
}
单个推送前端页面
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>单个推送</title>
<meta name="renderer" content="webkit">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<link rel="stylesheet" href="/layui/css/layui.css" media="all">
</head>
<body>
<fieldset class="layui-elem-field layui-field-title" style="margin-top: 20px; margin-left: 310px; margin-right: 310px">
<legend>输出内容</legend>
</fieldset>
<form class="layui-form" action="" style="margin-left: 200px; margin-right: 310px">
<div class="layui-form-item layui-form-text">
<div class="layui-input-block">
<textarea placeholder="请输入内容" class="layui-textarea" rows="20" id="contentArea"></textarea>
</div>
</div>
</form>
<form class="layui-form layui-form-pane" style="margin-left: 310px; margin-right: 310px" action="">
<div class="layui-form-item">
<label class="layui-form-label">输入</label>
<div class="layui-input-block">
<input type="text" name="content" id="content" autocomplete="off" placeholder="请输入内容" class="layui-input">
</div>
</div>
</form>
<div class="layui-form-item" style="margin-left: 310px; margin-right: 310px">
<button class="layui-btn" onclick="sendMsg()">发送</button>
</div>
<script src="/js/jquery.min.js" ></script>
<script src="/layui/layui.js" charset="utf-8"></script>
<script>
var websocket = new WebSocket("ws://127.0.0.1:8900/SingleSocket");
//WebSocket打开
websocket.onopen = function(evt) {
$('#contentArea').html("WebSocket服务连接成功");
};
//WebSocket推送
websocket.onmessage = function(evt) {
var val = $('#contentArea').val() + "
";
$('#contentArea').html(val + evt.data);
};
//WebSocket关闭
websocket.onclose = function(evt) {
$('#contentArea').html("WebSocket服务关闭成功");
};
function sendMsg() {
var text = $('#content').val();
websocket.send(text);
}
</script>
</body>
</html>
群发推送后端代码
/**
* All rights Reserved, Designed By 林溪
* Copyright: Copyright(C) 2016-2020
* Company 溪云阁 .
*/
package com.boots.websocket.websocket;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.yeauty.annotation.OnClose;
import org.yeauty.annotation.OnError;
import org.yeauty.annotation.OnMessage;
import org.yeauty.annotation.OnOpen;
import org.yeauty.annotation.ServerEndpoint;
import org.yeauty.pojo.Session;
import com.module.boots.exception.CommonRuntimeException;
import io.netty.handler.codec.http.HttpHeaders;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
/**
* 群组推送服务
* @author:溪云阁
* @date:2020年8月4日
*/
@Slf4j
@ServerEndpoint(path = "/groupSocket", host = "127.0.0.1", port = "8901")
public class GroupSocket {
// 定义存放Session的缓存对象
private Map<String, Session> map = new ConcurrentHashMap<>();
/**
* 新建WebSocket的时候,执行该方法
* @author 溪云阁
* @param session
* @param headers void
*/
@OnOpen
@SneakyThrows(CommonRuntimeException.class)
public void onOpen(Session session, HttpHeaders headers) {
// 把Session放到缓存中,后面群发使用
map.put(session.id().toString(), session);
log.info("WebSocket服务连接成功");
}
/**
* 关闭WebSocket的时候,执行该方法
* @author 溪云阁
* @param session void
*/
@OnClose
@SneakyThrows(CommonRuntimeException.class)
public void onClose(Session session) {
// 当关闭的时候,删除缓存中的session
if (map.containsKey(session.id().toString())) {
map.remove(session.id().toString());
}
log.info("WebSocket服务关闭成功");
}
/**
* WebSocket发生异常的时候,执行该方法
* @author 溪云阁
* @param session
* @param th void
*/
@OnError
public void onError(Session session, Throwable th) {
log.error("{}", th.fillInStackTrace());
th.printStackTrace();
}
/**
* WebSocket接收到的消息为字符串的时候,指定该方法
* @author 溪云阁
* @param session
* @param msg void
*/
@OnMessage
@SneakyThrows(CommonRuntimeException.class)
public void OnMessage(String msg) {
map.forEach((key, session) -> {
log.info("接收到的信息:{}", msg);
session.sendText(msg);
});
}
}
群发推送前端页面
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>群发推送</title>
<meta name="renderer" content="webkit">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<link rel="stylesheet" href="/layui/css/layui.css" media="all">
</head>
<body>
<fieldset class="layui-elem-field layui-field-title" style="margin-top: 20px; margin-left: 310px; margin-right: 310px">
<legend>输出内容</legend>
</fieldset>
<form class="layui-form" action="" style="margin-left: 200px; margin-right: 310px">
<div class="layui-form-item layui-form-text">
<div class="layui-input-block">
<textarea placeholder="请输入内容" class="layui-textarea" rows="10" id="contentArea1"></textarea>
</div>
</div>
</form>
<form class="layui-form" action="" style="margin-left: 200px; margin-right: 310px">
<div class="layui-form-item layui-form-text">
<div class="layui-input-block">
<textarea placeholder="请输入内容" class="layui-textarea" rows="10" id="contentArea2"></textarea>
</div>
</div>
</form>
<form class="layui-form layui-form-pane" style="margin-left: 310px; margin-right: 310px" action="">
<div class="layui-form-item">
<label class="layui-form-label">输入</label>
<div class="layui-input-block">
<input type="text" name="content" id="content" autocomplete="off" placeholder="请输入内容" class="layui-input">
</div>
</div>
</form>
<div class="layui-form-item" style="margin-left: 310px; margin-right: 310px">
<button class="layui-btn" onclick="sendMsg()">发送</button>
</div>
<script src="/js/jquery.min.js" ></script>
<script src="/layui/layui.js" charset="utf-8"></script>
<script>
var websocket1 = new WebSocket("ws://127.0.0.1:8901/groupSocket");
var websocket2 = new WebSocket("ws://127.0.0.1:8901/groupSocket");
//第一个WebSocket打开
websocket1.onopen = function(evt) {
$('#contentArea1').html("第一个WebSocket服务连接成功");
};
//第一个WebSocket推送
websocket1.onmessage = function(evt) {
var val = $('#contentArea1').val() + "
";
$('#contentArea1').html(val + evt.data);
};
//第一个WebSocket关闭
websocket1.onclose = function(evt) {
$('#contentArea1').html("第一个WebSocket服务关闭成功");
};
//第二个WebSocket打开
websocket2.onopen = function(evt) {
$('#contentArea2').html("第二个WebSocket服务连接成功");
};
//第二个WebSocket推送
websocket2.onmessage = function(evt) {
var val = $('#contentArea2').val() + "
";
$('#contentArea2').html(val + evt.data);
};
//第二个WebSocket关闭
websocket2.onclose = function(evt) {
$('#contentArea2').html("第二个WebSocket服务关闭成功");
};
function sendMsg() {
var text = $('#content').val();
websocket1.send(text);
websocket2.send(text);
}
</script>
</body>
</html>
启动类
package com.boots.websocket;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
/**
* 服务启动类
* @author:溪云阁
* @date:2020年5月2日
*/
@SpringBootApplication
@ComponentScan(basePackages = { "com.module", "com.boots" })
public class BootsWebSocketApplication {
public static void main(String[] args) {
SpringApplication.run(BootsWebSocketApplication.class, args);
}
}
单个推送测试
群发推送测试
总结及拓展
相比于需要自己整合Netty的配置,目前用起来还是很方便的,Netty的配置可以在注解类ServerEndpoint看到,里面一进去就会发现还是很清楚的。
目前发现有个不足的,就是超过一定时间不连接的时候,就会自动断开,不过这个可以在前端做个超时设置或者心跳检测,就可以了,问题不大,用起来还是很爽的。
--END--
作者:@溪云阁
原创作品,抄袭必究
如需要源码,转发,关注后私信我
部分图片或代码来源网络,如侵权请联系删除,谢谢!
历史文章
springboot2.2.X手册:抛弃ELK,百亿日志+调用链的Easylog很放心
springboot2.2.X手册:Eureka不更,Consul被禁,启用Nacos
springboot2.2.X手册:构建全局唯一的短链接数据中心
springboot2.2.X手册:放弃fastdfs,整合Minio做文件服务器真香
springboot2.2.X手册:分布式系统下,重复提交的解决方案
springboot2.2.X手册:Easypoi导出excel,最新版的手感香不香?
springboot2.2.X手册:项目从100M瘦身到100K,部署省事多了!
springboot2.2.X手册:redis的7种类型100个方法全解析
springboot2.2.X手册:是时候用Lettuce替换Jedis操作Redis缓存了
springboot2.2.X手册:构建多元化的API接口,我们这样子设计
springboot2.2.X手册:基于Jasypt的JavaConfig方式敏感信息加密
springboot2.2.X手册:整合最新版MybatisPlus 3.3.1版本
springboot2.2.X手册:对象复制哪种最快?7种复制方式性能对比
相关推荐
- YAML配置文件简介及使用(yaml 配置)
-
简介YAML是"YAMLAin'taMarkupLanguage"(YAML不是一种标记语言)的缩写。相比JSON格式的方便。...
- 教你如何解决最常见的58种网络故障排除方法
-
1.故障现象:网络适配器(网卡)设置与计算机资源有冲突。分析、排除:通过调整网卡资源中的IRQ和I/O值来避开与计算机其它资源的冲突。有些情况还需要通过设置主板的跳线来调整与其它资源的冲突。2.故障现...
- 一分钟带你了解服务器网卡(服务器网卡怎么用)
-
今天小编和大家聊一下服务器的网卡。什么是网卡?简单说网卡就是计算机与局域网互连的设备。计算机主要通过网卡接入网络。网卡又称为网络适配器或网络接口卡NIC(NetworkinterfaceCard)...
- linux文件之ssh配置文件的含义与作用
-
ssh远程登录命令是操作系统(包括linux和window系统)下常用的操作命令,可以帮助用户,远程登录服务器系统,查看,操作系统相关信息。linux系统对于ssh命令有专门保存其相关配置的目录和文件...
- Cilium 官方文档翻译 - IPAM(二)Kubernetes Host模式
-
KubernetesHostScopeciliumIPAM的kuberneteshost-scope模式通过选项ipam:kubernetes开启,将集群IP地址分配委托给每个独立的节点,并...
- 域名劫持跳转,域名劫持跳转的解决办法只需5步
-
简单来说,域名劫持就是把原本准备访问某网站的用户,在不知不觉中,劫持到仿冒的网站上,例如用户准备访问某家知名品牌的网上商店,黑客就可以通过域名劫持的手段,把其带到假的网上商店,同时收集用户的ID信息和...
- Linux 磁盘和文件系统管理(linux磁盘管理fdisk)
-
1检测并确认新硬盘...
- windows host文件怎么恢复?局域网访问全靠这些!
-
windowshost文件怎么恢复?windowshost文件是常用网址域名及其相应IP地址建立一个关联文件,通过这个host文件配置域名和IP的映射关系,以提高域名解析的速度,方便局域网用户使用...
- Nginx配置文件详解与优化建议(nginx 配置详解)
-
1、概述今天来详解一下Nginx的配置文件,以及给出一些配置建议,希望能对大家有所帮助。...
- Mac电脑hosts文件锁定,如何修改hosts文件权限
-
有时候我们需要修改hosts文件,但是网上很多教程都行不通,使用sudo命令也不行。其实有一个很简单的方法。打开终端命令行,使用如下命令即可:sudochflags-hvnoschg/etc/...
- windows电脑如何修改hosts文件?(windows 修改hosts文件)
-
先来简单说下电脑host的作用hosts文件的作用:hosts文件是一个用于储存计算机网络中各节点信息的计算机文件;作用是将一些常用的网址域名与其对应的IP地址建立一个关联“数据库”,当用户在浏览器中...
- Vigilante恶意软件行为怪异:修改Hosts文件以阻止受害者访问盗版网站
-
Sophos刚刚报道了一款名叫Vigilante的恶意软件,但其行为却让许多受害者感到不解。与其它专注于偷密码、搞破坏、或勒索赎金的恶意软件不同,Vigilante会通过修改Hosts文件...
- hosts文件无法修改几种现象和解决方法
-
第一种、hosts文件修改完不是直接保存而是弹出另存为窗口解决:1、右击hosts文件——属性——把“只读”前面勾去掉。第二种、打开hosts文件时提示“你没有权限打开该文件,请向文件的所有者或管理员...
- hosts文件位置在哪里,教你hosts文件位置在哪里
-
Hosts是一个没有扩展名的系统文件,其基本作用就是将一些常用的网址域名与其对应的IP地址建立一个关联"数据库",当用户在浏览器中输入一个需要登录的网址时,系统会首先自动从Hosts文件中寻找对应的I...
你 发表评论:
欢迎- 一周热门
-
-
前端面试:iframe 的优缺点? iframe有那些缺点
-
带斜线的表头制作好了,如何填充内容?这几种方法你更喜欢哪个?
-
漫学笔记之PHP.ini常用的配置信息
-
其实模版网站在开发工作中很重要,推荐几个参考站给大家
-
推荐7个模板代码和其他游戏源码下载的网址
-
[干货] JAVA - JVM - 2 内存两分 [干货]+java+-+jvm+-+2+内存两分吗
-
正在学习使用python搭建自动化测试框架?这个系统包你可能会用到
-
织梦(Dedecms)建站教程 织梦建站详细步骤
-
2024PHP在线客服系统源码+完全开源 带详细搭建教程
-
【开源分享】2024在线客服系统PHP源码(安装教程+全新UI)
-
- 最近发表
- 标签列表
-
- mybatis plus (70)
- scheduledtask (71)
- css滚动条 (60)
- java学生成绩管理系统 (59)
- 结构体数组 (69)
- databasemetadata (64)
- javastatic (68)
- jsp实用教程 (53)
- fontawesome (57)
- widget开发 (57)
- vb net教程 (62)
- hibernate 教程 (63)
- case语句 (57)
- svn连接 (74)
- directoryindex (69)
- session timeout (58)
- textbox换行 (67)
- extension_dir (64)
- linearlayout (58)
- vba高级教程 (75)
- iframe用法 (58)
- sqlparameter (59)
- trim函数 (59)
- flex布局 (63)
- contextloaderlistener (56)