Netty组件

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_25484147/article/details/85200506

概述

Channel–Socket

EventLoop–控制流,多线程处理,并发

ChannelFuture --异步通知

Channel

基本的I/O 操作(bind()、connect()、read()和write())依赖于底层网络传输所提供的原语。在基于Java 的网络编程中,其基本的构造是class Socket。Netty 的Channel 接口所提供的API,大大地降低了直接使用Socket 类的复杂性。

Netty自己定义的类有以下几种:

EmbeddedChannel;
LocalServerChannel;
NioDatagramChannel;
NioSctpChannel;
NioSocketChannel。

EventLoop接口

EventLoop定义了Netty的核心抽象,用于处理连接的生命周期中所发生的时间。

上图中的关系是:

一个EventLoopGroup 包含一个或者多个EventLoop;
一个EventLoop 在它的生命周期内只和一个Thread 绑定;
所有由EventLoop 处理的I/O 事件都将在它专有的Thread 上被处理;
一个Channel 在它的生命周期内只注册于一个EventLoop;
一个EventLoop 可能会被分配给一个或多个Channel。

ChannelFuture接口

Netty所有的I/O都是异步的,因为一个操作不会立即返回,所以我们需要一种用于在这之后的某个时间点确定其结果的方法。实例代码如下所示:

package com.bobo.netty.chapter01;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.net.InetSocketAddress;

/**
 * netty服务器端
 *
 * @author [email protected]
 * @create 2018-12-19 19:55
 **/
public class HelloServer {

    private Logger logger = LoggerFactory.getLogger(HelloServer.class);


    private final int port = 8080;

    public static void main(String[] args) throws Exception {

        new HelloServer().start();
    }


    public void start() throws Exception {
        final HelloServerHandler serverHandler = new HelloServerHandler();
        // 创建eventLoopGroup
        EventLoopGroup group = new NioEventLoopGroup();
        try {
            // 创建ServerBootstrap
            ServerBootstrap bootstrap = new ServerBootstrap();
            // 指定所使用的NIO传输Channel,指定端口套接字
            bootstrap.group(group).channel(NioServerSocketChannel.class)
                    .localAddress(new InetSocketAddress(port)).
                    childHandler(new ChannelInitializer<SocketChannel>() {
                // 添加一个serverHandler到子Channel的ChannelPipeLine
                @Override
                protected void initChannel(SocketChannel socketChannel) throws Exception {
                    socketChannel.pipeline().addLast(serverHandler);
                }
            });
            // 异步绑定服务器,调用sync方法阻塞等待直到绑定成功
            ChannelFuture future = bootstrap.bind().sync();
            // 获取Channel的CloseFuture,并且阻塞当前线程直到它完成
            future.channel().closeFuture().sync();
        }catch (Exception e) {
            logger.error("server error");
            //关闭EventLoopGroup,释放掉所有的资源
            group.shutdownGracefully().sync();
        }
    }
}

ChannelHander

ChannelHandler经常用于做业务逻辑处理,,有许多不同类型的ChannelHandler,它们各自的功能主要取决于它们的超类。Netty 以适配器类的形式提供了大量默认的ChannelHandler 实现,其旨在简化应用程序处理逻辑的开发过程。你已经看到了,ChannelPipeline中的每个ChannelHandler将负责把事件转发到链中的下一个ChannelHandler。这些适配器类(及它们的子类)将自动执行这个操作,所以你可以只重写那些你想要特殊处理的方法和事件。常用的适配类有以下几种:

 ChannelHandlerAdapter
 ChannelInboundHandlerAdapter
 ChannelOutboundHandlerAdapter
 ChannelDuplexHandler

编码器和解码器

概述

当你通过Netty发送或者是接受一个消息的时候,就会发生一次数据转换。入站消息会被解码,从自己转化为另外一种形式,通常是一个java对象。如果是出站消息,则会发横相反方向的转化,它会从当前格式编码为字节。所有由Netty 提供的编码器/解码器适配器类都实现ChannelOutboundHandler 或者ChannelInboundHandler 接口。

你将会发现对于入站数据来说,channelRead 方法/事件已经被重写了。对于每个从入站Channel 读取的消息,这个方法都将会被调用。随后,它将调用由预置解码器所提供的decode()方法,并将已解码的字节转发给ChannelPipeline 中的下一个ChannelInboundHandler。

ChannelPipeline 接口

ChannelPipeline提供 了ChannelHandler链的容器,并且定义了用于在该链上传播入站和出站时间流的API,当Channel被创建的时候,它会被自动的分配到它专属的ChannelPipeLine。

ChannelHandler 安装到ChannelPipeline 中的过程如下所示:

一个ChannelInitializer的实现被注册到了ServerBootstrap中;
当ChannelInitializer.initChannel()方法被调用时,ChannelInitializer
将在ChannelPipeline 中安装一组自定义的ChannelHandler;
ChannelInitializer 将它自己从ChannelPipeline 中移除。

这里讲述一下Netty应用程序中入站和出站数据流之间的区别,从一个客户端应用程序的角度来讲,如果事件的运动方向是从客户端到服务器daunt,那么我们成这些事件为出站的。反之是入站。

在这里插入图片描述

上图显示了入站和出站ChannelHandler 可以被安装到同一个ChannelPipeline中。如果一个消息或者任何其他的入站事件被读取,那么它会从ChannelPipeline 的头部开始流动,并被传递给第一个ChannelInboundHandler。这个ChannelHandler 不一定会实际地修改数据,具体取决于它的具体功能,在这之后,数据将会被传递给链中的下一个ChannelInboundHandler。最终,数据将会到达ChannelPipeline 的尾端,届时,所有处理就都结束了。

数据的出站运动(即正在被写的数据)在概念上也是一样的。在这种情况下,数据ChannelOutboundHandler 链的尾端开始流动,直到它到达链的头部为止。在这之后,出站数据将会到达网络传输层,这里显示为Socket。通常情况下,这将触发一个写操作。

注意:Netty能区分ChannelInboundHandler实现和ChannelOutboundHandler 实现,并确保数据只会在具有相同定向类型的两个ChannelHandler 之间传递。

当ChannelHandler 被添加到ChannelPipeline 时,它将会被分配一个ChannelHandler-Context,其代表了ChannelHandler 和ChannelPipeline 之间的绑定。虽然这个对象可以被用于获取底层的Channel,但是它主要还是被用于写出站数据。

在Netty 中,有两种发送消息的方式。你可以直接写到Channel 中,也可以写到和Channel-Handler相关联的ChannelHandlerContext 对象中。前一种方式将会导致消息从Channel-Pipeline 的尾端开始流动,而后者将导致消息从ChannelPipeline 中的下一个Channel-Handler 开始流动。

抽象类SimpleChannelInboundHandler

引导

Netty的引导类有两种,一种用于客户端(简单的称为BootStrap),而另一猴子那个(ServerBootstrap用于服务器,引导类的作用是为应用程序的网络层配置提供了容器。

这两者的区别在于引导一个客户端只需要一个EventLoopGroup,但是一个ServerBootstrap 则需要两个(也可以是同一个实例。

因为服务器需要两组不同的Channel。第一组将只包含一个ServerChannel,代表服务器自身的已绑定到某个本地端口的正在监听的套接字。而第二组将包含所有已创建的用来处理传入客户端连接(对于每个服务器已经接受的连接都有一个)的Channel。图3-4 说明了这个模型,并且展示了为何需要两个不同的EventLoopGroup。

在这里插入图片描述

git地址

https://github.com/wuxiaobo000111/java-learn.git

猜你喜欢

转载自blog.csdn.net/qq_25484147/article/details/85200506