Netty学习(三)-Netty重要接口讲解
上一节我们写了一个HelloWorld,对于Netty的运行有了一定的了解,知道Netty是如何启动客户端和服务器端。这一节我们简要的讲解一下几个重要的接口,初步探讨Netty的运行机制,当然刚学Netty就深入原理肯定是很枯燥的,所以我们就点到为止。
1. ChannelPipeLine和ChannelHandler
在上一篇中我们在ChannelInitializer类的initChannel方法中使用了ChannelPipeline,然后在ChannelPipeline中使用了handler来处理业务逻辑。
ChannelPipeline是ChannelHandler的容器,它负责ChannelHandler的管理和事件拦截与调度。Netty的ChannelPipeline和ChannelHandler机制类似于Servlet 和Filter 过滤器,这类拦截器实际上是职责链模式的一种变形,主要是为了方便事件的拦截和用户业务逻辑的定制。
Netty的channel运用机制和Filter过滤器机制一样,它将Channel 的数据管道抽象为ChannelPipeline. 消息在ChannelPipeline中流动和传递。ChannelPipeline 持有I/O事件拦截器ChannelHandler 的链表,由ChannelHandler 对I/0 事件进行拦截和处理,可以方便地通过新增和删除ChannelHandler 来实现小同的业务逻辑定制,不需要对已有的ChannelHandler进行修改,能够实现对修改封闭和对扩展的支持。
通过一张图我们来看一下他们之间的关系:
一个Channel中包含一个ChannelPipeline,用来处理Channel中的事件,一个ChannelPipeline中可以包含很多个handler,第二节的示例代码中我们也看到了,使用各种handler来处理通信信息。
同时我们也注意到在hadler中继承了ChannelInboundHandlerAdapter类并实现了他的一些方法,比如:channelRead,channelActive,channelInactive等等,我们看到这些方法中都有一个参数:ChannelHandlerContext ctx。这个ChannelHandlerContext就是handler的上下文对象,有了这个ChannelHandlerContext你就获得了一切,你可以获得通道,获得事件的控制权。
事实上,用户不需要自己创建pipeline,因为使用ServerBootstrap 或者Bootstrap 启动
服务端或者客户端时, Netty 会为每个Channel 连接创建一个独立的pipeline。
ChannelPipeline pipeline = socketChannel.pipeline();
pipeline.addLast("framer", new DelimiterBasedFrameDecoder(8192,Delimiters.lineDelimiter()));
pipeline.addLast("decoder", new StringDecoder());
pipeline.addLast("encoder", new StringEncoder());
// 客户端的逻辑
pipeline.addLast("handler", new HelloWorldClientHandler());
ChannelPipeline 是线程安全的, 这意味着N个业务线程可以并发地操作ChannelPipeline
而不存在多线程并发问题。但是,ChannelHandler却不是线程安全的,这意味着尽管
ChannelPipeline 是线程去全的, 但是仍然需要自己保证ChannelHandler的线程安全。
Netty 中的事件分为inbound 事件和outbound 事件。inbound 事件通常由I/O线程触发,例如TCP 链路建立事件、链路关闭事件、读事件、异常通知事件等。Outbound 事件通常是I/O 用户主动发起的网络I/O 操作,例如用户发起的连接操作、绑定操作、消息发送等操作。
我们常用的inbound事件有:
- ChannelHandlerContext fireChannelRegistered() //channel注册事件
- ChannelHandlerContext fireChannelActive() //channel激活事件
- ChannelHandlerContext fireExceptionCaught(Throwable var1) //channel异常处理事件
- ChannelHandlerContext fireUserEventTriggered(Object var1) //用户自定义事件
- ChannelHandlerContext fireChannelRead(Object var1) //读事件
pipeline 中以fireXXX命名的方法都是从I/O 线程流向用户业务Handler的inbound 事件,它们的实现因功能而异,但是处理步骤类似:
调用HeadHandler对应的fireXXX 方法
执行事件相关的逻辑操作
常用的outbound事件有:
- ChannelFuture bind(SocketAddress var1, ChannelPromise var2) //绑定地址
- ChannelFuture connect(SocketAddress var1, ChannelPromise var2) //连接服务器
- ChannelFuture write(Object var1) //发送事件
- ChannelHandlerContext flush() //刷新事件
上面我们说到事件,netty的事件机制是由前至后的,一般来说,都是一个channel的ChannnelActive方法中调用fireChannelActive来触发调用下一个handler中的ChannelActive方法,即你在ChannelPipeline中添加handler的时候,要在第一个handler的channelActive方法中调用fireChannelActive,以此来触发下一个事件。我们再来写一个案例说明一下:
客户端:
public class HWClient {
private int port;
private String address;
public HWClient(int port, String address) {
this.port = port;
this.address = address;
}
public void start(){
EventLoopGroup group = new NioEventLoopGroup();
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(group)
.channel(NioSocketChannel.class)
.handler(new ClientChannelInitializer());
try {
ChannelFuture future = bootstrap.connect(address,port).sync();
future.channel().writeAndFlush("Hello Netty Server ,I am a common client");
future.channel().closeFuture().sync();
} catch (Exception e) {
e.printStackTrace();
}finally {
group.shutdownGracefully();
}
}
public static void main(String[] args) {
HWClient client = new HWClient(7788,"127.0.0.1");
client.start();
}
}
客户端ClientChannelInitializer:
public class ClientChannelInitializer extends ChannelInitializer<SocketChannel> {
protected void initChannel(SocketChannel socketChannel) throws Exception {
ChannelPipeline pipeline = socketChannel.pipeline();
pipeline.addLast("decoder", new StringDecoder());
pipeline.addLast("encoder", new StringEncoder());
// 客户端的handler
//先调用handler在ChannnelActive方法中调用fireChannelActive会激活handler1
pipeline.addLast("handler", new HWClientHandler());
pipeline.addLast("handler1", new BaseClientHandler());
}
}
客户端handler:
public class HWClientHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
System.out.println("server say : "+msg.toString());
}
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.out.println("Handler1");
ctx.fireChannelActive();
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
System.out.println("Client is close");
}
}
客户端的第二个handler:
public class BaseClientHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.out.println("Handler2");
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
ctx.close();
}
}
服务端:
public class HWServer {
private int port;
public HWServer(int port) {
this.port = port;
}
public void start(){
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workGroup = new NioEventLoopGroup();
ServerBootstrap server = new ServerBootstrap().group(bossGroup,workGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new ServerChannelInitializer());
try {
ChannelFuture future = server.bind(port).sync();
future.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
bossGroup.shutdownGracefully();
workGroup.shutdownGracefully();
}
}
public static void main(String[] args) {
HWServer server = new HWServer(7788);
server.start();
}
}
服务端ServerChannelInitializer:
public class ServerChannelInitializer extends ChannelInitializer<SocketChannel> {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
ChannelPipeline pipeline = socketChannel.pipeline();
// 字符串解码 和 编码
pipeline.addLast("decoder", new StringDecoder());
pipeline.addLast("encoder", new StringEncoder());
// 自己的逻辑Handler
pipeline.addLast("handler", new HWServerHandler());
}
}
服务端handler:
public class HWServerHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.out.println("channelActive");
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
System.out.println(ctx.channel().remoteAddress()+"===>server: "+msg.toString());
ctx.write("received your msg");
ctx.flush();
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
super.exceptionCaught(ctx, cause);
ctx.close();
}
}
我们启动服务端和客户端,会发现客户端的两个handler都通过了。
先调用HWClientHandler,打印出:HWClientHandler channelActive;继而调用了BaseClientHandler ,打印出:BaseClient1Handler channelActive.
Netty学习(三)-Netty重要接口讲解的更多相关文章
- Netty学习——基于netty实现简单的客户端聊天小程序
Netty学习——基于netty实现简单的客户端聊天小程序 效果图,聊天程序展示 (TCP编程实现) 后端代码: package com.dawa.netty.chatexample; import ...
- Netty学习(九)-Netty编解码技术之Marshalling
前面我们讲过protobuf的使用,主流的编解码框架其实还有很多种: ①JBoss的Marshalling包 ②google的Protobuf ③基于Protobuf的Kyro ④Apache的Thr ...
- Netty学习三:线程模型
1 Proactor和Reactor Proactor和Reactor是两种经典的多路复用I/O模型,主要用于在高并发.高吞吐量的环境中进行I/O处理. I/O多路复用机制都依赖于一个事件分发器,事件 ...
- Netty学习(三)高性能之ByteBuf源码解析
原文链接: https://juejin.im/post/5db8ea506fb9a02061399ab3 Netty 的 ByteBuf 类型 Pooled(池化).Unpooled(非池化) Di ...
- Netty学习(七)-Netty编解码技术以及ProtoBuf和Thrift的介绍
在前几节我们学习过处理粘包和拆包的问题,用到了Netty提供的几个解码器对不同情况的问题进行处理.功能很是强大.我们有没有去想这么强大的功能是如何实现的呢?背后又用到了什么技术?这一节我们就来处理这个 ...
- Netty学习(八)-Netty的心跳机制
版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/a953713428/article/details/69378412我们知道在TCP长连接或者Web ...
- Netty学习摘记 —— Netty客户端 / 服务端概览
本文参考 本篇文章是对<Netty In Action>一书第二章"你的第一款 Netty 应用程序"的学习摘记,主要内容为编写 Echo 服务器和客户端 第一款应用程 ...
- 【Netty学习】Netty 4.0.x版本和Flex 4.6配合
笔者的男装网店:http://shop101289731.taobao.com .冬装,在寒冷的冬季温暖你.新品上市,环境选购 =================================不华丽 ...
- Netty学习(十)-Netty文件上传
今天我们来完成一个使用netty进行文件传输的任务.在实际项目中,文件传输通常采用FTP或者HTTP附件的方式.事实上通过TCP Socket+File的方式进行文件传输也有一定的应用场景,尽管不是主 ...
随机推荐
- restapi(0)- 平台数据维护,写在前面
在云计算的推动下,软件系统发展趋于平台化.云平台系统一般都是分布式的集群系统,采用大数据技术.在这方面akka提供了比较完整的开发技术支持.我在上一个系列有关CQRS的博客中按照实际应用的要求对akk ...
- C++模版的用法
模板是实现代码重用机制的一种工具,实质就是实现类型参数化,即把类型定义为参数. C++提供两种模板:函数模板,类模板 函数模板 template <typename T> T myMax( ...
- windows快速创建文本文档的几个方法快捷键和
1. 在平常使用电脑中要经常用到在左面创建文本文档txt最笨重的方法就是右键但是这样非常的慢,有没有什么快捷键呢 2. 快捷键 快捷键就是Win+R ,键入notepad 然后回车就可以编辑了 是不是 ...
- JAVA面试题 浅析Java中的static关键字
面试官Q1:请说说static关键字,你在项目中是怎么使用的? static 关键字可以用来修饰:属性.方法.内部类.代码块: static 修饰的资源属于类级别,是全体对象实例共享的资源: 使用 s ...
- idea中向pom.xml添加依赖时显示”not found dependency“
总结: 起因:再输入hibernate-core的version时,开始写的是对的,就是 5.0.11.Final(这个也是跟着教程来的),直接就报错了,左等右等没用,也点过maven的reimpor ...
- springcloud-注册中心快速构建
1. 场景描述 springcloud提供了一整套可行的构建分布式系统的方案,使的企业/开发人员能够快速沟通分布式系统,今天快速构建下springcloud的注册中心Eureka. 2. 解决方案 2 ...
- VS2010 winform开发笔记---combox的SelectedIndexChanged事件及级联问题
DisplayMember绑定需要显示的数据表字段, 而ValueMember绑定需要获取选择的项的值. 然后通过 combo.SelectedValue就可以取得选中项的值了. DisplayMem ...
- Python爬虫学习代码
[1]用一个简单的程序来显示Python的数字类型. code: class ShowNumType(object): def __init__(self): self.showInt() self. ...
- winapi创建不能改变大小的窗口
HWND hWnd = CreateWindow( "myWindowClass", //窗口类的名字 "my first window", //窗口标题 // ...
- java高并发系列 - 第15天:JUC中的Semaphore,最简单的限流工具类,必备技能
这是java高并发系列第15篇文章 Semaphore(信号量)为多线程协作提供了更为强大的控制方法,前面的文章中我们学了synchronized和重入锁ReentrantLock,这2种锁一次都只能 ...