Netty实例(二)——springboot集成Netty

前言

上一篇博客,简单整理了一下Netty集成HTTP和websocket服务的实例,通过采用netty的开箱即用的编解码器完成了简陋的HTTP和websocket服务器,这一篇博客打算总结一下netty集成到springboot中的操作。为后面实现一个基于springboot的即时通信小程序做准备。这里我们依旧以上篇博客的websocket的实例代码为基础

项目的搭建

搭建一个基于springboot的项目这里就不赘述了,这个很容易,在线的spring.io一键化搭建,我们需要做的无非就是将之前通过main函数启动的操作,封装成一个组件交给springboot去启动

//我们要做的无非就是将这段代码交给springboot去启动。
@Slf4j
public class SpringBootNettyWSServer {

    public static void main(String[] args) {
        new NettyWebSocketServer().start(9908,"127.0.0.1");
    }

    public void start(int port, String host) {
        EventLoopGroup mainGroup = new NioEventLoopGroup();
        EventLoopGroup subGroup = new NioEventLoopGroup();
        try {
            log.info("服务端启动");
            ServerBootstrap serverBootstrap = new ServerBootstrap();
            serverBootstrap.group(mainGroup, subGroup);
            serverBootstrap.localAddress(new InetSocketAddress(host,port));
            serverBootstrap.channel(NioServerSocketChannel.class);
            serverBootstrap.childHandler(new ChannelInitializer<SocketChannel>() {
                @Override
                protected void initChannel(SocketChannel ch) throws Exception {
                    ChannelPipeline pipeline = ch.pipeline();
                    //websocket基于Http协议,需要所有HTTP编解码器
                    pipeline.addLast(new HttpServerCodec());
                    //加入对写大数据流的支持
                    pipeline.addLast(new ChunkedWriteHandler());
                    //对HttpMessage进行聚合,聚合成FullHttpRequest或FullHttpResponse
                    pipeline.addLast(new HttpObjectAggregator(1024 * 64));

                    pipeline.addLast(new WebSocketServerProtocolHandler("/ws"));

                    pipeline.addLast(new NettyWebSocketServerHandler());
                }
            });
            ChannelFuture serverFuture = serverBootstrap.bind(port).sync();
            serverFuture.channel().closeFuture().sync();
        } catch (Exception e) {
            log.error("服务端出现异常,异常信息为:{}", e);
        } finally {
            subGroup.shutdownGracefully();
            mainGroup.shutdownGracefully();
        }
    }
}

我们要做的无非就是将上述这段代码交给springboot去启动。整体思路其实就是在springboot启动完成之后,再额外的开一个线程去启动netty

springboot集成netty

1、导入netty的依赖


<dependency>
    <groupId>io.nettygroupId>
    <artifactId>netty-allartifactId>
    <version>4.1.25.Finalversion>
dependency>

关于springboot的依赖,这里不会贴出。

2、将原有的netty启动类改成单例

上面说过,将netty的主类交给springboot托管无非就是将netty带有main方法的启动类,作为一个组件交给springboot托管,但是需要说明的是,netty的服务器只能有一个,不能有多个,因此需要改成单例

/**
 * autor:liman
 * createtime:2020/9/25
 * comment:WebSocket服务端启动类
 * springboot中整合Netty最单纯的就是将这个类交给spring管理,因此要保证只有一个
 * 容器交给了spring管理之后,不用我们手动停止Netty
 */
@Component
@Slf4j
public class SpringBootNettyWSServer {

    //用内部类的方式构建单例
    private static class SingleSpringBootNettyWSServer{
        static final SpringBootNettyWSServer nettyWSServerInstance = new SpringBootNettyWSServer();
    }

    public static SpringBootNettyWSServer getInstance(){
        return SingleSpringBootNettyWSServer.nettyWSServerInstance;
    }

    private EventLoopGroup mainGroup;
    private EventLoopGroup subGroup;
    private ServerBootstrap server;
    private ChannelFuture future;

    public SpringBootNettyWSServer() {
        mainGroup = new NioEventLoopGroup();
        subGroup = new NioEventLoopGroup();
        server = new ServerBootstrap();
        server.group(mainGroup, subGroup)
                .channel(NioServerSocketChannel.class);
        server.childHandler(new NettyWSHandlerInit());//这里加入的就会netty的初始化器,这里头就和springboot没有关系了。后续会贴上代码。
    }

    public void start() {
        future = server.bind(9908);///这里就是启动Netty,注意不要与springboot内嵌的tomcat端口冲突
        log.info("netty websocket server 启动完毕...");
    }
}

3、利用spring本身自带的事件监听属性,启动netty

当springboot的容器加载完成之后,我们就可以启动Netty,可以利用springboot的事件监听机制

/**
 * autor:liman
 * createtime:2020/9/27
 * comment:监听spring容器是否启动,如果启动了,netty也随之启动
 */
@Component
@Slf4j
public class NettyWSServerStartBoot implements ApplicationListener<ContextRefreshedEvent> {
    @Override
    public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
        if (contextRefreshedEvent.getApplicationContext().getParent() == null) {//表示可启动Netty了
            try {
                SpringBootNettyWSServer.getInstance().start();
            } catch (Exception e) {
                log.error("netty服务启动异常,异常信息为:{}",e);
            }
        }
    }
}

4、启动

可以看到日志中已经打印netty启动的日志,直接采用上一篇博客的方式,可直接测试netty整合springboot是否成功

Netty实例(二)——springboot集成Netty_第1张图片

总结

简单将netty利用springboot的事件通知机制进行了集成。

附录

netty的业务代码,和上一篇博客差不多

NettyWSHandlerInit

/**
 * autor:liman
 * createtime:2020/9/27
 * comment:netty中handler的initilizer
 */
public class NettyWSHandlerInit extends ChannelInitializer<SocketChannel> {
    @Override
    protected void initChannel(SocketChannel channel) throws Exception {
        ChannelPipeline pipeline = channel.pipeline();
        //websocket基于Http协议,需要所有HTTP编解码器
        pipeline.addLast(new HttpServerCodec());
        //加入对写大数据流的支持
        pipeline.addLast(new ChunkedWriteHandler());
        //对HttpMessage进行聚合,聚合成FullHttpRequest或FullHttpResponse
        pipeline.addLast(new HttpObjectAggregator(1024 * 64));
        pipeline.addLast(new WebSocketServerProtocolHandler("/ws"));
        pipeline.addLast(new NettyWebSocketServerHandler());
    }
}

NettyWebSocketServerHandler

/**
 * autor:liman
 * createtime:2020/9/25
 * comment:webservice的后端处理
 */
@Slf4j
public class NettyWebSocketServerHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {

    //用于记录和管理所有客户端的channel
    private static ChannelGroup clients = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);


    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        super.channelReadComplete(ctx);
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        super.exceptionCaught(ctx, cause);
    }

    @Override
    public boolean acceptInboundMessage(Object msg) throws Exception {
        return super.acceptInboundMessage(msg);
    }

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) throws Exception {
        String content = msg.text();
        log.info("接收到客户端的消息为:{}",msg);
        clients.writeAndFlush(new TextWebSocketFrame("[服务器在]" + LocalDateTime.now()
                + "接受到消息, 消息为:" + content));
    }

    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
        clients.add(ctx.channel());
    }

    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
        // 当触发handlerRemoved,ChannelGroup会自动移除对应客户端的channel
        log.info("客户端断开,channel对应的长id为:{}",ctx.channel().id().asLongText());
        log.info("客户端断开,channel对应的短id为:{}",ctx.channel().id().asLongText());
    }
}

你可能感兴趣的:(#,Netty,netty)