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(workerExecutor);
149 }
150 }
151
152 public void releaseExternalResources() {
153 shutdown();
154 ExecutorUtil.shutdownNow(workerExecutor);
155 }
156 }