标签:netty 链接 是什么 ase 业务 private 工具 应用程序 简化
为了后期更好地理解和进一步深入 Netty,有必要总体认识一下 Netty 所用到的核心组件以及他们在整个 Netty 架构中是如何协调工作的。Nettty 有如下几个核心组件:
1.Bootstrap或者ServerBootstrap,一个Netty应用通常由一个Bootstrap开始,它主要作用是配置整个Netty程序,串联起各个组件。
2.Channel:Channel 是 Netty 网络操作抽象类,它除了包括基本的 I/O 操作,如 bind、connect、read、write 之外,还包括了 Netty 框架相关的一些功能,如获取该 Channel的 EventLoop。在传统的网络编程中,作为核心类的 Socket ,它对程序员来说并不是那么友好,直接使用其成本还是稍微高了点。而Netty 的 Channel 则提供的一系列的 API :它大大降低了直接与 Socket 进行操作的复杂性。而相对于原生 NIO 的 Channel,Netty 的 Channel 具有如下优势:
在 Channel 接口层,采用 Facade 模式进行统一封装,将网络 I/O 操作、网络 I/O 相关联的其他操作封装起来,统一对外提供。
Channel 接口的定义尽量大而全,为 SocketChannel 和 ServerSocketChannel 提供统一的视图,由不同子类实现不同的功能,公共功能在抽象父类中实现,最大程度地实现功能和接口的重用。
具体实现采用聚合而非包含的方式,将相关的功能类聚合在 Channel 中,有 Channel 统一负责和调度,功能实现更加灵活。
Channel 与 socket 的关系:
在 Netty 中 Channel 有两种,对应客户端套接字通道NioSocketChannel,内部管理java.nio.channels.SocketChannel 套接字,对应服务器端监听套接字通道NioServerSocketChannel,其内部管理自己的 java.nio.channels.ServerSocketChannel 套接字。也就是 Channel 是对 socket 的装饰或者门面,其封装了对socket 的原子操作。
3.ChannelHandler:ChannelHandler 为 Netty 中最核心的组件,它充当了所有处理入站和出站数据的应用程序逻辑的容器。ChannelHandler 主要用来处理各种事件,这里的事件很广泛,比如可以是连接、数据接收、异常、数据转换等。ChannelHandler 有两个核心子类 ChannelInboundHandler 和 ChannelOutboundHandler,其中ChannelInboundHandler 用于接收、处理入站数据和事件,而 ChannelOutboundHandler 则相反。
4.ChannelPipeline:ChannelPipeline 为 ChannelHandler 链提供了一个容器并定义了用于沿着链传播入站和出站事件流的 API。一个数据或者事件可能会被多个 Handler 处理,在这个过程中,数据或者事件经流 ChannelPipeline,由 ChannelHandler 处理。在这个处理过程中,一个 ChannelHandler 接收数据后处理完成后交给下一个 ChannelHandler,或者什么都不做直接交给下一个 ChannelHandler。
当一个数据流进入 ChannlePipeline 时,它会从 ChannelPipeline 头部开始传给第一个 ChannelInboundHandler ,当第一个处理完后再传给下一个,一直传递到管道的尾部。与之相对应的是,当数据被写出时,它会从管道的尾部开始,先经过管道尾部的 “最后” 一个ChannelOutboundHandler,当它处理完成后会传递给前一个ChannelOutboundHandler 。当 ChannelHandler 被添加到 ChannelPipeline 时,它将会被分配一个 ChannelHandlerContext,它代表了 ChannelHandler 和 ChannelPipeline之间的绑定。其中 ChannelHandler 添加到 ChannelPipeline 过程如下:
一个 ChannelInitializer 的实现被注册到了 ServerBootStrap中
当 ChannelInitializer.initChannel() 方法被调用时,ChannelInitializer 将在 ChannelPipeline 中安装一组自定义的 ChannelHandler
ChannelInitializer 将它自己从 ChannelPipeline 中移除
5.EventLoop:Netty 基于事件驱动模型,使用不同的事件来通知我们状态的改变或者操作状态的改变。它定义了在整个连接的生命周期里当有事件发生的时候处理的核心抽象。Channel 为Netty 网络操作抽象类,EventLoop 主要是为Channel 处理 I/O 操作,两者配合参与 I/O 操作。下图是Channel、EventLoop、Thread、EventLoopGroup之间的关系(摘自《Netty In Action》):
当一个连接到达时,Netty 就会注册一个 Channel,然后从 EventLoopGroup 中分配一个 EventLoop 绑定到这个Channel上,在该Channel的整个生命周期中都是有这个绑定的 EventLoop 来服务的。
6.ChannelFuture:Netty 为异步非阻塞,即所有的 I/O 操作都为异步的,因此,我们不能立刻得知消息是否已经被处理了。Netty 提供了 ChannelFuture 接口,通过该接口的 addListener() 方法注册一个 ChannelFutureListener,当操作执行成功或者失败时,监听就会自动触发返回结果。
通过了解相应的组件,接下去先简单看一下Netty的基本使用,同样的市服务端与客户端的交互。
public class NettyServer { private static final String IP = "127.0.0.1"; private static final int port = 6666; private static final int BIZGROUPSIZE = Runtime.getRuntime().availableProcessors() * 2; private static final int BIZTHREADSIZE = 100; //创建两个EventLoopGroup对象,创建boss线程组 ?于服务端接受客户端的连接 private static final EventLoopGroup bossGroup = new NioEventLoopGroup(BIZGROUPSIZE); //创建 worker 线程组 ?于进? SocketChannel 的数据读写 private static final EventLoopGroup workGroup = new NioEventLoopGroup(BIZTHREADSIZE); public static void start() throws Exception { //启动类初始化 ServerBootstrap serverBootstrap = initServerBootstrap(); // 绑定端?,并同步等待成功,即启动服务端 ChannelFuture channelFuture = serverBootstrap.bind(IP, port).sync(); //成功绑定到端口之后,给channel增加一个 管道关闭的监听器并同步阻塞,直到channel关闭,线程才会往下执行,结束进程。 channelFuture.channel().closeFuture().sync(); System.out.println("server start"); } private static ServerBootstrap initServerBootstrap() { //一个Netty应用通常由一个Bootstrap开始 ServerBootstrap serverBootstrap = new ServerBootstrap(); //添加两个组,设置使?的EventLoopGroup serverBootstrap.group(bossGroup,workGroup) //初始化 channel,设置要被实例化的为 NioServerSocketChannel 类 .channel(NioServerSocketChannel.class) //初始化channelHandler,设置连?服务端的 Client 的 SocketChannel 的处理器 .childHandler(new ChannelInitializer<Channel>() { //我们再来设置下相应的过滤条件。 这?需要继承Netty中ChannelInitializer 类, //然后重写 initChannel 该?法,进?添加相应的设置,传输协议设置,以及相应的业务实现类 @Override protected void initChannel(Channel ch) throws Exception { //配置pipeline相关属性 ChannelPipeline pipeline = ch.pipeline(); pipeline.addLast(new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE,0,4,0,4)); pipeline.addLast(new StringDecoder(CharsetUtil.UTF_8)); pipeline.addLast(new StringEncoder(CharsetUtil.UTF_8)); // 相关处理 Handler pipeline.addLast(new TcpServerHandler()); } }); return serverBootstrap; } protected static void shutdown(){ workGroup.shutdownGracefully(); bossGroup.shutdownGracefully(); } public static void main(String[] args) throws Exception { System.out.println("启动Server..."); NettyServer.start(); } }
服务相关的设置的代码写完之后,我们再来编写主要的业务代码。 使?Netty编写 [业务层 ]的代码,我们需要继承 ChannelInboundHandlerAdapter 或 SimpleChannelInboundHandler 类,在这?说下它们两的区别吧。
继承 SimpleChannelInboundHandler 类之后,会在接收到数据后会?动 release 掉数据占?的 Bytebuffer 资源。并且继承该类需要指定数据格式。
?继承ChannelInboundHandlerAdapter 则不会?动释放,需要?动调?ReferenceCountUtil.release() 等?法进?释放。继承该类不需要指定数据格式。 所以在这?,个?推荐服务端继承 ChannelInboundHandlerAdapter ,?动进?释放,防?数据未处理完就?动释放了。?且服务端可能有多个客户端进?连接,并且每?个客户端请求的数据格式都不?致,这时便可以进?相应的处理。
客户端根据情况可以继承 SimpleChannelInboundHandler 类。好处是直接指定好传输的数据格式,就不需要再进?格式的转换了。
TcpServerHandler :
public class TcpServerHandler extends ChannelInboundHandlerAdapter { //建?连接时,发送?条庆祝消息 @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { System.out.println("chanelActive>>>>>>>"); } //业务逻辑处理 @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { System.out.println("server receive message:" + msg); ctx.channel().writeAndFlush("accept message "+ msg); ctx.close(); } //异常相关处理 @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { System.out.println("get server exception :"+cause.getMessage()); } }
客户端:客户端过滤其这块基本和服务端?致。不过需要注意的是,传输协议、编码和解码应该?致.
public class NettyClient implements Runnable { @Override public void run() { EventLoopGroup group = new NioEventLoopGroup(); try { Bootstrap bootstrap = new Bootstrap(); bootstrap.group(group); bootstrap.channel(NioSocketChannel.class) .option(ChannelOption.TCP_NODELAY, true) .handler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel ch) throws Exception { ChannelPipeline pipeline = ch.pipeline(); pipeline.addLast("frameEncoder", new LengthFieldPrepender(4)); pipeline.addLast("decoder", new StringDecoder(CharsetUtil.UTF_8)); pipeline.addLast("encoder", new StringEncoder(CharsetUtil.UTF_8)); pipeline.addLast("handler", new MyClient()); } }); for (int i=0;i<10;i++){ ChannelFuture f = bootstrap.connect("127.0.0.1",6666).sync(); f.channel().writeAndFlush("hello service !" + Thread.currentThread().getName()+ ":---->"+i); f.channel().closeFuture().sync(); } }catch (Exception e){ e.printStackTrace(); }finally { group.shutdownGracefully(); } } public static void main(String[] args) { for (int i = 0;i < 3 ;i++ ){ new Thread(new NettyClient(),">>> this thread "+i).start(); } } }
MyClient :这?有个注解, 该注解 Sharable 主要是为了多个handler可以被多个channel安全地共享,也就是保证线程安全。
public class MyClient extends ChannelInboundHandlerAdapter { //@Sharable @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { System.out.println("client receieve message: "+msg); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { System.out.println("get client exception :"+cause.getMessage()); } }
启动服务器,客户端即可看到演示效果。
了解了Netty 基础服务的构建,我们对Netty服务有了一定的认识,最后来一张线程模型图(Reactor主从多线程模型):
标签:netty 链接 是什么 ase 业务 private 工具 应用程序 简化
原文地址:https://www.cnblogs.com/wuzhenzhao/p/10418650.html