(children.length);
Collections.addAll(childrenSet, children);
readonlyChildren = Collections.unmodifiableSet(childrenSet);
}
总结一下上面的初始化步骤(3)中的一些重点:
1)NIOEventLoopGroup的线程池实现其实就是一个NIOEventLoop数组,一个NIOEventLoop可以理解成就是一个线程。
2)所有的NIOEventLoop线程是使用相同的 executor、SelectorProvider、SelectStrategyFactory、RejectedExecutionHandler以及是属于某一个NIOEventLoopGroup的。 这一点从 newChild(executor, args);
方法就可以看出:newChild()的实现是在NIOEventLoopGroup中实现的。
protected EventLoop newChild (Executor executor, Object... args) throws Exception {
return new NioEventLoop(this , executor, (SelectorProvider) args[0 ],
((SelectStrategyFactory) args[1 ]).newSelectStrategy(), (RejectedExecutionHandler) args[2 ]);
}
3)当有IO事件来时,需要从线程池中选择一个线程出来执行,这时候的NioEventLoop选择策略是由GenericEventExecutorChooser实现的, 并调用该类的next() 方法获取到下一个 NioEventLoop.
到了这里线程池的初始化就已经结束了, 基本这部分就只涉及 netty线程池的内容,不涉及到channel 与 channelPipeline和ChannelHandler等内容。
下面的内容就是分析 NioEventLoop的构造器实现了。
2. NioEventLoop
NioEventLoop 继承于 SingleThreadEventLoop; SingleThreadEventLoop 又继承于 SingleThreadEventExecutor;
SingleThreadEventExecutor 是 netty 中对本地线程的抽象, 它内部有一个 Thread thread 属性, 存储了一个本地 Java 线程. 因此我们可以认为, 一个 NioEventLoop 其实和一个特定的线程绑定, 并且在其生命周期内, 绑定的线程都不会再改变。
2.1 NioEventLoop 类层次结构
NioEventLoop 的类层次结构图还是比较复杂的, 不过我们只需要关注几个重要的点即可. 首先 NioEventLoop 的继承链如下:
NioEventLoop -> SingleThreadEventLoop -> SingleThreadEventExecutor -> AbstractScheduledEventExecutor
在 AbstractScheduledEventExecutor 中, Netty 实现了 NioEventLoop 的 schedule 功能, 即我们可以通过调用一个 NioEventLoop 实例的 schedule()方法来运行一些定时任务. 而在 SingleThreadEventLoop 中, 又实现了任务队列的功能, 通过它, 我们可以调用一个 NioEventLoop 实例的 execute() 方法来向任务队列中添加一个 task, 并由 NioEventLoop 进行调度执行.
通常来说, NioEventLoop 肩负着两种任务,: 1)第一个是作为 IO 线程, 执行与 Channel 相关的 IO 操作, 包括 调用 select 等待就绪的 IO 事件、读写数据与数据的处理等;
2)而第二个任务是作为任务队列, 执行 taskQueue 中的任务, 例如用户调用 eventLoop.schedule 提交的定时任务也是这个线程执行的.
2.2 NioEventLoop 的实例化过程
这里还是盗用一下网上的一张示意图(画图实在是太耗时间了,毕竟我懒):
相同的套路,想来分析实例化过程中发生了什么:
对于NioEventLoop的实例化,基本就是在NioEventLoopGroup.newChild() 中调用的,下面先给出源码:
@Override
protected EventLoop newChild (Executor executor, Object... args) throws Exception {
return new NioEventLoop(this , executor, (SelectorProvider) args[0 ],
((SelectStrategyFactory) args[1 ]).newSelectStrategy(), (RejectedExecutionHandler) args[2 ]);
}
从上面函数里面 new NioEventLoop()出发分析实例化过程:
(1)最先调用NioEventLoop 里面的构造函数:
NioEventLoop(NioEventLoopGroup parent, Executor executor, SelectorProvider selectorProvider,
SelectStrategy strategy, RejectedExecutionHandler rejectedExecutionHandler) {
super (parent, executor, false , DEFAULT_MAX_PENDING_TASKS, rejectedExecutionHandler);
if (selectorProvider == null ) {
throw new NullPointerException("selectorProvider" );
}
if (strategy == null ) {
throw new NullPointerException("selectStrategy" );
}
provider = selectorProvider;
selector = openSelector();
selectStrategy = strategy;
}
需要注意的是:构造器里面传入了 NioEventLoopGroup、Executor、SelectorProvider、SelectStrategyFactory、RejectedExecutionHandler。从这里可以看出,一个NioEventLoop属于某一个NioEventLoopGroup, 且处于同一个NioEventLoopGroup下的所有NioEventLoop 公用Executor、SelectorProvider、SelectStrategyFactory和RejectedExecutionHandler。
还有一点需要注意的是,这里的SelectorProvider构造参数传入的是通过在NioEventLoopGroup里面的构造器里面的 SelectorProvider.provider();
方式获取的, 而这个方法返回的是一个单例的SelectorProvider, 所以所有的NioEventLoop公用同一个单例SelectorProvider。
(2)核心的东西说完了,就是调用父类SingleThreadEventLoop的构造器:
protected SingleThreadEventLoop (EventLoopGroup parent, Executor executor,
boolean addTaskWakesUp, int maxPendingTasks,
RejectedExecutionHandler rejectedExecutionHandler) {
super (parent, executor, addTaskWakesUp, maxPendingTasks, rejectedExecutionHandler);
tailTasks = newTaskQueue(maxPendingTasks);
}
这里除了调用父类SingleThreadEventExecutor的构造器以外, 就是实例化了 tailTasks
这个变量; 对于tailTasks在SingleThreadEventLoop属性的定义如下:
private final Queue tailTasks;
队列的数量maxPendingTasks参数默认是SingleThreadEventLoop.DEFAULT_MAX_PENDING_TASK,其实就是Integer.MAX_VALUE; 对于new的这个队列, 其实就是一个LinkedBlockingQueue 无界队列。
(3)再看调用的父类SingleThreadEventExecutor的构造器:
protected SingleThreadEventExecutor (EventExecutorGroup parent, Executor executor,
boolean addTaskWakesUp, int maxPendingTasks,
RejectedExecutionHandler rejectedHandler) {
super (parent);
this .addTaskWakesUp = addTaskWakesUp;
this .maxPendingTasks = Math.max(16 , maxPendingTasks);
this .executor = ObjectUtil.checkNotNull(executor, "executor" );
taskQueue = newTaskQueue(this .maxPendingTasks);
rejectedExecutionHandler = ObjectUtil.checkNotNull(rejectedHandler, "rejectedHandler" );
}
自此,NioEventLoop的实例化过程已经分析完毕。
前面已经分析完了EventLoopGroup和EventLoop,那么有一个问题,我们知道一个EventLoop实际上是对应于一个线程,那么这个EventLoop是什么时候启动的呢?
2.3 NioEventLoop 的启动顺序 以及 将EventLoop 与 Channel 的关联
在前面我们已经知道了, NioEventLoop 本身就是一个 SingleThreadEventExecutor, 因此 NioEventLoop 的启动, 其实就是 NioEventLoop 所绑定的本地 Java 线程的启动。 在NioEventLoop的构造器初始化分析过程中,我们知道,直到SingleThreadEventExecutor我们传入了一个线程执行器 Executor,我想线程的启动就是通过这个线程执行器启动的。
在SingleThreadEventExecutor类中,有一个非常重要的属性 thread,这个线程也就是与NioEventLoop 所绑定的本地 Java 线程。我们看看这个线程是在什么时候初始化和启动的。
通过定位 thread 变量,发现在 doStartThread()
函数中,有一行代码:
thread = Thread . currentThread();
除了这个地方,发现没有其余的地方对thread进行显示的实例化。而且这行代码在
executor.execute(new Runnable() {
}
也就是说在executor执行的一个线程里面, 含义也就是当executor 第一次执行提交的任务创建的线程 赋值给 thread对象。由此可见,thread的启动其实也就是在这里。
通过不断的分析 doStartThread()
函数 的父调用关系,最顶层的调用就是 SingleThreadEventExecutor.execute(Runnable task)
也就是说哪里第一次调用了execute()函数,也就是启动了该NioEventLoop。下面就来分析一下最初是哪里调用了execute()函数,也就是NioEventLoop的启动流程。
在分析NioEventLoop的启动流程之前先来看看EventLoop 与 Channel 怎样关联的?
EventLoop 与 Channel 的关联
Netty 中, 每个 Channel 都有且仅有一个 EventLoop 与之关联, 它们的关联过程如下:
从上图中我们可以看到, 当调用了 AbstractChannel#AbstractUnsafe.register 后, 就完成了 Channel 和 EventLoop 的关联. register 实现如下:
@Override
public final void register (EventLoop eventLoop, final ChannelPromise promise) {
...
AbstractChannel.this .eventLoop = eventLoop;
if (eventLoop.inEventLoop()) {
register0(promise);
} else {
try {
eventLoop.execute(new OneTimeTask() {
@Override
public void run () {
register0(promise);
}
});
} catch (Throwable t) {
...
}
}
}
在 AbstractChannel#AbstractUnsafe.register 中, 会将一个 EventLoop 赋值给 AbstractChannel 内部的 eventLoop 字段, 到这里就完成了 EventLoop 与 Channel 的关联过程.
EventLoop 的启动
根据之前的分析,我们现在的任务就是寻找 在哪里第一次调用了 SingleThreadEventExecutor.execute() 方法。
留心的读者可能已经注意到了, 我们在 EventLoop 与 Channel 的关联 这一小节时, 有提到到在注册 channel 的过程中, 会在 AbstractChannel#AbstractUnsafe.register 中调用 eventLoop.execute 方法, 在 EventLoop 中进行 Channel 注册代码的执行, AbstractChannel#AbstractUnsafe.register 部分代码如下:
if (eventLoop.inEventLoop()) {
register0(promise);
} else {
try {
eventLoop.execute(new OneTimeTask() {
@Override
public void run () {
register0(promise);
}
});
} catch (Throwable t) {
...
}
}
很显然, 一路从 Bootstrap.bind 方法跟踪到 AbstractChannel#AbstractUnsafe.register 方法, 整个代码都是在主线程中运行的, 因此上面的 eventLoop.inEventLoop() 就为 false, 于是进入到 else 分支, 在这个分支中调用了 eventLoop.execute. eventLoop 是一个 NioEventLoop 的实例, 而 NioEventLoop 没有实现 execute 方法, 因此调用的是 SingleThreadEventExecutor.execute()方法:
@Override
public void execute (Runnable task) {
...
boolean inEventLoop = inEventLoop();
if (inEventLoop) {
addTask(task);
} else {
startThread();
addTask(task);
if (isShutdown() && removeTask(task)) {
reject();
}
}
if (!addTaskWakesUp && wakesUpForTask(task)) {
wakeup(inEventLoop);
}
}
我们已经分析过了, inEventLoop == false, 因此执行到 else 分支, 在这里就调用了 startThread() 方法来启动 SingleThreadEventExecutor 内部关联的 Java 本地线程了。
总结一句话, 当 EventLoop.execute 第一次被调用时, 就会触发 startThread() 的调用, 进而导致了 EventLoop 所对应的 Java 线程的启动。
我们将 EventLoop 与 Channel 的关联 小节中的时序图补全后, 就得到了 EventLoop 启动过程的时序图:
2.4 NioEventLoop 启动之后是怎么实现事件循环的?
经过上面的分析我们已经找到了启动NIoEventLoop线程的入口,这里来分析一下NioEventLoop启动之后是如何实现所谓的事件循环机制的呢?
也就是从SingleThreadEventExecutor.execute(Runnable task);
从源码开始分析,对于一些不重要的源码我就直接删除了,只保留核心源码
public void execute (Runnable task) {
/**
* 判断Thread.currentThread()当前线程是不是与NioEventLoop绑定的本地线程;
* 如果Thread.currentThread()== this.thread, 那么只用将execute()方法中的task添加到任务队列中就好;
* 如果Thread.currentThread()== this.thread 返回false, 那就先调用startThread()方法启动本地线程,然后再将task添加到任务队列中.
*/
boolean inEventLoop = inEventLoop();
if (inEventLoop) {
addTask(task);
} else {
startThread();
addTask(task);
if (isShutdown() && removeTask(task)) {
reject();
}
}
if (!addTaskWakesUp && wakesUpForTask(task)) {
wakeup(inEventLoop);
}
}
上面的addTask(task);
函数实际上也就是将task 任务入taskQueue 队列中。然后就是看startThread();
是怎么启动 NioEventLoop.
private void startThread () {
if (STATE_UPDATER.get(this ) == ST_NOT_STARTED) {
if (STATE_UPDATER.compareAndSet(this , ST_NOT_STARTED, ST_STARTED)) {
doStartThread();
}
}
}
再看doStartThread() 是怎么实现的, 这个函数实现依旧比较复杂,我只取出核心的业务逻辑:
private void doStartThread () {
assert thread == null ;
/** 通过executor启动一个新的task, 在task里面启动this.thread线程。*/
executor.execute(new Runnable() {
@Override
public void run () {
thread = Thread.currentThread();
try {
SingleThreadEventExecutor.this .run();
success = true ;
} catch (Throwable t) {
} finally {
}
}
});
}
thread的run() 函数
从上面可知,核心就是调用SingleThreadEventExecutor.this.run();
这是一个抽象函数,在NioEventLoop实现了这个函数,下面依然只给出能说明核心思想的核心源码,不重要的源码被我删除了。
protected void run () {
/** 死循环:NioEventLoop 事件循环的核心就是这里! */
for (;;) {
try {
switch (selectStrategy.calculateStrategy(selectNowSupplier, hasTasks())) {
case SelectStrategy.CONTINUE:
continue ;
case SelectStrategy.SELECT:
select(wakenUp.getAndSet(false ));
if (wakenUp.get()) {
selector.wakeup();
}
default :
}
cancelledKeys = 0 ;
needsToSelectAgain = false ;
final int ioRatio = this .ioRatio;
if (ioRatio == 100 ) {
try {
processSelectedKeys();
} finally {
runAllTasks();
}
} else {
final long ioStartTime = System.nanoTime();
try {
processSelectedKeys();
} finally {
final long ioTime = System.nanoTime() - ioStartTime;
runAllTasks(ioTime * (100 - ioRatio) / ioRatio);
}
}
} catch (Throwable t) {
handleLoopException(t);
}
}
}
上面函数中的一个死循环 for(;;) 就是NioEventLoop事件循环执行机制。死循环中的业务简单点就是: (1)通过调用 select/selectNow 函数,等待 IO 事件; (2)当有IO事件就绪时, 获取事件类型,分别处理这些IO事件,处理IO事件函数调用就是 processSelectedKeys();
下面对上面过程进行详解,分两步:IO事件轮询、IO事件的处理。
IO事件轮询
首先, 在 run() 方法中, 第一步是调用 hasTasks() 方法来判断当前任务队列中是否有任务:
protected boolean hasTasks () {
assert inEventLoop();
return !taskQueue.isEmpty();
}
这个方法很简单, 仅仅是检查了一下 taskQueue 是否为空. 至于 taskQueue 是什么呢, 其实它就是存放一系列的需要由此 EventLoop 所执行的任务列表. 关于 taskQueue, 我们这里暂时不表, 等到后面再来详细分析它.
1)当 taskQueue 不为空时, hasTasks() 就会返回TRUE,那么selectStrategy.calculateStrategy()
的实现里面就会执行selectSupplier.get()
而get()方法里面会调用 selectNow();
执行立即返回当前就绪的IO事件的个数,如果存在IO事件,那么在switch 语句中就会直接执行 default, 直接跳出switch语句,如果不存在,就是返回0, 对应于continue,忽略此次循环。
2)当taskQueue为空时,就会selectStrategy.calculateStrategy()
就会返回SelectStrategy.SELECT, 对用于switch case语句就是执行select()函数,阻塞等待IO事件就绪。
IO事件处理
在 NioEventLoop.run() 方法中, 第一步是通过 select/selectNow 调用查询当前是否有就绪的 IO 事件. 那么当有 IO 事件就绪时, 第二步自然就是处理这些 IO 事件啦.首先让我们来看一下 NioEventLoop.run 中循环的剩余部分(核心部分):
final int ioRatio = this .ioRatio;
if (ioRatio == 100 ) {
processSelectedKeys();
runAllTasks();
} else {
final long ioStartTime = System.nanoTime();
processSelectedKeys();
final long ioTime = System.nanoTime() - ioStartTime;
runAllTasks(ioTime * (100 - ioRatio) / ioRatio);
}
上面列出的代码中, 有两个关键的调用, 第一个是 processSelectedKeys() 调用, 根据字面意思, 我们可以猜出这个方法肯定是查询就绪的 IO 事件, 然后处理它; 第二个调用是 runAllTasks(), 这个方法我们也可以一眼就看出来它的功能就是运行 taskQueue 中的任务.
这里的代码还有一个十分有意思的地方, 即 ioRatio. 那什么是 ioRatio呢? 它表示的是此线程分配给 IO 操作所占的时间比(即运行 processSelectedKeys 耗时在整个循环中所占用的时间). 例如 ioRatio 默认是 50, 则表示 IO 操作和执行 task 的所占用的线程执行时间比是 1 : 1. 当知道了 IO 操作耗时和它所占用的时间比, 那么执行 task 的时间就可以很方便的计算出来了。
当我们设置 ioRate = 70 时, 则表示 IO 运行耗时占比为70%, 即假设某次循环一共耗时为 100ms, 那么根据公式, 我们知道 processSelectedKeys() 方法调用所耗时大概为70ms(即 IO 耗时), 而 runAllTasks() 耗时大概为 30ms(即执行 task 耗时).
当 ioRatio 为 100 时, Netty 就不考虑 IO 耗时的占比, 而是分别调用 processSelectedKeys()、runAllTasks(); 而当 ioRatio 不为 100时, 则执行到 else 分支, 在这个分支中, 首先记录下 processSelectedKeys() 所执行的时间(即 IO 操作的耗时), 然后根据公式, 计算出执行 task 所占用的时间, 然后以此为参数, 调用 runAllTasks().
我们这里先分析一下 processSelectedKeys() 方法调用,源码如下:
private void processSelectedKeys () {
if (selectedKeys != null ) {
processSelectedKeysOptimized();
} else {
processSelectedKeysPlain(selector.selectedKeys());
}
}
这个方法中, 会根据 selectedKeys 字段是否为空, 而分别调用 processSelectedKeysOptimized 或 processSelectedKeysPlain. selectedKeys 字段是在调用 openSelector() 方法时, 根据 JVM 平台的不同, 而有设置不同的值, 在我所调试这个值是不为 null 的. 其实 processSelectedKeysOptimized 方法 processSelectedKeysPlain 没有太大的区别, 为了简单起见, 我们以 processSelectedKeysOptimized 为例分析一下源码的工作流程吧.
private void processSelectedKeysOptimized () {
for (int i = 0 ; i < selectedKeys.size; ++i) {
final SelectionKey k = selectedKeys.keys[i];
selectedKeys.keys[i] = null ;
final Object a = k.attachment();
if (a instanceof AbstractNioChannel) {
processSelectedKey(k, (AbstractNioChannel) a);
} else {
NioTask task = (NioTask) a;
processSelectedKey(k, task);
}
if (needsToSelectAgain) {
selectedKeys.reset(i + 1 );
selectAgain();
i = -1 ;
}
}
}
其实你别看它代码挺多的, 但是关键的点就两个: 迭代 selectedKeys 获取就绪的 IO 事件, 然后为每个事件都调用 processSelectedKey 来处理它。
还有一点需要注意的是, 我们可以调用 selectionKey.attach(object) 给一个 selectionKey 设置一个附加的字段, 然后可以通过 Object attachedObj = selectionKey.attachment() 获取它. 上面代代码正是通过了 k.attachment() 来获取一个附加在 selectionKey 中的对象, 那么这个对象是什么呢? 它又是在哪里设置的呢? 我们再来回忆一下 SocketChannel 是如何注册到 Selector 中的: 在客户端的 Channel 注册过程中, 会有如下调用链:
Bootstrap.initAndRegister ->
AbstractBootstrap.initAndRegister ->
MultithreadEventLoopGroup.register ->
SingleThreadEventLoop.register ->
AbstractUnsafe.register ->
AbstractUnsafe.register0 ->
AbstractNioChannel.doRegister
最后的 AbstractNioChannel.doRegister 方法会调用 SocketChannel.register 方法注册一个 SocketChannel 到指定的 Selector:
@Override
protected void doRegister () throws Exception {
selectionKey = javaChannel().register(eventLoop().selector, 0 , this );
}
特别注意一下 register 的第三个参数, 这个参数是设置 selectionKey 的附加对象的, 和调用 selectionKey.attach(object) 的效果一样. 而调用 register 所传递的第三个参数是 this, 它其实就是一个 NioSocketChannel 的实例. 那么这里就很清楚了, 我们在将 SocketChannel 注册到 Selector 中时, 将 SocketChannel 所对应的 NioSocketChannel 以附加字段的方式添加到了selectionKey 中. 再回到 processSelectedKeysOptimized 方法中, 当我们获取到附加的对象后, 我们就调用 processSelectedKey 来处理这个 IO 事件:
final Object a = k.attachment();
if (a instanceof AbstractNioChannel) {
processSelectedKey(k, (AbstractNioChannel) a);
} else {
@SuppressWarnings ("unchecked" )
NioTask task = (NioTask) a;
processSelectedKey(k, task);
}
processSelectedKey 方法源码如下:
private void processSelectedKey (SelectionKey k, AbstractNioChannel ch) {
final AbstractNioChannel.NioUnsafe unsafe = ch.unsafe();
try {
int readyOps = k.readyOps();
if ((readyOps & SelectionKey.OP_CONNECT) != 0 ) {
int ops = k.interestOps();
ops &= ~SelectionKey.OP_CONNECT;
k.interestOps(ops);
unsafe.finishConnect();
}
if ((readyOps & SelectionKey.OP_WRITE) != 0 ) {
ch.unsafe().forceFlush();
}
if ((readyOps & (SelectionKey.OP_READ | SelectionKey.OP_ACCEPT)) != 0 || readyOps == 0 ) {
unsafe.read();
}
} catch (CancelledKeyException ignored) {
unsafe.close(unsafe.voidPromise());
}
}
这个代码是不是很熟悉啊? 完全是 Java NIO 的 Selector 的那一套处理流程嘛! processSelectedKey 中处理了三个事件, 分别是:
OP_READ, 可读事件, 即 Channel 中收到了新数据可供上层读取. OP_WRITE, 可写事件, 即上层可以向 Channel 写入数据. OP_CONNECT, 连接建立事件, 即 TCP 连接已经建立, Channel 处于 active 状态. 下面我们分别根据这三个事件来看一下 Netty 是怎么处理的吧.
OP_READ 处理
当就绪的 IO 事件是 OP_READ, 代码会调用 unsafe.read() 方法, 即:
if ((readyOps & (SelectionKey.OP_READ | SelectionKey.OP_ACCEPT)) != 0 || readyOps == 0 ) {
unsafe.read();
if (!ch.isOpen()) {
return ;
}
}
unsafe 这个字段, 我们已经和它打了太多的交道了, 在分析Bootstrap 时我们已经对它进行过浓墨重彩地分析了, 最后我们确定了它是一个 NioSocketChannelUnsafe 实例, 负责的是 Channel 的底层 IO 操作. 我们可以利用 Intellij IDEA 提供的 Go To Implementations 功能, 寻找到这个方法的实现. 最后我们发现这个方法没有在 NioSocketChannelUnsafe 中实现, 而是在它的父类 AbstractNioByteChannel 实现的, 它的实现源码如下:
@Override
public final void read () {
...
ByteBuf byteBuf = null ;
int messages = 0 ;
boolean close = false ;
try {
int totalReadAmount = 0 ;
boolean readPendingReset = false ;
do {
byteBuf = allocHandle.allocate(allocator);
int writable = byteBuf.writableBytes();
int localReadAmount = doReadBytes(byteBuf);
...
pipeline.fireChannelRead(byteBuf);
byteBuf = null ;
...
totalReadAmount += localReadAmount;
...
} while (++ messages < maxMessagesPerRead);
pipeline.fireChannelReadComplete();
allocHandle.record(totalReadAmount);
if (close) {
closeOnRead(pipeline);
close = false ;
}
} catch (Throwable t) {
handleReadException(pipeline, byteBuf, t, close);
} finally {
}
}
read() 源码比较长, 我为了篇幅起见, 删除了部分代码, 只留下了主干. 不过我建议读者朋友们自己一定要看一下 read() 源码, 这对理解 Netty 的 EventLoop 十分有帮助. 上面 read 方法其实归纳起来, 可以认为做了如下工作:
分配 ByteBuf
从 SocketChannel 中读取数据;
调用 pipeline.fireChannelRead 发送一个inbound 事件.
前面两点没什么好说的, 第三点 pipeline.fireChannelRead 读者朋友们看到了有没有会心一笑地感觉呢? 反正我看到这里时是有的。 pipeline.fireChannelRead 正好就是 inbound 事件起点. 当调用了 pipeline.fireIN_EVT() 后, 那么就产生了一个 inbound 事件, 此事件会以 head -> customContext -> tail 的方向依次流经 ChannelPipeline 中的各个 handler.
调用了 pipeline.fireChannelRead 后, 就是 ChannelPipeline 中所需要做的工作了。
OP_WRITE 处理
OP_WRITE 可写事件代码如下. 这里代码比较简单, 没有详细分析的必要了.
if ((readyOps & SelectionKey.OP_WRITE) != 0 ) {
ch.unsafe().forceFlush();
}
OP_CONNECT 处理
最后一个事件是 OP_CONNECT, 即 TCP 连接已建立事件.
if ((readyOps & SelectionKey.OP_CONNECT) != 0 ) {
int ops = k.interestOps();
ops &= ~SelectionKey.OP_CONNECT;
k.interestOps(ops);
unsafe.finishConnect();
}
OP_CONNECT 事件的处理中, 只做了两件事情:
正如代码中的注释所言, 我们需要将 OP_CONNECT 从就绪事件集中清除, 不然会一直有 OP_CONNECT 事件.
调用 unsafe.finishConnect() 通知上层连接已建立 .
unsafe.finishConnect() 调用最后会调用到 pipeline().fireChannelActive(), 产生一个 inbound 事件, 通知 pipeline 中的各个 handler TCP 通道已建立(即 ChannelInboundHandler.channelActive 方法会被调用)
到了这里, 我们整个 NioEventLoop 的 IO 操作部分已经了解完了, 接下来的一节我们要重点分析一下 Netty 的任务队列机制.
2.5 netty的任务队列机制
我们已经提到过, 在Netty 中, 一个 NioEventLoop 通常需要肩负起两种任务, 第一个是作为 IO 线程, 处理 IO 操作; 第二个就是作为任务线程, 处理 taskQueue 中的任务. 这一节的重点就是分析一下 NioEventLoop 的任务队列机制的.
任务的添加
普通 Runnable 任务
NioEventLoop 继承于 SingleThreadEventExecutor, 而 SingleThreadEventExecutor 中有一个 Queue taskQueue 字段, 用于存放添加的 Task. 在 Netty 中, 每个 Task 都使用一个实现了 Runnable 接口的实例来表示.
例如当我们需要将一个 Runnable 添加到 taskQueue 中时, 我们可以进行如下操作:
EventLoop eventLoop = channel.eventLoop();
eventLoop.execute(new Runnable() {
@Override
public void run () {
System.out.println("Hello, Netty!" );
}
});
当调用 execute 后, 实际上是调用到了 SingleThreadEventExecutor.execute() 方法, 它的实现如下:
@Override
public void execute (Runnable task) {
if (task == null ) {
throw new NullPointerException("task" );
}
boolean inEventLoop = inEventLoop();
if (inEventLoop) {
addTask(task);
} else {
startThread();
addTask(task);
if (isShutdown() && removeTask(task)) {
reject();
}
}
if (!addTaskWakesUp && wakesUpForTask(task)) {
wakeup(inEventLoop);
}
}
而添加任务的 addTask 方法的源码如下:
protected void addTask (Runnable task) {
if (task == null ) {
throw new NullPointerException("task" );
}
if (isShutdown()) {
reject();
}
taskQueue.add(task);
}
因此实际上, taskQueue 是存放着待执行的任务的队列。
schedule 任务
除了通过 execute 添加普通的 Runnable 任务外, 我们还可以通过调用 eventLoop.scheduleXXX 之类的方法来添加一个定时任务.
EventLoop 中实现任务队列的功能在超类 SingleThreadEventExecutor 实现的, 而 schedule 功能的实现是在 SingleThreadEventExecutor 的父类, 即 AbstractScheduledEventExecutor 中实现的.
在 AbstractScheduledEventExecutor 中, 有以 scheduledTaskQueue 字段:
Queue> scheduledTaskQueue;
scheduledTaskQueue 是一个队列(Queue), 其中存放的元素是 ScheduledFutureTask. 而 ScheduledFutureTask 我们很容易猜到, 它是对 Schedule 任务的一个抽象.
我们来看一下 AbstractScheduledEventExecutor 所实现的 schedule 方法吧:
@Override
public ScheduledFuture schedule (Runnable command, long delay, TimeUnit unit) {
ObjectUtil.checkNotNull(command, "command" );
ObjectUtil.checkNotNull(unit, "unit" );
if (delay < 0 ) {
throw new IllegalArgumentException(
String.format("delay: %d (expected: >= 0)" , delay));
}
return schedule(new ScheduledFutureTask(
this , command, null , ScheduledFutureTask.deadlineNanos(unit.toNanos(delay))));
}
这是其中一个重载的 schedule, 当一个 Runnable 传递进来后, 会被封装为一个 ScheduledFutureTask 对象, 这个对象会记录下这个 Runnable 在何时运行、已何种频率运行等信息. 当构建了 ScheduledFutureTask 后, 会继续调用 另一个重载的 schedule 方法:
ScheduledFuture schedule(final ScheduledFutureTask task) {
if (inEventLoop()) {
scheduledTaskQueue().add(task);
} else {
execute(new OneTimeTask() {
@Override
public void run () {
scheduledTaskQueue().add(task);
}
});
}
return task;
}
在这个方法中, ScheduledFutureTask 对象就会被添加到 scheduledTaskQueue 中了。
任务的执行
当一个任务被添加到 taskQueue 后, 它是怎么被 EventLoop 执行的呢?
让我们回到 NioEventLoop.run() 方法中, 在这个方法里, 会分别调用 processSelectedKeys() 和 runAllTasks() 方法, 来进行 IO 事件的处理和 task 的处理. processSelectedKeys() 方法我们已经分析过了, 下面我们来看一下 runAllTasks() 中到底有什么名堂吧。
runAllTasks 方法有两个重载的方法, 一个是无参数的, 另一个有一个参数的. 首先来看一下无参数的 runAllTasks:
protected boolean runAllTasks () {
fetchFromScheduledTaskQueue();
Runnable task = pollTask();
if (task == null ) {
return false ;
}
for (;;) {
try {
task.run();
} catch (Throwable t) {
logger.warn("A task raised an exception." , t);
}
task = pollTask();
if (task == null ) {
lastExecutionTime = ScheduledFutureTask.nanoTime();
return true ;
}
}
}
我们前面已经提到过, EventLoop 可以通过调用 EventLoop.execute 来将一个 Runnable 提交到 taskQueue 中, 也可以通过调用 EventLoop.schedule 来提交一个 schedule 任务到 scheduledTaskQueue 中. 在此方法的一开始调用的
fetchFromScheduledTaskQueue() 其实就是将 scheduledTaskQueue 中已经可以执行的(即定时时间已到的 schedule 任务) 拿出来并添加到 taskQueue 中, 作为可执行的 task 等待被调度执行,源码如下:
private void fetchFromScheduledTaskQueue () {
if (hasScheduledTasks()) {
long nanoTime = AbstractScheduledEventExecutor.nanoTime();
for (;;) {
Runnable scheduledTask = pollScheduledTask(nanoTime);
if (scheduledTask == null ) {
break ;
}
taskQueue.add(scheduledTask);
}
}
}
接下来 runAllTasks() 方法就会不断调用 task = pollTask() 从 taskQueue 中获取一个可执行的 task, 然后调用它的 run() 方法来运行此 task.
注意, 因为 EventLoop 既需要执行 IO 操作, 又需要执行 task, 因此我们在调用 EventLoop.execute 方法提交任务时, 不要提交耗时任务, 更不能提交一些会造成阻塞的任务, 不然会导致我们的 IO 线程得不到调度, 影响整个程序的并发量。
这里也是为什么用我们自己的线程池隔离一些可能阻塞的业务。
你可能感兴趣的:(netty)
Netty的心跳机制怎么实现的?
java1234_小锋
java java 开发语言
大家好,我是锋哥。今天分享关于【Netty的心跳机制怎么实现的?】面试题。希望对大家有帮助;Netty的心跳机制怎么实现的?Netty的心跳机制主要是通过在客户端和服务器之间定期发送特殊的数据包(比如空消息或自定义的控制消息)来保持连接的活跃状态,并检测网络连接的健康性。Netty并没有内建“心跳机制”,但是它通过IdleStateHandler和ChannelPipeline可以很方便地实现这种
Netty和Websocket的区别
moton2017
websocket 网络协议 网络
Netty和WebSocket没有直接可比性,因为它们在网络编程环境中具有不同的用途。Netty:Netty是一个高性能、事件驱动的网络框架,用于用Java构建网络应用程序。它提供了一组用于处理各种网络协议(例如TCP和UDP)的工具和抽象。Netty通常用于构建需要低延迟、高吞吐量通信的服务器和客户端。Websocket:WebSocket是一种通信协议,可通过单个长期连接提供全双工通信通道。它
【2024博客之星】我的年度技术总结:Netty渡劫指南--从线程暴走到百万长连接,这一年我踩过的坑比写的代码还多
码到三十五
netty解析 java netty
时间过得真快,作为一名十年的技术老鸟,这一年来跟Netty打交道打得不少。今天就聊聊这一年来我跟Netty的那些事儿,还有我在学习它技术原理时的一些总结。导读Netty再相见:捡起来、用起来Netty原理学习:边啃边写变总结Netty实战:干不爬我的终将被我干爬一、为什么选择Netty?二、线程模型:从车祸现场到秋名山车神三、内存管理:从OOM拳皇到内存刺客四、协议设计:从二进制乱码到量子通信五、
Spring Boot 3.4 正式发布,结构化日志!
m0_74823264
面试 学习路线 阿里巴巴 spring boot 后端 java
1从SpringBoot3.3升级到3.41.1RestClient和RestTemplate新增对RestClient和RestTemplate自动配置的支持,可用ReactorNetty的HttpClient或JDK的HttpClient。支持的客户端优先级:ApacheHTTPComponents(HttpComponentsClientHttpRequestFactory)JettyCli
Netty来创建一个TCP服务器,分包上传语音文件的处理
weixin_43833540
tcp/ip 服务器 网络协议 netty
创建一个TCP服务器。我们以在SpringBoot项目中集成Netty来创建一个TCP服务器为例,使用Netty创建一个TCP服务器是常见且可靠的,特别是在需要高性能、低延迟的网络通信时。添加依赖在pom.xml文件中添加Netty的依赖:io.nettynetty-all4.1.68.Final创建Netty服务器配置类创建一个配置类来启动Netty服务器。importio.netty.boot
【檀越剑指大厂--RocketMQ】RocketMQ运维篇
Kwan的解忧杂货铺@新空间代码工作室
s总 檀越剑指大厂 java-rocketmq rocketmq 运维
欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。推荐:kuan的首页,持续学习,不断总结,共同进步,活到老学到老导航檀越剑指大厂系列:全面总结java核心技术点,如集合,jvm,并发编程redis,kafka,Spring,微服务,Netty等常用开发工具系列:罗列常用的开发工具,如IDEA,M
如何用Netty实现一个负载均衡组件
youyouiknow
tech-review java 后端 架构
一、总体架构一个基本的负载均衡组件通常包含以下几个核心模块:服务注册与发现(ServiceRegistry&Discovery):功能:维护可用的后端服务实例列表(例如IP地址和端口)。实现要点:注册:服务实例启动时,将自己注册到注册中心。发现:负载均衡器从注册中心获取服务实例列表。健康检查:定期检查服务实例的健康状态,剔除不健康的实例。可选技术:ZooKeeper,etcd,Consul,Nac
SpringBoot+Netty+WebSocket 实现消息推送
_小趴菜_
springboot websocket spring boot java
关于NettyNetty是一个利用Java的高级网络的能力,隐藏其背后的复杂性而提供一个易于使用的API的客户端/服务器框架。Maven依赖io.nettynetty-all4.1.36.FinalSpringBootApplication启动器中需要new一个NettyServer,并显式调用启动netty。@SpringBootApplicationpublicclassSpringCloud
SpringBoot+Netty+WebSocket 如何实现消息推送
技术琐事
rxjava java java-ee 架构 后端
Netty是一个利用Java的高级网络的能力,隐藏底层的复杂性而提供一个易于使用/使用其服务器的API的客户端框架。基于SpringBoot+MyBatisPlus+Vue&Element实现的后台管理系统+用户小程序,支持RBAC动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能项目地址:https://gitee.com/zhijiantianya/ruoyi-vue-pro
NIO 和 Netty 在 Spring Boot 中的集成与使用
阿乾之铭
java 网络 开发语言
Netty到底是个啥,有啥子作用1.Netty的本质:对NIO的封装NIO的原生问题:Java的NIO提供了非阻塞I/O和多路复用机制,但其使用较为复杂(如Selector、Channel、Buffer的配置和管理)。开发者需要自己处理线程模型、资源管理、协议解析等底层细节,代码冗长且容易出错。Netty的改进:Netty对NIO进行了高级封装,提供了更加易用的API和灵活的抽象层,例如:Chan
SpringBoot集成Netty实战:构建高效TCPUDP通信服务端【物联网开发必备】
m0_74825678
面试 学习路线 阿里巴巴 spring boot 物联网 后端
SpringBoot集成Netty实现TCP/UDP通信协议【优化版】引言在现代物联网(IoT)应用中,设备与服务器之间的实时通信至关重要。Netty作为一个高性能的网络应用框架,与SpringBoot的集成可以简化开发过程,并提高应用性能。本文将详细介绍如何在SpringBoot中集成Netty,实现TCP和UDP通信协议。通讯协议在设计通讯协议时,我们考虑了数据的完整性和命令的明确性。以下是我
Java 中可作为 GC Roots 的对象有哪几种?
码炫课堂-码哥
java面试题 jvm 面试
作者简介:大家好,我是码炫码哥,前中兴通讯、美团架构师,现任某互联网公司CTO,兼职码炫课堂主讲源码系列专题代表作:《jdk源码&多线程&高并发》,《深入tomcat源码解析》,《深入netty源码解析》,《深入dubbo源码解析》,《深入springboot源码解析》,《深入spring源码解析》,《深入redis源码解析》等联系qq:184480602,加我进群,大家一起学习,一起进步,一起对
java netty长连接_Netty实现长连接服务端跟客户端,使用单独的业务线程池,并支持心跳...
漫姐
java netty长连接
Netty实现长连接服务端跟客户端,使用单独的业务线程池,并支持心跳背景前阵子完成过一个系统,对接某交易所接口,通过长连接收发交易报文,并由应用程序发送心跳维持长连接。受限于开发平台的限制,只能采用传统的BIO实现。好在交易量并不大,未出现性能问题,一直稳定运行。但BIO始终是老掉牙的东西,后来做为业余的练习,通过NIO实现了底层的通讯框架。鉴于NIO的epollbug,这次试试通过Netty来实
java netty长连接_用netty实现长连接
大笨妞
java netty长连接
示例代码1:https://github.com/howdyli/netty示例代码2:http://my.oschina.net/bv10000/blog/399388iosasyncsocket示例代码:http://www.superqq.com/blog/2015/04/03/ioskai-fa-zhi-asyncsocketshi-yong-jiao-cheng/长连接服务文章:http
Netty处理字符格式的报错class java.lang.String cannot be cast to class io.netty.buffer.ByteBuf
kkoneone11
java 开发语言
问题背景:由于第一次处理和打印机进行通信的业务,转化格式为ByteBuf的时候报错:classjava.lang.Stringcannotbecasttoclassio.netty.buffer.ByteBuf分析:根据报错大概意思是String类型不能转化成ByteBuf,但是我印象中也没做什么操作,所以大概是在初始化Handler的时候出现了点错误,因此去看代码发现初始化里编解码是不仅有二进制
netty~ 基于netty实现服务端的长连接
17245
NIO~Netty Netty 长连接 心跳检测 ScheduledFuture 消息编码解码
描述socket长连接即服务端不断开客户端channel的连接,客户端需要定时向服务端进行心跳检测,服务端需要将过期未进行心跳检测的socket关闭。服务端关闭过期的channel连接:Netty提供了ScheduledFuture,可以通过ChannelHandlerContext.executor().schedule()创建,支持延时提交,也支持取消任务,为自动关闭提供了一个很好的实现方案。
netty-简易聊天
2401_84046645
程序员 java 开发语言
publicvoidsend(Stringtext){channel.writeAndFlush(Unpooled.copiedBuffer(text.getBytes()));}publicvoidcloseConnect(){send(“bye”);channel.close();}}classMyHandlerextendsChannelInboundHandlerAdapter{@Over
Netty权威指南:Netty总结-高性能与可靠性
Ty_1106
Netty java 网络 rpc
第二十二章高性能之道22.1RPC调用性能模型分析22.1.1传统RPC调用性能差三宗罪:网络传输采用同步阻塞I/O导致经常性阻塞序列化性能差线程模型问题22.1.2I/O通信性能三要素传输:BIO、NIO或者AIO协议:HTTP公有协议,内部私有协议线程:数据报如何读取,Reactor线程模型22.2Netty高性能之道22.2.1异步非阻塞通信I/O多路复用技术22.2.2高效的Reactor
netty4源码阅读与分析---netty线程模型
红尘之一骑
java NIO netty源码阅读与分析
本文主要说下我自己对netty线程模型的理解,以及这样的线程模型的好处。通俗的来讲,netty的线程模型描述的就是老板和员工的故事。老板(通常情况下是一个老板)负责接活,与客户沟通,协调(netty的accept),谈成后(通道建立),他需要从员工中选出一位员工来负责处理后续具体的事宜(worker线程,这里我们有16位员工,编号1-16),员工做事时按照任务的先后顺序进行处理,这样可以避免错乱,
深度长文解析SpringWebFlux响应式框架15个核心组件源码
快乐非自愿
java spring
SpringWebFlux介绍SpringWebFlux是SpringFramework5.0版本引入的一个响应式Web框架,它与SpringMVC并存,提供了一种全新的编程范式,支持异步非阻塞的Web应用开发。WebFlux完全基于响应式编程模型,支持ReactiveStreams规范,可以在诸如Netty、Undertow以及Servlet3.1+容器上运行。WebFlux的核心控制器是Dis
Java网络编程:IO,NIO与Netty
坚持是一种态度
java java 网络编程 Netty Java IO NIO NIO与Netty
Java网络编程:IO,NIO与NettyJava网络编程:IO,NIO与NettyJavaI/O相关概念同步与异步阻塞与非阻塞OIONIOAIOreactor模型proactor模型JavaIO应用场景Netty简介NIO与Netty生产使用Java网络编程:IO,NIO与Netty新公司新项目,项目需要在硬件和软件平台进行信息传递,具体来说使用Netty。硬件和软件使用socket连接,硬件作
java reactor模式例子_Netty Reactor模式实现原理详解
weixin_39923623
java reactor模式例子
在前面的文章中(Reactor模型详解),我们讲解了Reactor模式的各种演变形式,本文主要讲解的则是Netty是如何实现Reactor模式的。这里关于Netty实现的Reactor模式,需要说明的是,其实现的模式如下图所示:对于Netty使用的Reactor模式,其主要特点如下:使用一个线程作为mainReactor,专门用于监听客户端的连接事件,当获取到事件之后就将该事件交由Acceptor
Netty实战二-实现UDP的单播和广播(1)
2401_84048671
程序员 udp 网络协议 网络
publicclassAnswerHandlerextendsSimpleChannelInboundHandler{/应答的具体内容从常量字符串数组中取得,由nextQuote方法随机获取/privatestaticfinalString[]DICTIONARY={“一个男生暗恋一个女生很久了。一天自习课上,男生偷偷的传了小纸条给女生,上面写着“其实我注意你很久了”。不一会儿,女生传了另一张纸条
netty源码解读三(NioEventLoop)
orcharddd_real
netty java netty
NioEventLoop初始化EventExecutor类型的数组数组大小默认为cpu数量的两倍,遍历数组,通过newNioEventLoop(xxx)往数组中添加元素,NioEventLoop继承了EventExecutor;每次需要线程时,执行chooser的next方法从数组中取出一个线程;关键代码打开netty源码,找到example包下的EchoService类,追溯创建boss线程组和
Netty源码分析:NioEventLoopGroup
HelloWorld_EE
netty源码分析 源代码 netty EventLoop
Netty源码分析:NioEventLoopGroup在工作之余,看到自己公司的超哥(俞超)关于Netty的系列博文,讲解的很好,因此,自己在学习之余也跟了下源代码,来了解Netty,也做了相关的笔记,将形成系列博文,这是第一篇。超哥的博文地址在这里:http://www.jianshu.com/p/c5068caab217Netty版本:4.0.23.Final借用超哥的例子,一般服务端的代码如
netty-NioServerSocketChannel和boss NioEventLoopGroup 如何建立关系的
一个人的想法
java netty
一、EventLoopGroupbossGroup=newNioEventLoopGroup();EventLoopGroupworkerGroup=newNioEventLoopGroup();1、boss或者workerNioEventLoopGroup初始化完成了一个NioEventLoop数组(默认机器核数的2倍)所有NioEventLoop持有了同一个Executor调用execute时
经验笔记:RPC与高性能NIO框架
漆黑的莫莫
计算机网络 笔记 rpc nio
RPC与高性能NIO框架的经验笔记引言随着互联网技术的发展,分布式系统逐渐成为了现代软件架构的标准之一。在这种架构中,服务之间的通信变得尤为关键,而RPC(远程过程调用)作为一种服务间通信的方式,因其简便的使用方式和高效的性能表现而受到了广泛的关注。在实现RPC的过程中,选择合适的网络通信框架至关重要,尤其是那些基于NIO(非阻塞I/O)的高性能框架,如Netty。本文将首先介绍RPC的概念及其在
基础学习之——Netty
北欧人写代码
学习 网络 服务器
Netty是一个基于Java的网络通信框架,提供了一种简单、高效、可扩展的方式来开发网络服务器和客户端应用程序。Netty的设计目标是提供一种统一的异步编程模型,使得开发者能够轻松地编写可维护和可重用的代码。Netty的核心组件包括:Channel:网络通信的通道,可以用于读取、写入和关闭操作。EventLoop:负责处理I/O事件,包括接收、发送和处理数据。ChannelHandler:用于处理
Springboot-websocket实现及底层原理
No.Ada
深入项目底层原理 spring boot websocket 后端
引入依赖SpringBoot中的WebSocket依赖于SpringWebFlux模块,使用了ReactorNetty库来实现底层的WebSocket通信。org.springframework.bootspring-boot-starter-websocket服务端配置/***WebSocket配置类*/@ConfigurationpublicclassWebSocketConfig{/***注
【Netty】实战:基于WebSocket的聊天室
马丁的代码日记
netty 聊天室 网络 websocket http
本文将使用Netty快速实现一个聊天室应用,该应用基于WebSocket协议,用户可以在浏览器内聊天。实现过程很简单,就几步。一、处理Http请求packagecn.md.netty.websocket.groupchat;importio.netty.channel.*;importio.netty.handler.codec.http.*;importio.netty.handler.ssl.
ViewController添加button按钮解析。(翻译)
张亚雄
c
<div class="it610-blog-content-contain" style="font-size: 14px"></div>// ViewController.m
// Reservation software
//
// Created by 张亚雄 on 15/6/2.
mongoDB 简单的增删改查
开窍的石头
mongodb
在上一篇文章中我们已经讲了mongodb怎么安装和数据库/表的创建。在这里我们讲mongoDB的数据库操作
在mongo中对于不存在的表当你用db.表名 他会自动统计
下边用到的user是表明,db代表的是数据库
添加(insert):
log4j配置
0624chenhong
log4j
1) 新建java项目
2) 导入jar包,项目右击,properties—java build path—libraries—Add External jar,加入log4j.jar包。
3) 新建一个类com.hand.Log4jTest
package com.hand;
import org.apache.log4j.Logger;
public class
多点触摸(图片缩放为例)
不懂事的小屁孩
多点触摸
多点触摸的事件跟单点是大同小异的,上个图片缩放的代码,供大家参考一下
import android.app.Activity;
import android.os.Bundle;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener
有关浏览器窗口宽度高度几个值的解析
换个号韩国红果果
JavaScript html
1 元素的 offsetWidth 包括border padding content 整体的宽度。
clientWidth 只包括内容区 padding 不包括border。
clientLeft = offsetWidth -clientWidth 即这个元素border的值
offsetLeft 若无已定位的包裹元素
数据库产品巡礼:IBM DB2概览
蓝儿唯美
db2
IBM DB2是一个支持了NoSQL功能的关系数据库管理系统,其包含了对XML,图像存储和Java脚本对象表示(JSON)的支持。DB2可被各种类型的企 业使用,它提供了一个数据平台,同时支持事务和分析操作,通过提供持续的数据流来保持事务工作流和分析操作的高效性。 DB2支持的操作系统
DB2可应用于以下三个主要的平台:
工作站,DB2可在Linus、Unix、Windo
java笔记5
a-john
java
控制执行流程:
1,true和false
利用条件表达式的真或假来决定执行路径。例:(a==b)。它利用条件操作符“==”来判断a值是否等于b值,返回true或false。java不允许我们将一个数字作为布尔值使用,虽然这在C和C++里是允许的。如果想在布尔测试中使用一个非布尔值,那么首先必须用一个条件表达式将其转化成布尔值,例如if(a!=0)。
2,if-els
Web开发常用手册汇总
aijuans
PHP
一门技术,如果没有好的参考手册指导,很难普及大众。这其实就是为什么很多技术,非常好,却得不到普遍运用的原因。
正如我们学习一门技术,过程大概是这个样子:
①我们日常工作中,遇到了问题,困难。寻找解决方案,即寻找新的技术;
②为什么要学习这门技术?这门技术是不是很好的解决了我们遇到的难题,困惑。这个问题,非常重要,我们不是为了学习技术而学习技术,而是为了更好的处理我们遇到的问题,才需要学习新的
今天帮助人解决的一个sql问题
asialee
sql
今天有个人问了一个问题,如下:
type AD value
A
意图对象传递数据
百合不是茶
android 意图Intent Bundle对象数据的传递
学习意图将数据传递给目标活动; 初学者需要好好研究的
1,将下面的代码添加到main.xml中
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http:/
oracle查询锁表解锁语句
bijian1013
oracle object session kill
一.查询锁定的表
如下语句,都可以查询锁定的表
语句一:
select a.sid,
a.serial#,
p.spid,
c.object_name,
b.session_id,
b.oracle_username,
b.os_user_name
from v$process p, v$s
mac osx 10.10 下安装 mysql 5.6 二进制文件[tar.gz]
征客丶
mysql osx
场景:在 mac osx 10.10 下安装 mysql 5.6 的二进制文件。
环境:mac osx 10.10、mysql 5.6 的二进制文件
步骤:[所有目录请从根“/”目录开始取,以免层级弄错导致找不到目录]
1、下载 mysql 5.6 的二进制文件,下载目录下面称之为 mysql5.6SourceDir;
下载地址:http://dev.mysql.com/downl
分布式系统与框架
bit1129
分布式
RPC框架 Dubbo
什么是Dubbo
Dubbo是一个分布式服务框架,致力于提供高性能和透明化的RPC远程服务调用方案,以及SOA服务治理方案。其核心部分包含: 远程通讯: 提供对多种基于长连接的NIO框架抽象封装,包括多种线程模型,序列化,以及“请求-响应”模式的信息交换方式。 集群容错: 提供基于接
那些令人蛋痛的专业术语
白糖_
spring Web SSO IOC
spring
【控制反转(IOC)/依赖注入(DI)】:
由容器控制程序之间的关系,而非传统实现中,由程序代码直接操控。这也就是所谓“控制反转”的概念所在:控制权由应用代码中转到了外部容器,控制权的转移,是所谓反转。
简单的说:对象的创建又容器(比如spring容器)来执行,程序里不直接new对象。
Web
【单点登录(SSO)】:SSO的定义是在多个应用系统中,用户
《给大忙人看的java8》摘抄
braveCS
java8
函数式接口:只包含一个抽象方法的接口
lambda表达式:是一段可以传递的代码
你最好将一个lambda表达式想象成一个函数,而不是一个对象,并记住它可以被转换为一个函数式接口。
事实上,函数式接口的转换是你在Java中使用lambda表达式能做的唯一一件事。
方法引用:又是要传递给其他代码的操作已经有实现的方法了,这时可以使
编程之美-计算字符串的相似度
bylijinnan
java 算法 编程之美
public class StringDistance {
/**
* 编程之美 计算字符串的相似度
* 我们定义一套操作方法来把两个不相同的字符串变得相同,具体的操作方法为:
* 1.修改一个字符(如把“a”替换为“b”);
* 2.增加一个字符(如把“abdd”变为“aebdd”);
* 3.删除一个字符(如把“travelling”变为“trav
上传、下载压缩图片
chengxuyuancsdn
下载
/**
*
* @param uploadImage --本地路径(tomacat路径)
* @param serverDir --服务器路径
* @param imageType --文件或图片类型
* 此方法可以上传文件或图片.txt,.jpg,.gif等
*/
public void upload(String uploadImage,Str
bellman-ford(贝尔曼-福特)算法
comsci
算法 F#
Bellman-Ford算法(根据发明者 Richard Bellman 和 Lester Ford 命名)是求解单源最短路径问题的一种算法。单源点的最短路径问题是指:给定一个加权有向图G和源点s,对于图G中的任意一点v,求从s到v的最短路径。有时候这种算法也被称为 Moore-Bellman-Ford 算法,因为 Edward F. Moore zu 也为这个算法的发展做出了贡献。
与迪科
oracle ASM中ASM_POWER_LIMIT参数
daizj
ASM oracle ASM_POWER_LIMIT 磁盘平衡
ASM_POWER_LIMIT
该初始化参数用于指定ASM例程平衡磁盘所用的最大权值,其数值范围为0~11,默认值为1。该初始化参数是动态参数,可以使用ALTER SESSION或ALTER SYSTEM命令进行修改。示例如下:
SQL>ALTER SESSION SET Asm_power_limit=2;
高级排序:快速排序
dieslrae
快速排序
public void quickSort(int[] array){
this.quickSort(array, 0, array.length - 1);
}
public void quickSort(int[] array,int left,int right){
if(right - left <= 0
C语言学习六指针_何谓变量的地址 一个指针变量到底占几个字节
dcj3sjt126com
C语言
# include <stdio.h>
int main(void)
{
/*
1、一个变量的地址只用第一个字节表示
2、虽然他只使用了第一个字节表示,但是他本身指针变量类型就可以确定出他指向的指针变量占几个字节了
3、他都只存了第一个字节地址,为什么只需要存一个字节的地址,却占了4个字节,虽然只有一个字节,
但是这些字节比较多,所以编号就比较大,
phpize使用方法
dcj3sjt126com
PHP
phpize是用来扩展php扩展模块的,通过phpize可以建立php的外挂模块,下面介绍一个它的使用方法,需要的朋友可以参考下
安装(fastcgi模式)的时候,常常有这样一句命令:
代码如下:
/usr/local/webserver/php/bin/phpize
一、phpize是干嘛的?
phpize是什么?
phpize是用来扩展php扩展模块的,通过phpi
Java虚拟机学习 - 对象引用强度
shuizhaosi888
JAVA虚拟机
本文原文链接:http://blog.csdn.net/java2000_wl/article/details/8090276 转载请注明出处!
无论是通过计数算法判断对象的引用数量,还是通过根搜索算法判断对象引用链是否可达,判定对象是否存活都与“引用”相关。
引用主要分为 :强引用(Strong Reference)、软引用(Soft Reference)、弱引用(Wea
.NET Framework 3.5 Service Pack 1(完整软件包)下载地址
happyqing
.net 下载 framework
Microsoft .NET Framework 3.5 Service Pack 1(完整软件包)
http://www.microsoft.com/zh-cn/download/details.aspx?id=25150
Microsoft .NET Framework 3.5 Service Pack 1 是一个累积更新,包含很多基于 .NET Framewo
JAVA定时器的使用
jingjing0907
java timer 线程 定时器
1、在应用开发中,经常需要一些周期性的操作,比如每5分钟执行某一操作等。
对于这样的操作最方便、高效的实现方式就是使用java.util.Timer工具类。
privatejava.util.Timer timer;
timer = newTimer(true);
timer.schedule(
newjava.util.TimerTask() { public void run()
Webbench
流浪鱼
webbench
首页下载地址 http://home.tiscali.cz/~cz210552/webbench.html
Webbench是知名的网站压力测试工具,它是由Lionbridge公司(http://www.lionbridge.com)开发。
Webbench能测试处在相同硬件上,不同服务的性能以及不同硬件上同一个服务的运行状况。webbench的标准测试可以向我们展示服务器的两项内容:每秒钟相
第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/
windows下制作bat启动脚本.
sanyecao2314
java cmd 脚本 bat
java -classpath C:\dwjj\commons-dbcp.jar;C:\dwjj\commons-pool.jar;C:\dwjj\log4j-1.2.16.jar;C:\dwjj\poi-3.9-20121203.jar;C:\dwjj\sqljdbc4.jar;C:\dwjj\voucherimp.jar com.citsamex.core.startup.MainStart
Java进行RSA加解密的例子
tomcat_oracle
java
加密是保证数据安全的手段之一。加密是将纯文本数据转换为难以理解的密文;解密是将密文转换回纯文本。 数据的加解密属于密码学的范畴。通常,加密和解密都需要使用一些秘密信息,这些秘密信息叫做密钥,将纯文本转为密文或者转回的时候都要用到这些密钥。 对称加密指的是发送者和接收者共用同一个密钥的加解密方法。 非对称加密(又称公钥加密)指的是需要一个私有密钥一个公开密钥,两个不同的密钥的
Android_ViewStub
阿尔萨斯
ViewStub
public final class ViewStub extends View
java.lang.Object
android.view.View
android.view.ViewStub
类摘要: ViewStub 是一个隐藏的,不占用内存空间的视图对象,它可以在运行时延迟加载布局资源文件。当 ViewSt