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