) e.getKey()).set(e.getValue());
}
try {
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);
}
}
a) 将我们在启动流程中设置的childHandler(如,「serverBootstrap.childHandler(new MyServerInitializer())」)以及childOptions(如,「serverBootstrap.childOption(ChannelOption.ALLOW_HALF_CLOSURE, true)」)、childAttrs(如,「serverBootstrap.childAttr(AttributeKey.valueOf("userID"), UUID.randomUUID().toString())」)设置到这个NioSocketChannel中。 b) 将配置好的NioSocketChannel注册到childGroup中,并注册了一个监听器,因为注册是一个异步操作,该监听器会得以调用在注册操作完成时(完成包括,成功的完成、失败的完成、取消的完成)。该监听器实现:如果发现该注册操作失败了,则会强制关闭当前这个NioSocketChannel。 而将NioSocketChannel注册到childGroup的操作和将NioServerSocketChannel注册到parentGroup的流程也是极其类似的。详细的说明请参阅Netty 源码解析 ——— 服务端启动流程 (下)。这里做一个简单的概述。 整个注册流程: a) 首先会通过轮询的方式从childGroup中获取一个NioEventLoop,将当前的NioSocketChannel注册到这个NioEventLoop上。 b) 将当前的SocketChannel注册到NioEventLoop中的Selector上,并将NioSocketChannel作为附加属性设置到SelectionKey中。 c) 回调我们自定义的ChannelInitializer的initChannel方法,将我们定义的一个个ChannelHandler添加到当前NioSocketChannel所关联的ChannelPipeline上,然后将ChannelInitializer本身从ChannelPipeline中移除。 d) 标记注册这个异步操作标志为成功完成。 e) 触发ChannelRegistered事件,该事件会在ChannelPipeline中得以传播。 f) 触发ChannelActive事件,该事件也是一个入站事件,它会从ChannelPipeline中的head开始传播,而head的channelActive方法除了将ChannelActive事件传播给下一个ChannelInboundHandler之外,还调用一个readIfIsAutoRead()方法。 而readIfIsAutoRead()最终会调用到「AbstractNioChannel.doBeginRead()」方法:
protected void doBeginRead() throws Exception {
// Channel.read() or ChannelHandlerContext.read() was called
final SelectionKey selectionKey = this.selectionKey;
if (!selectionKey.isValid()) {
return;
}
readPending = true;
final int interestOps = selectionKey.interestOps();
if ((interestOps & readInterestOp) == 0) {
selectionKey.interestOps(interestOps | readInterestOp);
}
}
而这个方法中就完成了将readInterestOp(即,上面在说明NioSocketChannel的构建过程中已经提及,该成员变量值为SelectionKey.OP_READ)设置为感兴趣的事件。这样一来,Selector就会监听该SocketChannel的读事件了。
到目前为止,NioServerSocketChannel通过accept接受了一个客户端的连接请求的整个流程就完成了。这里NioSocketChannel的创建是由NioServerSocketChannel所在的NioEventLoop( 实际上是NioEventLoop所在的线程上 )完成的。然后将创建好的NioSocketChannel注册到childGroup中,也就是通过轮询的方式的方式从childGroup中获取一个NioEventLoop,然后将NioSocketChannel注册的其上。在注册的过程中也完成了将SocketChannel注册到Selector,并设置SelectionKey.OP_READ为感兴趣的事件,这样Selector就会开始监听这个SocketChannel的读事件了。
SelectionKey.OP_CONNECT 事件处理流程
当SelectionKey.OP_CONNECT(连接事件)准备就绪时,我们执行如下操作:
// We first need to call finishConnect() before try to trigger a read(...) or write(...) as otherwise
// the NIO JDK channel implementation may throw a NotYetConnectedException.
if ((readyOps & SelectionKey.OP_CONNECT) != 0) {
// remove OP_CONNECT as otherwise Selector.select(..) will always return without blocking
// See https://github.com/netty/netty/issues/924
int ops = k.interestOps();
ops &= ~SelectionKey.OP_CONNECT;
k.interestOps(ops);
unsafe.finishConnect();
}
① 将SelectionKey.OP_CONNECT事件从SelectionKey所感兴趣的事件中移除,这样Selector就不会再去监听该连接的SelectionKey.OP_CONNECT事件了。而SelectionKey.OP_CONNECT连接事件是只需要处理一次的事件,一旦连接建立完成,就可以进行读、写操作了。
② unsafe.finishConnect():
注意,当连接操作既没有被取消也没有超时的情况下,该方法才会被事件循环所调用。
a) boolean wasActive = isActive():
public boolean isActive() {
SocketChannel ch = javaChannel();
return ch.isOpen() && ch.isConnected();
}
因为此时「SocketChannel.finishConnect()」还没调用,所以「ch.isConnected()」将返回false,因此isActive()的结果为false。 b) doFinishConnect():
protected void doFinishConnect() throws Exception {
if (!javaChannel().finishConnect()) {
throw new Error();
}
}
该方法会调用SocketChannel.finishConnect()来标识连接的完成,如果我们不调用该方法,就去调用read/write方法,则会抛出一个NotYetConnectedException异常。 注意,无论如何在connect后finishConnect()方法都是需要被调用的。调用finishConnect()的三种返回:
如果你在connect()后直接调用了finishConnect()( 并非在CONNECT事件中调用 ),则若finishConnect()返回了true,则表示channel连接已经建立,而且CONNECT事件也不会被触发了。
如果finishConnect()方法返回false,则表示连接还未建立好。那么就可以通过CONNECT事件来监听连接的完成。
如果finishConnect()方法抛出了一个IOException异常,则表示连接操作失败。
c) fulfillConnectPromise(connectPromise, wasActive):
I. 当异步的“连接尝试”操作通过取消来关闭了,那么则直接返回。因为当“连接尝试”操作被取消时,connectPromise会被置为null。
II. isActive():获取当前SocketChannel的状态,因为此时「SocketChannel.finishConnect()」已经被调用过了,因此该方法会返回true。 III. boolean promiseSet = promise.trySuccess(): 将当前的异步的“连接尝试”操作尝试标记为成功。如果用户取消了“连接尝试(即,调用connect操作后,用户调用了cancel来取消该操作)”的操作的话,该方法将返回false;否则返回true,并且如果有ChannelFutureListener注册到了这个ChannelFuture(即,ChannelPromise)上,那么监听器的operationComplete方法将得以回调。 IV. 无论当前的“连接尝试”操作是否被取消,channelActive()事件都将被触发,因为此时channel确实已经处于active状态了。 channelActive是一个入站事件,该事件会从ChannelPipeline的head开始传播至tail间的所有ChannelInboundHandler,并回调它们的channelActive方法。
我们来深入看看head对channelActive事件的处理:
它除了将channelActive事件传播给下一个ChannelInboundHandler外,还会进行一个「readIfIsAutoRead()」操作:
private void readIfIsAutoRead() {
if (channel.config().isAutoRead()) {
channel.read();
}
}
因为NioSocketChannel的NioSocketChannelConfig对象的autoRead属性默认就为1,因此isAutoRead()为true。那么就会调用channel.read()操作,这将触发一个read事件在ChannelPipeline中,而read是一个出站操作。它会从ChannelPipeline的尾部开始传播至head间的每个ChannelOutboundHandler。 我们接着来看head对read事件的处理:
// DefaultChannelPipeline#HeadContext#read()
public void read(ChannelHandlerContext ctx) {
unsafe.beginRead();
}
// AbstractChannel#AbstractUnsafe#beginRead()
public final void beginRead() {
assertEventLoop();
if (!isActive()) {
return;
}
try {
doBeginRead();
} catch (final Exception e) {
invokeLater(new Runnable() {
@Override
public void run() {
pipeline.fireExceptionCaught(e);
}
});
close(voidPromise());
}
}
// AbstractNioChannel#doBeginRead()
protected void doBeginRead() throws Exception {
// Channel.read() or ChannelHandlerContext.read() was called
final SelectionKey selectionKey = this.selectionKey;
if (!selectionKey.isValid()) {
return;
}
readPending = true;
final int interestOps = selectionKey.interestOps();
if ((interestOps & readInterestOp) == 0) {
selectionKey.interestOps(interestOps | readInterestOp);
}
}
这里的readInterestOp是SelectionKey.OP_READ,是在构建NioSocketChannel对象时传进来的。因此,我们可以知道,此时的read事件主要是完成了,在Selector中对已经注册到其上的NioSocketChannel的OP_READ标识为感兴趣的事件。这样Selector就监控该SocketChannel的读操作了。 V. 如果用户执行了取消“连接尝试”的操作,那么就关闭channel,并触发channelInactive事件。 d) 如果成员变量connectTimeoutFuture非空,则说明该“连接尝试”操作设置了一个连接超时时间。那么,此时连接已经完成了,我们就可以取消这个连接超时检测的定时任务了。超时任务会记录本次“连接尝试”操作为失败状态,并且会将connectTimeoutFuture成员变量置为null。 比如,可能存在这样一种情况:也就是当程序执行完fulfillConnectPromise方法中的「promise.trySuccess()」之后,以及在执行finally代码块之前,“连接尝试”的已经完成,并且ChannelPromise已标记为了true。但是此时设置的连接超时时间已到并且连接超时任务被得以执行,此时超时任务发现ChannelPromise的状态已经被标识过了也就不会进行关闭channel的操作。 因此如上流程我们知道,『connectTimeoutFuture == null』有两种情况:1,如果没有设置连接超时;2,设置了连接超时,并且只因为超时或其他原因已经执行了 NioSocketChannel 的 close() 操作。『connectTimeoutFuture#operationComplete』也有两种情况:1,在超时时间内连接还没建立,则执行相应的close操作;2,连接已经建立了,但是还未执行到connectTimeoutFuture#cancel(false)操作,connectTimeoutFuture 就触发了,此时因为 promise的状态已经被表示过了,也不会进行close操作。
总的来说,OP_CONNECT事件的触发时,表示当前的socket处于了可连接的状态了,需要调用SocketChannel.finishConnect()来完成连接的后续事件。同时会触发ChannelActive事件,该事件为一个入站事件,它会在NioSocketChannel所关联的ChannelPipeline管道得以传播,即,回调head到tail之间所有的ChannelInboundHandler的channelActive方法。而head的channelActive方法中又会触发一个channel的read操作,该操作最终会在NioSocketChannel所注册的Selector中标识OP_READ为感兴趣的事件,这使得Selector会监听NioSocketChannel上是否有可读的数据准备好被读取了。
SelectionKey.OP_READ 事件处理流程
当有可读数据准备被读取时,‘SelectionKey.OP_READ’将会触发。在NioEventLoop的事件循环中会对该事件进行处理:
if ((readyOps & (SelectionKey.OP_READ | SelectionKey.OP_ACCEPT)) != 0 || readyOps == 0) {
unsafe.read();
}
我们来看看unsafe.read()的实现,在NioSocketChannel中unsafe是一个NioByteUnsafe实例:
这里,我们主要针对读数据涉及的逻辑进行分析,关于allocHandle如何计算出一个最优缓冲区的大小用于创建缓冲区,以及判断读循环是否应该继续,已经在 Netty 源码解析 ——— AdaptiveRecvByteBufAllocator做了详细的分析了。所以此处,我们主要关注的是真实的读数据操作所涉及的流程。
① 首先,在循环前将分配器处理器中累加的消息/字节计数重置。接着,进行一个读循环操作:
I. 根据给定的分配器以及预测的最优缓冲区容量大小创建一个缓冲区用于准备接受可读取的数据。在Netty中,分配器(allocator)默认为PooledByteBufAllocator实例,而PooledByteBufAllocator实现通过使用jemalloc算法来实现高效的内存分配。
II. allocHandle.lastBytesRead(doReadBytes(byteBuf)):
使用已经分配好的ByteBuf来读取数据。并在分配器处理器中记录本次读取的字节数。
这里会将SocketChannel中的内容写到byteBuf中,从byteBuf的writerIndex开始写入数据,writerIndex会增加所写入的字节数。并且设置了最大可从SocketChannel读取的数据大小为“allocHandle.attemptedBytesRead()”,这也是byteBuf的容量大小。
III. 判断本次读操作所读取到的字节数:
a) 若‘读取到的字节数 < 0’,即为’-1’时,说明对端已经关闭了。close变量会被标记为true。因为没有读取到数据,因此调用‘byteBuf.release()’来释放bytebuf(因为,此时 bytebuf 不会在通过 channelPipeline进行传输了,也就是,这个 bytebuf 最后使用的地方就是当前方法,因此应该调用 release() 方法来声明对其的释放),然后退出读循环操作(break)。需要说明的是,因为byteBuf是通过PooledByteBufAllocator来分配的缓冲区,是一个池中的ByteBuf,因此是要通过release()方法来减小bytebud的引用计数,当bytebuf的引用计数为0时,则说明此时已经没有引用指向这个bytebuf了,那么它就会被“回收”;
b) 若‘读取到的字节数 == 0’,仅仅说明本次读操作没有读取到数据,那么就会执行同上面一样的释放bytebuf操作,即,‘byteBuf.release()’,然后退出读循环操作(break);
c) 若‘读取到的字节数 > 0’,说明本次读操作已经到达有效的数据了。那么执行:
[1]. 「allocHandle.incMessagesRead(1)」对读消息的次数进行累加。
[2]. 然后标识readPending为false,表示本次读操作已经读取到了有效数据,无需等待再一次的读操作。
[3]. 接着触发ChannelRead事件,它会在ChannelPipeline中传播,「pipeline.fireChannelRead(byteBuf)」。这是一个入站事件,它会从ChannelPipeline的head开始传播,依次顺序回调ChannelInboundHandler的channelRead()方法。这里可以看到,是读循环中每一次有效的读操作都对触发一次ChannelRead事件,并不是在所有数据都读取到之后才触发一次ChannelRead事件。因此,我们需要提供一系列的编解码器来将收到的数据分割成我们一个个的逻辑数据包,对此Netty也提供了一系列拆箱即有的编解码器为我们解决相关的问题。
IV. 根据当前的NioSocketChannel是否是自动读取的配置,以及已经读取的数据字节数,以及已经进行的读操作次数,以及最近一次读取的字节数来判断是否需要继续进行读循环操作。若需要则继续读循环操作;否则退出读循环,继续后面的流程。
② allocHandle.readComplete():在本次读循环结束后调用一次「allocHandle.readComplete()」来记录本次读循环的数据信息以用于预测下一次读事件触发时,应该分配多大的ByteBuf容量更加合理些。
③ pipeline.fireChannelReadComplete():触发ChannelReadComplete事件,用于表示当前读操作的最后一个消息已经被ChannelRead所消费。ChannelReadComplete是一个入站消息,它会从ChannelPipeline的head开始传播,依次顺序回调ChannelInboundHandler的channelReadComplete方法。
ChannelRead vs ChannelReadComplete
当Channel检测到对端有数据可以读取的时候,channelRead方法会被调用。
channelRead方法可能会被调用多次,当channelReadComplete方法被回调的时候,标识着数据已经都读取完了。也就是说,channelRead方法会被调用多次,当所有消息都读取完后channelReadComplete方法会得到一次调用。
④ 如果close被标识为了true,则说明对端已经关闭了连接。(即,读操作中读取的字节数量为-1,则表示远端已经关闭了),则执行「closeOnRead(pipeline)」
首先需要补充一点,在NIO传输模式下,当SocketChannel的read操作返回’-1’时,有两种情况:a) 对端已经关闭了连接,即SocketChannel被关闭了;b) 当前端执行了「socketChannel.shutdownInput()」。
I. 若“isInputShutdown0()”返回false,则说明是远端连接已经关闭了。那么此时,如果我们的程序配置了“ChannelOption.ALLOW_HALF_CLOSURE”属性(即,可以在启动引导类时通过option(ChannelOption.ALLOW_HALF_CLOSURE, true)来启用配置),那么就会进行shutdownInput()操作,并触发一个用户自定义的ChannelInputShutdownEvent.INSTANCE事件,在ChannelPipeline中传播。该事件是一个入站事件,它会从ChannelPipeline中的head开始传播,异常顺序调用ChannelInboundHandler的userEventTriggered方法。
II. 若“isInputShutdown0()”返回false,则说明是远端连接已经关闭了。并且此时我们的程序并没有配置启动“ChannelOption.ALLOW_HALF_CLOSURE”。那么此时就会进行相应SocketChannel的关闭等相关操作。
III. 若“isInputShutdown0()”返回true,则说明是当前的NioSocketChannel自动调用了shutdownInput()方法来关闭了输入流。那么此时就会触发一个用户自定义的ChannelInputShutdownEvent.INSTANCE事件,在ChannelPipeline中传播。该事件是一个入站事件,它会从ChannelPipeline中的head开始传播,异常顺序调用ChannelINboundHandler的userEventTriggered方法。
关于「SocketChannel.shutdownInput()」:关闭一个连接的读,但不关闭这个通道。一旦关闭了读,那么在这之后调用channel的read都将返回’-1’,来表示’流的结尾’。如果往已经关闭的输入流中发送数据,都会默认被丢弃。
⑤ 如果本次读操作已经读取到有效数据(即,最近一次读操作返回的读取字节数>0),并且当前的NioSocketChannel的配置为非自动读取(disable autoRead,说明此时用户不希望Selector去监听当前SocketChannel的读事件,用户可以根据业务逻辑的需要,在希望读取数据时再去添加OP_READ事件到Selector中。并且在每次读取到数据后就将OP_READ事件从所感兴趣的事件中移除),那么此时需要将OP_READ事件从所感兴趣的事件中移除,这样Selector就不会继续监听该SocketChannel的读事件了。
removeReadOp()操作就是将OP_READ从SelectionKey的interestOps集合中移除:
在NioSocketChannel中成员变量readInterestOp就是SelectionKey.OP_READ。
PS:注意,如果在当前端主动调用「channel.shutdownInput()」方法时,需要在处理’ChannelInputShutdownReadComplete’这个用户自定义的事件时调用「channel().config().setAutoRead(false);」来将autoRead置为false。不然,OP_READ事件会一直被触发,而上的步骤’III’会一直被调用,这会导致一些问题,比如不必要的CPU消耗。 调用方式类似:
public class MyServerHandler extends SimpleChannelInboundHandler {
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
Channel serverChannel = ctx.channel();
if(serverChannel instanceof NioSocketChannel) {
System.out.println("server shutdownInput...");
((NioSocketChannel) serverChannel).shutdownInput();
}
}
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
if(evt instanceof ChannelInputShutdownReadComplete) {
System.out.println("detail ChannelInputShutdownEvent event......");
ctx.channel().config().setAutoRead(false);
}
super.userEventTriggered(ctx, evt);
}
}
SelectionKey.OP_WRITE 事件处理流程
在NioEventLoop的事件循环中’SelectionKey.OP_WRITE’事件的处理流程如下:
// Process OP_WRITE first as we may be able to write some queued buffers and so free memory.
if ((readyOps & SelectionKey.OP_WRITE) != 0) {
// Call forceFlush which will also take care of clear the OP_WRITE once there is nothing left to write
ch.unsafe().forceFlush();
}
关于OP_WRITE事件: OP_WRITE事件的就绪条件并不是发生在调用channel的write方法之后,而是在当底层缓冲区有空闲空间的情况下。因为写缓冲区在绝大部分时候都是有空闲空间的,所以如果你注册了写事件,这会使得写事件一直处于就就绪,选择处理线程就会一直占用着CPU资源。所以,只有当你确实有数据要写时再注册写操作,并在写完以后马上取消注册。 SocketChannel会在写数据时,若发现当buffer还有数据,但写缓冲区已经满的情况下,socketChannel.write(buffer)会返回已经写出去的字节数,此时为0。那么这个时候我们就需要注册OP_WRITE事件,这样当写缓冲区又有空闲空间的时候就会触发OP_WRITE事件,这样我们就可以继续将没写完的数据继续写出了。而且在写完后,一定要记得将OP_WRITE事件注销。 比如,来看看NioSocketChannel的doWrite()操作(「 ch.unsafe().forceFlush()」方法最终也就是会调用到这里):
就像上面所说的那样,当发现socketChannel.write(buffer)返回的已经写出去的字节数为0时,则说明此时写缓冲区已经满了无法写入,因此就需要注册一个OP_WRITE事件,这样当写缓存有空间来继续接受数据时,该写事件就会被触发,这样我们就可以继续将没写完的数据继续写出了。而且在写完后,一定要记得将OP_WRITE事件注销。
关于写操作的具体流程分析请参见 Netty 源码解析 ——— writeAndFlush流程分析
后记
本文主要对NioEventLoop中涉及到的四种NIO事件的处理流程进行了分析。四个看似简单的处理流程,深入探索后发现其实并不简单,其实可以展开的点还有很多,特别是关于写事件涉及到ChannelOutboundBuffer以及Netty默认使用的PooledByteBufAllocator实现了jemalloc算法来完成高效的内存分配等等,希望在后面的文章中能继续和大家分享我的分析以及想法。 若文章有任何错误,望大家不吝指教:)
你可能感兴趣的:(Netty 源码解析 ——— 基于 NIO 网络传输模式的 OP_ACCEPT、OP_CONNECT、OP_READ、OP_WRITE 事件处理流程)
springboot 如何实现单点登录
lozhyf
工作 面试 学习 spring boot 后端 java
单点登录(SingleSign-On,SSO)允许用户使用一组凭证(如用户名和密码)登录到多个相关的应用系统中,而无需为每个系统单独进行身份验证。在SpringBoot中实现单点登录有多种方式,下面将分别介绍基于OAuth2.0和基于CAS(CentralAuthenticationService)这两种常见的实现方法。基于OAuth2.0实现单点登录1.项目依赖首先,在pom.xml中添加Spr
[Android]浏览器下载的apk文件无法识别无法安装问题
风浅月明
Android android
在Android电话机上,用浏览器下载apk进行版本更新,出现下载文件没被识别为apk,导致无法安装问题。原来的下载链接:https://mojsetup.obs.cn-southwest-2.myhuaweicloud.com/callphone-release-1.0.4.apk修改后的下载链接:https://mobile.mjsaas.com/callphone-release-1.0.4
清除无用的二进制日志(Binlog)
枯河垂钓
MySQL日志文件 服务器 linux 数据库
一、Binlog的清除方法1、自动清除binlog_expire_logs_secondsexpire_logs_days已经在MySQL8.0中被标记为废弃(deprecated),并计划在未来版本中被移除SETGLOBALexpire_logs_days=0;--弃用expire_logs_days参数SETGLOBALbinlog_expire_logs_seconds=259200;--保
改进A*算法并用于城市无人机路径规划
九亿AI算法优化工作室&
算法 matlab
独家原创!改进A*算法进行城市无人机路径规划,考虑碰撞,飞行高度等优化启发式搜索。所有指标超过A*和A算法!附有完整的文档说明算法设计、毕业设计、期刊专利!感兴趣可以联系我。代码获取方式1:私信博主代码获取方式2利用同等价值的matlab代码兑换博主的matlab代码先提供matlab代码运行效果图给博主评估其价值,可以的话,就可以进行兑换。
法律案例图像检索的前沿探索:基于AI的多模态搜索引擎设计【附保姆级代码】
一键难忘
精通AI实战千例专栏合集 人工智能 搜索引擎 法律案例图像检索
本文收录于专栏:精通AI实战千例专栏合集https://blog.csdn.net/weixin_52908342/category_11863492.html从基础到实践,深入学习。无论你是初学者还是经验丰富的老手,对于本专栏案例和项目实践都有参考学习意义。每一个案例都附带关键代码,详细讲解供大家学习,希望可以帮到大家。正在不断更新中文章目录法律案例图像检索的前沿探索:基于AI的多模态搜索引擎设
MySQL的Binlog解析
枯河垂钓
MySQL日志文件 mysql 服务器
查看当前主服务器(Master)的二进制日志(BinaryLog)状态SHOWMASTERSTATUS基于位点解析mysqlbinlog--start-position=4--stop-position=1248mysql-bin.000001>/data/01.sql基于时间解析mysqlbinlog--start-datetime="2025-02-2321:00:00"/data/mysql
电脑界面美化工具,让你的电脑C位出道!
学术裁缝李师傅
电脑 windows 软件构建 github
在这个万物皆可定制的时代,怎么能不对你的电脑进行定制呢!今天李师傅向大家安利这款「枫の美化工具箱」,堪称电脑界的魔法化妆师,三秒让你的桌面从路人甲变身Superstar!【海量美化资源】壁纸库简直像百宝箱!想找梦幻星空?有!想要霸气山河图?马上安排!连萌萌的动漫壁纸都给你备齐了。每张都超高清,不管大屏小屏都能完美适配。还有超多图标套装,系统图标、软件图标都能整套换新,再也不用看千篇一律的默认图标啦
yolov8:pt 转 onnx
DI_f
yolo YOLO
yolov8官方教程1.安装包我使用的是虚拟环境,yolov8包已经下载到本地了,因此直接在anacondaprompt命令行继续安装首先激活自己的虚拟环境,然后执行安装命令pipinstallultralyticsyolov8中没有requirement.txt文件,直接安装ultralytics即可2.进行模型转换将转换好的pt文件放在weight文件夹下,然后在这个文件夹创建一个py文件,里
java+jsp+sqlserver 2008+Tomcat实现一个简单的搜索引擎
麦田上的字节
高级java教程系列 Java 搜索引擎
导读:搜索引擎的定义就是指按照既定的策略与方法,采取相关的计算机程序,通过在互联网中进行寻找信息,并显示信息,最后把找到的信息进行整理和筛选,为搜索引擎的使用者提供检索信息的服务,终极目标是为了提供给使用者,他所搜索信息相关的资料的计算机系统。搜索引擎的种类繁多,既可以进行全文的索引,还可以进行目录的索引,不仅有集合式的搜索引擎,还有
android java 串口通信_Android串口通信
婧在机器学习中
android java 串口通信
1.解析SerialPortAPI串口通信例子首先分析一下例子中的类结构:通过类结构可知,最主要的还是在SerialPortJNI.java类,该类写了一些Native方法处理打开与关闭串口接收发送的SerialPort.Java代码如下:packagecom.dwin.navy.serialportapi;importjava.io.FileDescriptor;importandroid.ut
jmeter 接入deepseek 或者chatgpt
小赖同学啊
jmeter专栏 jmeter chatgpt
接入ChatGPT实现思路要将JMeter与ChatGPT接入,核心在于通过JMeter发送HTTP请求至OpenAI的API接口,以此调用ChatGPT进行交互。整个过程包含创建JMeter测试计划、添加必要组件、配置请求参数以及处理响应结果等步骤。具体步骤1.获取OpenAIAPI密钥在使用ChatGPTAPI之前,你需要在OpenAI平台注册账号并获取API密钥,此密钥用于身份验证,保障请求
jmeter 与大数据生态圈中的服务进行集成
小赖同学啊
jmeter专栏 jmeter 大数据
以下为你详细介绍JMeter与大数据生态圈中几种常见服务(HadoopHDFS、Spark、Kafka、Elasticsearch)集成的方法:与HadoopHDFS集成实现思路HDFS是Hadoop的分布式文件系统,JMeter可模拟客户端对HDFS进行文件读写操作,通常借助HDFS的JavaAPI编写自定义JMeter采样器。步骤添加依赖:将Hadoop的客户端JAR包添加到JMeter的li
医疗信息分析与知识图谱系统设计方案
翱翔-蓝天
知识图谱 人工智能
医疗信息分析与知识图谱系统设计方案0.系统需求0.1项目背景本系统旨在通过整合医疗机构现有的信息系统数据,结合向量数据库、图数据库和开源AI模型,实现医疗数据的深度分析、疾病预测和医疗知识图谱构建,为医疗决策提供智能化支持。0.2核心需求数据集成与分析:对接现有医疗信息系统(HIS/LIS/PACS/EMR)医疗数据标准化处理多维度统计分析趋势预测分析知识图谱构建:医疗知识抽取实体关系构建知识推理
在线教育系统设计方案
翱翔-蓝天
java
在线教育系统设计方案1.系统概述本系统是一个现代化的在线教育平台,采用前后端分离架构,提供课程管理、用户管理、视频点播、直播互动等功能。2.技术栈选型2.1后端技术栈核心框架:SpringBoot3.x数据库:MySQL8.x持久层框架:MyBatisPlus缓存:Redis消息队列:RabbitMQ搜索引擎:Elasticsearch文件存储:阿里云OSS视频点播:阿里云视频点播服务2.2前端技
冷门的Adobe系列软件小技巧分享
reddingtons
adobe
作为一名学生,我有幸通过学校的学生邮箱免费使用Adobe全系列软件。这意味着我可以畅享Adobe所有软件的强大功能,尤其是一些热门软件,如Photoshop、Illustrator和AfterEffects。然而,下面我分享的技巧涉及一些收费软件,可能会对免费用户造成一定限制,但希望这些技巧能为广大用户提供帮助。1.AdobePhotoshop:使用“选择和遮罩”工具选择和遮罩工具是Photosh
git设置代理
翱翔-蓝天
git elasticsearch 大数据
在Git中,如果你需要临时设置代理(例如访问某些需要代理的Git仓库),可以使用以下命令。代理设置将仅对当前的Git会话有效,关闭终端或终止Git会话后,设置将失效。设置HTTP和HTTPS代理设置HTTP代理:gitconfig--globalhttp.proxyhttp://:设置HTTPS代理:gitconfig--globalhttps.proxyhttps://:替换为代理服务器的地址,
Jmeter基础篇(20)压测时如何找到最佳并发量
綦枫Maple
Jmeter性能测试大全 jmeter
一、前言在进行压力测试(压测)时,找到最佳并发量是非常关键的一步。这需要考虑到多种因素,包括但不限于:你的系统资源、预期的用户行为、以及希望达到的性能目标。二、并发量计算方法确定基准了解系统规格:首先,你要清楚地知道你们的服务器硬件配置(如CPU、内存、磁盘I/O能力等)以及软件环境(操作系统、数据库、中间件等)。历史数据:如果可能的话,查看过去的数据以了解系统的正常负载情况。这些信息可以帮助你设
public or static包下的 html 丢了(404)? 你快回来! 我一人承受不来
barcke
html 前端 java 学习 spring
没想到吧,我把html还是放到了jar包中~环境:SpringBoot版本2.XJava版本1.8.0及以上问题:publicorstatic包下的html丢了(404)?话不多说先上图我的目录结构是这样的src└──main└──java└──com└──example└──demo└──DemoApplication.java└──resources└──public├──css│└──mai
Android系统源代码下载
nmgchfzhzhg
Android学习 Android资料
一、概述众所周知,Android移动操作系统是Google花费了很大的财力、物力及人力的前提下,推广到世界各个角落,以开放源代码的方式(当然也不是完全开放所有),使其在世界范围内迅速漫延开来,到目前虽说只有三四年的时间,火热程度,也不用我细说了,看看业界新闻就明白了。这里我们主要讨论Android系统源代码的下载,这里我们先了解两个工具:1、GitGit是面向Linux内核管理的一个工具,它是一种
头歌作业 函数
m0_73433005
算法
1题目描述本关任务:m个人的成绩存放在score数组中,请编写函数fun,它的功能是:将低于平均分的人数作为函数值返回,将低于平均分的人数放在below所指的数组中。注意:请勿改动主函数main和其它函数中的任何内容,仅在函数fun中填入你编写的若干语句。输入格式个人成绩数组。输出格式低于平均分的成绩。输入输出样例输入1102030405060708090输出110203040#include#i
合泰Holtek LCD驱动芯片选型全攻略:从原理到落地的工程师视角
holtek合泰芯片选型及支持
电子知识笔记 笔记 单片机 嵌入式硬件 mcu 硬件工程 大数据
一、LCD驱动芯片:显示系统的“神经中枢”LCD驱动芯片是连接主控MCU与液晶屏的核心器件,负责将数字信号转化为液晶分子偏转所需的模拟电压,同时协调时序与刷新逻辑。其核心功能包括:信号转换:将主控的SPI/I²C指令转换为行列驱动信号,控制每个像素的透光率;电压管理:通过调节偏置电压(BIAS)和占空比(DUTY),优化对比度与功耗;资源优化:减少主控IO占用(传统直驱方案需数百个IO,驱动芯片仅
ChatTTS-ui模型概述、安装及使用方法说明
醉心编码
人工智能基础 技术类 脚本基础 TTS 大模型 人工智能
ChatTTS-ui模型概述、安装及使用方法说明一、ChatTTS-ui模型概述二、ChatTTS-ui的技术优势三、ChatTTS-ui的安装与配置1.安装Python和Git环境2.下载ChatTTS-ui源码3.创建并激活虚拟环境4.安装依赖5.启动应用四、ChatTTS-ui的高级功能1.API调用2.音色自定义3.细粒度控制4.GPU加速五、ChatTTS-ui的应用场景六、ChatTT
Unity 脚本控制3D人物模型的BlendShape
lovingyk
unity unity
有些3D角色模型带有BlendShape面部控制,在Unity中可以通过接口访问并操作其参数可以表现不同的面部表情在Unity中选中角色模型的指定部位,这个是由模型师定义的,不固定.但肯定是在面部建模上.点选之后在检查器可以看到对应的BlendShapes设定项出现在SkinedMeshRenderer组件中.由于BlendShape中定义的每个项是在建模中由建模师自定义的,所以名称不固定,所以在
Unity 改善URP管线下阴影锯齿
lovingyk
unity unity
Unity在URP管线的默认配置下,阴影可能会表现出锯齿现象,这可能是因为管线配置下阴影中的最大距离数值过大导致,可以适当调整
【刷题】贪心算法
llllliznc
贪心算法 算法
贪心算法通常用于那些可以通过局部最优解达到全局最优解的问题,也就是说每一步都选择当前看起来最好的选项,从而希望最终的结果是最优的。基础概念[分配问题]:局部最优满足需求,排序后贪心分配分发饼干分发糖果[区间问题]:按区间右端点排序,最大化不重叠区间无重叠区间用最少数量的箭引爆气球经典贪心模型[跳跃问题]:维护最大覆盖范围,贪心选择下一步跳跃游戏跳跃游戏II[加油站问题]:总油量足够时,局部油量最低
P4447 [AHOI2018初中组]分组【贪心】
ln2037
贪心
题目描述小可可的学校信息组总共有n个队员,每个人都有一个实力值a[i]a[i]a[i]。现在,一年一度的编程大赛就要到了,小可可的学校获得了若干个参赛名额,教练决定把学校信息组的nnn个队员分成若干个小组去参加这场比赛。但是每个队员都不会愿意与实力跟自己过于悬殊的队员组队,于是要求分成的每个小组的队员实力值连续,同时,一个队不需要两个实力相同的选手。举个例子:[1,2,3,4,5][1,2,3,4
记录第一次在windows环境编译libuvc库 踩的坑
发光的小豆芽
相机连接 windows libuvc
最近遇到windows下编译libuvc库,实现经usb连接的摄像头拍摄采集。绕了一大圈,记录一下。首先,作为新手,肯定需要参考大神资料,但是还是踩了坑。要在windows环境下安装libuvc的驱动并确保可用,需要经过一系列流程,(1)先参考神人的博客记录呗有关windows下libuvc的使用https://blog.csdn.net/Arom_Corge/article/details/13
Android 应用强制更新策略
linshijun33
Android之路 android 强制更新 自动更新 更新策略
Android应用强制更新的用途十分广泛,特别上刚上线的应用肯定会存在或多或少的bug,特别是涉及移动支付这一块的内容,如果出错了会造成比较大的损失,所以强制更新显得尤为重要。一般来说,强制更新的策略就是:应用启动时请求后台,后台发送应用最新版本的信息(包括应用版本号、名称、更新内容说明、下载包的服务器地址、是否强制更新的标志位)等等。下面我们就将根据以上思路来写实现代码。1.AndroidMan
android应用下载后无法识别apk文件
胡帅君
开发环境&工具 android Android ANDROID 无法安装 无法识别
先说说我遇到的情况.最近在做个项目,用手机自带的浏览器下载apk文件时,提示文件类别无法识别,文件可以下载,但是从浏览器下载后却不能安装,系统始终不能识别apk文件,在ucweb上可以下载并安装,用apk安装器也可以安装,就是手机自带浏览器无法安装;为了方便所有用户下载并正常安装软件只有找到具体原因了。apk文件肯定没问题,那问题就可能出在服务器的设置上,go了下,基本找到了解决问题的思路,这里做
掌握角色设计与提示工程技术的深度整合
由数入道
提示词工程 角色设计
第一步:夯实基础-角色设计与提示工程的核心概念在深入整合之前,务必确保对角色设计和提示工程这两个领域有扎实的基础理解。1.角色设计基础(CharacterDesignFundamentals):角色构建要素(ElementsofCharacterDesign):背景故事(Backstory):角色的历史、出身、经历,塑造其价值观、动机和行为模式。(例:出身贫寒的街头混混vs.名门望族的天才科学家)
Algorithm
香水浓
java Algorithm
冒泡排序
public static void sort(Integer[] param) {
for (int i = param.length - 1; i > 0; i--) {
for (int j = 0; j < i; j++) {
int current = param[j];
int next = param[j + 1];
mongoDB 复杂查询表达式
开窍的石头
mongodb
1:count
Pg: db.user.find().count();
统计多少条数据
2:不等于$ne
Pg: db.user.find({_id:{$ne:3}},{name:1,sex:1,_id:0});
查询id不等于3的数据。
3:大于$gt $gte(大于等于)
&n
Jboss Java heap space异常解决方法, jboss OutOfMemoryError : PermGen space
0624chenhong
jvm jboss
转自
http://blog.csdn.net/zou274/article/details/5552630
解决办法:
window->preferences->java->installed jres->edit jre
把default vm arguments 的参数设为-Xms64m -Xmx512m
----------------
文件上传 下载 解析 相对路径
不懂事的小屁孩
文件上传
有点坑吧,弄这么一个简单的东西弄了一天多,身边还有大神指导着,网上各种百度着。
下面总结一下遇到的问题:
文件上传,在页面上传的时候,不要想着去操作绝对路径,浏览器会对客户端的信息进行保护,避免用户信息收到攻击。
在上传图片,或者文件时,使用form表单来操作。
前台通过form表单传输一个流到后台,而不是ajax传递参数到后台,代码如下:
<form action=&
怎么实现qq空间批量点赞
换个号韩国红果果
qq
纯粹为了好玩!!
逻辑很简单
1 打开浏览器console;输入以下代码。
先上添加赞的代码
var tools={};
//添加所有赞
function init(){
document.body.scrollTop=10000;
setTimeout(function(){document.body.scrollTop=0;},2000);//加
判断是否为中文
灵静志远
中文
方法一:
public class Zhidao {
public static void main(String args[]) {
String s = "sdf灭礌 kjl d{';\fdsjlk是";
int n=0;
for(int i=0; i<s.length(); i++) {
n = (int)s.charAt(i);
if((
一个电话面试后总结
a-john
面试
今天,接了一个电话面试,对于还是初学者的我来说,紧张了半天。
面试的问题分了层次,对于一类问题,由简到难。自己觉得回答不好的地方作了一下总结:
在谈到集合类的时候,举几个常用的集合类,想都没想,直接说了list,map。
然后对list和map分别举几个类型:
list方面:ArrayList,LinkedList。在谈到他们的区别时,愣住了
MSSQL中Escape转义的使用
aijuans
MSSQL
IF OBJECT_ID('tempdb..#ABC') is not null
drop table tempdb..#ABC
create table #ABC
(
PATHNAME NVARCHAR(50)
)
insert into #ABC
SELECT N'/ABCDEFGHI'
UNION ALL SELECT N'/ABCDGAFGASASSDFA'
UNION ALL
一个简单的存储过程
asialee
mysql 存储过程 构造数据 批量插入
今天要批量的生成一批测试数据,其中中间有部分数据是变化的,本来想写个程序来生成的,后来想到存储过程就可以搞定,所以随手写了一个,记录在此:
DELIMITER $$
DROP PROCEDURE IF EXISTS inse
annot convert from HomeFragment_1 to Fragment
百合不是茶
android 导包错误
创建了几个类继承Fragment, 需要将创建的类存储在ArrayList<Fragment>中; 出现不能将new 出来的对象放到队列中,原因很简单;
创建类时引入包是:import android.app.Fragment;
创建队列和对象时使用的包是:import android.support.v4.ap
Weblogic10两种修改端口的方法
bijian1013
weblogic 端口号 配置管理 config.xml
一.进入控制台进行修改 1.进入控制台: http://127.0.0.1:7001/console 2.展开左边树菜单 域结构->环境->服务器-->点击AdminServer(管理) &
mysql 操作指令
征客丶
mysql
一、连接mysql
进入 mysql 的安装目录;
$ bin/mysql -p [host IP 如果是登录本地的mysql 可以不写 -p 直接 -u] -u [userName] -p
输入密码,回车,接连;
二、权限操作[如果你很了解mysql数据库后,你可以直接去修改系统表,然后用 mysql> flush privileges; 指令让权限生效]
1、赋权
mys
【Hive一】Hive入门
bit1129
hive
Hive安装与配置
Hive的运行需要依赖于Hadoop,因此需要首先安装Hadoop2.5.2,并且Hive的启动前需要首先启动Hadoop。
Hive安装和配置的步骤
1. 从如下地址下载Hive0.14.0
http://mirror.bit.edu.cn/apache/hive/
2.解压hive,在系统变
ajax 三种提交请求的方法
BlueSkator
Ajax jqery
1、ajax 提交请求
$.ajax({
type:"post",
url : "${ctx}/front/Hotel/getAllHotelByAjax.do",
dataType : "json",
success : function(result) {
try {
for(v
mongodb开发环境下的搭建入门
braveCS
运维
linux下安装mongodb
1)官网下载mongodb-linux-x86_64-rhel62-3.0.4.gz
2)linux 解压
gzip -d mongodb-linux-x86_64-rhel62-3.0.4.gz;
mv mongodb-linux-x86_64-rhel62-3.0.4 mongodb-linux-x86_64-rhel62-
编程之美-最短摘要的生成
bylijinnan
java 数据结构 算法 编程之美
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
public class ShortestAbstract {
/**
* 编程之美 最短摘要的生成
* 扫描过程始终保持一个[pBegin,pEnd]的range,初始化确保[pBegin,pEnd]的ran
json数据解析及typeof
chengxuyuancsdn
js typeof json解析
// json格式
var people='{"authors": [{"firstName": "AAA","lastName": "BBB"},'
+' {"firstName": "CCC&
流程系统设计的层次和目标
comsci
设计模式 数据结构 sql 框架 脚本
流程系统设计的层次和目标
RMAN List和report 命令
daizj
oracle list report rman
LIST 命令
使用RMAN LIST 命令显示有关资料档案库中记录的备份集、代理副本和映像副本的
信息。使用此命令可列出:
• RMAN 资料档案库中状态不是AVAILABLE 的备份和副本
• 可用的且可以用于还原操作的数据文件备份和副本
• 备份集和副本,其中包含指定数据文件列表或指定表空间的备份
• 包含指定名称或范围的所有归档日志备份的备份集和副本
• 由标记、完成时间、可
二叉树:红黑树
dieslrae
二叉树
红黑树是一种自平衡的二叉树,它的查找,插入,删除操作时间复杂度皆为O(logN),不会出现普通二叉搜索树在最差情况时时间复杂度会变为O(N)的问题.
红黑树必须遵循红黑规则,规则如下
1、每个节点不是红就是黑。 2、根总是黑的 &
C语言homework3,7个小题目的代码
dcj3sjt126com
c
1、打印100以内的所有奇数。
# include <stdio.h>
int main(void)
{
int i;
for (i=1; i<=100; i++)
{
if (i%2 != 0)
printf("%d ", i);
}
return 0;
}
2、从键盘上输入10个整数,
自定义按钮, 图片在上, 文字在下, 居中显示
dcj3sjt126com
自定义
#import <UIKit/UIKit.h>
@interface MyButton : UIButton
-(void)setFrame:(CGRect)frame ImageName:(NSString*)imageName Target:(id)target Action:(SEL)action Title:(NSString*)title Font:(CGFloa
MySQL查询语句练习题,测试足够用了
flyvszhb
sql mysql
http://blog.sina.com.cn/s/blog_767d65530101861c.html
1.创建student和score表
CREATE TABLE student (
id INT(10) NOT NULL UNIQUE PRIMARY KEY ,
name VARCHAR
转:MyBatis Generator 详解
happyqing
mybatis
MyBatis Generator 详解
http://blog.csdn.net/isea533/article/details/42102297
MyBatis Generator详解
http://git.oschina.net/free/Mybatis_Utils/blob/master/MybatisGeneator/MybatisGeneator.
让程序员少走弯路的14个忠告
jingjing0907
工作 计划 学习
无论是谁,在刚进入某个领域之时,有再大的雄心壮志也敌不过眼前的迷茫:不知道应该怎么做,不知道应该做什么。下面是一名软件开发人员所学到的经验,希望能对大家有所帮助
1.不要害怕在工作中学习。
只要有电脑,就可以通过电子阅读器阅读报纸和大多数书籍。如果你只是做好自己的本职工作以及分配的任务,那是学不到很多东西的。如果你盲目地要求更多的工作,也是不可能提升自己的。放
nginx和NetScaler区别
流浪鱼
nginx
NetScaler是一个完整的包含操作系统和应用交付功能的产品,Nginx并不包含操作系统,在处理连接方面,需要依赖于操作系统,所以在并发连接数方面和防DoS攻击方面,Nginx不具备优势。
2.易用性方面差别也比较大。Nginx对管理员的水平要求比较高,参数比较多,不确定性给运营带来隐患。在NetScaler常见的配置如健康检查,HA等,在Nginx上的配置的实现相对复杂。
3.策略灵活度方
第11章 动画效果(下)
onestopweb
动画
index.html
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/
FAQ - SAP BW BO roadmap
blueoxygen
BO BW
http://www.sdn.sap.com/irj/boc/business-objects-for-sap-faq
Besides, I care that how to integrate tightly.
By the way, for BW consultants, please just focus on Query Designer which i
关于java堆内存溢出的几种情况
tomcat_oracle
java jvm jdk thread
【情况一】:
java.lang.OutOfMemoryError: Java heap space:这种是java堆内存不够,一个原因是真不够,另一个原因是程序中有死循环; 如果是java堆内存不够的话,可以通过调整JVM下面的配置来解决: <jvm-arg>-Xms3062m</jvm-arg> <jvm-arg>-Xmx
Manifest.permission_group权限组
阿尔萨斯
Permission
结构
继承关系
public static final class Manifest.permission_group extends Object
java.lang.Object
android. Manifest.permission_group 常量
ACCOUNTS 直接通过统计管理器访问管理的统计
COST_MONEY可以用来让用户花钱但不需要通过与他们直接牵涉的权限
D