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.oio; 17 18 import org.jboss.netty.channel.Channel; 19 import org.jboss.netty.channel.ChannelPipeline; 20 import org.jboss.netty.channel.ChannelSink; 21 import org.jboss.netty.channel.group.ChannelGroup; 22 import org.jboss.netty.channel.socket.ServerSocketChannel; 23 import org.jboss.netty.channel.socket.ServerSocketChannelFactory; 24 import org.jboss.netty.util.ThreadNameDeterminer; 25 import org.jboss.netty.util.internal.ExecutorUtil; 26 27 import java.util.concurrent.Executor; 28 import java.util.concurrent.Executors; 29 import java.util.concurrent.RejectedExecutionException; 30 31 /** 32 * A {@link ServerSocketChannelFactory} which creates a server-side blocking 33 * I/O based {@link ServerSocketChannel}. It utilizes the good old blocking 34 * I/O API which is known to yield better throughput and latency when there 35 * are relatively small number of connections to serve. 36 * 37 * <h3>How threads work</h3> 38 * <p> 39 * There are two types of threads in a {@link OioServerSocketChannelFactory}; 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 OioServerSocketChannelFactory} manages. 50 * 51 * <h4>Worker threads</h4> 52 * <p> 53 * Each connected {@link Channel} has a dedicated worker thread, just like a 54 * traditional blocking I/O thread model. 55 * 56 * <h3>Life cycle of threads and graceful shutdown</h3> 57 * <p> 58 * All threads are acquired from the {@link Executor}s which were specified 59 * when a {@link OioServerSocketChannelFactory} was created. Boss threads are 60 * acquired from the {@code bossExecutor}, and worker threads are acquired from 61 * the {@code workerExecutor}. Therefore, you should make sure the specified 62 * {@link Executor}s are able to lend the sufficient number of threads. 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 are also 66 * released when the boss and worker threads are released. Therefore, to shut 67 * down a service gracefully, you should do the following: 68 * 69 * <ol> 70 * <li>unbind all channels created by the factory, 71 * <li>close all child channels accepted by the unbound channels, 72 * (these two steps so far is usually done using {@link ChannelGroup#close()})</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 * <h3>Limitation</h3> 81 * <p> 82 * A {@link ServerSocketChannel} created by this factory and its child channels 83 * do not support asynchronous operations. Any I/O requests such as 84 * {@code "write"} will be performed in a blocking manner. 85 * 86 * @apiviz.landmark 87 */ 88 public class OioServerSocketChannelFactory implements ServerSocketChannelFactory { 89 90 final Executor bossExecutor; 91 private final Executor workerExecutor; 92 private final ChannelSink sink; 93 private boolean shutdownExecutor; 94 95 /** 96 * Create a new {@link OioServerSocketChannelFactory} with a {@link Executors#newCachedThreadPool()} 97 * for the boss and worker executor. 98 * 99 * See {@link #OioServerSocketChannelFactory(Executor, Executor)} 100 */ 101 public OioServerSocketChannelFactory() { 102 this(Executors.newCachedThreadPool(), Executors.newCachedThreadPool()); 103 shutdownExecutor = true; 104 } 105 106 /** 107 * Creates a new instance. 108 * 109 * @param bossExecutor 110 * the {@link Executor} which will execute the boss threads 111 * @param workerExecutor 112 * the {@link Executor} which will execute the I/O worker threads 113 */ 114 public OioServerSocketChannelFactory( 115 Executor bossExecutor, Executor workerExecutor) { 116 this(bossExecutor, workerExecutor, null); 117 } 118 119 /** 120 * Creates a new instance. 121 * 122 * @param bossExecutor 123 * the {@link Executor} which will execute the boss threads 124 * @param workerExecutor 125 * the {@link Executor} which will execute the I/O worker threads 126 * @param determiner 127 * the {@link ThreadNameDeterminer} to set the thread names. 128 */ 129 public OioServerSocketChannelFactory(Executor bossExecutor, Executor workerExecutor, 130 ThreadNameDeterminer determiner) { 131 if (bossExecutor == null) { 132 throw new NullPointerException("bossExecutor"); 133 } 134 if (workerExecutor == null) { 135 throw new NullPointerException("workerExecutor"); 136 } 137 this.bossExecutor = bossExecutor; 138 this.workerExecutor = workerExecutor; 139 sink = new OioServerSocketPipelineSink(workerExecutor, determiner); 140 } 141 142 public ServerSocketChannel newChannel(ChannelPipeline pipeline) { 143 return new OioServerSocketChannel(this, pipeline, sink); 144 } 145 146 public void shutdown() { 147 if (shutdownExecutor) { 148 ExecutorUtil.shutdownNow(bossExecutor); 149 ExecutorUtil.shutdownNow(workerExecutor); 150 } 151 } 152 153 public void releaseExternalResources() { 154 ExecutorUtil.shutdownNow(bossExecutor); 155 ExecutorUtil.shutdownNow(workerExecutor); 156 } 157 }