public class HelloWorldServer {
private int port;
public HelloWorldServer(int port){
this.port = port;
}
public void start(){
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup worker = new NioEventLoopGroup();
try{
//group指定ServerBootstrap的parentGroup 为bossGroup
//childGroup为woker
ServerBootstrap serverBootstrap = new ServerBootstrap().group(bossGroup, worker)
//channel调用的是ServerBootstrap的父类AbstractBootstrap,作用是初始化serverBootstrap的channelFactory,
// 里面的参数表示以后创建出来的channel为NioServceSocketChannle对象,channel返回的是serverBootstrap对象
.channel(NioServerSocketChannel.class)
//给serverBootstrap初始化localAddress属性
.localAddress(new InetSocketAddress(port))
//初始化childHandler,返回serverBootstrap
.childHandler(new ChannelInitializer() {
protected void initChannel(SocketChannel ch) throws Exception {
// ch.pipeline().addLast("framer", new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter()));
ch.pipeline().addLast("decoder", new StringDecoder());
ch.pipeline().addLast("encoder", new StringEncoder());
ch.pipeline().addLast(new HelloWorldServerHandler());
};
})
//给serverBootstrap中的option赋值,方法为AbstractBootstrap中的
//option -->new LinkedHashMap, Object>();
.option(ChannelOption.SO_BACKLOG, 128)
//给serverBootstrap中childOption赋值,方法为ServerBootstrap中的
//childOption -->new LinkedHashMap, Object>();
.childOption(ChannelOption.SO_KEEPALIVE, true);
// 绑定端口,开始接收进来的连接
//调用父类AbstractBootstrap中的bind
//bind首先判断group和channelFactory是否为空,根据这个可知,这2个属性为必须的
//最终调用AbstractBootstrap.doBind(localAddress); localAddress为.localAddress方法初始化的
//doBind(final SocketAddress localAddress):
// bind()返回的是DefaultChannelPromise对象
//sync()只是让ChannelFuture挂起
ChannelFuture future = serverBootstrap.bind(port).sync();
System.out.println("Server start listen at " + port );
future.channel().closeFuture().sync();
}catch (Exception ex){
ex.printStackTrace();
}finally {
bossGroup.shutdownGracefully();
worker.shutdownGracefully();
}
}
public static void main(String[] args) throws Exception {
int port;
if (args.length > 0) {
port = Integer.parseInt(args[0]);
} else {
port = 8080;
}
new HelloWorldServer(port).start();
}
}
这是一个启动Netty服务端的一代码,那么接下来进行一行行分析代码:
首先分析创建group:
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup worker = new NioEventLoopGroup();
根据调用的栈关系,可以看到:
最终进入了MultithreadEventExecutorGroup,
//nThreads,不指定的话,默认根据cpu核数的*2,类别线程池理解的话,这是核心线程数
//executor,调度线程工作的线程池,创建正在工作的线程
//chooserFactory,分配线程池的选择器
//args,可能会有2个,一个是maxPendingTasks,填充工作队列,一个是rejectedExecutionHandler,线程池的拒绝策略概念
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 = new ThreadPerTaskExecutor(newDefaultThreadFactory());
}
//当有连接到监听端口时,工作线程
children = new EventExecutor[nThreads];
for (int i = 0; i < nThreads; i ++) {
boolean success = false;
try {
//如果是NioEventLoop,那么最终创建的是SingleThreadEventLoop
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里面
if (!success) {
//创建失败,销毁已经创建成功的线程池
for (int j = 0; j < i; j ++) {
children[j].shutdownGracefully();
}
for (int j = 0; j < i; j ++) {
EventExecutor e = children[j];
try {
while (!e.isTerminated()) {
e.awaitTermination(Integer.MAX_VALUE, TimeUnit.SECONDS);
}
} catch (InterruptedException interrupted) {
// Let the caller handle the interruption.
Thread.currentThread().interrupt();
break;
}
}
}
}
}
//选择分配线程的Executor
//如果是2的幂的话,顺序选择根据 & 选择
//如果是不是的话,则取余数
//会有针对2的幂专门设计一个用 & ,这个细节可以分配线程的效率
chooser = chooserFactory.newChooser(children);
//为children设置监听Future
final FutureListener
代码中嵌入的注释只是比较粗糙的解释了启动服务器调用这些方法的作用,接下来将详细的跟踪代码。
ServerBootstrap serverBootstrap = new ServerBootstrap().group(bossGroup, worker)
进入ServerBootstrap发现有个group方法:
//可知group方法是重载方法
public ServerBootstrap group(EventLoopGroup group) {
return group(group, group);
}
/**
* Set the {@link EventLoopGroup} for the parent (acceptor) and the child (client). These
* {@link EventLoopGroup}'s are used to handle all the events and IO for {@link ServerChannel} and
* {@link Channel}'s.
*/
public ServerBootstrap group(EventLoopGroup parentGroup, EventLoopGroup childGroup) {
super.group(parentGroup);
if (childGroup == null) {
throw new NullPointerException("childGroup");
}
if (this.childGroup != null) {
throw new IllegalStateException("childGroup set already");
}
this.childGroup = childGroup;
return this;
}
private ChannelFuture doBind(final SocketAddress localAddress) {
//初始化ServerBootstrap相关配置,包括但不限于Channel的配置、创建创建Channel的工厂对象等
//创建一个保存并执行任务的线程池,通过PowerOfTwoEventExecutorChooser来选择创建什么类型的线程池
//此时的ChannelFuture 为DefaultChannelPromise.class
//此时是为了返回与bind和connect相关的channel
final ChannelFuture regFuture = initAndRegister();
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;
}
}
final ChannelFuture initAndRegister() {
Channel channel = null;
try {
//通过在channel()中,指定了channelFactory为ReflectiveChannelFactory.class
//此时的channel对象是通过反射创建而来的
//此时的channel是为了bind和connect的时候知道
channel = channelFactory.newChannel();
//给NIO管道初始化
//同时给ChannelPipeline增加默认的ChannelHandler
// ServerBootstrapAcceptor 和ServerBootstrapConfig.handler()
init(channel);
} catch (Throwable t) {
if (channel != null) {
// channel can be null if newChannel crashed (eg SocketException("too many open files"))
channel.unsafe().closeForcibly();
// as the Channel is not registered yet we need to force the usage of the GlobalEventExecutor
return new DefaultChannelPromise(channel, GlobalEventExecutor.INSTANCE).setFailure(t);
}
// as the Channel is not registered yet we need to force the usage of the GlobalEventExecutor
return new DefaultChannelPromise(new FailedChannel(), GlobalEventExecutor.INSTANCE).setFailure(t);
}
//config return ServerBootstrapConfig
//ServerBootstrapConfig.group() 返回的是 bossGroup EventLoopGroup
// 此时的ServerBootstarp对象是否是创建ServerBootstrap的bossGroup
// 疑问: 为啥不直接获取AbstractBootstarp中的group(),而要通过调用方法形式去获取group
// 解答: 根据netty 4.x的class文件中,这行代码是this.group().register(channel);
// 此时是没有走config()获取的
//此时的ChannelFuture 为DefaultChannelPromise.class
//group默认返回的是MultithreadEventLoopGroup.class
//执行register的线程是MultithreadEventLoopExecutorGroup.class
//真正执行register的是一个DefaultChannelPromise,真正注册的其实也是一个ChannelFuture,因为涉及到
//I/O操作,所以都交付给异步线程去操作
ChannelFuture regFuture = config().group().register(channel);
//通过register中获取的线程池,并且往线程池中提交即将要执行IO任务,放入到线程池中同时返回保存任务的对象
if (regFuture.cause() != null) {
if (channel.isRegistered()) {
channel.close();
} else {
channel.unsafe().closeForcibly();
}
}
// If we are here and the promise is not failed, it's one of the following cases:
// 1) If we attempted registration from the event loop, the registration has been completed at this point.
// i.e. It's safe to attempt bind() or connect() now because the channel has been registered.
// 2) If we attempted registration from the other thread, the registration request has been successfully
// added to the event loop's task queue for later execution.
// i.e. It's safe to attempt bind() or connect() now:
// because bind() or connect() will be executed *after* the scheduled registration task is executed
// because register(), bind(), and connect() are all bound to the same thread.
return regFuture;
}
//ServerBootstrap类中的方法
void init(Channel channel) throws Exception {
//里面的options再生成对象的时候创建,是一个linkHashMap
//通过option()方法赋值
final Map, Object> options = options0();
//ServerBootstrap时的channel可以认为是NioServerSocketChannel.class 对象
synchronized (options) {
//给NIO通道初始化相关属性
setChannelOptions(channel, options, logger);
}
// attrs = new LinkedHashMap, Object>();
//如果未指定ServerBootstrap的attrs,此时attrs是为空的默认为空
final Map, Object> attrs = attrs0();
synchronized (attrs) {
for (Entry, Object> e: attrs.entrySet()) {
@SuppressWarnings("unchecked")
AttributeKey key = (AttributeKey) e.getKey();
channel.attr(key).set(e.getValue());
}
}
//piple在创建channel的时候会创建,默认是DefaultChannelPipeline.class
ChannelPipeline p = channel.pipeline();
final EventLoopGroup currentChildGroup = childGroup;
final ChannelHandler currentChildHandler = childHandler;
final Entry, Object>[] currentChildOptions;
final Entry, Object>[] currentChildAttrs;
//里面的options再生成对象的时候创建,是一个linkHashMap,初始里面不存放值
synchronized (childOptions) {
currentChildOptions = childOptions.entrySet().toArray(newOptionArray(0));
}
synchronized (childAttrs) {
currentChildAttrs = childAttrs.entrySet().toArray(newAttrArray(0));
}
//新增默认的ChannelHandler,默认情况下为ServerBootstrapAcceptor
p.addLast(new ChannelInitializer() {
@Override
public void initChannel(final Channel ch) throws Exception {
final ChannelPipeline pipeline = ch.pipeline();
//默认情况下hanler为空,所以ServerBootstrapAcceptor是第一个ChannelHandler
ChannelHandler handler = config.handler();
if (handler != null) {
pipeline.addLast(handler);
}
//ServerBootstrapAcceptor 本质也是ChannelHandler
//所以第一个ChannelHandler是默认的
ch.eventLoop().execute(new Runnable() {
@Override
public void run() {
pipeline.addLast(new ServerBootstrapAcceptor(
ch, currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));
}
});
}
});
}
private static void doBind0(
final ChannelFuture regFuture, final Channel channel,
final SocketAddress localAddress, final ChannelPromise promise) {
// This method is invoked before channelRegistered() is triggered. Give user handlers a chance to set up
// the pipeline in its channelRegistered() implementation.
//此时的ChannelFuture 为DefaultChannelPromise.class
//ServerBootstrap下channel为NioServerSocketChannel
channel.eventLoop().execute(new Runnable() {
@Override
public void run() {
if (regFuture.isSuccess()) {
//bind()作用是将ChannelHandler串成一个串,给ChannelFuture最终遍历去执行
channel.bind(localAddress, promise).addListener(ChannelFutureListener.CLOSE_ON_FAILURE);
} else {
promise.setFailure(regFuture.cause());
}
}
});
}
原文地址:http://www.open-open.com/lib/view/open1346857871615.html
使用Java Mail API来发送邮件也很容易实现,但是最近公司一个同事封装的邮件API实在让我无法接受,于是便打算改用Spring Mail API来发送邮件,顺便记录下这篇文章。 【Spring Mail API】
Spring Mail API都在org.spri