陕西网站建设陕icp备蚌埠网站开发

当前位置: 首页 > news >正文

陕西网站建设陕icp备,蚌埠网站开发,网站空间 推荐,wordpress注册邮箱后缀文章目录 前言一、引导程序类二、AbstractBootStrap 抽象类三、Bootstrap 类四、ServerBootstrap 类五、引导服务器5.1、 实例化引导程序类5.2、设置 EventLoopGroup5.3、指定 Channel 类型5.4、指定 ChannelHandler5.5、设置 Channel 选项5.6、绑定端口启动服务 六、引导客户端… 文章目录 前言一、引导程序类二、AbstractBootStrap 抽象类三、Bootstrap 类四、ServerBootstrap 类五、引导服务器5.1、 实例化引导程序类5.2、设置 EventLoopGroup5.3、指定 Channel 类型5.4、指定 ChannelHandler5.5、设置 Channel 选项5.6、绑定端口启动服务 六、引导客户端6.1、实例化引导程序类6.2、设置 EventLoopGroup6.3、指定 Channel 类型6.4、设置 Channel 选项6.5、指定 ChannelHandler6.6、连接到服务器 总结 前言 回顾Netty系列文章 Netty 概述一Netty 架构设计二Netty Channel 概述三Netty ChannelHandler四ChannelPipeline源码分析五字节缓冲区 ByteBuf 六上字节缓冲区 ByteBuf七下Netty 如何实现零拷贝八 程序引导类Bootstrap可以理解为是一个程序的入口程序在 Java 程序中就是一个包含 main 方法的程序类。在 Netty 中引导程序还包含一系列的配置项。本篇文章我们就来介绍 Netty 的引导程序。 一、引导程序类 引导程序类是一种引导程序使 Netty 程序可以很容易地引导一个 Channel。在 Netty 中承担引导程序的是 AbstractBootStrap抽象类。 引导程序类都在io.netty.bootstrap包下。AbstractBootStrap抽象类有两个子类Bootstrap和ServerBootstrap分别用于引导客户端程序及服务的程序。 下图展示了引导程序类的关系 从上图可以看出AbstractBootStrap抽象类实现了Cloneable接口。那么为什么需要实现Cloneable接口呢 二、AbstractBootStrap 抽象类 在 Netty 中经常需要创建多个具有类似配置或者完全相同配置的Channel。为了支持这种模式而又不避免为每个Channel都创建并配置一个新的引导类实例因此AbstractBootStrap被标记为了Cloneable。在一个已经配置完成的引导实例上调用clone()方法将返回另一个可以立即使用的引导类实例。 这种方式只会创建引导类实例的EventLoopGroup的一个浅拷贝所以EventLoopGroup将在所有克隆的Channel实例之间共享。这是可以接受的毕竟这些克隆的Channel的生命周期都很短暂例如一个典型的场景是创建一个Channel以进行一次HTTP请求。 以下是AbstractBootStrap的核心源码 public abstract class AbstractBootstrapB extends AbstractBootstrapB, C, C extends Channel implements Cloneable {volatile EventLoopGroup group;private volatile ChannelFactory? extends C channelFactory;private volatile SocketAddress localAddress;private final MapChannelOption?, Object options new LinkedHashMap();private final MapAttributeKey?, Object attrs new ConcurrentHashMap();private volatile ChannelHandler handler;AbstractBootstrap() {//禁止从其他程序包扩展}AbstractBootstrap(AbstractBootstrapB, C bootstrap) {this.group bootstrap.group;this.channelFactory bootstrap.channelFactory;this.handler bootstrap.handler;this.localAddress bootstrap.localAddress;synchronized(bootstrap.options) {this.options.putAll(bootstrap.options);}this.attrs.putAll(bootstrap.attrs);}//… }从上述源码可以看出子类型B是其父类型的一个类型参数因此可以返回到运行时实例的引用以支持方法的链式调用。从私有变量可以看出AbstractBootStrap所要管理的启动配置包括EventLoopGroup、SocketAddress、Channel配置、ChannelHandler等信息。 AbstractBootStrap是禁止被除io.netty.bootstrap包外其他程序所扩展的因此可以看到AbstractBootStrap默认构造方法被设置为了包内可见。 三、Bootstrap 类 BootStrap 类是AbstractBootStrap抽象类的子类之一主要是用于客户端或者使用了无连接协议的应用程序。 以下是BootStrap 类的核心源码 public class Bootstrap extends AbstractBootstrapBootstrap, Channel {private static final InternalLogger logger InternalLoggerFactory.getInstance(Bootstrap.class);private static final AddressResolverGroup? DEFAULT_RESOLVER;private final BootstrapConfig config new BootstrapConfig(this);private volatile AddressResolverGroupSocketAddress resolver;private volatile SocketAddress remoteAddress;public Bootstrap() {this.resolver DEFAULT_RESOLVER;}private Bootstrap(Bootstrap bootstrap) {super(bootstrap);this.resolver DEFAULT_RESOLVER;this.resolver bootstrap.resolver;this.remoteAddress bootstrap.remoteAddress;}public Bootstrap resolver(AddressResolverGroup? resolver) {this.resolver resolver null ? DEFAULT_RESOLVER : resolver;return this;}public Bootstrap remoteAddress(SocketAddress remoteAddress) {this.remoteAddress remoteAddress;return this;}public Bootstrap remoteAddress(String inetHost, int inetPort) {this.remoteAddress InetSocketAddress.createUnresolved(inetHost, inetPort);return this;}public Bootstrap remoteAddress(InetAddress inetHost, int inetPort) {this.remoteAddress new InetSocketAddress(inetHost, inetPort);return this;}public ChannelFuture connect() {this.validate();SocketAddress remoteAddress this.remoteAddress;if (remoteAddress null) {throw new IllegalStateException(remoteAddress not set);} else {return this.doResolveAndConnect(remoteAddress, this.config.localAddress());}}public ChannelFuture connect(String inetHost, int inetPort) {return this.connect(InetSocketAddress.createUnresolved(inetHost, inetPort));}public ChannelFuture connect(InetAddress inetHost, int inetPort) {return this.connect(new InetSocketAddress(inetHost, inetPort));}public ChannelFuture connect(SocketAddress remoteAddress) {ObjectUtil.checkNotNull(remoteAddress, remoteAddress);this.validate();return this.doResolveAndConnect(remoteAddress, this.config.localAddress());}public ChannelFuture connect(SocketAddress remoteAddress, SocketAddress localAddress) {ObjectUtil.checkNotNull(remoteAddress, remoteAddress);this.validate();return this.doResolveAndConnect(remoteAddress, localAddress);}//…public Bootstrap validate() {super.validate();if (this.config.handler() null) {throw new IllegalStateException(handler not set);} else {return this;}}public Bootstrap clone() {return new Bootstrap(this);}public Bootstrap clone(EventLoopGroup group) {Bootstrap bs new Bootstrap(this);bs.group group;return bs;}public final BootstrapConfig config() {return this.config;}final SocketAddress remoteAddress() {return this.remoteAddress;}final AddressResolverGroup? resolver() {return this.resolver;}… }上述方法主要分为以下几类 group设置用于处理Channel所有事件的EventLoopGroup。channel指定了 Channel的实现类。localAddress指定 Channel应该绑定到的本地地址。如果没有指定则有操作系统创建一个随机的地址。或者也可以通过bind()或者connect()方法指定localAddress。option设置ChannelOption其将被应用到每个新创建的Channel的ChannelConfig。这些选项将会通过bind()或者connect()方法设置到Channel配置的顺序与调用先后没有关系。这个方法在Channel已经被创建后再次调用就不会再起任何效果了。支持什么样的ChannelOption取决于所使用的Channel类型。attr指定新创建的Channel的属性值。这些属性值是通过bind()或者connect()方法设置到Channel。配置的顺序取决于调用的先后顺序。这个方法在Channel已经被创建后再次调用就不会再起任何效果了。handler设置被添加到ChannelPipeline以接收事件通知的ChannelHandler。remoteAddress设置远程地址。也可以通过connect()方法来指定它。 clone创建一个当前Bootstrap的克隆其具有和原始的Bootstrap相同的设置信息。connect用于连接到远程节点并返回一个ChannelFuture并将会在连接操作完成后接收到通知。bind绑定Channel并返回一个ChannelFuture其将会在绑定操作完成之后接收到通知在那之后必须调用Channel。 BootStrap类中许多方法都继承自AbstractBootstrap类。 四、ServerBootstrap 类 ServerBootstrap 类是AbstractBootStrap抽象类的子类之一主要是用于引导服务器程序。 以下是 ServerBootstrap 类的源码 public class ServerBootstrap extends AbstractBootstrapServerBootstrap, ServerChannel {private static final InternalLogger logger InternalLoggerFactory.getInstance(ServerBootstrap.class);private final MapChannelOption?, Object childOptions new LinkedHashMap();private final MapAttributeKey?, Object childAttrs new ConcurrentHashMap();private final ServerBootstrapConfig config new ServerBootstrapConfig(this);private volatile EventLoopGroup childGroup;private volatile ChannelHandler childHandler;public ServerBootstrap() {}private ServerBootstrap(ServerBootstrap bootstrap) {super(bootstrap);this.childGroup bootstrap.childGroup;this.childHandler bootstrap.childHandler;synchronized(bootstrap.childOptions) {this.childOptions.putAll(bootstrap.childOptions);}this.childAttrs.putAll(bootstrap.childAttrs);}public ServerBootstrap group(EventLoopGroup group) {return this.group(group, group);}public ServerBootstrap group(EventLoopGroup parentGroup, EventLoopGroup childGroup) {super.group(parentGroup);if (this.childGroup ! null) {throw new IllegalStateException(childGroup set already);} else {this.childGroup (EventLoopGroup)ObjectUtil.checkNotNull(childGroup, childGroup);return this;}}public T ServerBootstrap childOption(ChannelOptionT childOption, T value) {ObjectUtil.checkNotNull(childOption, childOption);synchronized(this.childOptions) {if (value null) {this.childOptions.remove(childOption);} else {this.childOptions.put(childOption, value);}return this;}}public T ServerBootstrap childAttr(AttributeKeyT childKey, T value) {ObjectUtil.checkNotNull(childKey, childKey);if (value null) {this.childAttrs.remove(childKey);} else {this.childAttrs.put(childKey, value);}return this;}public ServerBootstrap childHandler(ChannelHandler childHandler) {this.childHandler (ChannelHandler)ObjectUtil.checkNotNull(childHandler, childHandler);return this;}//…public ServerBootstrap clone() {return new ServerBootstrap(this);}/** deprecated */Deprecatedpublic EventLoopGroup childGroup() {return this.childGroup;}final ChannelHandler childHandler() {return this.childHandler;}final MapChannelOption?, Object childOptions() {synchronized(this.childOptions) {return copiedMap(this.childOptions);}}final MapAttributeKey?, Object childAttrs() {return copiedMap(this.childAttrs);}public final ServerBootstrapConfig config() {return this.config;}//… }上述方法主要分为以下几类 group设置ServerBootstrap要用的EventLoopGroup。这个EventLoopGroup将用于ServerChannel和被接受的子Channel的 I/O 处理。channel设置将要被实例化的ServerChannel类。localAddress指定 ServerChannel应该绑定到的本地地址。如果没有指定则有操作系统创建一个随机的地址。或者也可以通过bind()方法指定localAddress。option指定要应用到新创建的ServerChannel的ChannelConfig的ChannelOption。这些选项将会通过bind()设置到Channel在bind()方法被调用之后设置或者改变ChannelOption将不会有任何效果。支持什么样的ChannelOption取决于所使用的Channel类型。childOption指定当子Channel被接受时应用到子Channel的ChannelConfig的ChannelOption。所支持的ChannelOption取决于所使用的Channel类型。attr指定ServerChannel上的属性值。这些属性值是通过bind()或者connect()方法设置到Channel。在bind()方法被调用之后它们将不会有任何效果。childAttr将属性设置给已经被接受的子Channel。之后再次调用将不会有任何效果。handler设置被添加到ServerChannel的ChannelPipeline中的ChannelHandler。childHandler设置将被添加到已被接受的子Channel的ChannelPipeline中的ChannelHandler。clone克隆一个设置好原始的ServerBootstrap相同的ServerBootstrap。bind绑定ServerChannel并返回一个ChannelFuture其将会在绑定操作完成之后接收到通知带着成功或者失败的结果。 ServerBootstrap类中许多方法都继承自AbstractBootstrap类。 五、引导服务器 为了能更好的理解引导程序下面就以 Echo 协议的服务器的代码为例。核心代码如下 // 多线程事件循环器 EventLoopGroup bossGroup new NioEventLoopGroup(); // boss EventLoopGroup workerGroup new NioEventLoopGroup(); // workertry {// 启动NIO服务的引导程序类ServerBootstrap b new ServerBootstrap(); b.group(bossGroup, workerGroup) // 设置EventLoopGroup.channel(NioServerSocketChannel.class) // 指明新的Channel的类型.childHandler(new EchoServerHandler()) // 指定ChannelHandler.option(ChannelOption.SO_BACKLOG, 128) // 设置的ServerChannel的一些选项.childOption(ChannelOption.SO_KEEPALIVE, true); // 设置的ServerChannel的子Channel的选项// 绑定端口开始接收进来的连接ChannelFuture f b.bind(port).sync(); System.out.println(EchoServer已启动端口 port);// 等待服务器 socket 关闭 。// 在这个例子中这不会发生但你可以优雅地关闭你的服务器。f.channel().closeFuture().sync(); } finally {// 优雅的关闭workerGroup.shutdownGracefully();bossGroup.shutdownGracefully(); }引导 Netty 服务器主要分为几下几个步骤。 5.1、 实例化引导程序类 在上述代码中首先是需要实例化引导程序类。由于是服务器端的程序所以实例化了一个ServerBootstrap。 5.2、设置 EventLoopGroup 设置ServerBootstrap的EventLoopGroup。上述服务器使用了两个NioEventLoopGroup一个代表boss线程组一个代表work线程组。 boss线程主要是接收客户端的请求并将请求转发给work线程处理。 boss线程是轻量的不会处理耗时的任务因此可以承受高并发的请求。而真实的 I/O 操作都是由work线程在执行。 NioEventLoopGroup是支持多线程的因此可以执行线程池的大小。如果没有指定则 Netty 会指定一个默认的线程池大小核心代码如下 private static final int DEFAULT_EVENT_LOOP_THREADS;static {// 默认 EventLoopGroup 的线程数DEFAULT_EVENT_LOOP_THREADS Math.max(1, SystemPropertyUtil.getInt(io.netty.eventLoopThreads, NettyRuntime.availableProcessors() * 2));if (logger.isDebugEnabled()) {logger.debug(-Dio.netty.eventLoopThreads: {}, DEFAULT_EVENT_LOOP_THREADS);}}public NioEventLoopGroup() {// 如果不指定 nThreads 0this(0);}/*** see MultithreadEventExecutorGroup#MultithreadEventExecutorGroup(int, Executor, Object…)/protected MultithreadEventLoopGroup(int nThreads, Executor executor, Object… args) {// 如果不指定nThreads 0默认值是 DEFAULT_EVENT_LOOP_THREADSsuper(nThreads 0 ? DEFAULT_EVENT_LOOP_THREADS : nThreads, executor, args);}从上述源码可以看出如果NioEventLoopGroup实例化时没有指定线程数则最终默认值是DEFAULT_EVENT_LOOP_THREADS而默认值DEFAULT_EVENT_LOOP_THREADS是根据当前机子的CPU 处理的个数乘以 2 得出的。 5.3、指定 Channel 类型 channel()方法用于指定ServerBootstrap的Channel类型。在本例中使用的是NioServerSocketChannel类型代表了服务器是一个基于ServerSocketChannel的实现使用基于 NIO 选择器的实现来接受新连接。 5.4、指定 ChannelHandler childHandler用于指定ChannelHandler以便处理Channel的请求。上述例子中指定的是自定义的EchoServerHandler。 5.5、设置 Channel 选项 option和childOption方法分别用于设置ServerChannel及ServerChannel的子Channel的选项。这些选项定义在ChannelOption类中包含以下常量 public class ChannelOptionT extends AbstractConstantChannelOptionT {public static final ChannelOptionByteBufAllocator ALLOCATOR valueOf(ALLOCATOR);public static final ChannelOptionRecvByteBufAllocator RCVBUF_ALLOCATOR valueOf(RCVBUF_ALLOCATOR);public static final ChannelOptionMessageSizeEstimator MESSAGE_SIZE_ESTIMATOR valueOf(MESSAGE_SIZE_ESTIMATOR);public static final ChannelOptionInteger CONNECT_TIMEOUT_MILLIS valueOf(CONNECT_TIMEOUT_MILLIS);/** deprecated Use {link MaxMessagesRecvByteBufAllocator}* and {link MaxMessagesRecvByteBufAllocator#maxMessagesPerRead(int)}./Deprecatedpublic static final ChannelOptionInteger MAX_MESSAGES_PER_READ valueOf(MAX_MESSAGES_PER_READ);public static final ChannelOptionInteger WRITE_SPIN_COUNT valueOf(WRITE_SPIN_COUNT);/** deprecated Use {link #WRITE_BUFFER_WATER_MARK}/Deprecatedpublic static final ChannelOptionInteger WRITE_BUFFER_HIGH_WATER_MARK valueOf(WRITE_BUFFER_HIGH_WATER_MARK);/** deprecated Use {link #WRITE_BUFFER_WATER_MARK}/Deprecatedpublic static final ChannelOptionInteger WRITE_BUFFER_LOW_WATER_MARK valueOf(WRITE_BUFFER_LOW_WATER_MARK);public static final ChannelOptionWriteBufferWaterMark WRITE_BUFFER_WATER_MARK valueOf(WRITE_BUFFER_WATER_MARK);public static final ChannelOptionBoolean ALLOW_HALF_CLOSURE valueOf(ALLOW_HALF_CLOSURE);public static final ChannelOptionBoolean AUTO_READ valueOf(AUTO_READ);/** If {code true} then the {link Channel} is closed automatically and immediately on write failure.* The default value is {code true}.*/public static final ChannelOptionBoolean AUTO_CLOSE valueOf(AUTO_CLOSE);public static final ChannelOptionBoolean SO_BROADCAST valueOf(SO_BROADCAST);public static final ChannelOptionBoolean SO_KEEPALIVE valueOf(SO_KEEPALIVE);public static final ChannelOptionInteger SO_SNDBUF valueOf(SO_SNDBUF);public static final ChannelOptionInteger SO_RCVBUF valueOf(SO_RCVBUF);public static final ChannelOptionBoolean SO_REUSEADDR valueOf(SO_REUSEADDR);public static final ChannelOptionInteger SO_LINGER valueOf(SO_LINGER);public static final ChannelOptionInteger SO_BACKLOG valueOf(SO_BACKLOG);public static final ChannelOptionInteger SO_TIMEOUT valueOf(SO_TIMEOUT);public static final ChannelOptionInteger IP_TOS valueOf(IP_TOS);public static final ChannelOptionInetAddress IP_MULTICAST_ADDR valueOf(IP_MULTICAST_ADDR);public static final ChannelOptionNetworkInterface IP_MULTICAST_IF valueOf(IP_MULTICAST_IF);public static final ChannelOptionInteger IP_MULTICAST_TTL valueOf(IP_MULTICAST_TTL);public static final ChannelOptionBoolean IP_MULTICAST_LOOP_DISABLED valueOf(IP_MULTICAST_LOOP_DISABLED);public static final ChannelOptionBoolean TCP_NODELAY valueOf(TCP_NODELAY);Deprecatedpublic static final ChannelOptionBoolean DATAGRAM_CHANNEL_ACTIVE_ON_REGISTRATION valueOf(DATAGRAM_CHANNEL_ACTIVE_ON_REGISTRATION);public static final ChannelOptionBoolean SINGLE_EVENTEXECUTOR_PER_GROUP valueOf(SINGLE_EVENTEXECUTOR_PER_GROUP);}5.6、绑定端口启动服务 bind()方法用于绑定端口会创建一个Channel而后启动服务。 绑定成功后返回一个ChannelFuture以代表是一个异步的操作。在上述的例子里使用的是sync()方法以同步的方式来获取服务启动的结果。 六、引导客户端 为了能更好的理解引导程序下面就以 Echo 协议的客户端的代码为例。核心代码如下 // 配置客户端 EventLoopGroup group new NioEventLoopGroup(); try {Bootstrap b new Bootstrap();b.group(group).channel(NioSocketChannel.class).option(ChannelOption.TCP_NODELAY, true).handler(new EchoClientHandler());// 连接到服务器ChannelFuture f b.connect(hostName, portNumber).sync();Channel channel f.channel();ByteBuffer writeBuffer ByteBuffer.allocate(32);try (BufferedReader stdIn new BufferedReader(new InputStreamReader(System.in))) {String userInput;while ((userInput stdIn.readLine()) ! null) {writeBuffer.put(userInput.getBytes());writeBuffer.flip();writeBuffer.rewind();// 转为ByteBufByteBuf buf Unpooled.copiedBuffer(writeBuffer);// 写消息到管道channel.writeAndFlush(buf);// 清理缓冲区writeBuffer.clear();}} catch (UnknownHostException e) {System.err.println(不明主机主机名为 hostName);System.exit(1);} catch (IOException e) {System.err.println(不能从主机中获取I/O主机名为 hostName);System.exit(1);} } finally {// 优雅的关闭group.shutdownGracefully(); }引导 Netty 客户端主要分为几下几个步骤。 6.1、实例化引导程序类 在上述代码中首先是需要实例化引导程序类。由于是客户端的程序所以实例化了一个Bootstrap。 6.2、设置 EventLoopGroup 设置Bootstrap的EventLoopGroup。不同于服务器客户端只需要使用了一个NioEventLoopGroup。 6.3、指定 Channel 类型 channel()方法用于指定Bootstrap的Channel类型。在本例中由于是客户端使用使用的是NioSocketChannel类型代表了客户端是一个基于SocketChannel的实现使用基于 NIO 选择器的实现来发起连接请求。 6.4、设置 Channel 选项 option用于设置Channel的选项。这些选项定义在ChannelOption类中。 6.5、指定 ChannelHandler Handler用于设置处理服务端请求的ChannelHandler。上述例子中指定的是自定义的EchoClientHandler。 6.6、连接到服务器 connect()方法用于连接到指定的服务器的Channel。 连接成功后返回一个ChannelFuture以代表是一个异步的操作。在上述的例子里使用的是sync()方法以同步的方式来获取服务启动的结果。 总结 通过上述对引导程序类的介绍相信大家对于服务端和客户端的引导程序以及一些配置项的都有了一定的了解。通过看源码我们就能更加清楚的知道 Netty 服务端和客户端的启动的过程。下节我们来分析Netty的线程模型。