Springboot2构建基于Netty的高性能Websocket服务器(netty-websocket-spring-boot-starter)

Netty为什么并发高

Netty是一款基于NIO(Nonblocking I/O,非阻塞IO)开发的网络通信框架,对比于BIO(Blocking I/O,阻塞IO),他的并发性能得到了很大提高。
在这里插入图片描述

Netty为什么传输快

Netty的传输快其实也是依赖了NIO的一个特性——零拷贝。我们知道,Java的内存有堆内存、栈内存和字符串常量池等等,其中堆内存是占用内存空间最大的一块,也是Java对象存放的地方,一般我们的数据如果需要从IO读取到堆内存,中间需要经过Socket缓冲区,也就是说一个数据会被拷贝两次才能到达他的的终点,如果数据量大,就会造成不必要的资源浪费。

Netty针对这种情况,使用了NIO中的另一大特性——零拷贝,当他需要接收数据的时候,他会在堆内存之外开辟一块内存,数据就直接从IO读到了那块内存中去,在netty里面通过ByteBuf可以直接对这些数据进行直接操作,从而加快了传输速度。

Netty和Tomcat有什么区别?

Netty和Tomcat最大的区别就在于通信协议,Tomcat是基于Http协议的,他的实质是一个基于http协议的web容器,但是Netty不一样,他能通过编程自定义各种协议,因为netty能够通过codec自己来编码/解码字节流,完成类似redis访问的功能,这就是netty和tomcat最大的不同。

官方文档

感谢大家的热情反馈,首先这个框架跟原生的websocket配置上有点不一样的,更多详情可以多多看官方的文档,当时我也是觉得很快,但是发现参数设置/端口配置等,还有有点差异的。
https://github.com/YeautyYE/netty-websocket-spring-boot-starter/blob/master/README_zh.md

Netty-websocket-spring-boot-starter

这是个开源的框架。通过它,我们可以像spring-boot-starter-websocket一样使用注解进行开发,只需关注需要的事件(如OnMessage)。并且底层是使用Netty,netty-websocket-spring-boot-starter其他配置和spring-boot-starter-websocket完全一样,当需要调参的时候只需要修改配置参数即可,无需过多的关心handler的设置。

对应的Maven配置:

<!-- 注释掉默认的websocket starter
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-websocket</artifactId>
	</dependency>-->
<!-- 启用Netty https://mvnrepository.com/artifact/org.yeauty/netty-websocket-spring-boot-starter -->
<dependency>
    <groupId>org.yeauty</groupId>
    <artifactId>netty-websocket-spring-boot-starter</artifactId>
    <version>0.7.6</version>
</dependency>

Websocker注入Bean问题

关于这个问题,可以看最新发表的这篇文章,在参考和研究了网上一些攻略后,项目已经通过该方法注入成功,大家可以参考【关于controller调用controller/service调用service/util调用service/websocket中autowired的解决方法】https://blog.csdn.net/moshowgame/article/details/83415545

快速开始

  • 添加依赖:
	<dependency>
		<groupId>org.yeauty</groupId>
		<artifactId>netty-websocket-spring-boot-starter</artifactId>
		<version>0.7.6</version>
	</dependency>
  • new一个ServerEndpointExporter对象,交给Spring IOC容器,表示要开启WebSocket功能,样例如下:
@Configuration
public class WebSocketConfig {
    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }
}
  • 在端点类上加上@ServerEndpoint@Component注解,并在相应的方法上加上@OnOpen@OnClose@OnError@OnMessage@OnBinaryOnEvent注解,样例如下:
@ServerEndpoint
@Component
public class MyWebSocket {

    @OnOpen
    public void onOpen(Session session, HttpHeaders headers, ParameterMap parameterMap) throws IOException {
        System.out.println("new connection");
        
        String paramValue = parameterMap.getParameter("paramKey");
        System.out.println(paramValue);
    }

    @OnClose
    public void onClose(Session session) throws IOException {
       System.out.println("one connection closed"); 
    }

    @OnError
    public void onError(Session session, Throwable throwable) {
        throwable.printStackTrace();
    }

    @OnMessage
    public void onMessage(Session session, String message) {
        System.out.println(message);
        session.sendText("Hello Netty!");
    }

    @OnBinary
    public void onBinary(Session session, byte[] bytes) {
        for (byte b : bytes) {
            System.out.println(b);
        }
        session.sendBinary(bytes); 
    }

    @OnEvent
    public void onEvent(Session session, Object evt) {
        if (evt instanceof IdleStateEvent) {
            IdleStateEvent idleStateEvent = (IdleStateEvent) evt;
            switch (idleStateEvent.state()) {
                case READER_IDLE:
                    System.out.println("read idle");
                    break;
                case WRITER_IDLE:
                    System.out.println("write idle");
                    break;
                case ALL_IDLE:
                    System.out.println("all idle");
                    break;
                default:
                    break;
            }
        }
    }

}
  • 打开WebSocket客户端,连接到ws://127.0.0.1:80

注解

@ServerEndpoint

当ServerEndpointExporter类通过Spring配置进行声明并被使用,它将会去扫描带有@ServerEndpoint注解的类
被注解的类将被注册成为一个WebSocket端点
所有的配置项都在这个注解的属性中 ( 如:@ServerEndpoint("/ws") )

@OnOpen

当有新的WebSocket连接进入时,对该方法进行回调
注入参数的类型:Session、HttpHeaders、ParameterMap

@OnClose

当有WebSocket连接关闭时,对该方法进行回调
注入参数的类型:Session

@OnError

当有WebSocket抛出异常时,对该方法进行回调
注入参数的类型:Session、Throwable

@OnMessage

当接收到字符串消息时,对该方法进行回调
注入参数的类型:Session、String

@OnBinary

当接收到二进制消息时,对该方法进行回调
注入参数的类型:Session、byte[]

@OnEvent

当接收到Netty的事件时,对该方法进行回调
注入参数的类型:Session、Object

配置

所有的配置项都在这个注解的属性中

属性默认值说明
path“/”WebSocket的path,也可以用value来设置
host“0.0.0.0”WebSocket的host,"0.0.0.0"即是所有本地地址
port80WebSocket绑定端口号。如果为0,则使用随机端口(端口获取可见 多端点服务)
bossLoopGroupThreads0bossEventLoopGroup的线程数
workerLoopGroupThreads0workerEventLoopGroup的线程数
useCompressionHandlerfalse是否添加WebSocketServerCompressionHandler到pipeline
prefix“”当不为空时,即是使用application.properties进行配置,详情在 通过application.properties进行配置
optionConnectTimeoutMillis30000与Netty的ChannelOption.CONNECT_TIMEOUT_MILLIS一致
optionSoBacklog128与Netty的ChannelOption.SO_BACKLOG一致
childOptionWriteSpinCount16与Netty的ChannelOption.WRITE_SPIN_COUNT一致
childOptionWriteBufferHighWaterMark64*1024与Netty的ChannelOption.WRITE_BUFFER_HIGH_WATER_MARK一致,但实际上是使用ChannelOption.WRITE_BUFFER_WATER_MARK
childOptionWriteBufferLowWaterMark32*1024与Netty的ChannelOption.WRITE_BUFFER_LOW_WATER_MARK一致,但实际上是使用 ChannelOption.WRITE_BUFFER_WATER_MARK
childOptionSoRcvbuf-1(即未设置)与Netty的ChannelOption.SO_RCVBUF一致
childOptionSoSndbuf-1(即未设置)与Netty的ChannelOption.SO_SNDBUF一致
childOptionTcpNodelaytrue与Netty的ChannelOption.TCP_NODELAY一致
childOptionSoKeepalivefalse与Netty的ChannelOption.SO_KEEPALIVE一致
childOptionSoLinger-1与Netty的ChannelOption.SO_LINGER一致
childOptionAllowHalfClosurefalse与Netty的ChannelOption.ALLOW_HALF_CLOSURE一致
readerIdleTimeSeconds0IdleStateHandler中的readerIdleTimeSeconds一致,并且当它不为0时,将在pipeline中添加IdleStateHandler
writerIdleTimeSeconds0IdleStateHandler中的writerIdleTimeSeconds一致,并且当它不为0时,将在pipeline中添加IdleStateHandler
allIdleTimeSeconds0IdleStateHandler中的allIdleTimeSeconds一致,并且当它不为0时,将在pipeline中添加IdleStateHandler
maxFramePayloadLength65536最大允许帧载荷长度

通过application.properties进行配置

对注解中的prefix进行设置后,即可在application.properties中进行配置。如下:

  • 首先在ServerEndpoint注解中设置prefix的值
@ServerEndpoint(prefix = "netty-websocket")
@Component
public class MyWebSocket {
    ...
}
  • 接下来即可在application.properties中配置
netty-websocket.host=0.0.0.0
netty-websocket.path=/
netty-websocket.port=80

application.properties中的key与注解@ServerEndpoint中属性的对应关系如下:

注解中的属性配置文件中的key例子
path{prefix}.pathnetty-websocket.path
host{prefix}.hostnetty-websocket.host
port{prefix}.portnetty-websocket.port
bossLoopGroupThreads{prefix}.boss-loop-group-threadsnetty-websocket.boss-loop-group-threads
workerLoopGroupThreads{prefix}.worker-loop-group-threadsnetty-websocket.worker-loop-group-threads
useCompressionHandler{prefix}.use-compression-handlernetty-websocket.use-compression-handler
optionConnectTimeoutMillis{prefix}.option.connect-timeout-millisnetty-websocket.option.connect-timeout-millis
optionSoBacklog{prefix}.option.so-backlognetty-websocket.option.so-backlog
childOptionWriteSpinCount{prefix}.child-option.write-spin-countnetty-websocket.child-option.write-spin-count
childOptionWriteBufferHighWaterMark{prefix}.child-option.write-buffer-high-water-marknetty-websocket.child-option.write-buffer-high-water-mark
childOptionWriteBufferLowWaterMark{prefix}.child-option.write-buffer-low-water-marknetty-websocket.child-option.write-buffer-low-water-mark
childOptionSoRcvbuf{prefix}.child-option.so-rcvbufnetty-websocket.child-option.so-rcvbuf
childOptionSoSndbuf{prefix}.child-option.so-sndbufnetty-websocket.child-option.so-sndbuf
childOptionTcpNodelay{prefix}.child-option.tcp-nodelaynetty-websocket.child-option.tcp-nodelay
childOptionSoKeepalive{prefix}.child-option.so-keepalivenetty-websocket.child-option.so-keepalive
childOptionSoLinger{prefix}.child-option.so-lingernetty-websocket.child-option.so-linger
childOptionAllowHalfClosure{prefix}.child-option.allow-half-closurenetty-websocket.child-option.allow-half-closure
readerIdleTimeSeconds{prefix}.reader-idle-time-secondsnetty-websocket.reader-idle-time-seconds
writerIdleTimeSeconds{prefix}.writer-idle-time-secondsnetty-websocket.writer-idle-time-seconds
allIdleTimeSeconds{prefix}.all-idle-time-secondsnetty-websocket.all-idle-time-seconds
maxFramePayloadLength{prefix}.maxFramePayloadLengthnetty-websocket.maxFramePayloadLength

自定义Favicon

配置favicon的方式与spring-boot中完全一致。只需将favicon.ico文件放到classpath的根目录下即可。如下:

src/
  +- main/
    +- java/
    |   + <source code>
    +- resources/
        +- favicon.ico

自定义错误页面

配置自定义错误页面的方式与spring-boot中完全一致。你可以添加一个 /public/error 目录,错误页面将会是该目录下的静态页面,错误页面的文件名必须是准确的错误状态或者是一串掩码,如下:

src/
  +- main/
    +- java/
    |   + <source code>
    +- resources/
        +- public/
            +- error/
            |   +- 404.html
            |   +- 5xx.html
            +- <other public assets>

多端点服务

  • 快速启动的基础上,在多个需要成为端点的类上使用@ServerEndpoint@Component注解即可
  • 可通过ServerEndpointExporter.getInetSocketAddressSet()获取所有端点的地址
  • 当地址不同时(即host不同或port不同),使用不同的ServerBootstrap实例
  • 当地址相同,路径(path)不同时,使用同一个ServerBootstrap实例
  • 当多个端点服务的port为0时,将使用同一个随机的端口号
  • 当多个端点的port和path相同时,host不能设为"0.0.0.0",因为"0.0.0.0"意味着绑定所有的host
  • 10
    点赞
  • 111
    收藏
    觉得还不错? 一键收藏
  • 36
    评论
### 回答1: Netty-WebSocket-Spring-Boot-Starter是一个用于将Websocket集成到Spring Boot应用程序中的库。它使用Netty作为底层框架,提供了一种快速和可靠的方式来处理异步通信。 这个库提供了一种简单的方法来创建Websocket端点,只需要使用注释和POJO类即可。在这些端点上可以添加动态的事件处理程序,以处理连接、断开连接和消息事件等。 此外,Netty-WebSocket-Spring-Boot-Starter还包括了一些安全性的特性,如基于令牌的授权和XSS保护,可以帮助您保持您的Websocket应用程序安全。 总的来说,Netty-WebSocket-Spring-Boot-Starter提供了一种快速和易于使用的方式来构建Websocket应用程序,使得它成为应用程序开发人员的有用工具。 ### 回答2: netty-websocket-spring-boot-starter 是一个开源的 Java Web 开发工具包,主要基于 Netty 框架实现了 WebSocket 协议的支持,同时集成了 Spring Boot 框架,使得开发者可以更加方便地搭建 WebSocket 服务器。 该工具包提供了 WebSocketServer 配置类,通过在 Spring Boot 的启动配置类中调用 WebSocketServer 配置类,即可启动 WebSocket 服务器。同时,该工具包还提供了多种配置参数,如端口号、URI 路径、SSL 配置、认证配置等等,可以根据业务需求进行自定义配置。 此外,该工具包还提供了一些可扩展的接口和抽象类,如 WebSocketHandler、ChannelHandlerAdapter 等,可以通过继承和实现这些接口和抽象类来实现业务逻辑的处理和拓展。 总的来说,netty-websocket-spring-boot-starter 提供了一个高效、简单、易用的 WebSocket 服务器开发框架,可以减少开发者的开发成本和工作量。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 36
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值