Java知识netty

《跟闪电侠学Netty》01: 服务端启动流程介绍[附视频]

2018-06-24  本文已影响2121人  简书闪电侠

最小化服务端启动demo

这篇文章通过图文+视频的方式介绍Netty服务端启动流程,习惯看视频操作,体验敲代码快感的小伙伴可以直接翻到文末哦~

我们先来看一下最小化服务端启动demo,我们直接来上代码,然后逐个解释

NettyServer.java

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

        ServerBootstrap serverBootstrap = new ServerBootstrap();
        serverBootstrap
                .group(boosGroup, workerGroup)
                .channel(NioServerSocketChannel.class)
                .childHandler(new ChannelInitializer<NioSocketChannel>() {
                    protected void initChannel(NioSocketChannel ch) {
                    }
                });

        serverBootstrap.bind(8000);
    }
}

自动绑定递增端口

在上面代码中我们绑定了8000端口,接下来我们实现一个稍微复杂一点的逻辑,我们指定一个起始端口号,比如1000,然后呢,我们从1000号端口往上找一个端口,直到这个端口能够绑定成功,比如1000端口不可用,我们就尝试绑定1001,然后1002,依次类推。

serverBootstrap.bind(8000);这个方法呢,它是一个异步的方法,调用之后是立即返回的,他的返回值是一个ChannelFuture,我们可以给这个ChannelFuture添加一个监听器GenericFutureListener,然后我们在GenericFutureListeneroperationComplete方法里面,我们可以监听端口是否绑定成功,接下来是监测端口是否绑定成功的代码片段

serverBootstrap.bind(8000).addListener(new GenericFutureListener<Future<? super Void>>() {
    public void operationComplete(Future<? super Void> future) {
        if (future.isSuccess()) {
            System.out.println("端口绑定成功!");
        } else {
            System.err.println("端口绑定失败!");
        }
    }
});

我们接下来从1000端口号,开始往上找端口号,直到端口绑定成功,我们要做的就是在 if (future.isSuccess())的else逻辑里面重新绑定一个递增的端口号,接下来,我们把这段绑定逻辑抽取出一个bind方法

private static void bind(final ServerBootstrap serverBootstrap, final int port) {
    serverBootstrap.bind(port).addListener(new GenericFutureListener<Future<? super Void>>() {
        public void operationComplete(Future<? super Void> future) {
            if (future.isSuccess()) {
                System.out.println("端口[" + port + "]绑定成功!");
            } else {
                System.err.println("端口[" + port + "]绑定失败!");
                bind(serverBootstrap, port + 1);
            }
        }
    });
}

然后呢,以上代码中最关键的就是在端口绑定失败之后,重新调用自身方法,并且把端口号加一,然后,在我们的主流程里面,我们就可以直接调用

bind(serverBootstrap, 1000)

读者可以自定修改代码,运行之后可以看到效果,最终会发现,端口成功绑定了在1024,从1000开始到1023,端口均绑定失败了,这是因为在我的MAC系统下,1023以下的端口号都是被系统保留了,需要ROOT权限才能绑定。

以上就是自动绑定递增端口的逻辑,接下来,我们来一起学习一下,服务端启动,我们的引导类ServerBootstrap除了指定线程模型,IO模型,连接读写处理逻辑之外,他还可以干哪些事情?

服务端启动其他方法

handler() 方法

serverBootstrap.handler(new ChannelInitializer<NioServerSocketChannel>() {
    protected void initChannel(NioServerSocketChannel ch) {
        System.out.println("服务端启动中");
    }
})

handler()方法呢,可以和我们前面分析的childHandler()方法对应起来,childHandler()用于指定处理新连接数据的读写处理逻辑,handler()用于指定在服务端启动过程中的一些逻辑,通常情况下呢,我们用不着这个方法。

attr() 方法

serverBootstrap.attr(AttributeKey.newInstance("serverName"), "nettyServer")

attr()方法可以给服务端的channel,也就是NioServerSocketChannel指定一些自定义属性,然后我们可以通过channel.attr()取出这个属性,比如,上面的代码我们指定我们服务端channel的一个serverName属性,属性值为nettyServer,其实说白了就是给NioServerSocketChannel维护一个map而已,通常情况下,我们也用不上这个方法。

那么,当然,除了可以给服务端channel NioServerSocketChannel指定一些自定义属性之外,我们还可以给每一条连接指定自定义属性

childAttr() 方法

serverBootstrap.childAttr(AttributeKey.newInstance("clientKey"), "clientValue")

上面的childAttr可以给每一条连接指定自定义属性,然后后续我们可以通过channel.attr()取出该属性,详情请看视频演示

childOption() 方法

serverBootstrap
        .childOption(ChannelOption.SO_KEEPALIVE, true)
        .childOption(ChannelOption.TCP_NODELAY, true)

childOption()可以给每条连接设置一些TCP底层相关的属性,比如上面,我们设置了三种TCP属性,其中

其他的参数这里就不一一讲解,有兴趣的同学可以去这个类里面自行研究。

option()

除了给每个连接设置这一系列属性之外,我们还可以给服务端channel设置一些属性,最常见的就是so_backlog,如下设置

serverBootstrap.option(ChannelOption.SO_BACKLOG, 1024)

表示系统用于临时存放已完成三次握手的请求的队列的最大长度,如果连接建立频繁,服务器处理创建新连接较慢,可以适当调大这个参数

总结

相信初学者跟着这篇文章敲打代码之后,能够独立地启动服务端,更多精彩,可以见视频
如果,你觉得这个过程比较简单,想深入学习,了解服务端启动的底层原理,可以在imooc.com上搜索netty,找到我的源码分析视频,对应的第三章有对本节的最强原理解释。

如果你没有学习过Netty,那么本系列将会是你零基础入门最好的资料,如果你对Netty有所掌握,想深入了解Netty的细节,摸透Netty的底层原理,那么我的源码分析视频(imooc.com搜索"Netty")将会是你最好的选择,截止目前,已经有400多位小伙伴加入。

附录 《跟闪电侠学Netty》目录,不定期更新,欢迎关注

上一篇下一篇

猜你喜欢

热点阅读