本篇內(nèi)容介紹了“netty ServerBootstrap.bind方法怎么使用”的有關(guān)知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領(lǐng)大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠?qū)W有所成!
創(chuàng)新互聯(lián)公司長期為千余家客戶提供的網(wǎng)站建設(shè)服務(wù),團隊從業(yè)經(jīng)驗10年,關(guān)注不同地域、不同群體,并針對不同對象提供差異化的產(chǎn)品和服務(wù);打造開放共贏平臺,與合作伙伴共同營造健康的互聯(lián)網(wǎng)生態(tài)環(huán)境。為海棠企業(yè)提供專業(yè)的成都網(wǎng)站設(shè)計、網(wǎng)站制作、外貿(mào)營銷網(wǎng)站建設(shè),海棠網(wǎng)站改版等技術(shù)服務(wù)。擁有10年豐富建站經(jīng)驗和眾多成功案例,為您定制開發(fā)。
我們都知道,netty是基于java nio的,那么具體的,java nio當中的一些具體元素,比如bind,selector,channel等等具體元素,在netty當中又是如何出現(xiàn)的。今天我們來找一找。
首先從ServerBootstrap.bind()方法開始。
該方法的具體實現(xiàn)在AbstractBootstrap當中,直接看到doBind方法,
final ChannelFuture regFuture = initAndRegister();
看看 initAndRegister 這里面具體干了點什么,
首先是創(chuàng)建了一個channel,并且從boss group當中分配一個進程給這個新創(chuàng)建的channel。
Channel channel;
try {
channel = createChannel();
} catch (Throwable t) {
return VoidChannel.INSTANCE.newFailedFuture(t);
}
然后是初始化的工作,具體內(nèi)容這里暫不深究。
try {
init(channel);
} catch (Throwable t) {
channel.unsafe().closeForcibly();
return channel.newFailedFuture(t);
}
然后是注冊,首先得到一個promise,這個東西按照我的理解,就是為了異步的返回各種操作的執(zhí)行結(jié)果,后續(xù)我們會經(jīng)常見到它。然后執(zhí)行register,我們又發(fā)現(xiàn)一個很詭異的東西unsafe,
這個東西是創(chuàng)建channel的時候帶出來的,具體作用是跟java nio打交道,注入bind,register等操作,都在這里面完成,為什么弄這么個東西,目前不明,以后再說。
ChannelPromise regFuture = channel.newPromise();
channel.unsafe().register(regFuture);
if (regFuture.cause() != null) {
if (channel.isRegistered()) {
channel.close();
} else {
channel.unsafe().closeForcibly();
}
}
然后是AbstractChannel$AbstractUnsafe.register(),第一個判斷很有意思,我們可以這樣理解,如果當前channel對應(yīng)的 eventLoop 已經(jīng)在執(zhí)行了,也就是說,當前代碼已經(jīng)處在了一個子線程當中,那么直接調(diào)用 register0 方法,否則,新起一個線程執(zhí)行。
if (eventLoop.inEventLoop()) {
register0(promise);
} else {
try {
eventLoop.execute(new Runnable() {
@Override
public void run() {
register0(promise);
}
});
} catch (Throwable t) {
logger.warn(
"Force-closing a channel whose registration task was not accepted by an event loop: {}",
AbstractChannel.this, t);
closeForcibly();
closeFuture.setClosed();
promise.setFailure(t);
}
}
然后我們看一下 register0 里面具體干了些什么:
try {
// check if the channel is still open as it could be closed in the mean time when the register
// call was outside of the eventLoop
if (!ensureOpen(promise)) {
return;
}
doRegister();
registered = true;
promise.setSuccess();
pipeline.fireChannelRegistered();
if (isActive()) {
pipeline.fireChannelActive();
}
} catch (Throwable t) {
// Close the channel directly to avoid FD leak.
closeForcibly();
closeFuture.setClosed();
if (!promise.tryFailure(t)) {
logger.warn(
"Tried to fail the registration promise, but it is complete already. " +
"Swallowing the cause of the registration failure:", t);
}
}
首先通過promise檢查了一下當前channel的狀態(tài),然后執(zhí)行doRegister,這個方法在AbstractNioChannel當中實現(xiàn), 主要內(nèi)容如下:
selectionKey = javaChannel().register(eventLoop().selector, 0, this);
好了目前我們明確了2個問題,第一selector存在于 eventLoop 當中,具體地說,應(yīng)該是在boss loop當中。第二個問題,在執(zhí)行register的時候,還沒有執(zhí)行bind。
在之后設(shè)置了promise,并通過pipeline出發(fā)了ChannelRegistered()事件。
下面再回到 AbstractBootstrap .doBind方法,第一步,初始化和注冊selector的過程都已經(jīng)完成了。
final ChannelFuture regFuture = initAndRegister();
final Channel channel = regFuture.channel();
if (regFuture.cause() != null) {
return regFuture;
}
final ChannelPromise promise;
if (regFuture.isDone()) {
promise = channel.newPromise();
doBind0(regFuture, channel, localAddress, promise);
} else {
// Registration future is almost always fulfilled already, but just in case it's not.
promise = new DefaultChannelPromise(channel, GlobalEventExecutor.INSTANCE);
regFuture.addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
doBind0(regFuture, channel, localAddress, promise);
}
});
}
怎么理解上面那段代碼,尤其是 if (regFuture.isDone()) 之后的部分,我們前面提到,具體的register過程可能是在一個子線程當中執(zhí)行的,所以這里需要等待register完成,才能進行下一步bind操作。
看doBind0的實現(xiàn),這里還是直接起一個線程來執(zhí)行bind操作。
channel.eventLoop().execute(new Runnable() {
@Override
public void run() {
if (regFuture.isSuccess()) {
channel.bind(localAddress, promise).addListener(ChannelFutureListener.CLOSE_ON_FAILURE);
} else {
promise.setFailure(regFuture.cause());
}
}
});
看 channel.bind ,方法實現(xiàn)在AbstractChannel當中,很有趣,直接通過pipeline來做這個bind。那么我們來看一下pipeline.bind,實現(xiàn)在DefaultChannelPipeline當中,我們看到的是tail.bind,那么很顯然,是要反向遍歷鏈表,最終通過head去執(zhí)行bind。那么我們看看HeadHandler.bind方法,unsafe.bind(localAddress, promise); 通過查找代碼,我們發(fā)現(xiàn), unsafe 就是最初我們創(chuàng)建的Server channel的unsafe,這是同一個東西。
我們再看一下AbstractChannel$AbstractUnsafe的bind方法,這里面涉及到一些對操作系統(tǒng)信息的讀取和判斷,然后調(diào)用了NioServerSocketChannel.doBind()方法,具體的真正的,java nio的bind操作,就在這里執(zhí)行。
最后一件事,java nio當中的channel在netty當中是怎么出現(xiàn)的。很簡單,看一下NioServerSocketChannel這個類,這個當中有一個newSocket方法,直接返回一個java 的ServerSocketChannel。
以上,我們基本達到了我們此次代碼分析的目的,搞清楚了,在netty服務(wù)器啟動的時候,具體干了些什么,
在netty當中找到了java nio當中的一些具體元素。
同時,我們找到了handler當中2個具體的event,bind和channelRegistered具體的發(fā)起時機。
另外,我們初步了解了netty提供的異步返回機制,ChannelFuture究竟是怎么工作的。
“netty ServerBootstrap.bind方法怎么使用”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識可以關(guān)注創(chuàng)新互聯(lián)網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實用文章!