资讯专栏INFORMATION COLUMN

【Netty】服务端和客户端

dreambei / 567人阅读

摘要:客户端客户端启动的常规代码如下用于接收客户端请求的线程池职责如下。注册对应的网络监听状态为到多路复用器。由多路复用器在现场中轮询个,处理连接结果。具体服务端与客户端如何通信,以及内存管理等方面的知识下一次再写。

欢迎关注公众号:【爱编程
如果有需要后台回复2019赠送1T的学习资料哦!!

本文是基于Netty4.1.36进行分析

服务端

Netty服务端的启动代码基本都是如下:

private void start() throws Exception {

        final EchoServerHandler serverHandler = new EchoServerHandler();
        /**
         * NioEventLoop并不是一个纯粹的I/O线程,它除了负责I/O的读写之外
         * 创建了两个NioEventLoopGroup,
         * 它们实际是两个独立的Reactor线程池。
         * 一个用于接收客户端的TCP连接,
         * 另一个用于处理I/O相关的读写操作,或者执行系统Task、定时任务Task等。
         */
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup childGroup = new NioEventLoopGroup();

        try {
            //ServerBootstrap负责初始化netty服务器,并且开始监听端口的socket请求
            ServerBootstrap b = new ServerBootstrap();
            b.group(bossGroup, childGroup)
                    .channel(NioServerSocketChannel.class)
                    .localAddress(new InetSocketAddress(port))
                    .childHandler(new ChannelInitializer() {
                        @Override
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
//                            为监听客户端read/write事件的Channel添加用户自定义的ChannelHandler
                            socketChannel.pipeline().addLast(serverHandler);
                        }
                    });

            ChannelFuture f = b.bind().sync();

            f.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            bossGroup.shutdownGracefully().sync();
            childGroup.shutdownGracefully().sync();
        }
    }

从上图的代码可以总结为以下几个步骤:

1、创建ServerBootStrap实例
2、设置并绑定Reactor线程池:EventLoopGroup,EventLoop就是处理所有注册到本线程的Selector上面的Channel
3、设置并绑定服务端的channel
4、5、创建处理网络事件的ChannelPipeline和handler,网络时间以流的形式在其中流转,handler完成多数的功能定制:比如编解码 SSl安全认证
6、绑定并启动监听端口
7、当轮训到准备就绪的channel后,由Reactor线程:NioEventLoop执行pipline中的方法,最终调度并执行channelHandler

服务端创建时序图

ServerBootStrap引导启动服务端

它就是主要引导启动服务端,工作包括以下:

1.创建服务端Channel

2.初始化服务端Channel

3.将Channel注册到selector

4.端口绑定

1.创建服务端Channel

流程:
首先从用户代码的bind()其实就是AbstractBootstrap.bind(),然后通过反射工厂将用户通过b.channel(NioServerSocketChannel.class)传入的NioServerSocketChannel通过调用底层的jdk的SelectorProvider创建channel,同时也接着创建好对应的ChannelPipeline
详情可以参考下图,自己去查看一下源码:

2.初始化服务端Channel

主要工作如下:

1)设置的option缓存到NioServerSocketChannelConfig里
2)设置的attr设置到channel里
3)保存配置的childOptions,配置的childAttrs 到ServerBootstrapAcceptor里
4)往NioSocketChannel的pipeline中添加一个ServerBootstrapAcceptor

主要的核心源码如下:

 @Override
    void init(Channel channel) throws Exception {
        final Map, Object> options = options0();
        synchronized (options) {
            setChannelOptions(channel, options, logger);
        }

        final Map, Object> attrs = attrs0();
        synchronized (attrs) {
            for (Entry, Object> e: attrs.entrySet()) {
                @SuppressWarnings("unchecked")
                AttributeKey key = (AttributeKey) e.getKey();
                channel.attr(key).set(e.getValue());
            }
        }

        ChannelPipeline p = channel.pipeline();

        final EventLoopGroup currentChildGroup = childGroup;
        final ChannelHandler currentChildHandler = childHandler;
        final Entry, Object>[] currentChildOptions;
        final Entry, Object>[] currentChildAttrs;
        synchronized (childOptions) {
            currentChildOptions = childOptions.entrySet().toArray(newOptionArray(0));
        }
        synchronized (childAttrs) {
            currentChildAttrs = childAttrs.entrySet().toArray(newAttrArray(0));
        }

        p.addLast(new ChannelInitializer() {
            @Override
            public void initChannel(final Channel ch) throws Exception {
                final ChannelPipeline pipeline = ch.pipeline();
                ChannelHandler handler = config.handler();
                if (handler != null) {
                    pipeline.addLast(handler);
                }

                ch.eventLoop().execute(new Runnable() {
                    @Override
                    public void run() {
                        pipeline.addLast(new ServerBootstrapAcceptor(
                                ch, currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));
                    }
                });
            }
        });
    }

小结:
总体如上面工作流程所述。
特别地建议:查看ServerBootstrapAcceptor源码,你可以发现ServerBootstrapAcceptor在channelRead事件触发的时候(也就有客户端连接的时候),把childHandler加到childChannel Pipeline的末尾,设置childHandler的options和attrs,最后把childHandler注册进childGroup

3.将Channel注册到selector

注册过程如下图

小结:
Channel 注册过程所做的工作就是将 Channel 与对应的 EventLoop 关联。

1).每个 Channel 都会关联一个特定的 EventLoop, 并且这个 Channel 中的所有 IO 操作都是在这个 EventLoop 中执行的;

2).当关联好 Channel 和 EventLoop 后, 会继续调用底层的 Java NIO SocketChannel 的 register 方法, 将底层的 Java NIO SocketChannel 注册到指定的 selector 中.

通过这两步, 就完成了 Netty Channel 的注册过程.

4.端口绑定

端口绑定的源码流程基本如下图,详情可以还是你自己读一下源码比较好点。

小结:
其实netty端口绑定是调用 jdk的javaChannel().bind(localAddress, config.getBacklog());进行绑定,然后TCP链路建立成功,Channel激活事件,通过channelPipeline进行传播。

客户端

客户端启动的常规代码如下:

  private void start() throws Exception {

        /**
         * Netty用于接收客户端请求的线程池职责如下。
         * (1)接收客户端TCP连接,初始化Channel参数;
         * (2)将链路状态变更事件通知给ChannelPipeline
         */
        EventLoopGroup group = new NioEventLoopGroup();
        try {
            Bootstrap b = new Bootstrap();
            b.group(group)
                    .channel(NioSocketChannel.class)
                    .remoteAddress(new InetSocketAddress(host,port))
                    .handler(new ChannelInitializer() {
                        @Override
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
                            socketChannel.pipeline().addLast(new EchoClientHandler());
                        }
                    });
            //绑定端口
            ChannelFuture f = b.connect().sync();

            f.channel().closeFuture().sync();
        } catch (Exception e) {
            group.shutdownGracefully().sync();
        }


    }

流程:

1.用户线程创建Bootstrap实例,通过API设置创建客户端相关的参数,异步发起客户端连接。
2.创建处理客户端连接、I/O读写的Reactor线程组NioEventLoopGroup,默认为CPU内核数的2倍。
3.通过Bootstrap的ChannelFactory和用户指定的Channel类型创建用于客户端NioSocketChannel,它的功能类似于JDK NIO类库提供的SocketChannel
4.创建默认的Channel Handler Pipeline,用于调度和执行网路事件。
5.异步发起TCP连接,判断连接是否成功。如果成功,则直接将NioSocketChannel注册到多路复用器上,监听读操作位,用于数据包读取和消息发送,如果没有立即连接成功,则注册连接监听为到多路复用器,等待连接结果。
6.注册对应的网络监听状态为到多路复用器。
7.由多路复用器在I/O现场中轮询个Channel,处理连接结果。
8.如果连接成功,设置Future结果,发送连接成功事件,触发ChannelPipeline执行。
9.由ChannelPipeline调度执行系统和用户的ChannelHandler,执行逻辑。

源码调用流程如下图:

小结:
客户端是如何发起 TCP 连接的?

如下图:

特别提醒:
在AbstractChannelHandlerContext.connect()#findContextOutbound这步操作是返回的结果next其实是头节点,也就是说在下一步next.invokeConnect()这里的next就是头节点,所以最终是调用HeadContext .connect()

总结

本文主要讲述netty服务端和客户端的简单工作流程。
具体服务端与客户端如何通信,以及内存管理等方面的知识下一次再写。

最后

如果对 Java、大数据感兴趣请长按二维码关注一波,我会努力带给你们价值。觉得对你哪怕有一丁点帮助的请帮忙点个赞或者转发哦。
关注公众号【爱编码】,回复2019有相关资料哦。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/74769.html

相关文章

  • netty实战》阅读笔记(1)——Netty 的概念及体系结构

    摘要:它使用了事件通知以确定在一组非阻塞套接字中有哪些已经就绪能够进行相关的操作。目前,可以把看作是传入入站或者传出出站数据的载体。出站事件是未来将会触发的某个动作的操作结果,这些动作包括打开或者关闭到远程节点的连接将数据写到或者冲刷到套接字。 netty的概念 定义 Netty 是一款异步的事件驱动的网络应用程序框架,支持快速地开发可维护的高性能的面向协议的服务器和客户端。我们可以很简单的...

    solocoder 评论0 收藏0
  • Netty(三) 什么是 TCP 拆、粘包?如何解决?

    摘要:是一个面向字节流的协议,它是性质是流式的,所以它并没有分段。可基于分隔符解决。编解码的主要目的就是为了可以编码成字节流用于在网络中传输持久化存储。 showImg(https://segmentfault.com/img/remote/1460000015895049); 前言 记得前段时间我们生产上的一个网关出现了故障。 这个网关逻辑非常简单,就是接收客户端的请求然后解析报文最后发送...

    YanceyOfficial 评论0 收藏0
  • Netty 源码分析之 一 揭开 Bootstrap 神秘的红盖头 (服务器端)

    摘要:目录源码分析之番外篇的前生今世的前生今世之一简介的前生今世之二小结的前生今世之三详解的前生今世之四详解源码分析之零磨刀不误砍柴工源码分析环境搭建源码分析之一揭开神秘的红盖头源码分析之一揭开神秘的红盖头客户端源码分析之一揭开神秘的红盖头服务器 目录 Netty 源码分析之 番外篇 Java NIO 的前生今世 Java NIO 的前生今世 之一 简介 Java NIO 的前生今世 ...

    张金宝 评论0 收藏0
  • 【自己读源码】Netty4.X系列(三) Channel Register

    摘要:我想这很好的解释了中,仅仅一个都这么复杂,在单线程或者说串行的程序中,编程往往是很简单的,说白了就是调用,调用,调用然后返回。 Netty源码分析(三) 前提概要 这次停更很久了,原因是中途迷茫了一段时间,不过最近调整过来了。不过有点要说下,前几天和业内某个大佬聊天,收获很多,所以这篇博文和之前也会不太一样,我们会先从如果是我自己去实现这个功能需要怎么做开始,然后去看netty源码,与...

    darkbug 评论0 收藏0
  • netty ssl 服务

    摘要:一证书本文只介绍版,其他系统只供参考生成证书下载并安装未编译编译好在目录下打开命令行,输入在本目录得到和文件生成服务端和客户端私钥命令行输入密码自己设定,好几个密码,别弄乱了就好,分不清的话都设成一样的根据生成文件 一、证书 (本文只介绍windows版,其他系统只供参考) 1.生成ca证书 下载 openssl 并安装 未编译 编译好 在openssl/bin目录下打开命令行,输入...

    rickchen 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<