1 /* 2 * Copyright 2009 Red Hat, Inc. 3 * 4 * Red Hat licenses this file to you under the Apache License, version 2.0 5 * (the "License"); you may not use this file except in compliance with the 6 * 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.internal.ExecutorUtil; 30 31 /** 32 * A {@link ServerSocketChannelFactory} which creates a server-side NIO-based 33 * {@link ServerSocketChannel}. It utilizes the non-blocking I/O mode which 34 * was introduced with NIO to serve many number of concurrent connections 35 * efficiently. 36 * 37 * <h3>How threads work</h3> 38 * <p> 39 * There are two types of threads in a {@link NioServerSocketChannelFactory}; 40 * one is boss thread and the other is worker thread. 41 * 42 * <h4>Boss threads</h4> 43 * <p> 44 * Each bound {@link ServerSocketChannel} has its own boss thread. 45 * For example, if you opened two server ports such as 80 and 443, you will 46 * have two boss threads. A boss thread accepts incoming connections until 47 * the port is unbound. Once a connection is accepted successfully, the boss 48 * thread passes the accepted {@link Channel} to one of the worker 49 * threads that the {@link NioServerSocketChannelFactory} manages. 50 * 51 * <h4>Worker threads</h4> 52 * <p> 53 * One {@link NioServerSocketChannelFactory} can have one or more worker 54 * threads. A worker thread performs non-blocking read and write for one or 55 * more {@link Channel}s in a non-blocking mode. 56 * 57 * <h3>Life cycle of threads and graceful shutdown</h3> 58 * <p> 59 * All threads are acquired from the {@link Executor}s which were specified 60 * when a {@link NioServerSocketChannelFactory} was created. Boss threads are 61 * acquired from the {@code bossExecutor}, and worker threads are acquired from 62 * the {@code workerExecutor}. Therefore, you should make sure the specified 63 * {@link Executor}s are able to lend the sufficient number of threads. 64 * It is the best bet to specify {@linkplain Executors#newCachedThreadPool() a cached thread pool}. 65 * <p> 66 * Both boss and worker threads are acquired lazily, and then released when 67 * there's nothing left to process. All the related resources such as 68 * {@link Selector} are also released when the boss and worker threads are 69 * released. Therefore, to shut down a service gracefully, you should do the 70 * following: 71 * 72 * <ol> 73 * <li>unbind all channels created by the factory, 74 * <li>close all child channels accepted by the unbound channels, and 75 * (these two steps so far is usually done using {@link ChannelGroup#close()})</li> 76 * <li>call {@link #releaseExternalResources()}.</li> 77 * </ol> 78 * 79 * Please make sure not to shut down the executor until all channels are 80 * closed. Otherwise, you will end up with a {@link RejectedExecutionException} 81 * and the related resources might not be released properly. 82 * 83 * @author <a href="http://www.jboss.org/netty/">The Netty Project</a> 84 * @author <a href="http://gleamynode.net/">Trustin Lee</a> 85 * 86 * @version $Rev: 2200 $, $Date: 2010-02-23 14:42:39 +0900 (Tue, 23 Feb 2010) $ 87 * 88 * @apiviz.landmark 89 */ 90 public class NioServerSocketChannelFactory implements ServerSocketChannelFactory { 91 92 final Executor bossExecutor; 93 private final Executor workerExecutor; 94 private final ChannelSink sink; 95 96 /** 97 * Creates a new instance. Calling this constructor is same with calling 98 * {@link #NioServerSocketChannelFactory(Executor, Executor, int)} with 2 * 99 * the number of available processors in the machine. The number of 100 * available processors is obtained by {@link Runtime#availableProcessors()}. 101 * 102 * @param bossExecutor 103 * the {@link Executor} which will execute the boss threads 104 * @param workerExecutor 105 * the {@link Executor} which will execute the I/O worker threads 106 */ 107 public NioServerSocketChannelFactory( 108 Executor bossExecutor, Executor workerExecutor) { 109 this(bossExecutor, workerExecutor, SelectorUtil.DEFAULT_IO_THREADS); 110 } 111 112 /** 113 * Creates a new instance. 114 * 115 * @param bossExecutor 116 * the {@link Executor} which will execute the boss threads 117 * @param workerExecutor 118 * the {@link Executor} which will execute the I/O worker threads 119 * @param workerCount 120 * the maximum number of I/O worker threads 121 */ 122 public NioServerSocketChannelFactory( 123 Executor bossExecutor, Executor workerExecutor, 124 int workerCount) { 125 if (bossExecutor == null) { 126 throw new NullPointerException("bossExecutor"); 127 } 128 if (workerExecutor == null) { 129 throw new NullPointerException("workerExecutor"); 130 } 131 if (workerCount <= 0) { 132 throw new IllegalArgumentException( 133 "workerCount (" + workerCount + ") " + 134 "must be a positive integer."); 135 } 136 this.bossExecutor = bossExecutor; 137 this.workerExecutor = workerExecutor; 138 sink = new NioServerSocketPipelineSink(workerExecutor, workerCount); 139 } 140 141 public ServerSocketChannel newChannel(ChannelPipeline pipeline) { 142 return new NioServerSocketChannel(this, pipeline, sink); 143 } 144 145 public void releaseExternalResources() { 146 ExecutorUtil.terminate(bossExecutor, workerExecutor); 147 } 148 }