1 /* 2 * Copyright 2012 The Netty Project 3 * 4 * The Netty Project licenses this file to you under the Apache License, 5 * version 2.0 (the "License"); you may not use this file except in compliance 6 * with the License. You may obtain a copy of the License at: 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 * License for the specific language governing permissions and limitations 14 * under the License. 15 */ 16 package org.jboss.netty.channel.socket.nio; 17 18 import java.nio.channels.Selector; 19 import java.util.concurrent.Executor; 20 import java.util.concurrent.Executors; 21 import java.util.concurrent.RejectedExecutionException; 22 23 import org.jboss.netty.channel.Channel; 24 import org.jboss.netty.channel.ChannelPipeline; 25 import org.jboss.netty.channel.ChannelSink; 26 import org.jboss.netty.channel.group.ChannelGroup; 27 import org.jboss.netty.channel.socket.ServerSocketChannel; 28 import org.jboss.netty.channel.socket.ServerSocketChannelFactory; 29 import org.jboss.netty.util.ExternalResourceReleasable; 30 import org.jboss.netty.util.internal.ExecutorUtil; 31 32 /** 33 * A {@link ServerSocketChannelFactory} which creates a server-side NIO-based 34 * {@link ServerSocketChannel}. It utilizes the non-blocking I/O mode which 35 * was introduced with NIO to serve many number of concurrent connections 36 * efficiently. 37 * 38 * <h3>How threads work</h3> 39 * <p> 40 * There are two types of threads in a {@link NioServerSocketChannelFactory}; 41 * one is boss thread and the other is worker thread. 42 * 43 * <h4>Boss threads</h4> 44 * <p> 45 * Each bound {@link ServerSocketChannel} has its own boss thread. 46 * For example, if you opened two server ports such as 80 and 443, you will 47 * have two boss threads. A boss thread accepts incoming connections until 48 * the port is unbound. Once a connection is accepted successfully, the boss 49 * thread passes the accepted {@link Channel} to one of the worker 50 * threads that the {@link NioServerSocketChannelFactory} manages. 51 * 52 * <h4>Worker threads</h4> 53 * <p> 54 * One {@link NioServerSocketChannelFactory} can have one or more worker 55 * threads. A worker thread performs non-blocking read and write for one or 56 * more {@link Channel}s in a non-blocking mode. 57 * 58 * <h3>Life cycle of threads and graceful shutdown</h3> 59 * <p> 60 * All threads are acquired from the {@link Executor}s which were specified 61 * when a {@link NioServerSocketChannelFactory} was created. Boss threads are 62 * acquired from the {@code bossExecutor}, and worker threads are acquired from 63 * the {@code workerExecutor}. Therefore, you should make sure the specified 64 * {@link Executor}s are able to lend the sufficient number of threads. 65 * It is the best bet to specify {@linkplain Executors#newCachedThreadPool() a cached thread pool}. 66 * <p> 67 * Both boss and worker threads are acquired lazily, and then released when 68 * there's nothing left to process. All the related resources such as 69 * {@link Selector} are also released when the boss and worker threads are 70 * released. Therefore, to shut down a service gracefully, you should do the 71 * following: 72 * 73 * <ol> 74 * <li>unbind all channels created by the factory, 75 * <li>close all child channels accepted by the unbound channels, and 76 * (these two steps so far is usually done using {@link ChannelGroup#close()})</li> 77 * <li>call {@link #releaseExternalResources()}.</li> 78 * </ol> 79 * 80 * Please make sure not to shut down the executor until all channels are 81 * closed. Otherwise, you will end up with a {@link RejectedExecutionException} 82 * and the related resources might not be released properly. 83 * 84 * @apiviz.landmark 85 */ 86 public class NioServerSocketChannelFactory implements ServerSocketChannelFactory { 87 88 final Executor bossExecutor; 89 private final WorkerPool<NioWorker> workerPool; 90 private final ChannelSink sink; 91 92 /** 93 * Create a new {@link NioServerSocketChannelFactory} using {@link Executors#newCachedThreadPool()} 94 * for the boss and worker. 95 * 96 * See {@link #NioServerSocketChannelFactory(Executor, Executor)} 97 */ 98 public NioServerSocketChannelFactory() { 99 this(Executors.newCachedThreadPool(), Executors.newCachedThreadPool()); 100 } 101 102 /** 103 * Creates a new instance. Calling this constructor is same with calling 104 * {@link #NioServerSocketChannelFactory(Executor, Executor, int)} with 2 * 105 * the number of available processors in the machine. The number of 106 * available processors is obtained by {@link Runtime#availableProcessors()}. 107 * 108 * @param bossExecutor 109 * the {@link Executor} which will execute the boss threads 110 * @param workerExecutor 111 * the {@link Executor} which will execute the I/O worker threads 112 */ 113 public NioServerSocketChannelFactory( 114 Executor bossExecutor, Executor workerExecutor) { 115 this(bossExecutor, workerExecutor, SelectorUtil.DEFAULT_IO_THREADS); 116 } 117 118 /** 119 * Creates a new instance. 120 * 121 * @param bossExecutor 122 * the {@link Executor} which will execute the boss threads 123 * @param workerExecutor 124 * the {@link Executor} which will execute the I/O worker threads 125 * @param workerCount 126 * the maximum number of I/O worker threads 127 */ 128 public NioServerSocketChannelFactory( 129 Executor bossExecutor, Executor workerExecutor, 130 int workerCount) { 131 this(bossExecutor, new NioWorkerPool(workerExecutor, workerCount)); 132 } 133 134 /** 135 * Creates a new instance. 136 * 137 * @param bossExecutor 138 * the {@link Executor} which will execute the boss threads 139 * @param workerPool 140 * the {@link WorkerPool} which will be used to obtain the {@link NioWorker} that execute 141 * the I/O worker threads 142 */ 143 public NioServerSocketChannelFactory( 144 Executor bossExecutor, WorkerPool<NioWorker> workerPool) { 145 if (bossExecutor == null) { 146 throw new NullPointerException("bossExecutor"); 147 } 148 if (workerPool == null) { 149 throw new NullPointerException("workerPool"); 150 } 151 152 this.bossExecutor = bossExecutor; 153 this.workerPool = workerPool; 154 sink = new NioServerSocketPipelineSink(workerPool); 155 } 156 157 public ServerSocketChannel newChannel(ChannelPipeline pipeline) { 158 return new NioServerSocketChannel(this, pipeline, sink); 159 } 160 161 public void releaseExternalResources() { 162 ExecutorUtil.terminate(bossExecutor); 163 if (workerPool instanceof ExternalResourceReleasable) { 164 ((ExternalResourceReleasable) workerPool).releaseExternalResources(); 165 } 166 } 167 }