• Netty服务端的业务流程分析


    Netty的服务端怎么和java NIO联系起来的,一直很好奇这块内容,这里跟下代码,下篇文章看下Channel相关的知识。

    1. finalChannelFuture initAndRegister(){
    2. finalChannel channel = channelFactory().newChannel();//
    3. try{
    4. init(channel);
    5. }catch(Throwable t){
    6. channel.unsafe().closeForcibly();//立即关闭通道且不会触发事件
    7. //因为这个通道还没有注册到EventLoop,所以我们需要强制GlobalEventExecutor的使用。
    8. returnnewDefaultChannelPromise(channel,GlobalEventExecutor.INSTANCE).setFailure(t);
    9. }
    10. //注册一个EventLoop
    11. ChannelFuture regFuture = group().register(channel);
    12. //注册失败
    13. if(regFuture.cause()!=null){
    14. if(channel.isRegistered()){
    15. channel.close();
    16. }else{
    17. channel.unsafe().closeForcibly();
    18. }
    19. }
    20. // If we are here and the promise is not failed, it's one of the following cases:
    21. // 程序运行到这里且promise没有失败,可能有如下几种情况
    22. // 1) If we attempted registration from the event loop, the registration has been completed at this point.
    23. // 如果试图注册到一个EventLoop,该注册完成,
    24. // i.e. It's safe to attempt bind() or connect() now because the channel has been registered.
    25. // 2) If we attempted registration from the other thread, the registration request has been successfully
    26. // added to the event loop's task queue for later execution.
    27. // 如果试图注册到其他线程,该注册已经成功,但是没有完成,添加一个事件到任务队列中,等会执行
    28. // i.e. It's safe to attempt bind() or connect() now:
    29. // because bind() or connect() will be executed *after* the scheduled registration task is executed
    30. // because register(), bind(), and connect() are all bound to the same thread.
    31. return regFuture;
    32. }
    注意这里的Channle的类型为NioServerSocketChannel类型,group()返回NioEventLoopGroup类型,他继承MultithreadEventLoopGroup,那么看下register的实现:
    1. @Override
    2. publicChannelFutureregister(Channel channel){
    3. return next().register(channel);
    4. }
    跟进去是调用了SingleThreadEventLoop类的register方法。实现如下.
    1. @Override
    2. publicChannelFutureregister(finalChannel channel,finalChannelPromise promise){
    3. if(channel ==null){
    4. thrownewNullPointerException("channel");
    5. }
    6. if(promise ==null){
    7. thrownewNullPointerException("promise");
    8. }
    9. channel.unsafe().register(this, promise);
    10. return promise;
    11. }
    调用了NioServerSocketChannel的unsafe()的register方法。
    1. @Override
    2. publicfinalvoidregister(EventLoop eventLoop,finalChannelPromise promise){
    3. if(eventLoop ==null){
    4. thrownewNullPointerException("eventLoop");
    5. }
    6. if(isRegistered()){
    7. promise.setFailure(newIllegalStateException("registered to an event loop already"));
    8. return;
    9. }
    10. if(!isCompatible(eventLoop)){
    11. promise.setFailure(
    12. newIllegalStateException("incompatible event loop type: "+ eventLoop.getClass().getName()));
    13. return;
    14. }
    15. AbstractChannel.this.eventLoop = eventLoop;
    16. if(eventLoop.inEventLoop()){
    17. register0(promise);
    18. }else{
    19. try{
    20. eventLoop.execute(newOneTimeTask(){
    21. @Override
    22. publicvoid run(){
    23. register0(promise);
    24. }
    25. });
    26. }catch(Throwable t){
    27. logger.warn(
    28. "Force-closing a channel whose registration task was not accepted by an event loop: {}",
    29. AbstractChannel.this, t);
    30. closeForcibly();
    31. closeFuture.setClosed();
    32. safeSetFailure(promise, t);
    33. }
    34. }
    35. }
    对这个eventloop.inEventLoop的理解不是很深刻,有点像android开发里面费时的操作不要放到主线程里面。eventLoop.inEventLoop()表示不在主线程里面。
    register的最终实现在,AbstractNioChannel类里面:
    1. @Override
    2. protectedvoid doRegister()throwsException{
    3. boolean selected =false;
    4. for(;;){
    5. try{
    6. selectionKey = javaChannel().register(eventLoop().selector,0,this);
    7. return;
    8. }catch(CancelledKeyException e){
    9. if(!selected){
    10. // Force the Selector to select now as the "canceled" SelectionKey may still be
    11. // cached and not removed because no Select.select(..) operation was called yet.
    12. eventLoop().selectNow();
    13. selected =true;
    14. }else{
    15. // We forced a select operation on the selector before but the SelectionKey is still cached
    16. // for whatever reason. JDK bug ?
    17. throw e;
    18. }
    19. }
    20. }
    21. }
    javaChannel()返回Java的Channel对象,eventLoop()返回NioEventLoop对象。里面包含一个selector对象。selectNow是个非阻塞的调用,调用此方法会清除所有以前调用 wakeup 方法所得的结果
    Netty的Channel是对JDK中Channel的包装和扩展。
     
    注册成功后就需要绑定端口了,
    1. privatestaticvoid doBind0(
    2. finalChannelFuture regFuture,finalChannel channel,
    3. finalSocketAddress localAddress,finalChannelPromise promise){
    4. // This method is invoked before channelRegistered() is triggered. Give user handlers a chance to set up
    5. // the pipeline in its channelRegistered() implementation.
    6. channel.eventLoop().execute(newRunnable(){
    7. @Override
    8. publicvoid run(){
    9. if(regFuture.isSuccess()){
    10. channel.bind(localAddress, promise).addListener(ChannelFutureListener.CLOSE_ON_FAILURE);
    11. }else{
    12. promise.setFailure(regFuture.cause());
    13. }
    14. }
    15. });
    16. }
    除了监听端口还不够,还要处理IO事件:
    1. @Override
    2. protectedvoid run(){
    3. for(;;){
    4. boolean oldWakenUp = wakenUp.getAndSet(false);
    5. try{
    6. if(hasTasks()){
    7. selectNow();
    8. }else{
    9. select(oldWakenUp);
    10. // 'wakenUp.compareAndSet(false, true)' is always evaluated
    11. // before calling 'selector.wakeup()' to reduce the wake-up
    12. // overhead. (Selector.wakeup() is an expensive operation.)
    13. //
    14. // However, there is a race condition in this approach.
    15. // The race condition is triggered when 'wakenUp' is set to
    16. // true too early.
    17. //
    18. // 'wakenUp' is set to true too early if:
    19. // 1) Selector is waken up between 'wakenUp.set(false)' and
    20. // 'selector.select(...)'. (BAD)
    21. // 2) Selector is waken up between 'selector.select(...)' and
    22. // 'if (wakenUp.get()) { ... }'. (OK)
    23. //
    24. // In the first case, 'wakenUp' is set to true and the
    25. // following 'selector.select(...)' will wake up immediately.
    26. // Until 'wakenUp' is set to false again in the next round,
    27. // 'wakenUp.compareAndSet(false, true)' will fail, and therefore
    28. // any attempt to wake up the Selector will fail, too, causing
    29. // the following 'selector.select(...)' call to block
    30. // unnecessarily.
    31. //
    32. // To fix this problem, we wake up the selector again if wakenUp
    33. // is true immediately after selector.select(...).
    34. // It is inefficient in that it wakes up the selector for both
    35. // the first case (BAD - wake-up required) and the second case
    36. // (OK - no wake-up required).
    37. if(wakenUp.get()){
    38. selector.wakeup();
    39. }
    40. }
    41. cancelledKeys =0;
    42. needsToSelectAgain =false;
    43. finalint ioRatio =this.ioRatio;
    44. if(ioRatio ==100){
    45. processSelectedKeys();
    46. runAllTasks();
    47. }else{
    48. finallong ioStartTime =System.nanoTime();
    49. processSelectedKeys();
    50. finallong ioTime =System.nanoTime()- ioStartTime;
    51. runAllTasks(ioTime *(100- ioRatio)/ ioRatio);
    52. }
    53. if(isShuttingDown()){
    54. closeAll();
    55. if(confirmShutdown()){
    56. break;
    57. }
    58. }
    59. }catch(Throwable t){
    60. logger.warn("Unexpected exception in the selector loop.", t);
    61. // Prevent possible consecutive immediate failures that lead to
    62. // excessive CPU consumption.
    63. try{
    64. Thread.sleep(1000);
    65. }catch(InterruptedException e){
    66. // Ignore.
    67. }
    68. }
    69. }
    70. }
    该方法是NioEventLoop
     
     
     
     
     





  • 相关阅读:
    javascript_数组
    Javascript_函数
    JavaScript思维导图
    django 中session的存储和获取
    使用 PyCharm 在centos 部署代码
    mac下使用async-profiler
    Mac系统如何清理mysql
    Java-多线程
    Java内部类
    WinForm使用发布方式进行安装的安装目录
  • 原文地址:https://www.cnblogs.com/gaoxing/p/4401791.html
Copyright © 2020-2023  润新知