Jetty线程模型

在近几年的开源Java容器市场上,Tomcat依旧保持在龙头老大的位置,其地位丝毫没有被撼动的迹象。与此同时Tomcat也因为架构臃肿结构复杂而饱受批评。作为Tomcat的另一款替代性的Java容器Jetty要比Tomcat简单很多,Jetty作为内嵌容器被开源社区广泛使用。基于Jetty之上有很多轻量级Java Web框架,比如著名的JFinal就是基于Jetty开发出来的。

Jetty的线程架构模型非常简单,分为acceptors,selectors和workers三个线程池。acceptors负责接受新连接,然后交给selectors处理HTTP消息协议的解包,最后由workers处理请求。前两个线程池采用非阻塞模型,一个线程可以处理很多socket的读写,所以线程池数量较小。

大多数项目,acceptors线程只需要1个,selectors线程配置2~4个足矣。workers是阻塞性的业务逻辑,往往有较多的数据库操作,需要的线程数量较多,具体数量随应用程序的QPS和IO事件占比而定。QPS越高,需要的线程数量越多,IO占比越高,等待的线程数越多,需要的总线程数也越多。

这张图的上半部分,被称之为ServerConnector连接器,一般是一个ServerSocket对应一个ServerConnector。如果服务器要监听多个端口,就会有多个ServerSocket,相应也会有多个ServerConnector。这上半部分Jetty已经给我们做好了,无需操心其内部实现。需要关心的是业务逻辑,在下半部分的Worker线程里运行,服务器启动前将URL的路由规则以及相应的业务处理器注册进去,然后服务器就可以跑起来了。多个ServerConnector共享同一个Server实例。

下面我们写一个最简单的Hello World

import java.io.IOException;
import java.io.PrintWriter;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.eclipse.jetty.server.HttpConnectionFactory;
import org.eclipse.jetty.server.NetworkTrafficServerConnector;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.server.handler.AbstractHandler;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.server.handler.ContextHandlerCollection;
import org.eclipse.jetty.util.thread.QueuedThreadPool;

class BlockChainHandler extends AbstractHandler {

	private String name;

	public BlockChainHandler(String name) {
		this.name = name;
	}

	@Override
	public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response)
			throws IOException, ServletException {
		response.setContentType("text/html; charset=utf-8");
		response.setStatus(HttpServletResponse.SC_OK);
		PrintWriter out = response.getWriter();
		out.printf("<h1>hello %s</h1>", this.name);
		baseRequest.setHandled(true);
	}

}

public class JettyHello {

	public static void main(String[] args) throws Exception {
		int w = 4; // worker threads
		int a = 1; // acceptor threads
		int s = 2; // selector threads
		QueuedThreadPool workers = new QueuedThreadPool(w);
		Server server = new Server(workers);

		Executor executors = Executors.newFixedThreadPool(a + s);
		ServerConnector connector = new NetworkTrafficServerConnector(server, executors, null, null, a, s,
				new HttpConnectionFactory());
		connector.setHost("127.0.0.1");
		connector.setPort(7777);

		server.addConnector(connector);

		ContextHandler btcHandler = new ContextHandler("/btc");
		btcHandler.setHandler(new BlockChainHandler("bitcoin"));

		ContextHandler ethHandler = new ContextHandler("/eth");
		ethHandler.setHandler(new BlockChainHandler("ethereum"));

		ContextHandlerCollection handlers = new ContextHandlerCollection();
		handlers.addHandler(btcHandler);
		handlers.addHandler(ethHandler);

		server.setHandler(handlers);

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

	}

}

在这个例子中我们提供了两个子路由/btc和/eth,分别映射到不同的处理器实例。我们定义了一个连接器,监听本地7777端口。连接器参数中有个HttpConnectionFactory,表示改端口适用HTTP协议,如果要走HTTPS协议,需要使用SslConnectionFactory。除此之外还有ProxyConnectionFactory,在编写代理服务器时需要使用。

注意例子中的线程配置,executors线程池的大小必须大于等于acceptors和selectors数量之和,否则请求会卡住。如果不提供executors参数,acceptors和selectors需要的线程会和workers线程共享线程池,最终实际运行的workers线程数将会偏小。

访问 http://localhost:7777/btc

我们使用eclipse看一下服务器的线程分配情况

可以看到除了main线程之外,还有1个acceptor线程,2个selectors[pool-1-thread-x]线程和4个workers[qtp]线程,其中qtp是QueuedThreadPool的首字母缩写。

Jetty提供了非常多的Handler处理器,可以让我们方便的处理各种请求

  1. ResourceHandler 静态资源处理器
  2. Redirector 重定向处理器
  3. ErrorHandler 错误处理器
  4. InetAccessHandler IP地址黑名单白名单限制处理器
  5. BufferedResponseHandler 输出缓冲处理器
  6. ThreadLimitHandler 限制单个IP的线程数,防止Dos攻击
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值