jetty quick start

最近学习solr的时候发现通过jetty快速启动,对jetty的使用方法进行总结。

jetty 依赖

 

	<dependency>
		  <groupId>org.mortbay.jetty</groupId>
		  <artifactId>jetty</artifactId>
		  <version>6.1.14</version>
	</dependency>

 

 

jetty quick start (org.mortbay.jetty.Server)

方法一:

 

		Server server = new Server(10008);
		ServletHandler handler = new ServletHandler();
		server.setHandler(handler);
		handler.addServletWithMapping(JobAddServlet.class, "/jobadd");
		handler.addServletWithMapping(JobInfoServlet.class, "/jobinfo");
		handler.addServletWithMapping(JobStatusServlet.class, "/jobstatus");
		handler.addServletWithMapping(ProxyServlet.class, "/*");

		server.start();
		server.join();

 这种方法比较简单,ServletHandler全部都封装好了。下面是另外一种更底层的方法,这样也能有更多的控制。

 

方法二:

 

Server server = new Server();
QueuedThreadPool threads = new QueuedThreadPool(3);
threads.setDaemon(true);

SocketConnector conn = new SocketConnector();
conn.setPort(8989);
conn.setMaxIdleTime(3600000);
conn.setLowResourceMaxIdleTime(600000);

server.setThreadPool(threads);
server.setConnectors(new Connector[] { conn });
server.setStopAtShutdown(true);
//Initialize the servlets
Context root = new Context(server, "/jettytest", Context.SESSIONS);
//root.addServlet(Servlet404.class, "/*");
root.addServlet(WaitServlet.class, "/*");

server.start();

Thread.currentThread().join();

 Connector除了SocketConnector更常见的是使用SelectChannelConnector

扫描二维码关注公众号,回复: 1201659 查看本文章

 

 

    SelectChannelConnector conn=new SelectChannelConnector();
    conn.setPort(port);
    conn.setLowResourcesConnections(10240);
    conn.setMaxIdleTime(3600000);
    conn.setLowResourceMaxIdleTime(600000);

 

 

jetty ThreadPool

jetty自己实现了ThreadPool,其实内部实现和j.u.c实现类似,预先创建好Thread,通过dispatch提交一个执行的任务。

 

 

jetty org.mortbay.jetty.Connector

HTTP Connector. Implementations of this interface provide connectors for the HTTP protocol. A connector receives requests (normally from a socket) and calls the handle method of the Handler object. These operations are performed using threads from the ThreadPool set on the connector. When a connector is registered with an instance of Server, then the server will set itself as both the ThreadPool and the Handler. Note that a connector can be used without a Server if a thread pool and handler are directly provided.

前面已经提到两种重要的Connector

1.SocketConnector

Socket Connector. This connector implements a traditional blocking IO and threading model.This Connector should only be used if NIO is not available.

 

2.SelectChannelConnector

Selecting NIO connector. This connector uses efficient NIO buffers with a non blocking threading model. Direct NIO buffers are used and threads are only allocated to connections with requests. Synchronization is used to simulate blocking for the servlet API, and any unflushed content at the end of request handling is written asynchronously.

This connector is best used when there are a many connections that have idle periods.

 

jetty 如何处理请求

从Connector开始着手,SocketConnector两个重要的方法:

 

  1. SocketConnector.open() 。打开socket_serverSocket= new ServerSocket()
  2. SocketConnector.accept() 。接受socket socket = _serverSocket.accept();

 

accept方法的调用顺序

 

代码如下:

 

Socket socket = _serverSocket.accept();
new SocketConnector.Connection(socket).dispatch(); 

 

第一步调用java自带的serverSocket.accept获取到Socket。

第二部 创建一个Connection对象来处理这个socket,这个Connection是SocketConnector的内部类。其dispatch方法实际调用threadPool的dispatch方法,Connection本质是一个任务。看看其run方法:

 

public void run(){
	
	synchronized(_connections)
	{
		_connections.add(this);
	}

	while (isStarted() && !isClosed())
	{
		if (_connection.isIdle())
		{
		    if (getServer().getThreadPool().isLowOnThreads())
		    {
		        int lrmit = getLowResourceMaxIdleTime();
		        if (lrmit>=0 && _sotimeout!= lrmit)
		        {
		            _sotimeout=lrmit;
		            _socket.setSoTimeout(_sotimeout);
		        }
		    }
		}                    
		_connection.handle();
	}
}

内部怎么又有一个_connection对象?这个 _connection是org.mortbay.jetty.HttpConnection的一个实例,在SocketConnector.Connection对象被new时内部创建。

 

 

Jetty org.mortbay.jetty.HttpConnection

 

A HttpConnection represents the connection of a HTTP client to the server and is created by an instance of a Connector. It's prime function is to associate Request and Response instances with a EndPoint.

 

A connection is also the prime mechanism used by jetty to recycle objects without pooling. The Request,Response, HttpParser, HttpGenerator and HttpFields instances are all recycled for the duration of a connection. Where appropriate, allocated buffers are also kept associated with the connection via the parser and/or generator.

 

 

HttpConnection还负责初始化 HttpParser,HttpFields,Request,Response,HttpGenerator,构造函数代码:

 

_parser = new HttpParser(_connector,endpoint,new RequestHandler(),_connector.getHeaderBufferSize(),_connector.getRequestBufferSize());
_requestFields = new HttpFields();
_responseFields = new HttpFields();
_request = new Request(this);
_response = new Response(this);
_generator = new HttpGenerator(_connector,_endp,_connector.getHeaderBufferSize(),_connector.getResponseBufferSize());

这里的Request和Response就是jetty对Servlet API中定义的javax.servlet.HttpServletRequest实现。

 

 

请求过来的调用顺序

写道
request-->SocketConnector.accept()-->new SocketConnector.Connection()-->new HttpConnection()--->QueuedThreadPool.dispatch(this)-->PoolThread.dispatch(Runnable)--->SocketConnector.Connection.run()--->HttpConnection.handle()

看到最后还是交给HttpConnection.handle()来处理这个请求。这里已经注意到了这个处理过程是通过ThreadPoll异步执行的,后面handle阻塞也不会影响到SocketConnector.accept()别的请求。PS: http client仍然会阻塞,因为等待HTTP结果。

 

当Servlet阻塞时,socketConnector.accept()还是会响应到新的socket。

threadPool.dispatch()是异步操作,SocketConnector.accept()的线程不会被阻塞。dispatch方法中会判断是否有空闲线程(_idleList.size()),如果有空闲线程继续用空闲线程。如果没有空闲线程,尝试开启新线程,判断_threads.size()是否小于_maxThreads?如果小于maxThreads创建一个Thread,否则直接返回。

 

这样存在的问题是虽然能响应socket,但实际这些socket永远不会被执行。

yunpeng@yunpeng-duitang:/duitang/dist/sys$ ab -n 10000 -c 500 http://localhost:8989/jettytest/1
yunpeng@yunpeng-duitang:/duitang/dist/sys$ netstat -antp | grep 8989 | wc -l
984

 

一些疑问

问题:SocketConnector.accept()的线程是main线程吗?

答案:当然不是。包括SocketConnector在内的所有Connector实现类都继承于一个AbstractConnector基类,在jetty启动的时候会调用其doStart方法。见stacktrace:

"main" prio=10 tid=0x000000004054b800 nid=0xf9e at breakpoint[0x00007f078256d000]
   java.lang.Thread.State: RUNNABLE
	at org.mortbay.jetty.AbstractConnector.doStart(AbstractConnector.java:279)
	at org.mortbay.jetty.bio.SocketConnector.doStart(SocketConnector.java:147)
	at org.mortbay.component.AbstractLifeCycle.start(AbstractLifeCycle.java:50)
	- locked <0x00000000e18c7b60> (a java.lang.Object)
	at org.mortbay.jetty.Server.doStart(Server.java:235)
	at org.mortbay.component.AbstractLifeCycle.start(AbstractLifeCycle.java:50)
	- locked <0x00000000e187f2a0> (a java.lang.Object)
	at mytest.MyJettyTester.main(MyJettyTester.java:28)

AbstractConnector.doStart()实现:

if (_threadPool==null)
	_threadPool=_server.getThreadPool();
if (_threadPool!=_server.getThreadPool() && (_threadPool instanceof LifeCycle))
	((LifeCycle)_threadPool).start();
    
// Start selector thread
synchronized(this){
    _acceptorThread=new Thread[getAcceptors()];

    for (int i=0;i<_acceptorThread.length;i++)
    {
        if (!_threadPool.dispatch(new Acceptor(i)))
        {
            Log.warn("insufficient maxThreads configured for {}",this);
            break;
        }
    }
}

AbstractConnector.Acceptor.run() 的实现,最终调用accept:

	while (isRunning() && getConnection()!=null){
	  accept(_acceptor); 

	}

可见也是通过threadPool调用的。

问题:serverSocket.setReuseAddress()的作用?

答案:google

 

 

猜你喜欢

转载自san-yun.iteye.com/blog/2012916