参考书目:Java网络编程/Servlet深入详解/Netty实战
目录
Web协议
TCP/IP
- TCP/IP介绍
- TCP/IP:传输控制协议(传输层)/网际协议(网际层)
- TCP/IP组成
- TCP传输控制协议-应用程序之间通信
- UDP用户数据报协议-应用程序之间的简单通信
- IP网际协议-计算机之间的通信
- ICMP因特网消息控制协议-针对错误和状态
- DHCP动态主机配置协议-针对动态寻址
- TCP连接
- 一个程序通过TCP与另一个应用程序通信时,会发送一个握手请求。
- 握手后,在两个程序之间建立一个全双工通信
- 占用两个计算机间的通信线路,知道被关闭为止
- UDP类似,但更简单且可靠性更低
- IP连接
- 无连接的通信协议,不会占用通信计算机之间的通信线路
- 通过IP,消息/数据被分割为小的独立包,并在internet中传送
- IP负责将每个包路由至它的目的地
- TCP/IP
- TCP负责为将数据分割并装入IP包,然后在到达时重新组合它们
- IP负责将包发送至接收者
- TCP/IP寻址
- 4组0~255数字为计算机编址
- 4×8比特共32比特=4字节
- DNS服务器负责将域名翻译为TCP/IP地址
- TCP/IP协议族
- TCP - 传输控制协议
- IP - 网际协议
- HTTP - 超文本传输协议
- SSL - 安全套接字层 - SSL加密数据传输
- SMTP/MIME/IMAP/POP - 邮件
- FTP - 文件传输协议
- NTP - 网络时间协议
- DHCP - 动态主机配置协议 - 用于向网络中的计算机分配动态IP地址
- SNMP - 简单网络管理协议
- LDAP - 轻量级目录访问协议
- ICMP - 因特网消息控制协议 - 负责网络中的错误处理
- ARP - 地址解析协议 - 用于通过IP来查找基于IP地址的计算机网卡的硬件地址(IP转MAC)
- RARP - 反向地址转换协议
- BOOTP - 自举协议 - 用于从网络中启动计算机
- PPTP - 点对点隧道协议 - 用于私人网络间的连接
HTTP
- HTTP介绍
- 基于TCP/IP通信协议进行超文本传输
- 浏览器通过URL向WEB服务器发送请求
- HTTP默认端口号为80
- HTTP消息结构
- HTTP使用统一资源标识符(URI)来传输数据和建立连接
- 客户端请求消息
GET /hello.txt HTTP/1.1
User-Agent: curl/7.16.3 libcurl/7.16.3 OpenSSL/0.9.7l zlib/1.2.3
Host: www.example.com
Accept-Language: en, mi
- 服务器响应消息
HTTP/1.1 200 OK
Date: Mon, 27 Jul 2009 12:28:53 GMT
Server: Apache
Last-Modified: Wed, 22 Jul 2009 19:15:56 GMT
ETag: "34aa387-d-1568eb00"
Accept-Ranges: bytes
Content-Length: 51
Vary: Accept-Encoding
Content-Type: text/plain
- HTTP请求方法
- 版本:HTTP/1.1
- GET --- 请求指定的页面信息,并返回实体主体
- HEAD --- 用于获取报头
- POST --- 向指定资源提交数据进行处理请求。数据被包含在请求体中
- PUT --- 从客户端向服务器传送数据取代指定文档内容
- DELETE --- 删除指定页面
- CONNECT --- HTTP/1.1协议中预留给能够将连接改为管道方式的代理服务器
- OPTIONS --- 允许客户端查看服务器的性能
- TRACE --- 回显服务器收到的请求,用于测试和诊断
- PATCH --- PUT的补充,对已知资源进行局部更新
- HTTP响应头信息
- Allow --- 服务器支持哪些请求方法
- Content-Encoding --- 文档的编码方法
- Content-Length --- 内容长度
- Content-Type --- 表示后面的文档属于什么MIME类型
- Date --- 当前的GMT时间
- Expires --- 应当在什么时候认为文档已经过期而不再缓存它
- Last-Modified --- 文档的最后改动时间
- Location --- 表示客户应当去哪里提取文档
- Refresh --- 表示浏览器应该在多少时间后刷新文档
- Server --- 服务器名字
- Set-Cookie --- 设置和页面关联的Cookie
- WWW-Authenticate --- 客户应该在Authorization头重提供什么类型的授权信息
- 状态码
- 常见状态码
- 200 - 请求成功
- 301 - 资源被永久转移到其它URL
- 404 - 请求的资源不存在
- 500 - 内部服务器错误
- 状态码分类
- 1** - 信息,服务器收到请求,需要请求者继续执行操作
- 2** - 成功,操作被成功接受并处理
- 3** - 重定向,需要进一步操作完成请求
- 4** - 客户端错误,请求包含语法错误或无法完成请求
- 5** - 服务器错误,服务器在处理请求中发生了错误
Servlet
Servlet技术
- Servlet:单实例多线程
- Servlet接口:
- javax.servlet.Servlet接口
public void init(ServletConfig config) throws ServletException
public void service(ServletRequest req,ServletResponse res) throws ServletException,IOException
public void destory()
public ServletConfig getServletConfig()
- 返回容器调用init()方法时传递的ServletConfig对象
public String getServletInfo()
- 通用Servlet:
- 使用:从GenericServlet类继承
- 实现其中的抽象方法service()
- HttpServlet:
- 继承自GenericServlet类
- 使用:从HttpServlet类继承
- 转化使用HttpServletRequest和HttpServletResponse对象
- 需要对其中的doGet/doPost/doHead/doPut/doDelete/doTrace/doOptions方法覆盖实现
- GET/POST:
- 数据量较小,又无安全性考虑 --- get方法提交
- 数据量较大或有安全性考虑 --- post方法提交
- Servlet异常:
- ServletException类
- UnavailableException类
- ServletException的子类
- 表示该Servlet永久(404)或暂时不可用(503)
- 生命周期:
- Servlet运行在容器中,生命周期由容器来管理
- init(),service(),destroy()
- Servlet上下文:
- 每个Web应用程序都有一个与之相关的Servlet上下文
- ServletContext接口
- 通过ServletConfig对象的
getServletContext()获得
- 读取、移除和设置共享属性
public Object getAttribute(String name)
public Enumeration getAttributeNames()
public void removeAttribute(String name)
public void setAttribute(String name,Object object)
- 同一应用程序中的不同Servlet实例共享
- 请求转发:
- MVC模型 --- Controller控制器
- javax.servlet.RequestDispatcher接口
- RequestDispatcher
- 封装一个由路径所标识的服务器资源
- 将请求传递给其它资源:
public void forward(ServletRequest request,ServletResponse response) throws Servlet Exception,IOException
- 在响应中包含其它资源的内容:
public void include(ServletRequest request,ServletResponse response) throws Servlet Exception,IOException
- 获取RequestDispatcher对象
- ServletRequest接口(相对根路径或Servlet路径)或ServletContext接口中的方法(相对根路径)
public RequestDispatcher getRequestDispatcher(String path)
- ServletContext的
getContext()方法可以获取另一个应用程序的上下文对象
- sendRedirect()
- HttpServletResponse接口
- sendRedirect()是让浏览器重新提交和访问另一个资源(servlet和html等)
- 可以将客户端重定向到其它服务器上的Web应用程序
会话跟踪
- 会话跟踪技术:
- Session:服务器把一个客户的所有请求联系在一起,并记住客户的操作状态
- SSL会话:客户端和服务器之间产生会话密钥,基于HTTPS协议的服务器可以使用该对称密钥建立会话
- 会话Cookie:服务器发送给客户的片段信息,存储在客户端浏览器的内存或硬盘上(Cookie名:JSESSIONID)
- URL重写:URL中附加标识客户的Session ID,将请求与特定的Session相关联(参数名:jsessionid)
- Servlet API:
- javax.servlet.http.HttpSession
- Servlet容器为HttpSession对象分配唯一的Session ID
- 对HttpSession对象读取、移除和设置属性
public Object getAttribute(String name)
public Enumeration getAttributeNames()
public void removeAttribute(String name)
public void setAttribute(String name,Object value)
- 得到Session --- HttpServletRequest接口
- 返回与此次请求相关的Session,如果未分配过,则创建一个
public HttpSession getSession()
- create参数为false时,若未创建过请求的Session,则返回null
public HttpSeession getSession(boolean create)
- Session生命周期:
<session-config><session-timeout>5</session-timout></session-config>
- Session的超时时间间隔,单位为分钟,默认为30分钟
- Session保存在内存中,无法持久化存储
- Cookie类:
- Cookie在客户端保存信息
- 相对于会话Cookie,可以在硬盘中储存
- 创建Cookie:
Cookie cookie = new Cookie(String name,String value)
- 发送Cookie:
response.addCookie(cookie)
- 读取Cookie:
Cookie[] cookies = request.getCookies()
- Session持久化:
Servlet异常处理
声明式异常处理
- web.xml文件中声明对各种异常的处理办法:元素
- HTTP错误代码:
- Java异常:
- 元素指定Java异常类的完整限定名
- 类似如:
java.io.FileNotFoundException
- 元素定位响应错误的资源路径
程序式异常处理
- 在Web程序中用try-catch语句捕获异常
- 日志
- ServletContext接口:
getServletContext().log("......")
- GenericServlet类:
log("......")
- 发送错误代码和信息:
response.senError(...,...)
- RequestDispatcher
线程安全
- 多线程Servlet模型:
- Servlet单实例多线程
- 线程池管理
- 减少产生Servlet实例的开销
- 线程安全:
- 使用局部变量
- synchronized同步
- 关闭数据库对象后(连接被放回连接池),将对象设为null
- ServletContext:非线程安全,应进行同步或拷贝
- HttpSession:非线程安全
- ServletRequest:线程安全
监听
- ServletContextListener接口:在Web程序启动/关闭时执行初始化任务
- HttpSessionBindingListener接口:当实现该接口的对象被绑定到Session或从Session中移除时,通知对象
非阻塞IO
- 服务器Socket通道
- ServerSocketChannel对象:
ServerSocketChannel serverChannel = ServerSocketChannel.open()
- 非阻塞:
serverChannel.configureBlocking(false)
- 绑定端口:
serverChannel.bind(new InetSocketAddress(19));
- 接受连接:
SocketChannel clientChannel = serverChannel.accept()
- 客户端通道非阻塞:
clientChannel.configureBlocking(false)
- 构建Selector:
Selector selector = Selector.open()
- 通道注册:
SelectionKey key = serverChannel.register(selector,SelectionKey.OP_ACCEPT)
SelectionKey key = clientChannel.register(selector,SelectionKey.OP_WRITER)
- 缓冲区关联:
key.attach(buffer)
- 缓冲区写入:
client.write(buffer)
- 缓冲区
- 创建缓冲区:
- 分配(用于输入):返回有固定容量的新缓冲区
ByteBuffer buffer = ByteBuffer.allocate(100);
- 获取缓冲区数据:
byte[] data = buffer.array();
- 包装(用于输出):
byte[] data = "Some data".getBytes("UTF_8")
ByteBuffer buffer = ByteBuffer.wrap(data);
- 填充和排空:
- 填充:
buffer.put(...)
- 回绕(把缓冲区限度设为当前位置并将位置设为0):
buffer.flip()
- 排空:
buffer.get()
- 排空完后,hasRemaining()返回false
- 批量方法:
buffer.get(byte[] array)
buffer.put(byte[] array)
- 压缩缓冲区
- 复制缓冲区
buffer.duplicate()
- 复制的缓冲区共享相同的数据
- 分片缓冲区
- 创建一个新缓冲区,与原缓冲区共享数据,起始位置是原缓冲区的当前位置,容量不超过原缓冲区限度
buffer.slice()
- 标记和重置
buffer.mark()
buffer.reset()
- 通道
- SocketChannel:读写TCP Socket
- 连接
SocketAddress address = new InetSocketAddress("www.abc.com",80)
- 带参数构造
SocketChannel channel = SocketChannel.open(address)
- 该方法会阻塞
- 无参构造
SocketChannel channel = SocketChannel.open()
channel.connect(address)
- 非阻塞
- 读取Channel
- 写入Channel
- 关闭
channel.close()
channel.isopen()
- ServerSocketChannel:接受入站连接
ServerSocketChannel server = ServerSocketChannel.open()
server.bind(address)
- 接受连接
server.accept()
- 阻塞:等待入站链接
- 非阻塞:立即返回(没有入站连接返回null)
- 异步通道
AsynchronousSocketChannel client = AsynchronousSocketChannel.open()
Future<Void> connected = client.connect(address)
- 等待连接完成:
connected.get()
- 从连接读取:
Future<Integer> future = client.read(buffer)
- 等待读取完成:
future.get()
AsynchronousServerSocketChannel
- Socket选项
channel.setOption(StandardSocketOptions.SO_LINGER,240)
- 选择器
- 创建选择器:
Selector selector = Selector.open()
- 注册:
channel.register(selector,SelectionKey.OP_READ)
- 选择:
- 获取:
- 得到SelectionKey对象
selector.selectedKeys()
- 关闭:
- SelectionKey类
- 相当于通道指针
- 测试操作
selectionkey.isAcceptable()
selectionkey.isConnectable()
selectionKey.isReadable()
selectionKey.isWritable()
- 获取通道
selectionkey.channel()
selectionkey.attachment()
- 撤销注册
Netty
异步和事件驱动
- 异步和非阻塞:
- 非阻塞网络调用使程序不必等待一个操作的完成,用于构造完全异步的I/O
- 选择器能够通过较少的线程监视许多连接上的事件
- Netty核心组件:
- Channel --- Java NIO:出入站数据的载体
- 回调 --- 一个指向已被提供给另外一个方法的方法的引用,使后者可以在适当的时候通知前者
- Future --- 异步操作结果的占位符,使得在将来时刻完成时提供对结果的访问
- 事件和ChannelHandler:
- 入站相关触发的事件:
- 连接已被激活或连接失活
- 数据读取
- 用户事件
- 错误事件
- 出站事件:
- 打开或关闭到远程节点的连接
- 将数据写到或冲刷到Socket
- 每个事件都可以被分发给ChannelHandler类中的某个用户实现的方法
Netty组件
Channel/EventLoop/ChannelFuture
- 网络抽象:
- Channel:Socket
- EventLoop:控制流、多线程处理、并发
- ChannelFuture:异步通知
- Channel接口:
- 降低了直接使用Socket类的复杂性
- EmbeddedChannel
- LcoalServerChannel
- NioDatagramChannel
- NioSctpChannel
- NioSocketChannel
- EventLoop接口:
- Netty的核心抽象,用于处理连接的生命周期中所发生的事件
- 一个EventLoopGroup包含一个或多个EventLoop
- 一个EventLoop在它的生命周期内只和一个Thread绑定
- 所有由EventLoop处理的I/O事件都将在它专有Thread上被处理
- 一个Channel在它的生命周期内只注册于一个EventLoop
- 一个EventLoop可能会被分配给多个Channel
- ChannelFuture接口:
- addListener()方法注册了一个ChannelFutureListener,以便在某个操作完成时得到通知
ChannelHandler/ChannelPipeline
- ChannelHandler接口:
- 充当所有处理入站数据和出站数据的应用程序逻辑的容器
- ChannelPipeline接口:
- ChannelPipeline为ChannelHandler链提供了容器,并定义了用于在该链上传播入站和出站事件流的API
- ChannelHandler安装到ChannelPipeline中的过程:
- 一个ChannelInitializer的实现被注册到了ServerBootstrap中
- 当ChannelInitializer.initChannel()方法被调用时,ChannelInitializer将在ChannelPipeline中安装一组自定义的ChannelHandler
- ChannelInitializer将它自己从ChannelPipeline中移除
- 当ChannelHandler被添加到ChannelPipeline时,会被分配一个ChannelHandlerContext,代表了Handler和Pipeline之间的绑定
- 适配器类:默认ChannelHandler实现
- ChannelHandlerAdapter
- ChannelInboundHandlerAdapter
- ChannelOutboundHandlerAdapter
- ChannelDuplexHandler
- 编码器/解码器:
- InboundHandler:将字节转化为Java对象
- OutboundHandler:将Java对象转化为字节
- SimpleChannelInboundHandler:
引导
- 应用程序网络层配置的容器
- 客户端:Bootstrap --- 对应一个EventLoopGroup
- 服务端:ServerBootstrap --- 对应两个EventLoopGroup
传输
- 传输的实现依赖于interface Channel、ChannelPipeline和ChannelHandler
- 传输API:
- 每个Channel都会被分配一个ChannelPipeline和ChannelConfig
- ChannelPipeline持有所有将应用于入站和出站数据以及事件的ChannelHandler实例
- ChannelHandler典型用途:
- 将数据从一种格式转换为另一种格式
- 提供异常的通知
- 提供Channel变为活动的或者非活动的通知
- 提供当Channel注册到EventLoop或者从EventLoop注销时的通知
- 提供有关用户自定义事件的通知
- 内置传输:
- NIO:基于选择器的全异步API
- 选择器运行在一个检查状态变化并对其作出相应响应的线程上
- Epoll:用于Linux的本地非阻塞传输
- OIO:阻塞传输
- Local传输:用于在同一个JVM中运行的客户端和服务器之间的异步通信
- Embedded传输:用于单元测试
缓冲区
- ByteBuf类:Netty的数据容器
- ByteBuf维护了读取和写入两个不同索引
- read/write方法会推进索引
- get/set方法不会改变索引
- 使用模式:
- 堆缓冲区:
- 数组支撑:将数据存储在JVM的堆空间中
- 在没有使用池化的情况下提供快速的分配和释放
byte[] array = bytebuf.array();
- 直接缓冲区:
- JVM通过本地调用来分配内存
- 相对于堆缓冲区,分配和释放较为昂贵
- 复合缓冲区:
- 为多个ByteBuf提供一个聚合视图,根据需要添加或删除ByteBuf实例
- CompositeByteBuf - 提供了一个将多个缓冲区表示为单个合并缓冲区的虚拟表示
- 消除了组合ByteBuffer时的无必要的复制
CompositeByteBuf buf = Unpooled.compositeBuffer();
- 字节级操作:
- 随机访问索引:
byte b = bytebuf.getByte(i)
- 移动索引:
readerIndex(index)/writerIndex(index)
- 顺序访问索引:被读过的可丢弃字节->readerIndex->未读的可读字节->writerIndex->可写字节->capacity
- 可丢弃字节:调用discardReadBytes()方法丢弃并回收空间(变为可写字节空间)
- 可读字节:分段存储了实际数据
- 可写字节:新分配的缓冲区的writeIndex的默认值为0
- 索引管理:
- 获取索引位置:
- markReaderIndex()
- markWriterIndex()
- 重置标记:
- resetReaderIndex()
- resetWriterIndex()
- 将reader和writer的Index都设为0:
- 查找操作:
- 确定指定值得索引
- indexOf()
int index = bytebuf.forEachByte(ByteProcessor.FIND_CR)
- 派生缓冲区:
- 为ByteBuf提供了专门的方式呈现其内容的视图
- duplicate()
- slice()
- slice(int,int)
- Unpooled.unmodifiableBuffer(...)
- order(ByteOrder)
- readSlice(int)
- 每个方法都将返回一个新的ByteBuf实例
- 读写操作:
- get|Boolean|Byte|UnsignedByte|Medium|UnsignedMedium|Int|UnsignedInt|Long|Short|UnsignedShort(int)
- getBytes(int,...) --- 将缓冲区中从给定索引开始的数据传送到指定目的地
- set|Boolean|Byte|Medium|Int|Long|Short(index,Value)
- read|Boolean|......()
- readBytes(ByteBuf|byte[] destination,int dstIndex [,int length])
- write|Boolean|......(value)
- writeBytes(source ByteBuf|byte[] [,int srcIndex,int length])
- 其它操作:
- isReadable()/isWriteable()
- readableBytes()/writableBytes() --- 返回可被读取/写入的字节数
- capacity()/maxCapacity()
- hasArray()/array()
- ByteBufHolder接口:
- ByteBuf分配:
- 按需分配:ByteBufAllocator接口
- 为了降低分配和释放内存的开销,使用interface ByteBufAllocator实现了池化来分配ByteBuf实例
ByteBufAllocator allocator = channel.alloc();
ByteBufAllocator allocator = ctx.alloc();
- Unpooled缓冲区:提供了静态方法来创建未池化的ByteBuf实例
- ByteUtil类:提供了用于操作ByteBuf的静态方法
- hexdump() --- 以十六进制的形式打印ByteBuf的内容
- 引用计数:
- 在某个对象所持有的资源不再被其它对象引用时释放该对象所持有的资源优化内存使用和性能
- interface ReferenceCounted
数据处理
ChannelHandler
- Channel生命周期:
- ChannelUnregistered:Channel已经被创建,但未注册到EventLoop
- ChannelRegistered:Channel被注册到EventLoop
- ChannelActive:Channel已经连接到远程节点
- ChannelInactive:Channel未连接到远程节点
- Channel的状态发生改变,事件会转发给ChannelPipeline中的ChannelHandler,并做出响应
- ChannelHandler生命周期:
- handlerAdded:ChannelHandler添加到ChannelPipeline中时调用
- handlerRemoved:从ChannelPipeline中移除ChannelHandler时调用
- exceptionCaught:处理过程中在ChannelPipeling中有错误时调用
- ChannelInboundHandler接口:
- channelRegistered/channelUnregistered
- channelActive/channelInactive
- channelReadComplete
- channelRead
- ChannelWritabilityChanged
- userEventTriggered
- ChannelOutboundHandler接口:
- bind
- connect/disconnect
- close
- deregister
- read
- flush/write
- ChannelHandler适配器:
- ChannelInboundHandlerAdapter
- ChannelOutboundHandlerAdapter
- 资源管理:
- 防止内存泄漏(使用完ByteBuf后,调整引用计数)
- ResourceLeakDetector.setLevel()
- DISABLED:禁用泄露检测
- SIMPLE:1%采样泄露检测(默认)
- ADVANCED
- PARANOID
- 释放资源:
ReferenceCountUtil.release(msg)
- 通知Promise:
promise.setSuccess()
ChannelPipeline
- ChannelPipeline:拦截流经Channel的入站和出站事件的ChannelHandler实例链
- 修改ChannelPipeline:
- 添加、删除和替换链上Handler
- 添加:
- addFirst
- addBefore
- addAfter
- addLast
- 删除:
- 替换:
- 访问ChannelHandler:
- 触发事件:
- 入站:调用链中下一个ChannelHandler的事件处理方法
- fireChannelRegistered/fireChannelUnregistered
- fireChannelActive/fireChannelInactive
- fireExceptionCaught
- fireUserEventTriggered
- fireChaneelRead
- fireChannelReadComplete
- fireChannelWritabilityChanged
- 出站:底层套接字操作
- bind
- connect/disconnect
- close/deregister
- flush/write/writeAndFlush
- read
ChannelHandlerContext
- ChannelHandlerContext代表了Handler和Pipeline之间的关联
- 主要功能:管理它所关联的ChannelHandler和在同一个ChannelPipeline中的其它ChannelHandler之间的交互
- 调用Channel和Pipeline的方法,会沿着整个Pipeline进行传播。而调用ChannelHandlerContext的方法,则是从关联的Handler开始,只会传播给下一个能够处理该事件的ChannelHandle
- 注意事项:
- ChannelHandlerContext和ChannelHandler之间的关联不会被改变,缓存对其的引用是安全的
- 相对其它类的同名方法,产生的事件流更短,应尽可能用这个类的方法
- 高级用法:
- 将ChannelHandler添加到ChannelPipeline中实现动态协议切换
- 缓存到ChannelHandlerContext的引用以供稍后使用
- 异常处理:
- 处理入站异常:
- 如果异常被抛出,则会从抛出点开始流经Pipeline(默认的异常处理方式)
- 处理异常的ChannelInboundHandler通常放于入站ChannelPipeline最后
- 处理出站异常:
- 每个出站操作会返回一个ChannelFuture,可在其上注册ChannelFutureListener用于通知
future.addListener(new ChannelFutureListener() {......});
- 几乎所有的ChannelOutboundHandler上的方法都会传入一个ChannelPromise实例
promise.addListener(new ChannelFutureListener () {......});
线程模型
- 线程模型概述:
- 从池的空闲线程列表中选择一个Thread并运行一个已提交的任务
- 任务完成时,将Thread返回列表,使其可被重用
- EventLoop接口:
- 所有的I/O操作和事件都由已经被分配给了EventLoop的Thread来处理
- io.netty.channel
- 任务调度:
- 经过60s后调度任务
ScheduledFuture<?> future = channel.eventLoop().schedule(new Runnable() {...} , 60 , TimeUnit.SECONDS)
- 60s后调度任务,并每60s调度任务
ScheduledFuture<?> future = channel.eventLoop().scheduleAtFixedRate(new Runnable() {...} , 60 , 60 , TimeUnit.SECONDS)
- 取消任务
- 实现细节:
- 线程管理:
- 每个EventLoop都有其自己的任务队列并独立于其它EventLoop
- 每个EventLoop都和一个Thread相关联
- 如当前调用线程是支撑EventLoop的线程,那么所提交的代码将被执行,否则EventLoop将该任务放到内部队列稍后运行
- 不要将长时间运行任务放入执行队列中(阻塞同一线程上执行的其它任务)
- 线程分配:
- 异步传输:少量的EventLoop,被多个Channel共享 ---> 少量的Thread支撑大量的Channel
- 阻塞传输:每个Channel被分配给一个EventLoop(以及Thread)
引导
- Bootstrap类:
- ServerBootstrap:使用一个父ServerChannel来接受来自客户端的连接,并创建子Channel用于它们之间的通信
- Bootstrap:单独的Channel用于所有的网络交互(用于客户端和无连接协议程序)
- 引导客户端:group -> channel -> handler -> bind/connect
- 引导服务器:group -> channel -> childHandler -> bind
- 从Channel引导客户端:共享EventLoop
bootstrap.group(ctx.channel().eventLoop())
- 重用EventLoop
- 添加多个ChannelHandler:
final class Initializer extends ChannelInitializer<C extends Channel>
protected void intiChannel(Channel ch) throws Exception
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast(new ...)
- ChannelOption:
- TCP心跳监测:
bootstrap.option(ChannelOption.SO_KEEPALIVE,true)
- 超时检测:
bootstrap.option(ChannelOption.CONNECT_TIMOEOUT_MILLIS,5000)
- 属性:
final AttributeKey<Integer> id = AttributeKey.newInstance("ID")
Integer idValue = ctx.channel().attr(id).get()
bootstrap.attr(id,123456)
单元测试
- EmbeddedChannel方法:
- writeInbound - 将入站消息写到EmbeddedChannel中
- readInbound - 从EmbeddedChannel中读取一个入站消息
- writeOutbound - 将出站消息写道EmbeddedChannel中
- readOutbound - 从EmbeddedChannel中读取出站消息
- finish - 将EmbeddedChannel标记为完成
编解码器
解码器
- 将字节解码为消息:ByteToMessageDecoder/ReplayingDecoder
- 将一种消息类型解码为另一种:MessageToMessageDecoder
- 抽象类ByteToMessageDecoder:
- 会对入站数据进行缓冲,直到做好准备处理
decode(ChannelHandlerContext ctx,ByteBuf in,List<Object> out)
- 传入一个包含了数据的ByteBuf,以及用来添加解码消息的List
decodeLast(...)
- 抽象类ReplayingDecoder:
- ByteToMeesageDecoder类的扩展,避免了调用readableBytes()方法
- 不是所有的ByteBuf操作都支持
- 慢于ByteToMessageDecoder
- io.netty.handler.codec.LineBasedFrameDecoder - 使用行尾控制符解析消息
- io.netty.handler.codec.http.HttpObjectDecoder - HTTP数据解码器
- 抽象类MessageToMessageDecoder:
- 继承于ChannelInboundHandlerAdapter
decode(ChannelHandlerContext ctx, I msg, List<Object> out)
编码器
- 将消息编码为字节/将消息编码为消息
- 抽象类MessageToByteEncoder:
encode(ChannelHandlerContext ctx, I msg, ByteBuf out)
- 抽象类MessageToMessageEncoder:
encode(ChannelHandlerContext ctx, I msg, List<Object> out)
- 编解码器:
- ByteToMessageCodec
- MessageToMessageCodec
- CombinedChannelDuplexHandler
- 充当了ChannelInboundHandler和ChannelOutboundHandler的容器
- 不必直接扩展抽象的编解码器类,自己实现
预置的ChannelHandler和编解码器
- SSL/TLS安全协议:SslHandler
SSLEngine engine = SslContext.newEngine(channel.alloc())
channel.pipeline().addFirst("ssl",new SslHandler(engine,true))
- HTTP/HTTPS:
- HTTP编解码器:
- HTTP消息组成:
HttpRequest(HttpResponse)\HttpContent\LastHttpContent
- HttpRequestEncoder
- HttpResponseEncoder
- HttpRequestDecoder
- HttpResponseDecoder
pipeline.addLast("decoder", new HttpResponseDecoder())
- Http消息聚合:
pipeline.addLast("codec",new HttpClientCodec())
pipeline.addLast("codec",new HttpServerCodec())
pipeline.addLast("aggregator",new HttpObjectAggregator(512*1024))
- Http消息压缩:
- 使用HTTPS:
- 将SslHandler添加到ChannelPipeline中
连接管理
- 用于空闲连接和超时的ChannelHandler:
- IdleStateHandler - 连接空闲时间太长,触发IdleStateEvent事件,在ChannelInboundHandler中重写userEventTriggered()处理事件
pipeline.addLast(new IdleStateHandler(0,0,60,TimeUnit.SECONDS))
- ReadTimeoutHandler - 指定时间间隔内没有收到入站数据,抛出TimeoutException,关闭对应Channel
- WriteTimeoutHandler - WriteTimeoutException
解码分隔符和长度协议
- 基于分隔符协议:
- DelimiterBasedFrameDecoder - 利用用户提供的分隔符提取帧的解码器
- LineBasedFrameDecoder - 提取有行尾符分割的帧的解码器
- 基于长度的协议:
- FixedLengthFrameDecoder - 提取定长帧
- LengthFieldBasedFrameDecoder
大型数据
- 大型数据:
- FielRegion接口
- 通过支持零拷贝的文件传输的Channel来发送的文件区域
- ChunkedWriteHandler - interface ChunkedInput< B >
- 支持异步写大型数据流
posted on
2020-08-13 09:46
Lith
阅读(
299)
评论()
收藏
举报