protected MultithreadEventExecutorGroup(int nThreads, Executor executor,
EventExecutorChooserFactory chooserFactory, Object... args) {
if (nThreads <= 0) {
throw new IllegalArgumentException(String.format("nThreads: %d (expected: > 0)", nThreads));
}
if (executor == null) {
//之前executor设为了null,这里会赋一个值,这个executor的作用是创建新 Thread并运行任务;后面会发现它的作用
executor = new ThreadPerTaskExecutor(newDefaultThreadFactory());
}
//这里会根据threads的值创建若干个EventExecutor
children = new EventExecutor[nThreads];
for (int i = 0; i < nThreads; i ++) {
boolean success = false;
try {
//往里看一下children为何物
children[i] = newChild(executor, args);
success = true;
} catch (Exception e) {
// TODO: Think about if this is a good exception type
throw new IllegalStateException("failed to create a child event loop", e);
} finally {
if (!success) {
.........
}
}
}
//初始了chooser,后面会使用该chooser选择一个EventLoop
chooser = chooserFactory.newChooser(children);
final FutureListener
private ChannelFuture doBind(final SocketAddress localAddress) {
final ChannelFuture regFuture = initAndRegister();
// 注册已经在EventLoop中进行,因此下列方法可能和注册过程并发进行
final Channel channel = regFuture.channel();
if (regFuture.cause() != null) {
return regFuture;
}
// 注册是通过线程池完成,因此这里要判断是否注册完成
// 注册过程可能很快,这里已经完成
if (regFuture.isDone()) {
// At this point we know that the registration was complete and successful.
ChannelPromise promise = channel.newPromise();
// 绑定本地端口
doBind0(regFuture, channel, localAddress, promise);
return promise;
} else {
// Registration future is almost always fulfilled already, but just in case it's not.
// 没有完成注册的话,就注册一个监听器,注册完成后进行回调
final PendingRegistrationPromise promise = new PendingRegistrationPromise(channel);
regFuture.addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
Throwable cause = future.cause();
if (cause != null) {
// Registration on the EventLoop failed so fail the ChannelPromise directly to not cause an
// IllegalStateException once we try to access the EventLoop of the Channel.
promise.setFailure(cause);
} else {
// Registration was successful, so set the correct executor to use.
// See https://github.com/netty/netty/issues/2586
promise.registered();
doBind0(regFuture, channel, localAddress, promise);
}
}
});
return promise;
}
}
//上面已完成Channel的初始化,这里的channel就是 NioServerSocketChannel
void init(Channel channel) throws Exception {
//一些attrs、options、handler以及childAttrs、childOptions、childHandler的设置
.......
//这里就是上面创建的DefaultChannelPipeline
ChannelPipeline p = channel.pipeline();
//之前创建的第二个NioEventLoopGroup(workerGroup)
final EventLoopGroup currentChildGroup = childGroup;
final ChannelHandler currentChildHandler = childHandler;
//pipeline的链表最后加入了一个ChannelHandler
p.addLast(new ChannelInitializer() {
@Override
public void initChannel(Channel ch) throws Exception {
final ChannelPipeline pipeline = ch.pipeline();
ChannelHandler handler = config.handler();
if (handler != null) {
pipeline.addLast(handler);
}
// We add this handler via the EventLoop as the user may have used a ChannelInitializer as handler.
// In this case the initChannel(...) method will only be called after this method returns. Because
// of this we need to ensure we add our handler in a delayed fashion so all the users handler are
// placed in front of the ServerBootstrapAcceptor.
ch.eventLoop().execute(new Runnable() {
@Override
public void run() {
pipeline.addLast(new ServerBootstrapAcceptor(
currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));
}
});
}
});
}
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;
}
// NioServerSocketChannel的eventLoop 赋了值
AbstractChannel.this.eventLoop = eventLoop;
// 系统刚启动时,EventLoopGroup中所有的EventLoop都是未启动状态,EventLoop的thread属性也为null
if (eventLoop.inEventLoop()) {
register0(promise);
} else {
try {
// 由上文可知,execute()方法用来提交任务,启动一个新的EventLoop
// 在此,channel已经和EventLoop产生了联系
eventLoop.execute(new Runnable() {
@Override
public void run() {
register0(promise);
}
});
} catch (Throwable t) {
........
}
}
}
AbstractChannel # AbstractUnsafe # register0()
private void register0(ChannelPromise promise) {
try {
// check if the channel is still open as it could be closed in the mean time when the register
// call was outside of the eventLoop
if (!promise.setUncancellable() || !ensureOpen(promise)) {
return;
}
boolean firstRegistration = neverRegistered;
// JDK的注册:将ServerSocketChannel注册到selector上;有一点要注意,这里注册的时候,并没有注册感兴趣的事件;
// selectionKey = javaChannel().register(eventLoop().selector, 0, this);
doRegister();
neverRegistered = false;
registered = true;
// 到这里注册完成
// Ensure we call handlerAdded(...) before we actually notify the promise. This is needed as the
// user may already fire events through the pipeline in the ChannelFutureListener.
pipeline.invokeHandlerAddedIfNeeded();
safeSetSuccess(promise);
pipeline.fireChannelRegistered();
// Only fire a channelActive if the channel has never been registered. This prevents firing
// multiple channel actives if the channel is deregistered and re-registered.
if (isActive()) {
if (firstRegistration) {
pipeline.fireChannelActive();
} else if (config().isAutoRead()) {
// This channel was registered before and autoRead() is set. This means we need to begin read
// again so that we process inbound data.
//
// See https://github.com/netty/netty/issues/4805
beginRead();
}
}
} catch (Throwable t) {
// Close the channel directly to avoid FD leak.
closeForcibly();
closeFuture.setClosed();
safeSetFailure(promise, t);
}
}
到这里可以看出Netty中完成注册的标识必须满足下面三个条件:
NioServerSocketChannel 的eventLoop 字段赋上了值
这个EventLoop要启动(新建的EventLoop是未启动状态)
JDK级别的ServerSocketChannel 注册到selector上(不一定有事件)
在前面我们准备了一些动作必须等注册完成后才能触发,这里再回顾一下是哪些动作: a. AbstractBootstrap # doBind() 里注册了一个监听器,注册完成后调用; b. DefaultChannelPipeline # addLast() 里注册了一个回调链,也是等注册完成后调用,实际就是调用handlerAdded();
注册后继续做了如下动作:
pipeline.invokeHandlerAddedIfNeeded();
// 设置成功标志,这里会调用listener;前面注册的ChannelFutureListener就会在此调用,进行本地端口的绑定;
safeSetSuccess(promise);
pipeline.fireChannelRegistered();
//
if (isActive()) {
if (firstRegistration) {
pipeline.fireChannelActive();
} else if (config().isAutoRead()) {
beginRead();
}
}
pipeline.invokeHandlerAddedIfNeeded()会走到下面代码
private void callHandlerAddedForAllHandlers() {
final PendingHandlerCallback pendingHandlerCallbackHead;
synchronized (this) {
assert !registered;
// This Channel itself was registered.
registered = true;
pendingHandlerCallbackHead = this.pendingHandlerCallbackHead;
// Null out so it can be GC'ed.
this.pendingHandlerCallbackHead = null;
}
// 这里就是 上文添加的Handler链;ChannelInitializer就是其中之一
PendingHandlerCallback task = pendingHandlerCallbackHead;
while (task != null) {
// 这里会调用到ChannelInitializer的initChannel()
task.execute();
task = task.next;
}
}
Description:插入大量测试数据
use xmpl;
drop procedure if exists mockup_test_data_sp;
create procedure mockup_test_data_sp(
in number_of_records int
)
begin
declare cnt int;
declare name varch
MYSQL的随机抽取实现方法。举个例子,要从tablename表中随机提取一条记录,大家一般的写法就是:SELECT * FROM tablename ORDER BY RAND() LIMIT 1。但是,后来我查了一下MYSQL的官方手册,里面针对RAND()的提示大概意思就是,在ORDER BY从句里面不能使用RAND()函数,因为这样会导致数据列被多次扫描。但是在MYSQL 3.23版本中,