Netty成长之路(一):netty初体验

Netty成长之路(一):netty初体验

1、前言

相信小伙伴们在实际生产中,或多或少的都使用过或者听过netty,那netty是个啥呢?

根据官网说明:

Netty is a NIO client server framework which enables quick and easy development of network applications such as protocol servers and clients. It greatly simplifies and streamlines network programming such as TCP and UDP socket server.
‘Quick and easy’ doesn’t mean that a resulting application will suffer from a maintainability or a performance issue. Netty has been designed carefully with the experiences earned from the implementation of a lot of protocols such as FTP, SMTP, HTTP, and various binary and text-based legacy protocols. As a result, Netty has succeeded to find a way to achieve ease of development, performance, stability, and flexibility without a compromise.

Netty是一个NIO客户端服务器框架,可以快速轻松地开发网络应用程序,例如协议服务器和客户端。 它极大地简化和简化了TCP和UDP套接字服务器等网络编程。

"快捷方便"并不意味着最终的应用程序将遭受可维护性或性能问题的困扰。 Netty经过精心设计,结合了许多协议(例如FTP,SMTP,HTTP以及各种基于二进制和文本的旧式协议)的实施经验。 结果,Netty成功地找到了一种无需妥协即可轻松实现开发,性能,稳定性和灵活性的方法。

2、初体验Demo

我是springboot + netty4(4.1.37.Final)做的小demo,大家不尽相同,主要看看思想
首先创建一个springboot项目,在pom文件中添加配置

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

接着创建一个客户端:

import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;

public class Client {
    // IP地址
    private static final String HOST = System.getProperty("host", "127.0.0.1");
    // 端口号
    private static final int PORT1 = Integer.parseInt(System.getProperty("port", "8000"));
    private static final int PORT2 = Integer.parseInt(System.getProperty("port", "8001"));

    public static void main(String[] args) throws InterruptedException {
        // 循环发送信息
        for (;;) {
            // 睡一秒
            Thread.sleep(1000);
            EventLoopGroup workGroup = new NioEventLoopGroup();
            // 客户端
            Bootstrap b = new Bootstrap();
            // 绑定线程组
            b.group(workGroup)
                    // 客户端 -->NioSocketChannel
                    .channel(NioSocketChannel.class)
                    // 保持长连接
                    .option(ChannelOption.SO_KEEPALIVE, true)
                    // handler
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
                            // 具体的业务实现
                            socketChannel.pipeline().addLast(new ClientHandler());
                        }
                    });
            // 创建异步连接 添加多个端口
            ChannelFuture cf1 = b.connect(HOST, PORT1).sync();
            ChannelFuture cf2 = b.connect(HOST, PORT2).sync();

            // client向server端发送数据  Buffer形式
            cf1.channel().writeAndFlush(Unpooled.copiedBuffer("Hello Server".getBytes()));
            cf2.channel().writeAndFlush(Unpooled.copiedBuffer("Hello World".getBytes()));
            
            // 异步关闭连接
            cf1.channel().closeFuture().sync();
            cf2.channel().closeFuture().sync();

            // 销毁
            workGroup.shutdownGracefully();
        }
    }
    
}

再创建一个客户端

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;

public class Server {
    // 端口号
    private static final int PORT1 = Integer.parseInt(System.getProperty("port", "8000"));
    private static final int PORT2 = Integer.parseInt(System.getProperty("port", "8001"));

    public static void main(String[] args) {
        EventLoopGroup bossGroup = null;
        EventLoopGroup workerGroup = null;
        ServerBootstrap b = null;
        try {
            // 1、boss 线程组用于接收Client连接的
            bossGroup = new NioEventLoopGroup();
            // 2、worker用于实际业务操作
            workerGroup = new NioEventLoopGroup();
            // 3、创建启动NIO服务的辅助启动类ServerBootstrap,配置server
            b = new ServerBootstrap();
            // 4、绑定线程组
            b.group(bossGroup, workerGroup)
            // 5、指定通道 服务端为NioServerSocketChannel
            .channel(NioServerSocketChannel.class)
            // 6、使用childHandler绑定具体的事务处理器(重要)
            .childHandler(new ChannelInitializer<SocketChannel>() {
                @Override
                protected void initChannel(SocketChannel socketChannel) throws Exception {
                    // 7、将自定义的serverHandler加入到管道中
                    // handler中实现真正的业务逻辑
                    socketChannel.pipeline().addLast(new ServerHandler());
                }
            })
            // 8、设置TCP连接的缓冲区
            .option(ChannelOption.SO_BACKLOG, 128)
            // 9、保持连接
            .childOption(ChannelOption.SO_KEEPALIVE,true);
            // 10、绑定指定的端口,进行监听
            ChannelFuture cf2 = b.bind(PORT1).sync();
            ChannelFuture cf3 = b.bind(PORT2).sync();
            // 11、异步等待关闭
            cf2.channel().closeFuture().sync();
            cf3.channel().closeFuture().sync();

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            assert workerGroup != null;
            workerGroup.shutdownGracefully();
            bossGroup.shutdownGracefully();
        }
    }
}

客户端的实现handler

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.ReferenceCountUtil;

public class ClientHandler extends ChannelInboundHandlerAdapter {
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        try {
            // 1、接收服务端发来的数据 ByteBuf
            ByteBuf buf = (ByteBuf)msg;
            // 2、创建一个和buf一样长度的字节空数组
            byte[] data = new byte[buf.readableBytes()];
            // 3、将buf中的数据读取到data数组中
            buf.readBytes(data);
            // 4、将data数组进行包装并以String格式输出
            String response = new String(data, "utf-8");
            // 打印数据
            System.out.println("server response is " + response);
            ctx.close();
        } finally {
            ReferenceCountUtil.release(msg);
        }
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        // 出现异常的时候关闭连接
        cause.printStackTrace();
        ctx.close();
    }
}

服务端的实现handler

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.ReferenceCountUtil;

public class ServerHandler extends ChannelInboundHandlerAdapter {

    /**
     * 每当从客户端收到新的数据时,这个方法会在收到消息时被调用
     * ByteBuf是一个引用计数对象,这个对象必须显示地调用release()方法来释放。
     * 请记住处理器的职责是释放所有传递到处理器的引用计数对象。
    */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        try {
            // 以下代码为接收客户端信息
            // 1、接收客户端发来的数据 ByteBuf
            ByteBuf buf = (ByteBuf)msg;
            // 2、创建一个和buf一样长度的字节空数组
            byte[] data = new byte[buf.readableBytes()];
            // 3、将buf中的数据读取到data数组中
            buf.readBytes(data);
            // 4、将data数组进行包装并以String格式输出
            String request = new String(data, "utf-8");
            // 打印数据
            System.out.println("client request is " + request);

            // server端向client端发送反馈数据 绑定了多端口,则都会去发送信息
            // 添加监听 当服务端向客户端发送完数据后,关闭connect连接
            ctx.writeAndFlush(Unpooled.copiedBuffer("client,client, this is server".getBytes()))
                    .addListener(ChannelFutureListener.CLOSE);
            /**
             * ChannelFutureListener,当一个写请求完成时通知并且关闭Channel
             * 加上监听 意味着服务端回送数据到客户端时 连接关闭(短连接)
             * 不加监听 意味着客户端与服务端一直保持连接状态(长连接)
             */
            ctx.close();
        } finally {
            ReferenceCountUtil.release(msg);
        }

    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        // 出现异常的时候关闭连接
        cause.printStackTrace();
        ctx.close();
    }
}

3、运行

先启动server端,然后启动client端,client结果如下
在这里插入图片描述
server结果
在这里插入图片描述
这只是一个非常小的demo,难上台面,但是主要感受的是netty的NIO思想,netty编程是一个系列,我将持续更新,同时,公司主要产品即将重构,作为架构组一员,我将全程记录我们重构之路,感兴趣的可以关注我,共同学习,共同进步。

越知道你就越不知道,技术有限,理解有限,欢迎指正~

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值