??xml version="1.0" encoding="utf-8" standalone="yes"?>亚洲欧洲精品无码AV,亚洲第一视频在线观看免费,国产亚洲视频在线观看网址http://www.tkk7.com/cooperzh/category/50400.htmlzh-cnThu, 05 Jan 2012 22:15:20 GMTThu, 05 Jan 2012 22:15:20 GMT60《构建高性能的大型分布式Java应用》笔?W一?IOhttp://www.tkk7.com/cooperzh/archive/2011/12/27/367323.htmlcooperzhcooperzhTue, 27 Dec 2011 03:50:00 GMThttp://www.tkk7.com/cooperzh/archive/2011/12/27/367323.html (异步dasynchronous IO)

jdk1.6及之前都只实现BIO ?NIO
jdk1.7开始支持AIOQ即NIO 2.0


在BIOd模式下server?
1 new ServerSocket(int port) 监听端口
2 serverSocket.accept() d式等待客L的连接,有连接才q回Socket对象
3 socket.getINputStream() 获取客户端发q来的信息流
4 socket.getOutputStream() 获取输出对象,从而写入数据返回客L

client端:
1 newSocketQString host,int port) 建立与服务器端的q接Q如果服务器没启动,报Connection refused异常
2 socket.getInputStream() d服务器端q回的流
3 socket.getOutputStream() 获取输出,写入数据发送到服务器端


在NIO模式下Server端:
1 ServerSocketChannel.open() 获取serverScoketChannel实例
2 serverScoketChannel.configueBlocking(false) 讄channel为非d模式
3 serverSocketChannel.socket() 获取serverSocket对象
4 serverSocket.bind(port) 监听端口
5 Selector.open() 打开SelectorQ获取selector实例
6 serverSocketChannel.register(Selector,int) 向selector注册channel和感兴趣的事?/span>
7 while(true) 循环以保证正常情况下服务器端一直处于运行状?/span>
8 selector.select() 获取selector实例中需要处理的SelectionKey的数?/span>
9 for(SelectionKey key:selector.selectedKeys()) 遍历selector.selectedKeys,以对每个SelectionKey的事件进行处?/span>
10 key.isAcceptable() 判断SelectionKey的类型是否ؓ客户端徏立连接的cd
11 key.channel() 当SelectionKey的类型是acceptabelӞ获取l定的ServerSocketChannel对象
12 serverSocketChannel.accept() 接受客户端徏立连接的hQƈq回SocketChannel对象
13 socketChannel.regiseter(Selector,int) 向Selector注册感兴的事gcdQ如read,write
14 key.isReadable() 判断SelectionKey是否为readableQ如是则意味着有消息流在等待处?/span>
15 socketChannel.read(ByteBuffer) 从SelectionKey中绑定的SocketChannel对象d消息?/span>
16 socketChannel.write(ByteBuffer) 从SelectionKey中绑定的SocketChannel对象输出消息?/span>

client端:
1 SocketChannel.open() 打开SocketChannel
2 SocketChannel.configureBlocking(false) SocketChannel配置为非d模式
3 SocketChannel.connect(host,port) q接到指定的目标地址
4 Selector.open() 打开Selector
5 SocketChannel.register(Selector,int) 向Selector注册感兴的事g,connected,read,write
6 while(true) 循环执行保证客户端一直处于运行状?/span>
7 Selector.select() 从Selector中获取是否有可读的key信息
8 for(SelectionKey key:selector.selectedKeys()) 遍历selector中所有selectedKeys
9 SelectionKey.isConnectable() 判断是否接徏立的cd
10 SelectionKey.channel() 获取l定的SocketChannel
11 SocketChannel.finishConnect() 完成q接的徏立(TCP/IP的三ơ握手)
12 SelectionKey.isReadable() 判断是否为可ȝ?/span>
13 SelectionKey.channel() 获取l定的SocketChannel
14 SocketChannel.read(ByteBuffer) 从SocketChannel中读取数到ByteBuffer?/span>
15 SocketChannel.write(ByteBuffer) 向SocketChannel中写入ByteBuffer对象数据


cooperzh 2011-12-27 11:50 发表评论
]]>
NIO trick and trap NIO的技巧与陷阱http://www.tkk7.com/cooperzh/archive/2011/12/20/366884.htmlcooperzhcooperzhTue, 20 Dec 2011 12:41:00 GMThttp://www.tkk7.com/cooperzh/archive/2011/12/20/366884.html出处Q?a title="http://www.tkk7.com/killme2008/archive/2011/06/30/353422.html" href="http://www.tkk7.com/killme2008/archive/2011/06/30/353422.html">http://www.tkk7.com/killme2008/archive/2011/06/30/353422.html

IO划分Z个阶D:
1 {待数据qA
2 从内核缓冲区copy到进E缓冲区Q从socket通过socketChannel复制到ByteBufferQ?/span>

non-direct ByteBuffer: HeapByteBufferQ创建开销?/span>
direct ByteBufferQ通过操作pȝnative代码Q创建开销?/span>

Zblock的传输通常比基于流的传输更高效

使用NIO做网l编E容易,但离散的事g驱动模型~程困难Q而且陷阱重重

Reactor模式Q经典的NIO|络框架
核心lgQ?/span>
1 Synchronous Event Demultiplexer : Event loop + 事g分离
2 DispatcherQ事件派发,可以多线E?/span>
3 Request HandlerQ事件处理,业务代码


理想的NIO框架Q?/span>
1 优雅地隔IO代码和业务代?/span>
2 易于扩展
3 易于配置Q包括框架自w参数和协议参数
4 提供良好的codec框架Q方便marshall/unmarshall
5 透明性,内置良好的日志记录和数据l计
6 高性能

NIO框架性能的关键因?/span>
1 数据的copy
2 上下文切?context switch)
3 内存理
4 TCP选项Q高UIO函数
5 框架设计

减少数据copyQ?/span>
ByteBuffer的选择
View ByteBuffer
FileChannel.transferTo/transferFrom
FileChannel.map/MappedByteBuffer

ByteBuffer的选择Q?/span>
不知道用哪种bufferӞ用Non-Direct
没有参与IO操作Q用Non-Direct
中小规模应用(<1Kq发q接)Q用Non-Direct
长生命周期,较大的缓冲区Q用Direct
试证明Direct比Non-Direct更快Q用Direct
q程间数据共?JNI)Q用Direct
一个Buffer发给多个ClientQ考虑使用view ByteBuffer׃n数据Qbuffer.slice()


HeapByteBuffer~存

使用ByteBuffer.slice()创徏view ByteBufferQ?/span>
ByteBuffer buffer2 = buffer1.slice()Q?/span>
则buffer2的内容和buffer1的从position到limit的数据内容完全共?/span>
但是buffer2的positionQlimit是独立于buffer1?/span>

传输文g的传l方式:
byte[] buf = new byte[8192];
while(in.read(buf)>0){
    out.write(buf);
}
使用NIO后:
FileChannel in = ...
WriteableByteChannel out = ...
in.transferTo(0,fsize,out);
性能会有60%的提?/span>

FileChannel.map
文件映ؓ内存区域——MappedByteBuffer
提供快速的文g随机d能力
q_相关
适合大文Ӟ只读型操作,如大文g的MD5校验{?/span>
没有unmapҎQ什么时候被回收取决于GC

减少上下文切?/span>
旉~存
Selector.wakeup
提高IOd效率
U程模型

旉~存Q?/span>
1|络服务器通常需要频J获取系l时_定时器,协议旉戻I~存q期{?/span>
2 System.currentTimeMillis
   a linux调用gettimeofday需要切换到内核?/span>
   b 普通机器上Q?000万次调用需?2U,q_一?.3毫秒
   c 大部分应用不需要特别高的精?/span>
3 SystemTimer.currentTimeMillisQ自己创建)
   a 独立U程定期更新旉~存
   b currentTimeMillis直接q回~存?/span>
   c _ֺ取决于定期间?/span>
   d 1000万次调用降低?9毫秒


Selector.wakeup() 主要作用Q?/span>
解除d在Selector.select()上的U程Q立卌?/span>
两次成功的select()之间多次调用wakeup{h于一ơ调?/span>
如果当前没有d在select()上,则本ơwakeup作用在下次select()?/span>
什么时候wakeup() Q?/span>
注册了新的Channel或者事?/span>
Channel关闭Q取消注?/span>
优先U更高的事g触发Q如定时器事ӞQ希望及时处?/span>

wakeup的原理:
1 linux上利用pipe调用创徏一个管?/span>
2 windows上是一个loopback的tcpq接Q因为win32的管道无法加入select的fd set
3 管道或者tcpq接加入selected fd set
4 wakeup向管道或者连接写入一个字?/span>
5 d的select()因ؓ有IO旉qAQ立卌?/span>
可见wakeup的调用开销不可忽视

减少wakeup调用Q?/span>
1 仅在有需要时才调用。如往q接发送数据,通常是缓存在一个消息队列,当且仅当队列为空时注册writeqwakeup
booleanneedsWakeup=false;
synchronized(queue){
    if(queue.isEmpty())  needsWakeup=true;
    queue.add(session);
}
if(needsWakeup){
    registerOPWrite();
    selector.wakeup();
}
2 记录调用状态,避免重复调用Q例如Netty的优?/span>

d或者写?个字节:
不代表连接关?/span>
高负载或者慢速网l下很常见的情况
通常的处理方法是q回ql注册read/writeQ等待下ơ处理,~点是系l调用开销和线E切换开销
其他解决办法Q@环一定次数写入(如MinaQ或者yield一定次?/span>
启用临时选择器Temporary Selector在当前线E注册ƈpollQ例如Girzzy?/span>

在当前线E写入:
当发送缓冲队列ؓI的时候,可以直接往channel写数据,而不是放入缓冲队列,interest了write{待IOU程写入Q可以提高发送效?/span>
优点是可以减系l调用和U程切换
~点是当前线E中断会引vchannel关闭

U程模型
selector的三个主要事ӞreadQwriteQacceptQ都可以q行在不同的U程?/span>

通常Reactor实现Z个线E,内部l护一个selector

1 Boss Thread + worker Thread
   boss thread处理acceptQconnect
   worker thread处理readQwrite

ReactorU程数目Q?/span>
1 Netty 1 + 2 * cpu
2 Mina 1 + cpu + 1
3 Grizzly 1 + 1

常见U程模型Q?/span>
1 read和accept都运行在reactorU程?/span>
2 acceptq行在reactorU程上,readq行在单独线E?/span>
3 read和accept都运行在单独U程
4 readq行在reactorU程上,acceptq行在单独线E?/span>

选择适当的线E模型:
cecho应用Qunmashall和业务处理的开销非常低,选择模型1
模型2Q模?Q模?的accept处理开销很低
最佳选择Q模?。unmashall一般是cpu-boundQ而业务逻辑代码一般比较耗时Q不要在reactorU程处理

内存理
1 java能做的事情非常有?/span>
2 ~冲区的理
   a 池化。ThreadLocal cacheQ环形缓冲区
   b 扩展。putString,getString{高UAPIQ缓冲区自动扩展和׾~,处理不定长度字节
   c 字节序。跨语言通讯需要注意,默认字节序Big-EndianQjava的IO库和class文g

数据l构的选择
1 使用单的数据l构Q链表,队列Q数l,散列?/span>
2 使用j.u.c框架引入的ƈ发集合类Qlock-freeQspin lock
3 M数据l构都要注意定w限制QOutOfMemoryError
4 适当选择数据l构的初始容量,降低GC带来的媄?/span>

定时器的实现
1 定时器在|络E序中频J?br />    a 周期事g的触?br />    b 异步时的通知和移?br />    c 延迟事g的触?br />2 三个旉复杂?br />
    a 插入定时?br />    b 删除定时?br />    c PerTickBookkeepingQ一ơtick内系l需要执行的操作
3 Tick的方?br />    Selector.select(timeout)
    Thread.sleep(timeout)

定时器的实现Q链?br />定时器l织成链表结?br />插入定时器,加入链表N
删除定时?br />
PerTickBookkeepingQ遍历链表查找expire事g

定时器的实现Q排序链?/div>定时器l织成有序链表结构,按照expire截止旉升序排序
插入定时器,扑ֈ合适的位置插入
删除定时?br />
PerTickBookkeepingQ直接从表头找v

定时器的实现Q优先队?/div>定时器l织成优先队列,按照expire截止旉作ؓ优先U,优先队列一般采用最堆实现
插入定时?br />删除定时?br />
PerTickBookkeepingQ直接取root判断

定时器的实现QHash wheel timer
定时器l织成时间轮
指针按照一定周期旋转,一个tick跛_一个槽?br />定时器根据g时时间和当前指针位置插入到特定槽?br />
插入定时?br />删除定时?/div>
PerTickBookkeeping
槽位和tick军_了精度和延时

定时器的实现QHierarchical Timing
Hours WheelQMinutes WheelQSeconds Wheel

q接IDLE的判?br />1 q接处于IDLE状态:一D|间没有IOd事g发生
2 实现方式Q?br />    a 每次IOd都记录IOd写的旉?br />    b 定时扫描所有连接,判断当前旉和上一ơ读或写的时间差是否过讑֮阀|过卌接处于IDLE状态,通知业务处理?br />
   c 定时的方式:Zselect(timeout)或者定时器。MinaQselect(timeout);Netty:HashWheelTimer

合理讄TCP/IP选项Q有时会起到显著效果Q需要根据应用类型、协议设计、网l环境、OSq_{因素做考量Q以试l果为准

Socket~冲|选项QSO_RCVBUF ?SO_SNDBUF
Socket.setReceiveBufferSize/setSendBufferSize 仅仅是对底层q_的提C,是否有效取决于底层^台。因此getq回的不是真实的l果?br />讄原则Q?br />1 以太|上Q?k通常是不够的Q增加到16kQ吞吐量增加?0%
2 Socket~冲区大至应该是q接的MSS的三倍,MSS=MTU+40Q一?/span>以太|卡的MTU=1500字节?br />    MSSQ最大分D大?br />    MTUQ最大传输单?br />3 send buffer最好与对端的receive buffer寸一?br />4 对于一ơ性发送大量数据的应用Q增加缓冲区?8k?4k可能是唯一最有效的提高性能的方式?br />    Z最大化性能Q?/span>send buffer臛_要跟BDP(带宽延迟乘积)一样大?span style="font-size: 11px; ">
5 同样Q对于大量接收数据的应用Q提高接收缓冲区Q能减少发送端的阻?br />6 如果应用既发送大量数据,又接收大量数据,?/span>send buffer?span class="Apple-style-span" style="font-size: 11px; ">receive buffer应该同时增加
7 如果讄的ServerSocket?span class="Apple-style-span" style="font-size: 11px; ">receive buffer过RFC1323定义?4kQ那么必dl定端口前设|,以后accept产生的socket承这一讄
8 无论~冲区大多,你都应该可能地帮助TCP臛_以那样大的块写?br />
BDP(带宽延迟乘积)
Z优化TCP吞吐量,发送端应该发送够的数据包以填满发送端和接收端之间的逻辑通道
BDP = 带宽 * RTT

Nagle法QSO_TCPNODELAY
通过缓冲区内的包自动相连l成大包Q阻止发送大量小包阻塞网l,提高|络应用效率对于实时性要求较高的应用(telnet、网?Q需要关闭此法
Socket.setTcpNoDelay(true) 关闭法
Socket.setTcpNoDelay(false) 
打开法Q默?br />
SO_LINGER选项Q控制socket关闭后的行ؓ
Socket.setSoLinger(boolean linger,int timeout)
linger=falseQtimeout=-1
当socketdcloseQ调用的U程会马上返回,不会dQ然后进入CLOSING状态,D留在缓冲区中的数据l发送给对端Qƈ且与对端q行FIN-ACK协议交换Q最后进入TIME_WAIT状?br />
linger=trueQtimeout>0
调用close的线E将dQ发生两U可能的情况Q一是剩余的数据l箋发送,q行关闭协议交换Q二是超时过期,剩余数据被删除Q?/span>q行FIN-ACK协议交换
linger=trueQtimeout=0
q行所?#8220;hard-close”QQ何剩余的数据被丢弃Qƈ且FIN-ACK交换也不会发生,替代产生RSTQ让对端抛出“connection reset”的SocketException
4 慎重使用此选项QTIME_WAIT状态的价|
    可靠实现TCPq接l止
    允许
老的分节在网l中失Q防止发l新的连?br />   持箋旉=2*MSLQMSL为最大分节生命周期,一般ؓ30U到2分钟Q?span style="font-size: 11px; ">

SO_REUSEADDRQ重用端?br />Socket.setReuseAddress(boolean) 默认false
适用场景Q?br />1 当一个用本地地址和端口的socket1处于TIME_WAIT状态时Q你启动的socket2要占用该地址和端口,p用到此选项
SO_REUSEADDR允许同一端口上启动一个服务的多个实例Q多个进E)Q但每个实例l定的地址是不能相同的
3 SO_REUSEADDR允许完全相同的地址和端口的重复l定。但q只用于UDP的多播,不适用TCP

SO_REUSEPORT
listen做四元组Q多q程同一地址同一端口做acceptQ适合大量短连接的web server
Freebsd独有

其他选项Q?br />Socket.setPerformancePreferences(connectionTime, latency, bandwidth) 讄q接旉、gq、带宽的相对重要?br />Socket.setKeepAlive(boolean) q是TCP层的keep-alive概念Q非HTTP协议的。用于TCPq接保活Q默认间?时Q徏议在应用层做心蟩
Socket.sendUrgentData(data) 带外数据


技巧:
1 d公^
    Mina限制一ơ写入的字节C过最大的ȝ冲区?.5?br />2 针对FileChannel.transferTo的bug
    Mina判断异常Q如果是temporarily unavailable的IOExceptionQ则认ؓ传输字节Cؓ0
3 发送消息,通常是放入一个缓冲区队列注册writeQ等待IOU程d
    U程切换Q系l调?br />    如果队列为空Q直接在当前U程channel.writeQ隐患是当前U程的中断会引vq接关闭
4 事g处理优先U?br />    ACE框架推荐Qaccept > write > read (推荐)
     Mina ?NettyQread > write
5 处理事g注册的顺?br />
    在select()之前
    在select()之后Q处理wakeup竞争条g

Java Socket实现在不同^C的差?br />׃各种OSq_的socket实现不尽相同Q都会媄响到socket的实?br />需要考虑性能和健壮?br />    
 














cooperzh 2011-12-20 20:41 发表评论
]]>NIO Selectorhttp://www.tkk7.com/cooperzh/archive/2011/12/19/366789.htmlcooperzhcooperzhMon, 19 Dec 2011 10:53:00 GMThttp://www.tkk7.com/cooperzh/archive/2011/12/19/366789.html
处于qA状态的通道׃{待Selector选择

选择器提供了询问通道是否已经准备好执行每个IO操作的能?br />
qA选择的真正h值在于潜在的大量通道可以同时q行qA状态的查,Selector可以L军_选择哪个通道

真正的就l必ȝ操作pȝ来检查,操作pȝ处理IOhq知各个U程它们的数据已l已l准备好了,而选择器封装了q种抽象

原因Qjava代码来进行就l选择Q甚至jvm来进行就l检查代价都十分昂贵Qƈ且不够原子性,甚至会破坏线E的正常q行

实际上只有三个类用于执行qA选择Q?br />SelectorQ管理着一个被注册的通道集合信息和他们的qA状?br />SelectableChannelQ被注册到选择器中Qƈ且可以声明对哪种操作感兴?br />一个通道可以注册到多个选择器上
一个通道在一个选择器上只能注册一个操作集Q该操作集不会叠?br />SelectionKeyQ封装了特定通道与特定选择器的注册关系Q被SelectableChannel.register()q回

SelectableChannel在注册到选择器上之前Q先讄为非d模式?br />如果注册一个阻塞模式的SelectableChannel会抛出IllegalBlockingModeException异常
另外Q已l注册的SelectableChannel不能再修攚w塞模式,否则也会抛出IllegalBlockingModeException异常
注册一个已l关闭的SelectableChannelQ将会抛出ClosedChannelException异常
SelectableChannel注册到Selector上之后,Selector控制了通道的选择q程

通道有四U操作:
SelectionKey.OP_ACCEPT,
SelectionKey.OP_CONNECT,
SelectionKey.OP_READ,
SelectionKey.OP_WRITE

SocketChannel不支持accept操作

SelectionKey.cancel()Ҏ可以l结通道和选择器的关系Q然后SelectionKey会放在Selector.cancelledKeys() 的集合里
取消后SelectionKey会立卛_效,但注册不会立卛_消,select()调用l束后,cancelledKeys会处理Q相应的注销完成?br />
关闭通道Ӟ所有的相关的SelectionKey都会自动取消

Selector关闭Ӟ所有注册的通道会注销Q所有相关的SelectionKey会立卛_?br />
一个SelectionKey实例中有两个byte掩码Q?br />instrest集合Q指C通道/选择器结合体兛_的操?br />ready集合Q通道已经qA的操?br />
instrest集合通过SelectionKey.interestOps()获取Q通过SelectionKey.interestOps(int ops)来改?br />当相关的Selector正在select()操作时改变SelectionKey的instrest集合Q不影响正在q行的select()操作Q只会在下次select()时体?br />
ready集合通过SelectionKey.readyOps()获取Q是instrest集合的子集,是上ơselect()操作后就l的操作

最单的状态测试方法是Q?br />
SelectionKey.isAcceptable()   {h?nbsp;((key.readyOps( ) & SelectionKey.OP_ACCEPT) != 0
SelectionKey.isConnectable()   {h?nbsp;((key.readyOps( ) & SelectionKey.OP_CONNECT) != 0
SelectionKey.isReadable()   {h?nbsp;((key.readyOps( ) & SelectionKey.OP_READ) != 0
SelectionKey.isWritable()   {h?nbsp;((key.readyOps( ) & SelectionKey.OP_WRITE) != 0

SelectionKey.readyOps()q回的ready集合指示个提C,不是l对的保证,因ؓ底层的通道在Q何时候都可能会被改变

SelectionKey是线E安全的Q但修改instrest集合的操作是通过Selectorq行同步?br />q导致SelectionKey.interestOps()的调用会d不确定长的时?br />
Selector使用的锁{略是依赖具体实现的

在单U程理多个通道时不会出现问题,而多个线E共享Selector时会遇到同步的问题,到那时就应该重新设计

每个Selector都要同时l护三个SelectionKey集合Q?br />已注册的键的集合Registered key setQ不能直接修改,只能通过SelectionKey.interestOps()重置
已选择的键的集合Selected key setQ其中每个SelectionKey都有一个内嵌的ready集合Q可以直接移除其中的SelectionKeyQ但不能d
已取消的键的集合Cancelled key setQ是Selector的私有成员,无法直接讉K

Selector的核心是select()Q是Ҏ作系l的本地调用(native call)的封装,但是它不仅仅是向操作pȝ代码传递参敎ͼq对每个select操作应用不同的过E?/div>
select()的执行步骤:
1 查Cancelled key set。如果非I,每个取消的key从另外两个集合中移除,相关通道被注销Q执行完毕,Cancelled key set为空
2 查Registered key set中每个键的instrest集合Q检查中如果对instrest集合有改动,不会影响剩余的检查过E?br />   底层操作pȝ会q行查询Q确定每个通道兛_的操作的真实qA状态,依赖特定的select调用。如果没有就l的通道Q则dQ直臌?br />   q个q程会调用U程睡眠一D|_直至通道的就l状态被定下来
   对于那些操作pȝ指示臛_已经准备好instrest集合中的一U操作的通道Q将执行下面的一U操作:
   a 如果通道的SelectionKey没有处于Selected key set中,那么SelectionKey的ready集合会清空Q表C当前通道已经准备好的操作的Byte掩码被讄
   b 通道的SelectionKey已经处于Selected key set中,每个SelectionKey的ready集合更新为已l准备好的操作的Byte掩码(SelectionKey.OP_READ QSelectionKey.OP_WRITE{?
3 步骤2会花费很长时_特别是所Ȁ发的U程处于休眠状态时。步?l束Ӟ步骤1会重新执行,以完成Q意一个在选择q行的过E当中,键已l被取消的通道的注销
4 select()q回的值是步骤2中被修改的键的数量,即上一个select()调用之后q入qA状态的通道的数量,而不是Selected key set中通道的数量。之前的调用中已l就l的Qƈ且在本次调用中仍然就l的通道不会被计入。返回值有可能?

使用Cancelled key set来gq注销Q是一U防止线E在取消键时dQƈ防止与正在进行的选择操作冲突的好Ҏ

注销通道是一U代价很高的操作Q清理已取消的键Qƈ在选择操作之前或之后立x销通道Q可以消除它们可能正好在选择的过E中执行的潜在的问题?br />
Selector的select()有三UŞ式:
1 Selector.select() 
   该调用在没有通道qA时将无限dQ一旦有臛_一个已注册的通道qAQSelector的选择键将会立x斎ͼ每个选择键的ready集合也会被更?br />2 Selector.select(long timeout) 
   在提供的时旉内没有就l通道Q将会返?。如果设|timeout=0Q则{同于select() 
3 Selector.selectNow() 是完全非d的,会立卌回?br />
Selector.wakeup() 提供了ɾU程从被d的select()Ҏ中优雅的退出的能力
同样有三U方式唤醒select()Ҏ中睡眠的U程Q?br />1 Selector.wakeup() Selector上第一个没有返回的select()操作立即q回Q如果当前没有正在进行的select()操作Q则下次select()操作会立卌回?br />2 Selector.close() 被调用时Q所有在select()操作中阻塞的U程都将被唤醒,Selector相关通道被注销Q相关SelectionKey被取消
3 Thread.interrupt()被调用时Q返回状态将被设|。如果唤醒之后的U程试图在通道上执行IO操作Q通道会立卛_闭,U程捕捉C个异?br />
如果惌一个睡眠线E在中断之后l箋q行Q需要执行一些步骤来清理中断状?br />
q些操作不会改变单个相关通道Q中断一个Selector和中断一个通道是不一L。Selector只会查通道的状态?br />
当一个select() 操作时睡眠的U程发生了中断,对于通道状态而言Q是没有歧义?br />

选择器把合理的管理SelectionKeyQ以保它们表示的状态不会变得陈旧的d交给了程序员

当SelectionKey已经不在选择器的Selected key set中时Q会发生什?br />
当通道上至一个感兴趣的操作就l时QSelectionKey的ready集合会被清空Qƈ且当前已l就l的操作会被d到ready集合里,该SelectionKey随后会被d到Selected key set?br />
清理一个SelectionKey的ready集合的方式是这个key从Selected key set中移?br />
SelectionKey的就l状态只有在Selector的select() 操作q程中才会修改,因ؓ只有Selected key set中的SelectionKey才被认ؓ是包含了合法的就l信息的Q这些信息在SelectionKey中长久存在,知道key从Selected key set中移除,以通知Selector你已l看到ƈ对它q行了处理。当下一ơ通道的感兴趣的操作发生时Qkey被重新讄以反映当旉道的状态,q再ơ被d到Selected key set?br />
q种框架提供了非常大的灵zL?br />
常规做法是先调用select() 操作Q再遍历selectKeys()q回的key的集合。在按顺序进行检查每个key的过E中Q相关的通道也根据key的就l集合进行处理。然后key从Selected key set中移除,查下一个key。完成后通过调用select() 重复循环?/div>服务器端使用Ҏ
1 创徏ServerSocketChannelQ注册到Selector中,感兴的操作为accept
2 轮询Selector的select()操作Q从qAkey集合中遍历key的ready集合Q有accept则调用ServerSocketChannel的accept()Ҏ获取SocketChannelQ?u>其中包含接收到的socket的句?/u>。再SocketChannel注册到Selector中感兴趣的操作ؓread
3 当下ơselect()操作中key的ready集合中有readӞ开始做?br />
Selector是线E安全的Q但key set不是。Selector.keys ?nbsp;Selector.selectkeys() q回的是Selector内部U有key set的引用。这个集合可能随时被改变。P代器Iterator是快速失败的(fail-fast)Q如果P代的时候key set发生改变Q抛出ConcurrentModificationException。所有如果希望在多个U程间共享Selector或SelectionKeyQ则要对此做好准备。当你修改一个key的时候,可能会破坏另一个线E的Iterator

如果在多个线Eƈ发的讉K一个Selector的key setӞ需要合理地同步讉K。在select()操作Ӟ先在Selector上进行同步,再是Registered key setQ最后是Selected key set。按照这L序QCancelled key set׃在第一步和W三步之间保持同步?br />
在多U程的场景中Q如果需要对M一个key setq行更改Q不是直接更改q是其他操作带来的副作用Q都需要以相同的顺序,在同一对象上进行同步。如果竞争的U程没有以同L序h锁,则会有死锁的隐患?br />
Selector的close()和select()操作都会有一直阻塞的可能Q当在select()的过E当中,所有对close()的调用都会被dQ直到select()l束Q或者执行select()的线E进入睡眠。当select()的线E进入睡眠时Qclose()的线E获得锁后会立即唤醒select()的线E,q关闭Selector
如果不采取相同的序同步Q则key set中key的信息不保证是有效的Q相关通道也不保证是打开?br />

一个cpu的时候用一个线E来理所有通道Q是一个合适的解决ҎQ但会浪费其他cpu的运行能?br />
在大量通道上执行select()操作不会有太大开销Q因为大多数工作都是操纵pȝ完成?br />
Ҏ1 理多个SelectorQƈ随机分配通道不是一个好Ҏ
Ҏ2 所有通道使用一个SelectorQ将qA通道的服务委托给其他U程。相当于用一个线E监控通道的就l状态,使用另外的工作线E池来处理接收到的数据。而线E池是可以调整的Q或者动态调整?br />
在方?中,如果某些通道要求更高的响应速度Q可以用两个选择器来解决。ƈ且线E池可以l化为日志线E池、命令线E池、状态请求线E池{?br />



















cooperzh 2011-12-19 18:53 发表评论
]]>NIO Channel 道http://www.tkk7.com/cooperzh/archive/2011/12/19/366764.htmlcooperzhcooperzhMon, 19 Dec 2011 07:57:00 GMThttp://www.tkk7.com/cooperzh/archive/2011/12/19/366764.html
java.nio中的道只是在jvmq程内部传输数据Q优势在于封装?br />
q样允许单个用LE适用一个Selector从多个通道攉数据QƈLl合|络q接或本地工作线E适用

pipe的另一个用处是试Q可以将某个试c连接到道?#8220;?#8221;端,q检查管?#8220;?#8221;端出来的数据

cooperzh 2011-12-19 15:57 发表评论
]]>
NIO channel Socket通道http://www.tkk7.com/cooperzh/archive/2011/12/19/366752.htmlcooperzhcooperzhMon, 19 Dec 2011 06:15:00 GMThttp://www.tkk7.com/cooperzh/archive/2011/12/19/366752.html
DatagramChannel和SocketChannel实现了ReadableByteChannel ?nbsp;WritableByteChannel接口Q而ServerSocketChannel没实?br />
ServerSocketChannel只负责监听传入的q接Q创建新的SocketChannelQ它自n从不传输数据

socketcLSocket,ServerSocket,DatagramSocket

socket和socket通道之间的关p:
1 socket位于java.net包下Qsocket通道位于java.nio
2 Socket通道被创建的时候都会创Z个对{的Socket实例
3 通道的socket()可以调用socket,socket的getChannel()可以调用通道
4 Socket通道都有对应的Socket对象Q但直接定义的Socket对象是没有对应的通道
5 Socket通道委派协议操作l对应的Socket对象

要把一个Socket通道|ؓ非阻塞模式,p依靠所有Socket通道cȝ父类SelectableChannel

ServerSocketChannel是一个基于通道的Socket监听器,因ؓ增加了通道语意Q所以可以在非阻塞模式下q行?br />
ServerSocketChannel没有bindҎQ只能通过其对应的ServerSocket来实?/div>
ServerSocketChannel和其对应的ServerSocket都有accept()ҎQ?/div>
ServerSocketChannel.accept() q回SocketChannel对象QSocketChannel中包含对应的Socket
ServerSocket.accept() dq返回一个Socket对象

非阻塞模式下Q没有连接传入时QServerSocketChannel.accept()q回null

SocketChannel使用最频繁

Socket,SocketChannelcd装点对点Q有序的|络q接Q类gTCP/IP|络q接
Socket是面向流?br />
SocketChannel扮演客户端发起一个监听服务器的连接,直到q接成功Q才能收到数据,q且只从q接到的地址接收

SocketChannel的connect()Ҏ是阻塞式的,同其对应Scoket的connect()Ҏ一?br />
在非d模式下调用SocketChannel的finishConnect()Ҏ会返回的l果Q?br />1 connect()Ҏ没调用,产生NoConnectionPendingException异常
2 q接正在建立Q尚未完成,立即q回false
3 非阻塞模式下调用connect()后,SocketChannel回到d模式Q立卌回false
4 q接已徏立后QSocketChannel内部状态会更新为已q接状态,finishConnect()Ҏ会返回true,SocketChannel可以开始传输数据了

DatagramChannel对应DatagramSocket对象Q模拟包导向的无q接协议UDP/IP
DatagramSocket
每个数据报Datagram都包含自q地址

DatagramChannel可以发送DatagramC同的地址Q同样也可以接收不同地址的Datagram

Z包的吞吐量比Z的高很多,因ؓ互联|基设施是包导向的

TCPq样面向的协议会生巨大的开销Qƈ且不能适用所有的情Ş

选择DatagramSocket而不是流Socket的前提:
1 E序可以承受数据丢失和无序数?br />2 发送后不需要知道对Ҏ否已l接Ӟ即fire and forget
3 数据吞吐量比可靠性更重要
4 同时发送数据给多个接收?br />5 包导向更适合现在的Q?/div>


cooperzh 2011-12-19 14:15 发表评论
]]>NIO channelhttp://www.tkk7.com/cooperzh/archive/2011/12/19/366733.htmlcooperzhcooperzhMon, 19 Dec 2011 03:26:00 GMThttp://www.tkk7.com/cooperzh/archive/2011/12/19/366733.html
channel的实现经怋用操作系l的本地代码

implement InterruptibleChannel 后标C通道可以被中断,大多数channel都是可以被中断的
面向字节的接口:ReadableByteChannel,WriteableByteChannel
ByteChannel接口l承了ReadableByteChannel和WriteableByteChannel接口

IOq义上可以分为file IO ?stream IOQ对应file通道和socket通道
file通道的类QFileChanel
socket通道的类QSocketChannel,ServerSocketChannel,DatagramChanenel

FileChannel不能直接创徏Q只能通过打开的RandomAccessFile,FileInputStream,FileOutputStream调用getChannel()获得
socket通道可以直接调用其工厂方法获得实?br />
只实现ReadableByteChannel或WriteableByteChannel的通道都是单向的,两个都实现就是双向的
实现ByteChannel的通道都是双向?br />
所有socketChannelc都是双向的

read() 字节从通道d~冲?br />write() 字节从~冲区写入通道

讲byteBuffer的数据写入通道Q?br />while(buffer.hasRemaining()){
    dest.write(buffer);
}
因ؓwrite操作可能会因为其他线E的调用而阻?br />
~冲区可以重复用,而通道是一ơ性的Q用完就关闭。通道关闭后,代表的与socket的连接会丢失

closeҎ是阻塞式的,多次close没有坏处。close的实现取决于操作pȝ?br />
实现InterruptibleChannel接口的类Q如果线E在该通道上被dQ同时线E被中断Q则通道会关闭Q阻塞线E会报异常:ClosedByInterruptException.

另外Q设|了interrupt status的线讉K一个通道Ӟ该通道会立即被关闭,同时抛出ClosedByInterruptException异常
而线E的interrupt status是线E的interrupt()Ҏ讄Qƈ通过Thread.interrupted()清除?br />
通道上的U程休眠Q则通道会关?br />
当一个通道被关闭的时候,所有在此通道上休眠的U程都将被唤醒,q收C个AsynchronousCloseExceptionQ接着通道被关闭?br />
一个通道同时对多个byteBuffer的操作称为分散和聚合
Scatter分散体现在channel.read(ByteBuffer[] dsts) ?channel.read(ByteBuffer[] dsts,int offset,int length); 
Gather分散体现在channel.write(ByteBuffer[] dsts) ?channel.write(ByteBuffer[] dsts,int offset,int length);
offset ?length指的是ByteBuffer[]中第几个ByteBuffer





cooperzh 2011-12-19 11:26 发表评论
]]>
NIO bufferhttp://www.tkk7.com/cooperzh/archive/2011/12/18/366679.htmlcooperzhcooperzhSun, 18 Dec 2011 13:10:00 GMThttp://www.tkk7.com/cooperzh/archive/2011/12/18/366679.html
ByteBuffer buffer = ByteBuffer.allocateDirect(int);
allocateq回的其实是ByteBuffer的子cHeapByteBuffer;
allocateDirectq回的其实是MappedByteBuffer的子cDirectByteBuffer;

CharBuffer.allocate(int cap); 创徏的是char[]数组Q即cap是char的数量,而不是byte的数量(1个char长度?个byteQ?/span>

buffer的四个属?br />0 <= mark <= position <= limit <= capacity

buffer支持U联调用Q?br />buffer.position(2).mark().position(4);
因ؓposition,markҎq回的都是buffer的引?br />
position军_了read和write的v始位|,于position位置的char都忽?br />
buffer.get() ?buffer.put(byte) 都是相对ҎQ都会导致position++
buffer.get(int) ?buffer.put(int,byte) 都是l对ҎQ不会导致position变化

buffer.clear() 只是position|ؓ0Qlimit讄为capacityQƈ不真正清I数?br />
buffer.position(int) ?newPosition < mark Ӟ丢弃mark(讄mark=-1)

buffer.mark() ?nbsp;position赋值给 mark ;
buffer.reset() ?nbsp;mark 赋值给 position;

buffer.remaining() 表示position到limit的数?br />
buffer的比较是比较position到limit之间的内?br />
dbuffer的内容,是从position开始读取的Qƈ不是??br />
反{bufferQbuffer.flip();
1 limit = position;
2 position = 0;
3 mark = -1;
flip只能{1ơ,{两次会Dlimit也等?Q通常用于read状态切换到write状态,或者write状态切换到read状?/span>

buffer.put(buffer) 会报IllegalArgumentException异常Q因为position的移动相矛盾

buffer.slice() 用position和limit之间的内Ҏ生成一个bufferQ该buffer的capacity为limit-position的?br />

charBuffer.wrap(char[], int start, int end) 会讄positon=startQƈchar[]直接传递给charBuffer成ؓ内部char array

从bufferd数据到数l,必须指定数据的长度:
1 buffer.remaining() < arr.length dbuffer的position到limit的所有?br />2 buffer.remaining() > arr.length dbuffer中arr.length长度的|q立卛_理,然后再读取buffer中剩余的?br />
从数l读取数据到bufferQ?br />
1 buffer.remaining() > arr.length d数组的所有?br />2 buffer.remaining() < arr.length 抛出BufferOverflowException

buffer中多字节的数据类型存储的字节序没有标准Q目前有
1 大端字节序(BIG_ENDIAN) Q高位值在低地址(靠左?Qmotorola,sun,prow pc的处理器
2 端字节序 (LITTLE_ENDIAN)Q高位值在高地址(靠右?Qintel处理?br />q是由操作系l决定的Q可以用ByteOrder.nativeOrder()的返回|ByteOrder.BIG_ENDIAN ?nbsp;ByteOrder.LITTLE_ENDIAN来确?br />默认都是BIG_ENDIAN Q这是jvm军_的,所以jvmq行在intel处理器上会有性能隐患
只有ButeBuffer可以修改字节序Q通过order()ҎQ除非显式修改,否则创徏后永不改?br />其他cd的buffer可以转换为ByteBuffer处理

直接~冲区是IO操作的最好选择。但q在一开始就一定要创徏?br />直接~冲区是调用操作pȝ的代码分配的Q绕开了jvm的堆栈。创建和销毁的代h都比较大?br />只有byteBuffer.allocateDirect()才能创徏直接~冲?br />allocate() ?wrap() 的包装方法都创徏的是非直接缓冲区(即jvm堆栈?

ByteBuffer允许通过创徏视图来讲byte映射为其他原始数据类型,使用asLongBuffer(){方?br />
ByteBuffer byteBuffer = ByteBuffer.allocate(7).order(ByteOrder.BIG_ENDIAN);
CharBuffer charBuffer = byteBuffer.asCharBuffer();
charBuffer只是一个viewQ只是ؓ了方便操作,本质上数据依旧是存放在byteBuffer中的

ByteBuffer的getInt{方法是获取position到limit中的byteq{换ؓ对应的int{数据类?br />注意Q不同的字节序Q取出的数值是不同?br />
java中的byte都是有符L
获取及存放无W号整数需要自己写一个工LQ?br />如byte型:
buffer.put( (byte) (value & 0xff) ); 保存value的低8位的?br />(short)(buffer.get() & 0xff) ?br />short型:
buffer.putShort((short)(value & 0xffff));
(int)(buffer.getShort() & 0xffff);
int?
buffer.putInt((int)(value & 0xffffffffL));
(long)(buffer.getInt() & 0xffffffffL);







cooperzh 2011-12-18 21:10 发表评论
]]>
վ֩ģ壺 йŮ69ٸ| ѾƷƵ| ձŷҹƬŮԱ| þùƵ| Ļavѷdvd| Ѽվһҳ| ޾Ʒ| 91޾Ʒһۺϲ| Ʒ޳ɦɦ߹ۿ| ˽ӰԺƵվ| hƵ߹ۿ| Ů˽Ƶwww| ĻӰ߿ѹۿ| AVһۿ| 㽶þһ| ޳Ļ| AVר޾Ʒ| ˾þô߽| ޾Ʒav| aëƬ߹ۿ| Ʒһ߹ۿ| AVƬ߹ۿ| þۺAVѹۿ| һ߲ѹۿİƵ | ۺϾþó69| ձ߿Ƭ| AVպƷþþþ| VAĻһ| ĻƷ| ɫƷ88ɫ¶ | | ˳Ƶ| ձҳַ߿Ѳ | ޾ƷƬ߹ۿ| Ʒպһ| aɻ߹ۿ| Ʒ޳ɦɦ߹ۿ | ߹ۿ| 뾫Ʒ| avҹƷһ| ޼Ƶ߹ۿ|