Netty hello world 入门源码分析

第一节简单提了什么是网络编程,Netty 做了什么,Netty 都有哪些功能组件。这一节就具体进入 Netty 的世界,我们从用 Netty 的功能实现基本的网络通信开始分析 各个组件的使用。

1. 一个简单的发送接收消息的例子

话不多说,先来实现一个发送接收消息的例子。本实例基于 SpringBoot 工程搭建。

项目类文件如下:

在这里插入图片描述

客户端和服务端的主要代码分为3个部分:启动器,ChannelInitializer,eventHandler。

相关代码已经上传 GitHub,请参阅:点我 (๑¯ ³ ¯๑)

Server端:

package com.rickiyang.learn.helloWorld;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import lombok.extern.slf4j.Slf4j;

/**
 * @author: rickiyang
 * @date: 2020/3/15
 * @description: server 端
 */
@Slf4j
public class HwServer {
   
   

    private int port;

    public HwServer(int port) {
   
   
        this.port = port;
    }

    public void start(){
   
   
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workGroup = new NioEventLoopGroup();

        ServerBootstrap server = new ServerBootstrap().group(bossGroup,workGroup)
                .channel(NioServerSocketChannel.class)
                .childHandler(new ServerChannelInitializer());

        try {
   
   
            ChannelFuture future = server.bind(port).sync();
            future.channel().closeFuture().sync();
        } catch (InterruptedException e) {
   
   
            log.error("server start fail",e);
        }finally {
   
   
            bossGroup.shutdownGracefully();
            workGroup.shutdownGracefully();
        }
    }

    public static void main(String[] args) {
   
   
        HwServer server = new HwServer(7788);
        server.start();
    }
}

server initializer:

package com.rickiyang.learn.helloWorld;

import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import lombok.extern.slf4j.Slf4j;

/**
 * @author: rickiyang
 * @date: 2020/3/15
 * @description:
 */
@Slf4j
public class HwServerHandler extends ChannelInboundHandlerAdapter {
   
   

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
   
   
        log.info("server channelActive");
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
   
   
        log.info(ctx.channel().remoteAddress()+"===>server: "+msg.toString());
        ctx.write("hi, received your msg");
        ctx.flush();
    }

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

}

server handler:

package com.rickiyang.learn.helloWorld;

import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import lombok.extern.slf4j.Slf4j;

/**
 * @author: rickiyang
 * @date: 2020/3/15
 * @description:
 */
@Slf4j
public class HwServerHandler extends ChannelInboundHandlerAdapter {
   
   

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
   
   
        log.info("server channelActive");
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
   
   
        log.info(ctx.channel().remoteAddress()+"===>server: "+msg.toString());
        ctx.write("hi, received your msg");
        ctx.flush();
    }

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

}

客户端代码:

client:

package com.rickiyang.learn.helloWorld;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import lombok.extern.slf4j.Slf4j;

/**
 * @author: rickiyang
 * @date: 2020/3/15
 * @description:
 */
@Slf4j
public class HwClient {
   
   

    private  int port;
    private  String address;

    public HwClient(int port, String address) {
   
   
        this.port = port;
        this.address = address;
    }

    public void start(){
   
   
        EventLoopGroup group = new NioEventLoopGroup();

        Bootstrap bootstrap = new Bootstrap();
        bootstrap.group(group)
                .channel(NioSocketChannel.class)
                .handler(new ClientChannelInitializer());
        try {
   
   
            ChannelFuture future = bootstrap.connect(address,port).sync();
            future.channel().writeAndFlush("Hello world, i'm online");
            future.channel().closeFuture().sync();
        } catch (Exception e) {
   
   
            log.error("client start fail",e);
        }finally {
   
   
            group.shutdownGracefully();
        }

    }

    public static void main(String[] args) {
   
   
        HwClient client = new HwClient(7788,"127.0.0.1");
        client.start();
    }
}

client initializer :

package com.rickiyang.learn.helloWorld;

import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;

/**
 * Created by Administrator on 2017/3/11.
 */
public class ClientChannelInitializer extends  ChannelInitializer<SocketChannel> {
   
   

    protected void initChannel(SocketChannel socketChannel) throws Exception {
   
   
        ChannelPipeline pipeline = socketChannel.pipeline();

        pipeline.addLast("decoder", new StringDecoder());
        pipeline.addLast("encoder", new StringEncoder());

        // 客户端的逻辑
        pipeline.addLast("handler", new HwClientHandler());
    }
}

client handler :

package com.rickiyang.learn.helloWorld;

import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import lombok.extern.slf4j.Slf4j;

/**
 * @author: rickiyang
 * @date: 2020/3/15
 * @description:
 */
@Slf4j
public class HwClientHandler extends ChannelInboundHandlerAdapter {
   
   

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
   
   
        log.info("server say : " + msg.toString());
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
   
   
        log.info("client channelActive");
        ctx.fireChannelActive();
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
   
   
        log.info("Client is close");
    }


}

代码很简单,主要功能就是启动 服务端 和 客户端, 然后实现一个简单的 handler ,在handler 中获取消息并打印,本地先启动服务端 main 函数,再启动客户端即可。

2. 从 EventLoopGroup 开始说起

观看客户端 和 服务端 的启动类,我们看到都有相同的特性:

创建 EventLoopGroup 去监听 channel,然后使用定义的 handler处理对应的事件。

Netty 是一个异步事件驱动的 NIO 框架,所有IO操作都是异步非阻塞的。Netty 实际上是使用 Threads(多线程)处理 I/O 事件。

EventLoopGroup 是个啥东西呢?我们回想一下 Reactor 模型,主要的操作是使用 Selector 监听 channel 上的事件,Reactor 模型有三种结构,首先是单线程模型:

在这里插入图片描述

这种模型显而易见始终只有一个 Acceptor 线程在处理客户端连接事件和服务端产生的读写事件,好处是始终只有一个线程在工作不会产生并发带来的一系列问题。但是不足之处也显而易见:

  1. 一个线程来处理对于现在的多核系统来说有点浪费资源;
  2. 虽然是使用异步非阻塞I/O处理,但是面对大并发的请求场景,很有可能会负载过重,堆积事件,这样客户端就会有超时发生,然后重复发送请求,必然会造成系统超载;
  3. 单线程如果挂掉了系统就停止了,这种场景如何处理。

所以这种单线程模型对于当今系统的发展是没有适用场景的。接着又演变出多线程的 Reactor 模型。

在这里插入图片描述

在多线程模型下,Acceptor 是一个单独的线程专门处理 Client 的请求连接事件,所有的 I/O 操作都由一个特定的 NIO 线程池负责,每个客户端都与一个特定的 NIO 线程池绑定,因此这个客户端连接中的所有 I/O 操作都是在同一个线程中完成的。客户端连接是很多的,但是 NIO 线程很少,所以 一个 NIO 线程可以同时绑定到多个客户端连接中。

从上面的模型找缺点的话,很显然能发现还是有单点的问题:处理客户端连接请求的线程仍旧只有一个,如果这个线程挂了,整个系统将不可用。所以这种超级并发的情况也要考虑啊,系统不能有单点,接着改:

在这里插入图片描述

现在的系统就没有单点问题了,但是也增加了复杂性。

我们刚才在说 EventLoopGroup ,为啥突然又转到了 Reactor 模型上去了呢? 前面说过,Netty 是基于 NIO 编程的,NIO 又是基于 Reactor 模型的,自然 Netty 的编程模型也是 Reactor 。而 EventLoopGroup 其实就是来设置 Reactor 模型的类型根据不同的参数方式。

我们来看 Server 启动类中的写法:

EventLoopGroup bossGroup = new NioEventLoopGroup
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值