netty集成开发-个人笔记

本文档展示了如何在Spring Boot项目中利用Dubbo和Netty搭建WebSocket服务。首先引入相关依赖,然后创建NettyServer启动类,配置NioEventLoopGroup和ServerBootstrap。接着,定义NettyServerInitializer配置HTTP协议支持和心跳检测。最后,实现ChatHandler处理WebSocket消息,并通过NettyAllChannelServiceImpl进行广播。此外,还包含了一个简单的远程调用接口示例。

导包

本人项目是用web形式启用netty的(看个人需求)

<dependency>
    <groupId>org.apache.dubbo</groupId>
    <artifactId>dubbo-spring-boot-starter</artifactId>
</dependency>

单独引入使用

<dependency>
	<groupId>io.netty</groupId>
	<artifactId>netty-all</artifactId>
	<version>4.1.63.Final</version>
</dependency>

项目代码

启动类

package com.ronrun.communication.config.nertty;

import com.ronrun.common.util.LogPrintUtil;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import lombok.Data;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;

@Component
@Data
public class NettyServer {

	//netty服务绑定端口
    private int port = 9999;

    // 构造方法少了会报错
    public NettyServer() {
    }

    /**
     * 描述:启动Netty Websocket服务器
     */
    @PostConstruct //这个注解会随着类加载而加载 就不需要使用 ServletContextListener(服务上下文监听了)
    public void start() throws Exception {
    	//EventLoopGroup事件循环组-NioEventLoopGroup非阻塞的事件循环组
    	//boss组(老板) 用于处理ServerSocketChannel的数据
        NioEventLoopGroup bossGroup = new NioEventLoopGroup();
        //worker组(工人) 用于处理SocketChannel的数据
        NioEventLoopGroup workerGroup = new NioEventLoopGroup();
        //netty服务启动类
        ServerBootstrap serverBootstrap = new ServerBootstrap();
        serverBootstrap.group(bossGroup, workerGroup) //boss辅助客户端的tcp连接请求  worker负责与客户端之前的读写操作
                .channel(NioServerSocketChannel.class) //配置客户端的channel类型-一般使用非阻塞的管道
                .childHandler(new NettyServerInitialzer()); //绑定I/O事件的处理类,WebSocketChildChannelHandler中定义

		//sync 一定要开启同步,否则会在nio机制的影响,netty还没启动完,就先执行其他代码
        ChannelFuture sync = serverBootstrap.bind(port).sync();
        //检查ChannelFuture是否启动完毕
        if (sync.isSuccess()) {
        	//忽略这个是我写静态日志调用类
            LogPrintUtil.logRes("Netty Websocket服务器启动完成, ms:已绑定端口<{}>阻塞式等候客户端连接",port);
        }
    }

}

添加netty服务配置

package com.ronrun.communication.config.nertty;

import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
import io.netty.handler.timeout.IdleStateHandler;

/*
 * 功能描述:
 * 〈netty服务配置类〉
 *
 * @param null 1
 * @return :
 * @author : ljq-刘俊秦
 * @date : 2020/5/29 0029 下午 8:14
 */
public class NettyServerInitialzer extends ChannelInitializer<SocketChannel> {

    @Override
    protected void initChannel(SocketChannel socketChannel) throws Exception {

        //========================  支持http协议的 =========================
        ChannelPipeline pipeline = socketChannel.pipeline();
        // websocket 基于http协议,所以要有http编解码器
        pipeline.addLast(new HttpServerCodec());
        // 对httpMessage进行聚合,聚合成FullHttpRequest或FullHttpResponse
        // 几乎在netty中的编程,都会使用到此handler 1024*64=65536
        pipeline.addLast(new HttpObjectAggregator(65536));

        //========================  增加心跳支持 start =========================
        pipeline.addLast(new IdleStateHandler(8, 10, 12));
        //自定义心跳空闲检查
        pipeline.addLast(new HeartBaeatHandler());
        //========================  增加心跳支持 end =========================

        /*
         * websocket 服务处理协议,用于指定给客户端连接的访问路由:/ws
         * */
        pipeline.addLast(new WebSocketServerProtocolHandler("/ws"));


        //自定义
        pipeline.addLast(new ChatHandler());
    }

}

自定义心跳检测

package com.ronrun.communication.config.nertty;

import com.ronrun.common.util.LogPrintUtil;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.timeout.IdleStateEvent;

/**
 * 功能描述:
 * 〈心跳检测〉
 *
 * @author : ljq-刘俊秦
 * @return :
 * @date : 2020/6/2 0002 下午 8:09
 */

public class HeartBaeatHandler extends ChannelInboundHandlerAdapter {

    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
        Channel channel = ctx.channel();
        if (evt instanceof IdleStateEvent) {
            IdleStateEvent event = (IdleStateEvent) evt;
            switch (event.state()) {
                case READER_IDLE :
                    //进入读空闲,不做处理
                    LogPrintUtil.logRes("移除读空闲通道", channel.id().asLongText());
                    break;
                case WRITER_IDLE :
                    //进入写空闲不做处理
                    LogPrintUtil.logRes("移除写空闲通道", channel.id().asLongText());
                    break;
                case ALL_IDLE :
                    channel.close();
                    LogPrintUtil.logRes("移除读写空闲通道", channel.id().asLongText());
                    break;
            }
        }
    }
}


自定义聊天通道处理器

package com.ronrun.communication.config.nertty;

import com.ronrun.common.util.LogPrintUtil;
import com.ronrun.communication.service.impl.NettyAllChannelServiceImpl;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;

public class ChatHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {

    @Override
    protected void channelRead0(ChannelHandlerContext channelHandlerContext, TextWebSocketFrame textWebSocketFrame) throws Exception {
        String content = textWebSocketFrame.text();
        String f_text = "服务器接收到的参数:" + content;
        LogPrintUtil.logRes(f_text);
        NettyAllChannelServiceImpl.sendAllStatic(new TextWebSocketFrame(f_text));
    }

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

    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
        //管道组会自动移除所以不需要解决
//        channels.remove(ctx.channel());
    }
}

简易的远程调用接口(duboo形式实现)

package com.ronrun.communication.service.impl;

import com.ronrun.common.service.INettyAllChannelService;
import io.netty.channel.Channel;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.util.concurrent.GlobalEventExecutor;
import org.apache.dubbo.config.annotation.Service;

@Service
public class NettyAllChannelServiceImpl implements INettyAllChannelService {

    private static ChannelGroup channels = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);

    public static void addChannel(Channel channel) {
        channels.add(channel);
    }

    public static void removeChannel(Channel channel) {
        channels.remove(channel);
    }

    public static void sendAllStatic(TextWebSocketFrame textWebSocketFrame) {
        channels.writeAndFlush(textWebSocketFrame);
    }

    @Override
    public boolean sendAll(String text) {
        //不能直接使用channels 需要调用静态自己分方法。否则会报错
        sendAllStatic(new TextWebSocketFrame(text));
        return true;
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

尘叶风凌

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值