服务端启动流程

我们回顾前面讲解的netty启动流程,服务端这边有两个EventLoopGroup,一个专门用来处理连接,一个用来处理后续的io事件

服务端启动还是跟nio一样,绑定端口进行监听,我们先来看绑定流程

  1. // 绑定端口并同步阻塞直到绑定结束
  2. ChannelFuture cf = serverBootstrap.bind(8080).sync();
  3.  
  4. private ChannelFuture doBind(final SocketAddress localAddress) {
  5. // 注册一个NioServerScoketChannel
  6. final ChannelFuture regFuture = initAndRegister();
  7. final Channel channel = regFuture.channel();
  8. if (regFuture.cause() != null) {
  9. return regFuture;
  10. }
  11.  
  12. if (regFuture.isDone()) {
  13. // 注册完成 进行端口绑定,并返回一个异步绑定结果
  14. ChannelPromise promise = channel.newPromise();
  15. // 绑定端口
  16. doBind0(regFuture, channel, localAddress, promise);
  17. return promise;
  18. } else {
  19. // 未注册完成 返回带注册结果的promise
  20. final PendingRegistrationPromise promise = new PendingRegistrationPromise(channel);
  21. // 添加一个注册异步监听器 但注册完成会进行绑定操作
  22. regFuture.addListener(new ChannelFutureListener() {
  23. @Override
  24. public void operationComplete(ChannelFuture future) throws Exception {
  25. Throwable cause = future.cause();
  26. if (cause != null) {
  27. // 注册时有发生异常 通知相关监听器
  28. promise.setFailure(cause);
  29. } else {
  30. // 设置注册结果为true
  31. promise.registered();
  32. // 绑定端口
  33. doBind0(regFuture, channel, localAddress, promise);
  34. }
  35. }
  36. });
  37. return promise;
  38. }
  39. }
  40.  
  41. final ChannelFuture initAndRegister() {
  42. Channel channel = null;
  43. try {
  44. /**
  45. * 通过我们之前设置的socket类型进行实例化
  46. * serverBootstrap.channel(NioServerSocketChannel.class) 会返回一个ReflectiveChannelFactory工厂
  47. **/
  48. channel = channelFactory.newChannel(); // 1
  49. // 初始化channel
  50. init(channel); // 2
  51. } catch (Throwable t) {
  52. if (channel != null) {
  53. // 发生异常 将内部nio中的channel进行关闭
  54. channel.unsafe().closeForcibly();
  55. // 返回一个结果失败的Promise
  56. return new DefaultChannelPromise(channel, GlobalEventExecutor.INSTANCE).setFailure(t);
  57. }
  58. return new DefaultChannelPromise(new FailedChannel(), GlobalEventExecutor.INSTANCE).setFailure(t);
  59. }
  60.  
  61. // 将channel注册到bossEventLoopGroup上,里面会将channel注册到EventLoop中的selector
  62. ChannelFuture regFuture = config().group().register(channel); // 3
  63. if (regFuture.cause() != null) {
  64. if (channel.isRegistered()) {
  65. channel.close();
  66. } else {
  67. channel.unsafe().closeForcibly();
  68. }
  69. }
  70. return regFuture;
  71. }
  72.  
  73. private static void doBind0(
  74. final ChannelFuture regFuture, final Channel channel,
  75. final SocketAddress localAddress, final ChannelPromise promise) {
  76.  
  77. // 添加一个异步绑定任务
  78. channel.eventLoop().execute(new Runnable() {
  79. @Override
  80. public void run() {
  81. if (regFuture.isSuccess()) {
              // 绑定!!!
  82. channel.bind(localAddress, promise).addListener(ChannelFutureListener.CLOSE_ON_FAILURE); // 4
  83. } else {
  84. promise.setFailure(regFuture.cause());
  85. }
  86. }
  87. });
  88. }

对上述流程进行一个简单的说明: 先创建一个NioServerScoketChannel,然后进行初始化操作,然后注册到bossEventLoop中selector上,nio需要做的流程 netty都要做,然后进行绑定 返回一个绑定异步promise

绑定流程有4个比较重要的操作 我们来一一详解

1、实例化NioServerScoketChannel,内部会创建一个ServerSocketChannel 然后持有,同时创建unsafe和pipeline

  1. /***********************NioServerSocketChannel**********************/
  2. // selector提供者,提供创建selector、ServerSocketChannel、SocketChannel
  3. private static final SelectorProvider DEFAULT_SELECTOR_PROVIDER = SelectorProvider.provider();
  4.  
  5. public NioServerSocketChannel() {
  6. this(newSocket(DEFAULT_SELECTOR_PROVIDER));
  7. }
  8.  
  9. private static ServerSocketChannel newSocket(SelectorProvider provider) {
  10. try {
  11. // 返回一个nio中的ServerSocketChannel
  12. return provider.openServerSocketChannel();
  13. } catch (IOException e) {
  14. throw new ChannelException(
  15. "Failed to open a server socket.", e);
  16. }
  17. }
  18.  
  19. public NioServerSocketChannel(ServerSocketChannel channel) {
  20. // 调用父类构造
  21. super(null, channel, SelectionKey.OP_ACCEPT);
  22. // NioServerSocketChannel配置类
  23. config = new NioServerSocketChannelConfig(this, javaChannel().socket());
  24. }
  25.  
  26. /***********************AbstractNioChannel***********************/
  27. protected AbstractNioChannel(Channel parent, SelectableChannel ch, int readInterestOp) {
  28. super(parent);
  29. this.ch = ch;
  30. this.readInterestOp = readInterestOp;
  31. try {
  32. // 将ServerSocketChannel设置为非阻塞
  33. ch.configureBlocking(false);
  34. } catch (IOException e) {
  35. try {
  36. ch.close();
  37. } catch (IOException e2) {
  38. if (logger.isWarnEnabled()) {
  39. logger.warn(
  40. "Failed to close a partially initialized socket.", e2);
  41. }
  42. }
  43.  
  44. throw new ChannelException("Failed to enter non-blocking mode.", e);
  45. }
  46. }
  1. /***********************AbstractChannel*************************/
  2. protected AbstractChannel(Channel parent) {
  3. this.parent = parent;
  4. id = newId();
  5. // 创建一个unsafe 实例为NioMessageUnsafe类型
  6. unsafe = newUnsafe();
  7. // 创建pipeline,类型为DefaultChannelPipeline
  8. pipeline = newChannelPipeline();
  9. }
  10. /***********************DefaultChannelPipeline*************************/
  11. protected DefaultChannelPipeline(Channel channel) {
  12. this.channel = ObjectUtil.checkNotNull(channel, "channel");
  13. succeededFuture = new SucceededChannelFuture(channel, null);
  14. voidPromise = new VoidChannelPromise(channel, true);
  15.  
  16. tail = new TailContext(this);
  17. head = new HeadContext(this);
  18.  
  19. head.next = tail;
  20. tail.prev = head;
  21. }
  1.  

2、初始化NioServerScoketChannel

  1. /***********************ServerBootstrap*************************/
  2. void init(Channel channel) throws Exception {
  3. final Map<ChannelOption<?>, Object> options = options0();
  4. // 设置我们之前设置的options serverBootstrap.option(ChannelOption<T> option, T value)
  5. synchronized (options) {
  6. setChannelOptions(channel, options, logger);
  7. }
  8. // 设置channel自定义属性
  9. final Map<AttributeKey<?>, Object> attrs = attrs0();
  10. synchronized (attrs) {
  11. for (Entry<AttributeKey<?>, Object> e: attrs.entrySet()) {
  12. @SuppressWarnings("unchecked")
  13. AttributeKey<Object> key = (AttributeKey<Object>) e.getKey();
  14. channel.attr(key).set(e.getValue());
  15. }
  16. }
  17.  
  18. ChannelPipeline p = channel.pipeline();
  19.  
  20. final EventLoopGroup currentChildGroup = childGroup;
  21. final ChannelHandler currentChildHandler = childHandler;
  22. final Entry<ChannelOption<?>, Object>[] currentChildOptions;
  23. final Entry<AttributeKey<?>, Object>[] currentChildAttrs;
  24. synchronized (childOptions) {
  25. currentChildOptions = childOptions.entrySet().toArray(newOptionArray(childOptions.size()));
  26. }
  27. synchronized (childAttrs) {
  28. currentChildAttrs = childAttrs.entrySet().toArray(newAttrArray(childAttrs.size()));
  29. }
  30.  
  31. // 添加一个ChannelInitializer,用来注册后续的NioSocketChannel
  32. p.addLast(new ChannelInitializer<Channel>() {
  33. @Override
  34. public void initChannel(final Channel ch) throws Exception {
  35. final ChannelPipeline pipeline = ch.pipeline();
  36. ChannelHandler handler = config.handler();
  37. if (handler != null) {
  38. pipeline.addLast(handler);
  39. }
  40.  
  41. ch.eventLoop().execute(new Runnable() {
  42. @Override
  43. public void run() {
  44. // 这个handler就是后续监听客户端连接事件后,会将创建的NioSocketChannel 在这里进行注册
  45. pipeline.addLast(new ServerBootstrapAcceptor(
  46. ch, currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));
  47. }
  48. });
  49. }
  50. });
  51. }
  52.  
  53. private static class ServerBootstrapAcceptor extends ChannelInboundHandlerAdapter {
  54. ServerBootstrapAcceptor(
  55. final Channel channel, EventLoopGroup childGroup, ChannelHandler childHandler,
  56. Entry<ChannelOption<?>, Object>[] childOptions, Entry<AttributeKey<?>, Object>[] childAttrs) {
  57. this.childGroup = childGroup;
  58. this.childHandler = childHandler;
  59. this.childOptions = childOptions;
  60. this.childAttrs = childAttrs;
  61.  
  62. enableAutoReadTask = new Runnable() {
  63. @Override
  64. public void run() {
  65. channel.config().setAutoRead(true);
  66. }
  67. };
  68. }
  69.  
  70. @Override
  71. @SuppressWarnings("unchecked")
  72. public void channelRead(ChannelHandlerContext ctx, Object msg) {
  73. // NioServerScoketChannel在监听accpet后会创建一个NioSocketChannel 然后调用handler将该channel传递进来,handler那边只是简单的创建 并没有完成注册
  74. final Channel child = (Channel) msg;
  75. // 在serverBootstrap.childHandler(new ChannelInitializer<SocketChannel>()添加的handler 加入到NioSocketChannel
  76. child.pipeline().addLast(childHandler);
  77. // 设置NioSocketChannel的Options
  78. setChannelOptions(child, childOptions, logger);
  79. // 设置NioSocketChannel的attr
  80. for (Entry<AttributeKey<?>, Object> e: childAttrs) {
  81. child.attr((AttributeKey<Object>) e.getKey()).set(e.getValue());
  82. }
  83.  
  84. try {
  85. // 将NioSocketChannel注册到workerEventLoopGroup上 然后添加一个监听器
  86. childGroup.register(child).addListener(new ChannelFutureListener() {
  87. @Override
  88. public void operationComplete(ChannelFuture future) throws Exception {
  89. if (!future.isSuccess()) {
  90. forceClose(child, future.cause());
  91. }
  92. }
  93. });
  94. } catch (Throwable t) {
  95. forceClose(child, t);
  96. }
  97. }
  98.  
  99. private static void forceClose(Channel child, Throwable t) {
  100. child.unsafe().closeForcibly();
  101. logger.warn("Failed to register an accepted channel: {}", child, t);
  102. }
  103.  
  104. @Override
  105. public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
  106. final ChannelConfig config = ctx.channel().config();
  107. if (config.isAutoRead()) {
  108. // 将autoread设置为false,一秒后重新设置为true
  109. config.setAutoRead(false);
  110. ctx.channel().eventLoop().schedule(enableAutoReadTask, 1, TimeUnit.SECONDS);
  111. }
  112. ctx.fireExceptionCaught(cause);
  113. }
  114. }

上图大致流程:将之前在ServerBootstrap设置的属性在这里相应设置,然后给NioServerScoketChannel添加一个处理器,这个处理器用来接收accept请求时将后续的NioSocketChannel注册到对应的workerEventLoop中,后续的io事件就交给NioSocketChannel来完成

3、channel注册到EventLoop,其实就是将channel注册到EventLoop中的selector上

  1. /************************MultithreadEventLoopGroup*********************/
  2. public ChannelFuture register(Channel channel) {
  3. return next().register(channel);
  4. }
  5.  
  6. /***********************MultithreadEventExecutorGroup**********************/
  7. public EventExecutor next() {
  8. // 选择一个EventLoop
  9. return chooser.next();
  10. }
  11.  
  12. /***********************SingleThreadEventLoop**********************/
  13. public ChannelFuture register(Channel channel) {
  14. return register(new DefaultChannelPromise(channel, this));
  15. }
  16.  
  17. public ChannelFuture register(final ChannelPromise promise) {
  18. // 拿到channel对应的Unsafe进行注册
  19. promise.channel().unsafe().register(this, promise);
  20. return promise;
  21. }
  22.  
  23. /***********************AbstractUnsafe**********************/
  24. public final void register(EventLoop eventLoop, final ChannelPromise promise) {
  25. AbstractChannel.this.eventLoop = eventLoop;
  26.  
  27. if (eventLoop.inEventLoop()) {
  28. register0(promise);
  29. } else {
  30. try {
  31. eventLoop.execute(new Runnable() {
  32. @Override
  33. public void run() {
  34. register0(promise);
  35. }
  36. });
  37. } catch (Throwable t) {
  38. closeForcibly();
  39. closeFuture.setClosed();
  40. safeSetFailure(promise, t);
  41. }
  42. }
  43. }
  44.  
  45. private void register0(ChannelPromise promise) {
  46. try {
  47. // 判断当前注册promise是否被取消或者channel通道是否还处于打开状态
  48. if (!promise.setUncancellable() || !ensureOpen(promise)) {
  49. return;
  50. }
  51. boolean firstRegistration = neverRegistered;
  52. // 进行nio注册
  53. doRegister();
  54. neverRegistered = false;
  55. registered = true;
  56.  
  57. // 注册好后调用我们之前设置的channelInitializer.initChannel(channel) 方法
  58. pipeline.invokeHandlerAddedIfNeeded();
  59. // 设置状态成功
  60. safeSetSuccess(promise);
  61. // 注册成功事件传播
  62. pipeline.fireChannelRegistered();
  63. // 完成注册正好是active状态
  64. if (isActive()) {
  65. if (firstRegistration) {
  66. pipeline.fireChannelActive();
  67. } else if (config().isAutoRead()) {
  68. beginRead();
  69. }
  70. }
  71. } catch (Throwable t) {
  72. closeForcibly();
  73. closeFuture.setClosed();
  74. safeSetFailure(promise, t);
  75. }
  76. }
  77.  
  78. /***********************AbstractNioUnsafe**********************/
  79. protected void doRegister() throws Exception {
  80. boolean selected = false;
  81. for (;;) {
  82. try {
  83. // 将channel注册到EventLoop上的selector
  84. selectionKey = javaChannel().register(eventLoop().unwrappedSelector(), 0, this);
  85. return;
  86. } catch (CancelledKeyException e) {
  87. if (!selected) {
  88. eventLoop().selectNow();
  89. selected = true;
  90. } else {
  91. throw e;
  92. }
  93. }
  94. }
  95. }

4、绑定端口,通过pipeline.bind发起绑定,绑定端口是出站事件,由tail像前进行传递,直到执行到head的bind()方法,然后通过其中的unsafe调用NioServerScoketChannel的doBind()进行绑定

  1. /************************tailHandler*********************/
  2. public ChannelFuture bind(final SocketAddress localAddress, final ChannelPromise promise) {
  3. final AbstractChannelHandlerContext next = findContextOutbound();
  4. EventExecutor executor = next.executor();
  5. if (executor.inEventLoop()) {
  6. next.invokeBind(localAddress, promise);
  7. } else {
  8. safeExecute(executor, new Runnable() {
  9. @Override
  10. public void run() {
  11. next.invokeBind(localAddress, promise);
  12. }
  13. }, promise, null);
  14. }
  15. return promise;
  16. }
  17.  
  18. private void invokeBind(SocketAddress localAddress, ChannelPromise promise) {
  19. if (invokeHandler()) {
  20. try {
  21. ((ChannelOutboundHandler) handler()).bind(this, localAddress, promise);
  22. } catch (Throwable t) {
  23. notifyOutboundHandlerException(t, promise);
  24. }
  25. } else {
  26. bind(localAddress, promise);
  27. }
  28. }
  29.  
  30. /************************headHandler*********************/
  31. public void bind(
  32. ChannelHandlerContext ctx, SocketAddress localAddress, ChannelPromise promise)
  33. throws Exception {
  34. unsafe.bind(localAddress, promise);
  35. }
  36.  
  37. /************************AbstractUnsafe*********************/
  38. public final void bind(final SocketAddress localAddress, final ChannelPromise promise) {
  39. assertEventLoop();
  40.  
  41. if (!promise.setUncancellable() || !ensureOpen(promise)) {
  42. return;
  43. }
  44.  
  45. boolean wasActive = isActive();
  46. try {
  47. doBind(localAddress);
  48. } catch (Throwable t) {
  49. safeSetFailure(promise, t);
  50. closeIfClosed();
  51. return;
  52. }
  53.  
  54. if (!wasActive && isActive()) {
  55. invokeLater(new Runnable() {
  56. @Override
  57. public void run() {
  58. pipeline.fireChannelActive();
  59. }
  60. });
  61. }
  62.  
  63. safeSetSuccess(promise);
  64. }
  65.  
  66. /************************NioServerSocketChannel*********************/
  67. protected void doBind(SocketAddress localAddress) throws Exception {
  68. if (PlatformDependent.javaVersion() >= 7) {
  69. javaChannel().bind(localAddress, config.getBacklog());
  70. } else {
  71. javaChannel().socket().bind(localAddress, config.getBacklog());
  72. }
  73. }

服务端的启动流程就讲的差不多了,对上述流程大致进行一个梳理,进行端口绑定时首先对创建一个NioServerScoketChannel 用来处理accpet,然后注册到BossEventLoop上,通过给NioServerScoketChannel添加一个处理器 将来发生accpet时,将生成的NioSocketChannel注册到WorkerEventLoop上,后续的io事件就在该NioSocketChannel完成

客户端启动流程

客户端这边启动流程和服务端大致类似,连接ip端口时,创建一个用来和服务端通信的NioSocketChannel(内部包裹着SocketChannel),然后注册到对应的EventLoop上的selector开启事件监听

  1. ChannelFuture cf = bootstrap.connect("127.0.0.1", 8080).sync();
  2. cf.channel().closeFuture().sync();
  3.  
  4. /************************Bootstrap*********************/
  5. private ChannelFuture doResolveAndConnect(final SocketAddress remoteAddress, final SocketAddress localAddress) {
  6. // 创建一个NioSocketChannel然后进行注册,流程跟NioServerScoketChannel差不多,返回一个注册future
  7. final ChannelFuture regFuture = initAndRegister();
  8. final Channel channel = regFuture.channel();
  9.  
  10. if (regFuture.isDone()) {
  11. if (!regFuture.isSuccess()) {
  12. return regFuture;
  13. }
  14. return doResolveAndConnect0(channel, remoteAddress, localAddress, channel.newPromise());
  15. } else {
  16. // 还未注册完成 添加一个监听器 用作将来注册成功后进行连接操作
  17. final PendingRegistrationPromise promise = new PendingRegistrationPromise(channel);
  18. regFuture.addListener(new ChannelFutureListener() {
  19. @Override
  20. public void operationComplete(ChannelFuture future) throws Exception {
  21. Throwable cause = future.cause();
  22. if (cause != null) {
  23. promise.setFailure(cause);
  24. } else {
  25. promise.registered();
  26. doResolveAndConnect0(channel, remoteAddress, localAddress, promise);
  27. }
  28. }
  29. });
  30. return promise;
  31. }
  32. }
  33.  
  34. private ChannelFuture doResolveAndConnect0(final Channel channel, SocketAddress remoteAddress,
  35. final SocketAddress localAddress, final ChannelPromise promise) {
  36. try {
  37. final EventLoop eventLoop = channel.eventLoop();
  38. final AddressResolver<SocketAddress> resolver = this.resolver.getResolver(eventLoop);
  39.  
  40. //解析器无法解析ip地址或者已经被解析
  41. if (!resolver.isSupported(remoteAddress) || resolver.isResolved(remoteAddress)) {
  42. doConnect(remoteAddress, localAddress, promise);
  43. return promise;
  44. }
  45.  
  46. final Future<SocketAddress> resolveFuture = resolver.resolve(remoteAddress);
  47.  
  48. if (resolveFuture.isDone()) {
  49. // 解析完成 进行连接
  50. final Throwable resolveFailureCause = resolveFuture.cause();
  51.  
  52. if (resolveFailureCause != null) {
  53. // Failed to resolve immediately
  54. channel.close();
  55. promise.setFailure(resolveFailureCause);
  56. } else {
  57. // Succeeded to resolve immediately; cached? (or did a blocking lookup)
  58. doConnect(resolveFuture.getNow(), localAddress, promise);
  59. }
  60. return promise;
  61. }
  62.  
  63. // 添加一个监听器用来将来地址解析成功然后进行连接
  64. resolveFuture.addListener(new FutureListener<SocketAddress>() {
  65. @Override
  66. public void operationComplete(Future<SocketAddress> future) throws Exception {
  67. if (future.cause() != null) {
  68. channel.close();
  69. promise.setFailure(future.cause());
  70. } else {
  71. doConnect(future.getNow(), localAddress, promise);
  72. }
  73. }
  74. });
  75. } catch (Throwable cause) {
  76. promise.tryFailure(cause);
  77. }
  78. return promise;
  79. }
  80.  
  81. private static void doConnect(
  82. final SocketAddress remoteAddress, final SocketAddress localAddress, final ChannelPromise connectPromise) {
  83.  
  84. final Channel channel = connectPromise.channel();
  85. // 提交一个连接任务到EventLoop上
  86. channel.eventLoop().execute(new Runnable() {
  87. @Override
  88. public void run() {
  89. if (localAddress == null) {
  90. channel.connect(remoteAddress, connectPromise);
  91. } else {
  92. channel.connect(remoteAddress, localAddress, connectPromise);
  93. }
  94. // 添加一个关闭或失败的监听器,会将channel进行关闭
  95. connectPromise.addListener(ChannelFutureListener.CLOSE_ON_FAILURE);
  96. }
  97. });
  98. }
  99.  
  100. // 设置channel的options和attrs
  101. void init(Channel channel) throws Exception {
  102. ChannelPipeline p = channel.pipeline();
  103. p.addLast(config.handler());
  104.  
  105. final Map<ChannelOption<?>, Object> options = options0();
  106. synchronized (options) {
  107. setChannelOptions(channel, options, logger);
  108. }
  109.  
  110. final Map<AttributeKey<?>, Object> attrs = attrs0();
  111. synchronized (attrs) {
  112. for (Entry<AttributeKey<?>, Object> e: attrs.entrySet()) {
  113. channel.attr((AttributeKey<Object>) e.getKey()).set(e.getValue());
  114. }
  115. }
  116. }

至此我们服务端和客户端的启动流程都分析完了,我们前面说过EventLoop可以当做一个线程来执行, channel.eventLoop().execute(new Runnable(),后续的io事件监听和自定义任务处理都在EventLoop内执行,我们下节就来剖析其内部实现

netty服务端客户端启动流程分析的更多相关文章

  1. Netty服务端的启动源码分析

    ServerBootstrap的构造: public class ServerBootstrap extends AbstractBootstrap<ServerBootstrap, Serve ...

  2. Netty服务端NioEventLoop启动及新连接接入处理

    一 Netty服务端NioEventLoop的启动 Netty服务端创建.初始化完成后,再向Selector上注册时,会将服务端Channel与NioEventLoop绑定,绑定之后,一方面会将服务端 ...

  3. Unity进阶之ET网络游戏开发框架 02-ET的客户端启动流程分析

    版权申明: 本文原创首发于以下网站: 博客园『优梦创客』的空间:https://www.cnblogs.com/raymondking123 优梦创客的官方博客:https://91make.top ...

  4. Netty 服务端创建

    参考:http://blog.csdn.net/suifeng3051/article/details/28861883?utm_source=tuicool&utm_medium=refer ...

  5. Netty 拆包粘包和服务启动流程分析

    Netty 拆包粘包和服务启动流程分析 通过本章学习,笔者希望你能掌握EventLoopGroup的工作流程,ServerBootstrap的启动流程,ChannelPipeline是如何操作管理Ch ...

  6. 【转】Netty 拆包粘包和服务启动流程分析

    原文:https://www.cnblogs.com/itdragon/archive/2018/01/29/8365694.html Netty 拆包粘包和服务启动流程分析 通过本章学习,笔者希望你 ...

  7. Netty之旅三:Netty服务端启动源码分析,一梭子带走!

    Netty服务端启动流程源码分析 前记 哈喽,自从上篇<Netty之旅二:口口相传的高性能Netty到底是什么?>后,迟迟两周才开启今天的Netty源码系列.源码分析的第一篇文章,下一篇我 ...

  8. Netty 服务端启动过程

    在 Netty 中创建 1 个 NioServerSocketChannel 在指定的端口监听客户端连接,这个过程主要有以下  个步骤: 创建 NioServerSocketChannel 初始化并注 ...

  9. 7.4 服务远程暴露 - 创建Exporter与启动netty服务端

    为了安全:服务启动的ip全部使用10.10.10.10 远程服务的暴露总体步骤: 将ref封装为invoker 将invoker转换为exporter 启动netty 注册服务到zookeeper 订 ...

随机推荐

  1. Leetcode学习笔记(1)

    scrapy爬虫的学习告一段落,又因为现在在学习数据结构,做题平台是lettcode:https://leetcode-cn.com/ 每周都要交一次做题的笔记,所以把相关代码和思路同时放在博客上记录 ...

  2. 《深入理解计算机系统》实验一 —Data Lab

    本文是CSAPP第二章的配套实验,通过使用有限的运算符来实现正数,负数,浮点数的位级表示.通过完成这13个函数,可以使我们更好的理解计算机中数据的编码方式. 准备工作   首先去官网Lab Assig ...

  3. mybatis批量修改数据

    xxxMapper.xml: <update id="updateSensorWarnings" parameterType="java.util.List&quo ...

  4. (数据科学学习手札99)掌握pandas中的时序数据分组运算

    本文示例代码及文件已上传至我的Github仓库https://github.com/CNFeffery/DataScienceStudyNotes 1 简介 我们在使用pandas分析处理时间序列数据 ...

  5. selenium模拟淘宝登陆,过所有验证

    淘宝模拟登陆实现 由于淘宝使用了滑动验证码,需要进行模糊手动滑动,因此考虑使用selenium+chromedriver进行模拟登陆. 淘宝的登陆网址:https://login.taobao.com ...

  6. 图论补档——KM算法+稳定婚姻问题

    突然发现考前复习图论的时候直接把 KM 和 稳定婚姻 给跳了--emmm 结果现在刷训练指南就疯狂补档.QAQ. KM算法--二分图最大带权匹配 提出问题 (不严谨定义,理解即可) 二分图 定义:将点 ...

  7. 深入理解Java虚拟机(四)——HotSpot垃圾收集器详解

    垃圾收集器 新生代收集器 1.Serial收集器 特点: 单线程工作,收集的时候就会停止其他所有工作线程,用户不可知不可控,会使得用户界面出现停顿. 简单高效,是所有收集器中额外内存消耗最少的. 没有 ...

  8. 云服务器 ECS Linux 安装 VNC Server 实现图形化访问配置说明

    阿里云官方公共 Linux 系统镜像,基于性能及通用性等因素考虑,默认没有安装 VNC 服务组件.本文对常见操作系统下的 VNC Server 安装配置进行简要说明. 本文中仅讨论VNC的安装,关于图 ...

  9. iOS10 App适配权限 Push Notifications 字体Frame 遇到的坑!!!!

    添加配置权限 <!-- 相册 --> <key>NSPhotoLibraryUsageDescription</key> <string>"x ...

  10. ReentrantReadWriterLock源码(state设计、读写锁、共享锁、独占锁及锁降级)

    ReentrantReadWriterLock 读写锁类图(截图来源https://blog.csdn.net/wangbo199308/article/details/108688148) stat ...