blob: 8ed6249ec35240679bef497866fc2e46ea2d697e [file] [log] [blame]
Jake Slack03928ae2014-05-13 18:41:56 -07001//
2// ========================================================================
3// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
4// ------------------------------------------------------------------------
5// All rights reserved. This program and the accompanying materials
6// are made available under the terms of the Eclipse Public License v1.0
7// and Apache License v2.0 which accompanies this distribution.
8//
9// The Eclipse Public License is available at
10// http://www.eclipse.org/legal/epl-v10.html
11//
12// The Apache License v2.0 is available at
13// http://www.opensource.org/licenses/apache2.0.php
14//
15// You may elect to redistribute this code under either of these licenses.
16// ========================================================================
17//
18
19package org.eclipse.jetty.server.nio;
20
21import java.io.IOException;
22import java.net.InetSocketAddress;
23import java.net.Socket;
24import java.nio.channels.SelectionKey;
25import java.nio.channels.ServerSocketChannel;
26import java.nio.channels.SocketChannel;
27
28import org.eclipse.jetty.continuation.Continuation;
29import org.eclipse.jetty.io.AsyncEndPoint;
30import org.eclipse.jetty.io.ConnectedEndPoint;
31import org.eclipse.jetty.io.Connection;
32import org.eclipse.jetty.io.EndPoint;
33import org.eclipse.jetty.io.nio.AsyncConnection;
34import org.eclipse.jetty.io.nio.SelectChannelEndPoint;
35import org.eclipse.jetty.io.nio.SelectorManager;
36import org.eclipse.jetty.io.nio.SelectorManager.SelectSet;
37import org.eclipse.jetty.server.AsyncHttpConnection;
38import org.eclipse.jetty.server.Request;
39import org.eclipse.jetty.util.thread.ThreadPool;
40
41/* ------------------------------------------------------------------------------- */
42/**
43 * Selecting NIO connector.
44 * <p>
45 * This connector uses efficient NIO buffers with a non blocking threading model. Direct NIO buffers
46 * are used and threads are only allocated to connections with requests. Synchronization is used to
47 * simulate blocking for the servlet API, and any unflushed content at the end of request handling
48 * is written asynchronously.
49 * </p>
50 * <p>
51 * This connector is best used when there are a many connections that have idle periods.
52 * </p>
53 * <p>
54 * When used with {@link org.eclipse.jetty.continuation.Continuation}, threadless waits are supported.
55 * If a filter or servlet returns after calling {@link Continuation#suspend()} or when a
56 * runtime exception is thrown from a call to {@link Continuation#undispatch()}, Jetty will
57 * will not send a response to the client. Instead the thread is released and the Continuation is
58 * placed on the timer queue. If the Continuation timeout expires, or it's
59 * resume method is called, then the request is again allocated a thread and the request is retried.
60 * The limitation of this approach is that request content is not available on the retried request,
61 * thus if possible it should be read after the continuation or saved as a request attribute or as the
62 * associated object of the Continuation instance.
63 * </p>
64 *
65 * @org.apache.xbean.XBean element="nioConnector" description="Creates an NIO based socket connector"
66 */
67public class SelectChannelConnector extends AbstractNIOConnector
68{
69 protected ServerSocketChannel _acceptChannel;
70 private int _lowResourcesConnections;
71 private int _lowResourcesMaxIdleTime;
72 private int _localPort=-1;
73
74 private final SelectorManager _manager = new ConnectorSelectorManager();
75
76 /* ------------------------------------------------------------------------------- */
77 /**
78 * Constructor.
79 *
80 */
81 public SelectChannelConnector()
82 {
83 _manager.setMaxIdleTime(getMaxIdleTime());
84 addBean(_manager,true);
85 setAcceptors(Math.max(1,(Runtime.getRuntime().availableProcessors()+3)/4));
86 }
87
88 @Override
89 public void setThreadPool(ThreadPool pool)
90 {
91 super.setThreadPool(pool);
92 // preserve start order
93 removeBean(_manager);
94 addBean(_manager,true);
95 }
96
97 /* ------------------------------------------------------------ */
98 @Override
99 public void accept(int acceptorID) throws IOException
100 {
101 ServerSocketChannel server;
102 synchronized(this)
103 {
104 server = _acceptChannel;
105 }
106
107 if (server!=null && server.isOpen() && _manager.isStarted())
108 {
109 SocketChannel channel = server.accept();
110 channel.configureBlocking(false);
111 Socket socket = channel.socket();
112 configure(socket);
113 _manager.register(channel);
114 }
115 }
116
117 /* ------------------------------------------------------------ */
118 public void close() throws IOException
119 {
120 synchronized(this)
121 {
122 if (_acceptChannel != null)
123 {
124 removeBean(_acceptChannel);
125 if (_acceptChannel.isOpen())
126 _acceptChannel.close();
127 }
128 _acceptChannel = null;
129 _localPort=-2;
130 }
131 }
132
133 /* ------------------------------------------------------------------------------- */
134 @Override
135 public void customize(EndPoint endpoint, Request request) throws IOException
136 {
137 request.setTimeStamp(System.currentTimeMillis());
138 endpoint.setMaxIdleTime(_maxIdleTime);
139 super.customize(endpoint, request);
140 }
141
142 /* ------------------------------------------------------------------------------- */
143 @Override
144 public void persist(EndPoint endpoint) throws IOException
145 {
146 AsyncEndPoint aEndp = ((AsyncEndPoint)endpoint);
147 aEndp.setCheckForIdle(true);
148 super.persist(endpoint);
149 }
150
151 /* ------------------------------------------------------------ */
152 public SelectorManager getSelectorManager()
153 {
154 return _manager;
155 }
156
157 /* ------------------------------------------------------------ */
158 public synchronized Object getConnection()
159 {
160 return _acceptChannel;
161 }
162
163 /* ------------------------------------------------------------------------------- */
164 public int getLocalPort()
165 {
166 synchronized(this)
167 {
168 return _localPort;
169 }
170 }
171
172 /* ------------------------------------------------------------ */
173 public void open() throws IOException
174 {
175 synchronized(this)
176 {
177 if (_acceptChannel == null)
178 {
179 // Create a new server socket
180 _acceptChannel = ServerSocketChannel.open();
181 // Set to blocking mode
182 _acceptChannel.configureBlocking(true);
183
184 // Bind the server socket to the local host and port
185 _acceptChannel.socket().setReuseAddress(getReuseAddress());
186 InetSocketAddress addr = getHost()==null?new InetSocketAddress(getPort()):new InetSocketAddress(getHost(),getPort());
187 _acceptChannel.socket().bind(addr,getAcceptQueueSize());
188
189 _localPort=_acceptChannel.socket().getLocalPort();
190 if (_localPort<=0)
191 throw new IOException("Server channel not bound");
192
193 addBean(_acceptChannel);
194 }
195 }
196 }
197
198 /* ------------------------------------------------------------ */
199 @Override
200 public void setMaxIdleTime(int maxIdleTime)
201 {
202 _manager.setMaxIdleTime(maxIdleTime);
203 super.setMaxIdleTime(maxIdleTime);
204 }
205
206 /* ------------------------------------------------------------ */
207 /**
208 * @return the lowResourcesConnections
209 */
210 public int getLowResourcesConnections()
211 {
212 return _lowResourcesConnections;
213 }
214
215 /* ------------------------------------------------------------ */
216 /**
217 * Set the number of connections, which if exceeded places this manager in low resources state.
218 * This is not an exact measure as the connection count is averaged over the select sets.
219 * @param lowResourcesConnections the number of connections
220 * @see #setLowResourcesMaxIdleTime(int)
221 */
222 public void setLowResourcesConnections(int lowResourcesConnections)
223 {
224 _lowResourcesConnections=lowResourcesConnections;
225 }
226
227 /* ------------------------------------------------------------ */
228 /**
229 * @return the lowResourcesMaxIdleTime
230 */
231 @Override
232 public int getLowResourcesMaxIdleTime()
233 {
234 return _lowResourcesMaxIdleTime;
235 }
236
237 /* ------------------------------------------------------------ */
238 /**
239 * Set the period in ms that a connection is allowed to be idle when this there are more
240 * than {@link #getLowResourcesConnections()} connections. This allows the server to rapidly close idle connections
241 * in order to gracefully handle high load situations.
242 * @param lowResourcesMaxIdleTime the period in ms that a connection is allowed to be idle when resources are low.
243 * @see #setMaxIdleTime(int)
244 */
245 @Override
246 public void setLowResourcesMaxIdleTime(int lowResourcesMaxIdleTime)
247 {
248 _lowResourcesMaxIdleTime=lowResourcesMaxIdleTime;
249 super.setLowResourcesMaxIdleTime(lowResourcesMaxIdleTime);
250 }
251
252
253 /* ------------------------------------------------------------ */
254 /*
255 * @see org.eclipse.jetty.server.server.AbstractConnector#doStart()
256 */
257 @Override
258 protected void doStart() throws Exception
259 {
260 _manager.setSelectSets(getAcceptors());
261 _manager.setMaxIdleTime(getMaxIdleTime());
262 _manager.setLowResourcesConnections(getLowResourcesConnections());
263 _manager.setLowResourcesMaxIdleTime(getLowResourcesMaxIdleTime());
264
265 super.doStart();
266 }
267
268 /* ------------------------------------------------------------ */
269 protected SelectChannelEndPoint newEndPoint(SocketChannel channel, SelectSet selectSet, SelectionKey key) throws IOException
270 {
271 SelectChannelEndPoint endp= new SelectChannelEndPoint(channel,selectSet,key, SelectChannelConnector.this._maxIdleTime);
272 endp.setConnection(selectSet.getManager().newConnection(channel,endp, key.attachment()));
273 return endp;
274 }
275
276 /* ------------------------------------------------------------------------------- */
277 protected void endPointClosed(SelectChannelEndPoint endpoint)
278 {
279 connectionClosed(endpoint.getConnection());
280 }
281
282 /* ------------------------------------------------------------------------------- */
283 protected AsyncConnection newConnection(SocketChannel channel,final AsyncEndPoint endpoint)
284 {
285 return new AsyncHttpConnection(SelectChannelConnector.this,endpoint,getServer());
286 }
287
288
289 /* ------------------------------------------------------------ */
290 /* ------------------------------------------------------------ */
291 /* ------------------------------------------------------------ */
292 private final class ConnectorSelectorManager extends SelectorManager
293 {
294 @Override
295 public boolean dispatch(Runnable task)
296 {
297 ThreadPool pool=getThreadPool();
298 if (pool==null)
299 pool=getServer().getThreadPool();
300 return pool.dispatch(task);
301 }
302
303 @Override
304 protected void endPointClosed(final SelectChannelEndPoint endpoint)
305 {
306 SelectChannelConnector.this.endPointClosed(endpoint);
307 }
308
309 @Override
310 protected void endPointOpened(SelectChannelEndPoint endpoint)
311 {
312 // TODO handle max connections and low resources
313 connectionOpened(endpoint.getConnection());
314 }
315
316 @Override
317 protected void endPointUpgraded(ConnectedEndPoint endpoint, Connection oldConnection)
318 {
319 connectionUpgraded(oldConnection,endpoint.getConnection());
320 }
321
322 @Override
323 public AsyncConnection newConnection(SocketChannel channel,AsyncEndPoint endpoint, Object attachment)
324 {
325 return SelectChannelConnector.this.newConnection(channel,endpoint);
326 }
327
328 @Override
329 protected SelectChannelEndPoint newEndPoint(SocketChannel channel, SelectSet selectSet, SelectionKey sKey) throws IOException
330 {
331 return SelectChannelConnector.this.newEndPoint(channel,selectSet,sKey);
332 }
333 }
334}