Netty原理分析及实战(三)-高可用服务端搭建

本文是Netty原理分析及实战(三)-高可用服务端搭建,若要关注前文,请点击传送门:

Netty原理分析及实战(二)-同步非阻塞模型(NIO)

前文NIO通讯实现方式。从这篇文章开始,我们正式开始讲Netty,之前我们已经通过BIO、NIO通讯模型实现了聊天室的功能,不知道各位有没有觉得异常麻烦,如果通过Netty来做的话,这个过程会简便很多,并且基于Netty搭建的服务端会更加高可用。

一、Netty简介

Netty 是一个 NIO client-server(客户端服务器)框架,使用 Netty 可以快速开发网络应用,例如服务器和客户 端协议。 Netty 提供了一种新的方式来使开发网络应用程序,这种新的方式使得它很容易使用和有很强的扩展性。 Netty 的内部实现时很复杂的,但是 Netty 提供了简单易用的 api 从网络处理代码中解耦业务逻辑。 Netty 是完全基 于 NIO 实现的,所以整个 Netty 都是异步的。简单点说就是Netty提供了一个简单,间接的方法来操作网络之间的通讯。

Netty架构示意图如下:

è¿éåå¾çæè¿°

 如何理解NioEventLoop和NioEventLoopGroup 

1)NioEventLoop实际上就是工作线程,可以直接理解为一个线程。NioEventLoopGroup是一个线程池,线程池中的线程就是NioEventLoop。 

2)实际上bossGroup中有多个NioEventLoop线程,每个NioEventLoop绑定一个端口,也就是说,如果程序只需要监听1个端口的话,bossGroup里面只需要有一个NioEventLoop线程就行了。

每个NioEventLoop都绑定了一个Selector,所以在Netty的线程模型中,是由多个Selecotr在监听IO就绪事件。而Channel注册到Selector。

一个Channel绑定一个NioEventLoop,相当于一个连接绑定一个线程,这个连接所有的ChannelHandler都是在一个线程中执行的,避免了多线程干扰。更重要的是ChannelPipline链表必须严格按照顺序执行的。单线程的设计能够保证ChannelHandler的顺序执行。

一个NioEventLoop的selector可以被多个Channel注册,也就是说多个Channel共享一个EventLoop。EventLoop的Selecctor对这些Channel进行检查。

二、准备工作

和前文环境一样就可以了。

三、创建Netty服务端(Netty Server)

我们服务端分别创建Server和自定义逻辑处理类,服务端绑定端口时,如果发现端口被占用,端口号会递增直到绑定端口成功。

1、NettyServer

/**
 * Netty服务端
 */
public class NettyServer {

    public static void main(String[] args) {
        NioEventLoopGroup bossGroup = new NioEventLoopGroup();
        NioEventLoopGroup workerGroup = new NioEventLoopGroup();

        ServerBootstrap serverBootstrap = new ServerBootstrap();
        serverBootstrap
                .group(bossGroup, workerGroup)
                .channel(NioServerSocketChannel.class)
                .childHandler(new ChannelInitializer() {
                    protected void initChannel(NioSocketChannel ch) {
                        System.out.println("服务端启动中");
                        ch.pipeline().addLast(new FirstServerHandler());
                    }
                });
        serverBootstrap.attr(AttributeKey.newInstance("serverName"), "nettyServer");
        bind(serverBootstrap, 1000);
    }

    private static void bind(final ServerBootstrap serverBootstrap, final int port) {
        serverBootstrap.bind(port).addListener(future -> {
            if (future.isSuccess()) {
                System.out.println("端口绑定成功!");
            } else {
                System.err.println("端口绑定失败!");
            }
        });
    }
}

我们在服务端初始化阶段注册了我们第一个逻辑处理类FirstServerHandler。

2、自定义逻辑处理类

我们在自定义逻辑处理类的时候需要继承ChannelInboundHandlerAdapter,可通过重写它不同阶段的方法来达到收发消息的目的,下面我们通过重写了channelRead方法来接收并回复服务端消息。

/**
 * Netty服务端处理逻辑
 */
public class FirstServerHandler extends ChannelInboundHandlerAdapter {

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) {
        // ... 收数据逻辑省略
        ByteBuf byteBuf = (ByteBuf) msg;
        System.out.println(new Date() + ": 服务端读到数据 -> " + byteBuf.toString(Charset.forName("utf-8")));
        // 回复数据到客户端
        System.out.println(new Date() + ": 服务端写出数据");
        ByteBuf out = getByteBuf(ctx);
        ctx.channel().writeAndFlush(out);
    }

    private ByteBuf getByteBuf(ChannelHandlerContext ctx) {
        byte[] bytes = "你好,欢迎关注我的CSDN博客!".getBytes(Charset.forName("utf-8"));

        ByteBuf buffer = ctx.alloc().buffer();

        buffer.writeBytes(bytes);

        return buffer;
    }
}

到此Netty服务端搭建完成。

你可能感兴趣的:(Netty,Netty原理分析及实战)