上文的內容還有一些沒有結尾,這篇補上。在ExpiringMap類中,使用了一個私人內部類ExpiringObject來表示待檢查逾時的對象,它包括三個域,鍵,值,上次訪問時間,以及用於上次訪問時間這個域的讀寫鎖:
private K key;
private V value;
private long lastAccessTime;
private final ReadWriteLock lastAccessTimeLock = new ReentrantReadWriteLock();
而ExpiringMap中包括了下述幾個變數:
private final ConcurrentHashMap<K, ExpiringObject> delegate;//逾時代理集合,儲存待檢查對象
private final CopyOnWriteArrayList<ExpirationListener<V>> expirationListeners;//逾時監聽者
private final Expirer expirer;//逾時檢查線程
現在再來看看IoSession的一個抽象實作類別AbstractIoSession。這是它的幾個重要的成員變數:
private IoSessionAttributeMap attributes;//會話屬性對應圖
private WriteRequestQueue writeRequestQueue;//寫請求隊列
private WriteRequest currentWriteRequest;//當前寫請求
當要結束當前會話時,會發送一個一個寫請求CLOSE_REQUEST。而closeFuture這個CloseFuture會在串連關閉時狀態被設定為”closed”,它的監聽器是SCHEDULED_COUNTER_RESETTER。
close和closeOnFlush都是非同步關閉操作,區別是前者立即關閉串連,而後者是在寫請求隊列中放入一個CLOSE_REQUEST,並將其即時重新整理出去,若要真正等待關閉完成,需要調用方在返回的CloseFuture等待
public final CloseFuture close() {
synchronized (lock) {
if (isClosing()) {
return closeFuture;
} else {
closing = true;
}
}
getFilterChain().fireFilterClose();//fire出關閉事件
return closeFuture;
}
public final CloseFuture closeOnFlush() {
getWriteRequestQueue().offer(this, CLOSE_REQUEST);
getProcessor().flush(this);
return closeFuture;
}
下面來看看讀資料的過程:
public final CloseFuture close() {
synchronized (lock) {
if (isClosing()) {
return closeFuture;
} else {
closing = true;
}
}
getFilterChain().fireFilterClose();//fire出關閉事件
return closeFuture;
}
public final CloseFuture closeOnFlush() {
getWriteRequestQueue().offer(this, CLOSE_REQUEST);
getProcessor().flush(this);
return closeFuture;
}
private Queue<ReadFuture> getReadyReadFutures() {//返回可被讀資料隊列
Queue<ReadFuture> readyReadFutures =
(Queue<ReadFuture>) getAttribute(READY_READ_FUTURES_KEY);//從會話映射表中取出可被讀資料隊列
if (readyReadFutures == null) {//第一次讀資料
readyReadFutures = new CircularQueue<ReadFuture>();//構造一個新讀資料隊列
Queue<ReadFuture> oldReadyReadFutures =
(Queue<ReadFuture>) setAttributeIfAbsent(
READY_READ_FUTURES_KEY, readyReadFutures);
if (oldReadyReadFutures != null) {
readyReadFutures = oldReadyReadFutures;
}
}
return readyReadFutures;
}
public final ReadFuture read() {//讀資料
if (!getConfig().isUseReadOperation()) {//會話配置不允許讀資料(這是預設情況)
throw new IllegalStateException("useReadOperation is not enabled.");
}
Queue<ReadFuture> readyReadFutures = getReadyReadFutures();//擷取已經可被讀資料隊列
ReadFuture future;
synchronized (readyReadFutures) {//鎖住讀資料隊列
future = readyReadFutures.poll();//取隊頭資料
if (future != null) {
if (future.isClosed()) {//關聯的會話已經關閉了,讓讀者知道此情況
readyReadFutures.offer(future);
}
} else {
future = new DefaultReadFuture(this);
getWaitingReadFutures().offer(future); //將此資料插入等待被讀取資料的隊列,這個代碼和上面的getReadyReadFutures類似,只是索引值不同而已
}
}
return future;
}
再來看寫資料到指定遠端地址的過程,可以寫三種類型資料:IoBuffer,整個檔案或檔案的部分地區,這會通過傳遞寫請求給過濾器鏈條來完成資料向目的遠端的傳輸。
public final WriteFuture write(Object message, SocketAddress remoteAddress) {
FileChannel openedFileChannel = null;
try
{
if (message instanceof IoBuffer&& !((IoBuffer) message).hasRemaining())
{// 空訊息
throw new IllegalArgumentException(
"message is empty. Forgot to call flip()?");
}
else if (message instanceof FileChannel)
{//要發送的是檔案的某一地區
FileChannel fileChannel = (FileChannel) message;
message = new DefaultFileRegion(fileChannel, 0, fileChannel.size());
}
else if (message instanceof File)
{//要發送的是檔案,開啟檔案通道
File file = (File) message;
openedFileChannel = new FileInputStream(file).getChannel();
message = new DefaultFileRegion(openedFileChannel, 0, openedFileChannel.size());
}
}
catch (IOException e)
{
ExceptionMonitor.getInstance().exceptionCaught(e);
return DefaultWriteFuture.newNotWrittenFuture(this, e);
}
WriteFuture future = new DefaultWriteFuture(this);
getFilterChain().fireFilterWrite(
new DefaultWriteRequest(message, future, remoteAddress)); //構造寫請求,通過過濾器鏈發送出去,寫請求中指明了要發送的訊息,目的地址,以及返回的結果
//如果開啟了一個檔案通道(發送的檔案的部分地區或全部),就必須在寫請求完成時關閉檔案通道
if (openedFileChannel != null) {
final FileChannel finalChannel = openedFileChannel;
future.addListener(new IoFutureListener<WriteFuture>() {
public void operationComplete(WriteFuture future) {
try {
finalChannel.close();//關閉檔案通道
} catch (IOException e) {
ExceptionMonitor.getInstance().exceptionCaught(e);
}
}
});
}
return future;//寫請求成功完成
}
最後,來看看一個WriteRequestQueue的實現,唯一加入的一個功能就是若在隊頭髮現是請求關閉,則會去關閉會話。
private class CloseRequestAwareWriteRequestQueue implements WriteRequestQueue {
private final WriteRequestQueue q;//內部實際的寫請求隊列
public CloseRequestAwareWriteRequestQueue(WriteRequestQueue q) {
this.q = q;
}
public synchronized WriteRequest poll(IoSession session) {
WriteRequest answer = q.poll(session);
if (answer == CLOSE_REQUEST) {
AbstractIoSession.this.close();
dispose(session);
answer = null;
}
return answer;
}
public void offer(IoSession session, WriteRequest e) {
q.offer(session, e);
}
public boolean isEmpty(IoSession session) {
return q.isEmpty(session);
}
public void clear(IoSession session) {
q.clear(session);
}
public void dispose(IoSession session) {
q.dispose(session);
}
}
作者:phinecos(洞庭散人)
出處:http://phinecos.cnblogs.com/
本文著作權歸作者和部落格園共有,歡迎轉載,但請保留此段聲明,並在文章頁面明顯位置給出原文串連。
作者:洞庭散人
出處:http://phinecos.cnblogs.com/
本部落格遵從Creative Commons Attribution 3.0 License,若用於非商業目的,您可以自由轉載,但請保留原作者資訊和文章連結URL。