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.group.ChannelGroup; 26 import org.jboss.netty.channel.socket.ClientSocketChannelFactory; 27 import org.jboss.netty.channel.socket.SocketChannel; 28 import org.jboss.netty.util.ExternalResourceReleasable; 29 import org.jboss.netty.util.internal.ExecutorUtil; 30 31 /** 32 * A {@link ClientSocketChannelFactory} which creates a client-side NIO-based 33 * {@link SocketChannel}. It utilizes the non-blocking I/O mode which was 34 * 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 NioClientSocketChannelFactory}; 40 * one is boss thread and the other is worker thread. 41 * 42 * <h4>Boss thread</h4> 43 * <p> 44 * One {@link NioClientSocketChannelFactory} has one boss thread. It makes 45 * a connection attempt on request. Once a connection attempt succeeds, 46 * the boss thread passes the connected {@link Channel} to one of the worker 47 * threads that the {@link NioClientSocketChannelFactory} manages. 48 * 49 * <h4>Worker threads</h4> 50 * <p> 51 * One {@link NioClientSocketChannelFactory} can have one or more worker 52 * threads. A worker thread performs non-blocking read and write for one or 53 * more {@link Channel}s in a non-blocking mode. 54 * 55 * <h3>Life cycle of threads and graceful shutdown</h3> 56 * <p> 57 * All threads are acquired from the {@link Executor}s which were specified 58 * when a {@link NioClientSocketChannelFactory} was created. A boss thread is 59 * acquired from the {@code bossExecutor}, and worker threads are acquired from 60 * the {@code workerExecutor}. Therefore, you should make sure the specified 61 * {@link Executor}s are able to lend the sufficient number of threads. 62 * It is the best bet to specify {@linkplain Executors#newCachedThreadPool() a cached thread pool}. 63 * <p> 64 * Both boss and worker threads are acquired lazily, and then released when 65 * there's nothing left to process. All the related resources such as 66 * {@link Selector} are also released when the boss and worker threads are 67 * released. Therefore, to shut down a service gracefully, you should do the 68 * following: 69 * 70 * <ol> 71 * <li>close all channels created by the factory usually using 72 * {@link ChannelGroup#close()}, and</li> 73 * <li>call {@link #releaseExternalResources()}.</li> 74 * </ol> 75 * 76 * Please make sure not to shut down the executor until all channels are 77 * closed. Otherwise, you will end up with a {@link RejectedExecutionException} 78 * and the related resources might not be released properly. 79 * 80 * @apiviz.landmark 81 */ 82 public class NioClientSocketChannelFactory implements ClientSocketChannelFactory { 83 84 private static final int DEFAULT_BOSS_COUNT = 1; 85 86 private final Executor bossExecutor; 87 private final WorkerPool<NioWorker> workerPool; 88 private final NioClientSocketPipelineSink sink; 89 90 /** 91 * Creates a new {@link NioClientSocketChannelFactory} which uses {@link Executors#newCachedThreadPool()} 92 * for the worker and boss executors. 93 * 94 * See {@link #NioClientSocketChannelFactory(Executor, Executor)} 95 */ 96 public NioClientSocketChannelFactory() { 97 this(Executors.newCachedThreadPool(), Executors.newCachedThreadPool()); 98 } 99 100 /** 101 * Creates a new instance. Calling this constructor is same with calling 102 * {@link #NioClientSocketChannelFactory(Executor, Executor, int, int)} with 103 * 1 and (2 * the number of available processors in the machine) for 104 * <tt>bossCount</tt> and <tt>workerCount</tt> respectively. The number of 105 * available processors is obtained by {@link Runtime#availableProcessors()}. 106 * 107 * @param bossExecutor 108 * the {@link Executor} which will execute the boss thread 109 * @param workerExecutor 110 * the {@link Executor} which will execute the worker threads 111 */ 112 public NioClientSocketChannelFactory( 113 Executor bossExecutor, Executor workerExecutor) { 114 this(bossExecutor, workerExecutor, DEFAULT_BOSS_COUNT, SelectorUtil.DEFAULT_IO_THREADS); 115 } 116 117 /** 118 * Creates a new instance. Calling this constructor is same with calling 119 * {@link #NioClientSocketChannelFactory(Executor, Executor, int, int)} with 120 * 1 as <tt>bossCount</tt>. 121 * 122 * @param bossExecutor 123 * the {@link Executor} which will execute the boss thread 124 * @param workerExecutor 125 * the {@link Executor} which will execute the worker threads 126 * @param workerCount 127 * the maximum number of I/O worker threads 128 */ 129 public NioClientSocketChannelFactory( 130 Executor bossExecutor, Executor workerExecutor, int workerCount) { 131 this(bossExecutor, workerExecutor, DEFAULT_BOSS_COUNT, workerCount); 132 } 133 134 /** 135 * Creates a new instance. 136 * 137 * @param bossExecutor 138 * the {@link Executor} which will execute the boss thread 139 * @param workerExecutor 140 * the {@link Executor} which will execute the worker threads 141 * @param bossCount 142 * the maximum number of boss threads 143 * @param workerCount 144 * the maximum number of I/O worker threads 145 */ 146 public NioClientSocketChannelFactory( 147 Executor bossExecutor, Executor workerExecutor, 148 int bossCount, int workerCount) { 149 this(bossExecutor, bossCount, new NioWorkerPool(workerExecutor, workerCount)); 150 } 151 152 public NioClientSocketChannelFactory( 153 Executor bossExecutor, int bossCount, 154 WorkerPool<NioWorker> workerPool) { 155 156 if (bossExecutor == null) { 157 throw new NullPointerException("bossExecutor"); 158 } 159 if (workerPool == null) { 160 throw new NullPointerException("workerPool"); 161 } 162 if (bossCount <= 0) { 163 throw new IllegalArgumentException( 164 "bossCount (" + bossCount + ") " + 165 "must be a positive integer."); 166 } 167 168 169 this.bossExecutor = bossExecutor; 170 this.workerPool = workerPool; 171 sink = new NioClientSocketPipelineSink( 172 bossExecutor, bossCount, workerPool); 173 } 174 175 public SocketChannel newChannel(ChannelPipeline pipeline) { 176 return new NioClientSocketChannel(this, pipeline, sink, sink.nextWorker()); 177 } 178 179 public void releaseExternalResources() { 180 ExecutorUtil.terminate(bossExecutor); 181 if (workerPool instanceof ExternalResourceReleasable) { 182 ((ExternalResourceReleasable) workerPool).releaseExternalResources(); 183 } 184 } 185 }