netty源码浅析-客户端接入

【Netty源码分析】 客户端接入流程 服务端启动完成,这时候客户端连接就可以接入进来了,下面我们就来分析下客户端连接接入的流程。之前分析过线程启动方法是,由于这个方法里面的逻辑比较复杂,并没有展开,这一节就是从这个方法开始分析。 这个方法主要主要完成2件事:利用将的状态由修改成,即表示线程启动;执行方法;方法看着比较复杂,核心逻辑如下,向线程池执行器提交一个任务,而这个线程池执行器类型是,即每次执行任务都会创建一个新线程,而且这个任务是无限循环的:事件轮询、事件处理和任务队列处理,这样就和具体的线程进行了关联: 该方法大致完成2件事:: 阅读详情

客户端接入

当客户端调用connect后,服务端reactor线程在循环过程中会检查到这个事件,然后在processSelectedKey方法中处理。我们在上面也已经提到了这个方法,我们继续跟进,当客户端接入后,服务端发生了什么。

//读事件和连接事件就绪,执行unsafe.read()
//如果是boss线程则是OP_ACCEPT事件,如果是work线程则是OP_READ事件
if ((readyOps & (SelectionKey.OP_READ | SelectionKey.OP_ACCEPT)) != 0 || readyOps == 0) {
     unsafe.read();
}

上面代码我们可以看到,当客户端接入后是OP_ACCEPT事件,这里会走到unsaferead()方法,我们继续跟进为是服务端所以来到了AbstractNioMessageChannel的read方法

public void read() {
       //内部线程执行
        assert eventLoop().inEventLoop();
        //channel配置
        final ChannelConfig config = config();
        final ChannelPipeline pipeline = pipeline();
        //AdaptiveRecvByteBufAllocator内部HandleImpl
        final RecvByteBufAllocator.Handle allocHandle = unsafe().recvBufAllocHandle();
        //重置初始化值
        allocHandle.reset(config);

        boolean closed = false;
        Throwable exception = null;
        try {
            try {
                do {
                    //如果有客户端接入,localRead返回值为1
                    int localRead = doReadMessages(readBuf);
                    if (localRead == 0) {
                        break;
                    }
                    if (localRead < 0) {
                        closed = true;
                        break;
                    }

                    allocHandle.incMessagesRead(localRead);
                } while (allocHandle.continueReading());
            } catch (Throwable t) {
                exception = t;
            }

            int size = readBuf.size();
            for (int i = 0; i < size; i ++) {
                readPending = false;
                //触发ChannelRead事件,最后传到了ServerBootstrapAcceptor的channelRead方法
                pipeline.fireChannelRead(readBuf.get(i));
            }
            readBuf.clear();
            allocHandle.readComplete();
            pipeline.fireChannelReadComplete();
            //如果过程中有错误发生,则触发报错事件
            if (exception != null) {
                closed = closeOnReadError(exception);

                pipeline.fireExceptionCaught(exception);
            }

            if (closed) {
                inputShutdown = true;
                if (isOpen()) {
                    close(voidPromise());
                }
            }
        } finally {
            if (!readPending && !config.isAutoRead()) {
                removeReadOp();
            }
        }
    }
}

我们将上面代码分开看

//内部线程执行
assert eventLoop().inEventLoop();
//channel配置
final ChannelConfig config = config();
final ChannelPipeline pipeline = pipeline();
//AdaptiveRecvByteBufAllocator内部HandleImpl
final RecvByteBufAllocator.Handle allocHandle = unsafe().recvBufAllocHandle();
//重置初始化值
allocHandle.reset(config);

首先保证必须在内部线程执行,然后赋值ChannelConfg和ChannelPipeline,allocHandle是什么呢?我们走到创建方法中AbstractUnsafe的recvBufAllocHandle方法,recvHandle第一次调用时初始值就是null

public RecvByteBufAllocator.Handle recvBufAllocHandle() {
    if (recvHandle == null) {
        recvHandle = config().getRecvByteBufAllocator().newHandle();
    }
    return recvHandle;
}

通过config()getRecvByteBufAllocator()获取的就是我们在前面分析过得在创建NioServerSocketChannel时,在创建NioServerSocketChannelConfig时创建的AdaptiveRecvByteBufAllocator

public NioServerSocketChannel(ServerSocketChannel channel) {
    //1.调用父类构造方法
    super(null, channel, SelectionKey.OP_ACCEPT);
    //2.创建配置类
    config = new NioServerSocketChannelConfig(this, javaChannel().socket());
}
public DefaultChannelConfig(Channel channel) {
   //byteBuf分配器
    this(channel, new AdaptiveRecvByteBufAllocator());
}

所以上面的newHandle的方法会走到AdaptiveRecvByteBufAllocator中可以看到创建的就是HandleImpl

public Handle newHandle() {
   return new HandleImpl(minIndex, maxIndex, initial);
}

创建完成后调用reset方法设置初始值,由于Handlelmpl是MaxMessageHandle的子类所以这里走到 DefaultMaxMessagesRecvByteBufAllocator的内部类MaxMessageHandle的reset方法

public void reset(ChannelConfig config) {
    this.config = config;
    maxMessagePerRead = maxMessagesPerRead();
    totalMessages = totalBytesRead = 0;
}

我们可以看到这里是设置 些初始值,maxMessagePerRead是每次读取连接的最大值 默认为16个
totalMessages为总连接数,totalBytesRead为总共读取的字节数,全部设置为0。我们继续分析循环结构

try {
    do {
        //如果有客户端接入,localRead返回值为1
        int localRead = doReadMessages(readBuf);
        if (localRead == 0) {
            break;
        }
        if (localRead < 0) {
            closed = true;
            break;
        }

        allocHandle.incMessagesRead(localRead);
    } while (allocHandle.continueReading());
} catch (Throwable t) {
    exception = t;
}

我们先看do…while循环中的操作,doReadMessages处理客户端接入

protected int doReadMessages(List<Object> buf) throws Exception {
    //执行accept方法返回连接的channel
    SocketChannel ch = SocketUtils.accept(javaChannel());

    try {
        //如果channel不为null
        if (ch != null) {
            //将接入的nio底层的channel,放入新创建的NioSocketChannel实例中
            buf.add(new NioSocketChannel(this, ch));
            return 1;
        }
    } catch (Throwable t) {
        logger.warn("Failed to create a new channel from an accepted socket.", t);

        try {
            ch.close();
        } catch (Throwable t2) {
            logger.warn("Failed to close a socket.", t2);
        }
    }

    return 0;
}

可以看到首先调用nio底层的accept方法,其实就是ServerSocketChannel执行accept方法返回接入的
SocketChannel,然后将返回的SocketChannel包装在一个NioSocketChannel中,而NioSocketChannel的创建我们在上面也已经提到过,我们再来看一次

public NioServerSocketChannel(ServerSocketChannel channel) {
       //1.调用父类构造方法
       super(null, channel, SelectionKey.OP_ACCEPT);
       //2.创建配置类
       config = new NioServerSocketChannelConfig(this, javaChannel().socket());
   }
protected AbstractNioChannel(Channel parent, SelectableChannel ch, int readInterestOp) {
       super(parent);
       this.ch = ch;//nio的ServerSocketChannel
       this.readInterestOp = readInterestOp;//连接事件
       try {
           ch.configureBlocking(false);//设置非阻塞模式
       } catch (IOException e) {
           try {
               ch.close();//发生异常就关闭channel
           } catch (IOException e2) {
               if (logger.isWarnEnabled()) {
                   logger.warn(
                           "Failed to close a partially initialized socket.", e2);
               }
           }

           throw new ChannelException("Failed to enter non-blocking mode.", e);
       }
   }

设置一些属性值,设置连接事件和nio底层的代码,将SocketChannel设置为非阻塞,继续跟踪

protected AbstractChannel(Channel parent) {
        this.parent = parent;//null
        //1.channel唯一标识id
        id = newId();
        //2.unsafe是channel的内部类,封装了很多底层nio的操作
        unsafe = newUnsafe();
        //3.每创建一个channel就会创建一个channelPipe用于组织handler
        pipeline = newChannelPipeline();
    }

这里面传入了parent值就是NioServerSocketChannel,然后进行赋值,创建channel唯一id,然后创建unsafe对象

protected AbstractNioUnsafe newUnsafe() {
       return new NioMessageUnsafe();
   }

最后给channel创建一个pipeline

public DefaultServerSocketChannelConfig(ServerSocketChannel channel, ServerSocket javaSocket) {
        super(channel);
        if (javaSocket == null) {
            throw new NullPointerException("javaSocket");
        }
        this.javaSocket = javaSocket;
    }
public DefaultChannelConfig(Channel channel) {
        //byteBuf分配器
        this(channel, new AdaptiveRecvByteBufAllocator());
    }

我们继续回到doReadMessages中,将创建好的
NioSocketChannel添加到了buf集合中,然后返回1,然后调用allocHandleincMessagesRead(localRead)统计接入的连接数。我们看下跳出do…while循环的条件是什么

while (allocHandle.continueReading());
public boolean continueReading() {
           return continueReading(defaultMaybeMoreSupplier);
       }
public boolean continueReading(UncheckedBooleanSupplier maybeMoreDataSupplier) {
           //config.isAutoRead()默认为true
           return config.isAutoRead() &&
                   //respectMaybeMoreData默认为true
                  (!respectMaybeMoreData || maybeMoreDataSupplier.get()) &&
                   //已经读取到的总连接数是不是超过了maxMessagePerRead,默认是16
                  totalMessages < maxMessagePerRead &&
                   //totalBytesRead设置为0,一直为false
                  totalBytesRead > 0;
       }

这里我们可以看到是一些条件的组合我们分开看

  • configisAutoRead()默认值就是true
  • (!respectMaybeMoreDatamaybeMoreDataSupplierget()):respectMaybeMoreData的默认值就是 true,maybeMoreDataSupplierget()我们跟进到方法中其实就是比较attemptedBytesRead == lastBytesRead这里初始值都是0所以返回true
  • totalMessages<maxMessagePerRead,判断接入的连接数是不是超过了最大值,最大值默认为16 totalBytesRead>0这里我们前边已经分析过
  • totalBytesRead的初始化赋值为0,所以这里返回false

也就是默认只要有一个连接接入就会跳出这个循环
我们继续分析

int size = readBuf.size();
for (int i = 0; i < size; i ++) {
    readPending = false;
    //触发ChannelRead事件,最后传到了ServerBootstrapAcceptor的channelRead方法
    pipeline.fireChannelRead(readBuf.get(i));
}
readBuf.clear();

这里会遍历刚才加入到集合中的NioSocketChannel,然后触发channelRead事件,而我们在分析服务端启动流程的时候说过,pipeline中会加入一个ServerBootstrapAcceptor,这里就会触发该类的channelRead方法

 public void channelRead(ChannelHandlerContext ctx, Object msg) {
            //就是传过来的NioSocketChannel内部包装了接入的channel
            final Channel child = (Channel) msg;
            //向传过来的NioSocketChannel的pipeline中添加childHandler
            child.pipeline().addLast(childHandler);
            //设置配置
            setChannelOptions(child, childOptions, logger);

            for (Entry<AttributeKey<?>, Object> e: childAttrs) {
                child.attr((AttributeKey<Object>) e.getKey()).set(e.getValue());
            }

            try {
                //将传过来的NioSocketChannel注册到childGroup上
                childGroup.register(child).addListener(new ChannelFutureListener() {
                    @Override
                    public void operationComplete(ChannelFuture future) throws Exception {
                        if (!future.isSuccess()) {
                            forceClose(child, future.cause());
                        }
                    }
                });
            } catch (Throwable t) {
                forceClose(child, t);
            }
        }

将传过来的对象强转为Channel对象,因为我们传过来的就是NioSocketChannel,然后向channel中添加了 childHandler,这里我们知道就是我们在服务端启动过程中分析过得添加的就是Channellnitializer,然后设置些配置,来到了重点方法register,这个childGroup就是我们前面创建的workGroup,然后执行register方法这个方法我们在上面也提到了,我们跟进方法中

public ChannelFuture register(Channel channel) {
        //1.创建一个DefaultChannelPromise用于返回执行结果
        return register(new DefaultChannelPromise(channel, this));
    }
public ChannelFuture register(final ChannelPromise promise) {
        ObjectUtil.checkNotNull(promise, "promise");//校验
        //调用unsafe的register方法
        promise.channel().unsafe().register(this, promise);
        return promise;
    }
public final void register(EventLoop eventLoop, final ChannelPromise promise) {
            if (eventLoop == null) {
                throw new NullPointerException("eventLoop");
            }
            if (isRegistered()) {
                promise.setFailure(new IllegalStateException("registered to an event loop already"));
                return;
            }
            if (!isCompatible(eventLoop)) {
                promise.setFailure(
                        new IllegalStateException("incompatible event loop type: " + eventLoop.getClass().getName()));
                return;
            }
            //关联channel和eventLoop
            AbstractChannel.this.eventLoop = eventLoop;
            //1.当前运行的线程是eventLoopGroup线程,及内部线程
            if (eventLoop.inEventLoop()) {
                register0(promise);
            } else {
                try {
                    //2.调用execute方法将register0方法包装成一个任务放入NioEventLoop的
                    //任务队列中,由NioEventLoop线程执行
                    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();
                    safeSetFailure(promise, t);
                }
            }
        }

这里调用的是boss线程所以会走到else中,将注册方法封装成一个普通任务添加到workGroup的eventLoop中。这里我们在前面也已经分析过,就会将workGroup的NioEventLoop线程启动,我们继续跟踪到register0方法中

private void register0(ChannelPromise promise) {
            try {
                if (!promise.setUncancellable() || !ensureOpen(promise)) {
                    return;
                }
                //1.设置第一此执行注册标志
                boolean firstRegistration = neverRegistered;
                //2.完成channel注册,返回连接selectionKey
                doRegister();
                neverRegistered = false;
                registered = true;
                //3.检查是否触发HandlerAdded事件
                pipeline.invokeHandlerAddedIfNeeded();
                //4.设置promise注册成功
                safeSetSuccess(promise);
                //5.触发ChannelRegistered事件
                pipeline.fireChannelRegistered();
                //6.检查channel是不是已经属于连接成功状态,这里channel还没有bind,所以返回false
                if (isActive()) {
                    if (firstRegistration) {
                        pipeline.fireChannelActive();
                    } else if (config().isAutoRead()) {
                        //7.给channel注册读事件
                        beginRead();
                    }
                }
            } catch (Throwable t) {
                closeForcibly();
                closeFuture.setClosed();
                safeSetFailure(promise, t);
            }
        }

调用底层的注册方法

protected void doRegister() throws Exception {
        boolean selected = false;
        for (;;) {
            try {
                //1.执行nio底层的register方法,并且attch了channel,不关系任何事件
                selectionKey = javaChannel().register(eventLoop().unwrappedSelector(), 0, this);
                return;
            } catch (CancelledKeyException e) {
                if (!selected) {
                    //2.强行执行一次selectNow,取消了的SelectionKey可能还在缓存中未消除
                    eventLoop().selectNow();
                    selected = true;
                } else {
                    throw e;
                }
            }
        }
    }

可以看到执行了nio底层的注册方法,并且不关注任何事件,并attch上AbstractNioChannel。然后执行 pipeline.invokeHandlerAddedIfNeeded()方法

//3.检查是否触发HandlerAdded事件
pipeline.invokeHandlerAddedIfNeeded();

这个方法我们在上面也分析过就是执行Channelnitializer的init方法,将channelHandler加入到流水线中。 然后执行

//4.设置promise注册成功
safeSetSuccess(promise);

设置promise值触发回调listener的方法

//5.触发ChannelRegistered事件
pipeline.fireChannelRegistered();

触发ChannelRegistered事件

//6.检查channel是不是已经属于连接成功状态,这里channel还没有bind,所以返回false
if (isActive()) {
    if (firstRegistration) {
        pipeline.fireChannelActive();
    } else if (config().isAutoRead()) {
        //7.给channel注册读事件
        beginRead();
    }
}

这里由于channel已经完成连接,所有isActive()返回true,由于是第一次注册所有会触发ChannelActive事件,然后执行beginRead()方法

public final void beginRead() {
            //1.校验
            assertEventLoop();
            //当前是不是活跃状态
            if (!isActive()) {
                return;
            }

            try {
                doBeginRead();
            } catch (final Exception e) {
                invokeLater(new Runnable() {
                    @Override
                    public void run() {
                        pipeline.fireExceptionCaught(e);
                    }
                });
                close(voidPromise());
            }
        }
protected void doBeginRead() throws Exception {
        // Channel.read() or ChannelHandlerContext.read() was called
        //1.判断selectionKey是不是有效的
        final SelectionKey selectionKey = this.selectionKey;
        if (!selectionKey.isValid()) {
            return;
        }

        readPending = true;
        //2.获取关心的事件,这里其实什么都没关注
        final int interestOps = selectionKey.interestOps();
        //3.如果什么事件都不关系,则设置关注OP_ACCEPT事件
        if ((interestOps & readInterestOp) == 0) {
            selectionKey.interestOps(interestOps | readInterestOp);
        }
    }

最后设置NioSocketChannel关注的事件为读事件,如果发生错误,就触发一些channel的关闭操作。我们继续回到io.nettychannel.nio.AbstractNioMessageChannel.NioMessageUnsafe#read中

 allocHandle.readComplete();
 pipeline.fireChannelReadComplete();

  if (exception != null) {
      closed = closeOnReadError(exception);

      pipeline.fireExceptionCaught(exception);
  }

  if (closed) {
      inputShutdown = true;
      if (isOpen()) {
          close(voidPromise());
      }
  }

allocHandlereadComplete()对于接入操作没做什么重要的操作,然后触发ChannelReadComplete事件,如果上面过程中发生了错误就触发ExceptionCaught事件,如果设置了关闭就执行一些关闭操作

if (!readPending && !config.isAutoRead()) {
    removeReadOp();
}

如果不是AutoRead设置不是1并且readPending设置为false就将channel关注的读事件移除。 到这里客户端的接
已经分析结束,客户端连接对应的channel已经注册到了workGroup中NioEventLoop中的selector上,之后客户端的read和write事件就通过work线程来处理,这里也可以看到boss线程组主要负责客户端的连接,而work线程组主要负责客户端连接后的读写事件。如果有分析错误的地方还请不吝指正。感谢!!!

netty4创建tcp客户端连接源码流程分析 获取一个EventLoop去注册,EventLoop是一个线程池,SingleThreadEventExecutor 是只有一个线程的 EventLoop 线程池。addTask(task)把注册任务放队列里,startThread()执行IO轮询操作。最终注册成功,进行注册成功事件传播。后台线程继续轮询IO事件。中可以取到 Channel为。到一个线程池,这里只有一个线程。当有轮询到IO读事件,这里使用。(),从队列里拿注册任务执行。这里开始启动线程池去执行任务。到这里IO事件正常监听,回到。 阅读详情

相关推荐

Netty源码分析(一) --- Server/Client启动流程,建立连接和Read剖析

一、Netty源码分析 https://github.com/netty/netty/tree/4.1/example/src/main/java/io/netty/example/echo 上找到echo这个example 1.1 服务端启动流程 1.1.1 两个NioEventLoopGroup 服务端代码首先要定义两个NioEventLoopGroup,打个断点进去 NioEventLoopGroup继承MultithreadEventLoopGroup,加载的时候执行父类静态方法,初始化DEFA

化身强盗的博客 1366

Netty源码解析(三)之客户端的连接

在上篇文章中,我们介绍了Netty服务器启动的流程,这篇文章中,我们继续介绍客户端是如何与服务器端建立连接的。

benjam1n77的博客 842

netty服务端启动流程

说明:本文参考的 Netty 源码版本为 4.1.42.Final。 启动服务的过程中我们可以了解到 Netty 各大核心组件的关系,这将是学习 Netty 源码一个非常好的切入点,让我们一起看看 Netty 的每个零件是如何运转起来的吧。 文章目录从 Echo 服务器示例入手服务端启动全过程服务端 Channel 初始化及注册创建服务端 Channel初始化服务端 Channel注册服务端 Channel端口绑定netty服务端如何处理客户端新建连接总结 从 Echo 服务器示例入手 在这里我们实现了.

762

基于SpringBoot快速使用Netty - 客户端

支持启动自动连接,断线无限重连,定时心跳等,代码可直接复制,只需修改配置文件中的IP和端口即可使用,但没有提供Controller,要是需要,可以自己新建一个Controller,再ClientBoot类中写一个sendMsg()方法,方法中调用connect()方法,然后在你的Controller里注入ClientBoot,调用sendMsg()即可。

10JQKA的博客 2565

蓝桥杯单片机开发实战:Keil环境配置与IAP15F2K61S2头文件设置详解

本文详细讲解了为蓝桥杯单片机竞赛配置Keil开发环境的关键步骤,重点解析了IAP15F2K61S2专用头文件的获取与设置方法。通过使用STC-ISP工具添加芯片型号和头文件,确保编译器能正确识别寄存器,是成功进行单片机编程和竞赛备赛的基础。文中还提供了从软件安装、工程创建到常见问题排查的完整实战指南。

soda5的博客 886

为什么传统CNN在视网膜分割中败给Mamba?从血管拓扑学揭秘Serp-Mamba的蛇形扫描黑科技

本文深入分析了传统CNN在视网膜血管分割任务中的局限性,特别是在处理高分辨率图像和复杂血管拓扑结构时。针对这些问题,文章重点介绍了Serp-Mamba模型的核心创新——蛇形交织自适应扫描(SIA)机制,该机制通过让扫描路径自适应地贴合血管曲率,并结合模糊驱动双重校准(ADDR)模块应对极端类别失衡,从而在视网膜血管分割任务上实现了性能的显著突破。

ss78901的博客 890

Netty源码—客户端接入流程

在服务端Channel对应的NioEventLoop的run()方法的processSelectedKeys()方法里,发现产生的IO事件是ACCEPT事件之后,会通过JDK Channel的accept()方法取创建JDK的Channel,并把它包装成Netty自定义的NioSocketChannel。在这个过程中会通过一个RecvByteBufAllocator.Handle对象控制连接接入的速率,默认一次性读取16个连接。创建NioSocketChannel的逻辑可以分成两部分。

dsgdauigfs的博客 689

UltraScale+ FPGA中GTY收发器参考时钟的跨Bank共享机制解析

本文深入解析了UltraScale+ FPGA中GTY收发器参考时钟的跨Bank共享机制,详细介绍了时钟布线架构、SLR区域限制及Vivado工程配置要点。通过实际案例和优化建议,帮助工程师解决高速串行通信中的时钟共享问题,提升系统性能。

weixin_29280219的博客 630

Netty源码—4.客户端接入流程一

第一部分是逐层调用父类的构造方法,其中会设置这个客户端Channel的阻塞模式为false,然后再把感兴趣的读事件OP_READ保存到这个Channel的成员变量中以便后续注册到Selector,接着会创建一系列的组件,包括作为Channel唯一标识的Id组件、用来进行底层数据读写的unsafe组件、用来作为业务逻辑载体的pipeline组件。Netty中最核心的是两种类型的Reactor线程,这两种类型的Reactor线程可以看作Netty中的两组发动机,驱动着Netty整个框架的运转。

mjunz的博客 1206

2021-03-08 使用SPDK部署NVMe over TCP

NVMe-oF Target入门指南 SPDK NVMe over Fabrics Target是一个用户空间应用程序,通过以太网,Infiniband或光纤通道等结构呈现块设备,SPDK目前支持RDMA和TCP传输。 本文将在已经编译好SPDK的基础上演示如何使用SPDK搭建NVMe over TCP,前提是您已经将一块NVMe硬盘挂载到linux系统中。 如果您想获取有关SPDK更详细的信息,请参考 官方文档。 演示过程的Host主机的ip地址是192.168.0.21,Target主机的ip地址是19

Pancras的博客 2843

AIGC游戏开发:如何评估生成内容的玩家满意度?

如何量化评估生成内容对玩家体验的影响?本文聚焦AIGC生成的游戏内容(包括但不限于剧情、关卡、NPC对话、道具),系统探讨玩家满意度的评估方法论,覆盖主观与客观评估、静态与动态内容、短期与长期体验等场景。核心概念:定义AIGC与玩家满意度的关联维度;评估方法:主观(问卷/访谈)与客观(行为/生理数据)评估的技术细节;数学模型:构建多指标融合的评估模型;实战案例:以AIGC生成关卡为例,演示从数据采集到模型落地的全流程;工具资源:推荐数据采集、分析与建模的工具链;

AI天才研究院 1222

Netty:客户端接入流程(源码)

Netty:客户端接入流程(源码)

weixin_46619605的博客 1136

基于simulink的电动汽车电机驱动系统IGBT开路故障检测

本文介绍了基于Simulink的电动汽车电机驱动系统IGBT开路故障检测方法。通过搭建永磁同步电机(PMSM)驱动系统模型,实现IGBT开路故障注入,并设计基于电流平均值与PWM逻辑比较的故障检测算法。系统可实时采集三相电流信号,通过MATLAB Function模块实现故障诊断,当检测到PWM信号与电流不匹配时输出故障报警。仿真结果显示,系统能在2.05秒内准确识别A相上管开路故障,并通过Scope和XYGraph直观展示故障特征。该方法为电动汽车电力电子系统故障诊断与容错控制提供了实用解决方案,后续可扩

xiaoheshang_123的博客 149

Netty源码—4.客户端接入流程

第一部分是逐层调用父类的构造方法,其中会设置这个客户端Channel的阻塞模式为false,然后再把感兴趣的读事件OP_READ保存到这个Channel的成员变量中以便后续注册到Selector,接着会创建一系列的组件,包括作为Channel唯一标识的Id组件、用来进行底层数据读写的unsafe组件、用来作为业务逻辑载体的pipeline组件。Netty中最核心的是两种类型的Reactor线程,这两种类型的Reactor线程可以看作Netty中的两组发动机,驱动着Netty整个框架的运转。

weixin_55010563的博客 803

coze 怎么读取excel文件,使用循环,数据库完成LLM问答并存储

本文介绍了如何在Coze平台实现"Excel读取+循环处理+LLM问答+数据库存储"的工作流。主要分为三个步骤:1)使用代码节点解析Excel文件为问答对数组;2)通过循环节点遍历问答对,调用LLM节点生成回答;3)利用数据库节点将问答结果存储到Coze内置数据库中。文中提供了代码示例和节点配置说明,并强调了注意事项如Excel格式要求、LLM调用频率控制等。该工作流适用于批量构建知识库问答系统并持久化存储结果。

ZhangJiqun&191cm&85kg 1783

YOLOv13改进策略【特征提取层】| 引入注意力卷积模块RFAConv,关注感受野空间特征 助力yolov13精度提升

RFAConv:创新空间注意力和标准卷积运算感受野空间特征是针对卷积核设计的,根据卷积核大小动态生成。以3×3卷积核为例,它由非重叠滑动窗口组成,每个窗口代表一个感受野滑块。

Limiiiing的博客 359

netty 客户端接入源码分析

​ netty 客户端接入源码分析 netty 线程选择为NioEventLoop其实际运行的线程函数为 @Override protected void run() { for (;;) { try { try { switch (selectStrategy.calculateStrategy(selectNowS...

l1161558158的博客 208

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

Netty 核心源码解析

Netty 第一讲:Netty 架构与原理本文是《Netty 三讲》的第二讲:Netty 核心源码解析(部分),大纲如下:前言1. Netty 服务端启动过程源码剖析1.1. 执行 ne...

weixin_41385912的博客 1万+

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

锂电池充电器电路设计 里面有电路

锂电池充电器电路设计 里面有电路 3.7V升压到5V电路设计。

Netty源码—4.客户端接入流程二

接着还会传播ChannelActive事件。其中javaChannel().register()会将新连接NioSocketChannel绑定到Reactor线程的Selector上,这样后续这个新连接NioSocketChannel所有的事件都由绑定的Reactor线程的Selector来轮询。然后,新连接接入调用到服务端Channel的Pipeline的fireChannelRead()方法时,便会触发调用ServerBootstrapAcceptor处理器的channelRead()方法。

mjunz的博客 733

pscad IEEE标准系统搭建说明

pscad IEEE标准系统搭建说明,包含典型的IEEE 9节点、14节点、30节点、39节点和118节点系统。内含标准测试系统的原始数据和潮流结果。

netty源码学习之服务端客户端初始化

本篇博文从netty的使用例子出发,服务端bind()方法和客户端的connect()方法,分别分析了一下netty服务端和客户端的建立过程(大量篇幅),最终给予对上述理解简要总结了一次基于netty的一次请求和响应过程图解,希望对大家学习netty知识有所帮助,如果帮到你的话希望点赞、转发、一键三连让我们一起在技术的海洋遨游,奥利给!!!

liushangzaibeijing的博客 5万+

Netty源码浅析

前言 到目前为止,已经看了不少框架的源码了,但如何对源码做笔记并写博客,不同框架适合的做笔记方式也不同 对于Netty框架而言,我认为以对比学习+回答问题的方式来浅析源码会好一些,因为Netty本质上也就是基于NIO的一个非阻塞网络编程框架,那么本篇笔记就对比一个简单的NIO非阻塞网络编程,看看Netty是如何实现的 流程解析 看Netty源码主要就是去看其如何实现Reactor模式的,关于Reactor的演变,在《初识Netty》中我做了对比,不进行赘述 本节主要比较一下NIO的传统非阻塞方式与Netty

DaHuangXiao的博客 701
上一篇: netty源码浅析-NioEventLoop-reactor线程分析
下一篇: netty源码浅析-服务端数据接入
胖柯G
博客等级 码龄10年 453粉丝 57原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值