View Javadoc

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 }