文章目录
- new Server()
- 初始化线程池
- 初始化ServerConnector
- 初始化ScheduledExecutorScheduler
- 初始化ByteBufferPool
- 维护ConnectionFactory
- 取得可用CPU数量
- 更新acceptor数量
- 创建acceptor线程组
- 初始化ServerConnectorManager
- 设置port
- 关联Sever和Connector
- Server.start()
- Http请求
本次笔记是对jetty,一个servlet容器,一个httpServer,内部的实现,因为代码量量比较多,不会从架构的方向介绍,作为一个servlet容器,不会介绍如何使用jsp,而是通过多线程的方向,从代码触发,看究竟是如何提高并发量的。
综上本次笔记并没有对jetty具体是如何用的做介绍,主要是学习jetty高并发的处理手段。
new Server()
public Server(@Name("port")int port) {
this((ThreadPool)null);
ServerConnector connector=new ServerConnector(this); connector.setPort(port);
setConnectors(new Connector[]{connector});
}
初始化线程池
public Server(@Name("threadpool") ThreadPool pool) {
_threadPool=pool!=null?pool:new QueuedThreadPool(); addBean(_threadPool);
setServer(this);
}
QueuedThreadPool
并不是初始化jdk的线程池,而是new自己的QueuedThreadPool,QueuedThreadPool实现了SizedThreadPool
execute()方法
@Override
public void execute(Runnable job) {
if (!isRunning() || !_jobs.offer(job)) {
LOG.warn("{} rejected {}", this, job);
throw new RejectedExecutionException(job.toString()); }
else {
// Make sure there is at least one thread executing the job. if (getThreads() == 0)
startThreads(1);
}
}
这里的核心是将任务压入队列 : _jobs.offer(job)
BlockingQueue
这里的_jobs是BlockingQueue,保存线程池执行的所有的任务。
将任务推入
BlockingQueue org.eclipse.jetty.util.thread.QueuedThreadPool._jobs
BlockingQueue不是一个高性能的,所以execute不太可能被非常频繁的调用。
初始化ServerConnector
处理一些http的连接以及NIO、Selector一些的东西。
HTTP connector using NIO ByteChannels and Selectors
继承自 AbstractConnector
初始化ScheduledExecutorScheduler
调度器
based on JDK’s {@link ScheduledThreadPoolExecutor}.
有些任务是需要每隔一段时间执行一次的,比如每一分钟需要检查一些东西,诸如此类的任务就是由schedule来调度的。
初始化ByteBufferPool
ByteBuffer是一个数组,分为两种_indirect ByteBuffer(分配在堆当中)和_directByteBuffer(分配在内存当中),相当于一个对象池,这其中的byteBuffer实际上是可以复用的。对象池的好处就是可以减少gc,减少new。在java中new是经过了绝对的优化的,性能还是比较高,关键是回收,新生代的回收会更加的频繁。如果自己写一个线程池,还不如通过new、gc的方式,因为线程池必须是线程安全的,所以好多线程在用,拿到对象和归还对象的时候必须是线程安全的,如果简单的加synchronize,除非是特别大的超级对象,性能可能比new,好一点,否则,如果用synchronize构造的线程池性能还不如new出来的。
ArrayByteBufferPool
普通对象池中的对象都是对立的,我去拿任何一个对象都是一样的,都是等价的,但是ByteBufferPool不同,因为你可能需要2k的bytebuffer,也可能需要一个2m的bytebuffer
public ArrayByteBufferPool(int minSize, int increment, int maxSize)
public ArrayByteBufferPool() {
this(0,1024,64*1024);
}
_direct=new Bucket[maxSize/increment];
_indirect=new Bucket[maxSize/increment];
minSize其实大小(最小的容量),increment增量,maxSize最大的大小
结构
Bucket理解为篮子,一个篮子里面只放一中大小的buffer
因为1-63k不可能每一个都有,也许系统中只需要32k,这个时候1k、2k等等都不需要,所以有一个延迟加载的功能
_direct Bucket数组 _indirect Bucket数组
为每一个大小,新建一个Bucket 但不初始化ByteBuffer
int size=0;
for (int i=0;i<_direct.length;i++) {
size+=_inc;
_direct[i]=new Bucket(size);
_indirect[i]=new Bucket(size);
}
一个Bucekt存放大小相同的所有的ByteBuffer
_size
bytebuffer大小
_queue
public final Queue _queue= new ConcurrentLinkedQueue<>();
初始化ByteBuPool的时候所有的Bucket都创建一遍,但是Bucket里面的Queue里面的内容都是延迟加载的
acquire
请求线程池。
public ByteBuffer acquire(int size, boolean direct)
size多大的内存,direct直接内存还是堆。
取得合适的Bucket 每个Bucket的大小不同,这里找到最合适的,取最接近的Buket大小
Bucket bucket = bucketFor(size,direct);
从Bucket中取得ByteBuffer
ByteBuffer buffer = bucket==null?null:bucket._queue.poll();
不存在则新建
if (buffer == null) {
int capacity = bucket==null?size:bucket._size;
buffer = direct ? BufferUtil.allocateDirect(capacity) : BufferUtil.allocate(capacity);
}
release
用完了释放掉,还给线程池。
public void release(ByteBuffer buffer) {
if (buffer!=null) {
Bucket bucket = bucketFor(buffer.capacity(),buffer.isDirect());
if (bucket!=null)
{
BufferUtil.clear(buffer);
bucket._queue.offer(buffer);
}
}
}
取得合适的Bucket
Bucket bucket = bucketFor(buffer.capacity(),buffer.isDirect());
清空Buffer
BufferUtil.clear(buffer);
归还Pool
bucket._queue.offer(buffer);
例外处理
如果申请的ByteBuffer过大或者过小,无法在POOL中满足,则可以申请成功,但无法归还给POOL。
加入目前只有1-64k的bytebuffer,但是我需要120k大小的bytebuffer,就需要申请。
可以看到上面的require没有的话会创建,但是因为无法归还,所以最后会被gc
总结
通过学习ByteBufferPool,首先是对于所有的对象池都应该无锁的,如果有锁,还不如new出来,第二个就是bucket,因为bytebuffer大小是不确定的,所做的相应的处理。
维护ConnectionFactory
HttpConnectionFactory
用于创建连接, 比如Accept后,需要创建一个表示连接的对象
取得可用CPU数量
int cores = Runtime.getRuntime().availableProcessors();
我的系统中应该使用多少accept线程,应该使用多少个selector线程,都要由cores算出来的,这也就是对于高并发的程序来讲,你必须自适应cpu的数量,否则在2核的cpu上和64核的cpu上,完全没有办法很好的协调,
更新acceptor数量
根据cpu的数量更新acceptor数量
if (acceptors < 0)
acceptors=Math.max(1, Math.min(4,cores/8));
它认为accept的数量应该是比较小的,上面可以发现不会超过四个。
创建acceptor线程组
_acceptors = new Thread[acceptors];
初始化ServerConnectorManager
继承自 SelectorManager
_manager = new ServerConnectorManager(getExecutor(), getScheduler(), selectors>0?selectors:Math.max(1,Math.min(4,Runtime.getRuntime().availableProcessors()/2)));
保存selector线程数量
Math.min(4,Runtime.getRuntime().availableProcessors()/2))
最多也不超过四个
设置port
connector.setPort(port);
关联Sever和Connector
setConnectors(new Connector[]{connector});
Server.start()
org.eclipse.jetty.server.Server
启动web服务器
WebAppContext context = new WebAppContext();
context.setContextPath("/");
context.setResourceBase("./web/"); context.setClassLoader(Thread.currentThread().getContextClassLoader()); server.setHandler(context);
server.start();
设置启动状态
AbstractLifeCycle
private void setStarting() {
if (LOG.isDebugEnabled()) LOG.debug("starting {}",this);
_state = __STARTING;
for (Listener listener : _listeners)
listener.lifeCycleStarting(this);
}
启动过程doStart()
Server
启动整个server
protected void doStart() throws Exception
{
//If the Server should be stopped when the jvm exits, register //with the shutdown handler thread.
if (getStopAtShutdown())
ShutdownThread.register(this);
//Register the Server with the handler thread for receiving //remote stop commands
ShutdownMonitor.register(this);
//Start a thread waiting to receive "stop" commands. ShutdownMonitor.getInstance().start(); // initialize
LOG.info("jetty-" + getVersion()); HttpGenerator.setJettyVersion(HttpConfiguration.SERVER_VERSION); MultiException mex=new MultiException();
// check size of thread pool
SizedThreadPool pool = getBean(SizedThreadPool.class); int max=pool==null?-1:pool.getMaxThreads();
int selectors=0;
int acceptors=0;
if (mex.size()==0)
{
for (Connector connector : _connectors) {
if (connector instanceof AbstractConnector) acceptors+=((AbstractConnector)connector).getAcceptors();
if (connector instanceof ServerConnector) selectors+=((ServerConnector)connector).getSelectorManager().getSelectorCount();
}
}
int needed=1+selectors+acceptors; if (max>0 && needed>max)
throw new IllegalStateException(String.format("Insufficient threads: max=%d < needed(acceptors=%d + selectors=%d + request=1)",max,acceptors,selectors));
try {
super.doStart();
}
catch(Throwable e) {
mex.add(e); }
// start connectors last
for (Connector connector : _connectors) {
try {
connector.start();
}catch(Throwable e) {
mex.add(e);
}
}
if (isDumpAfterStart()) dumpStdErr();
mex.ifExceptionThrow();
LOG.info(String.format("Started @%dms",Uptime.getUptime()));
}
注册ShutdownMonitor
远程控制接口
//Register the Server with the handler thread for receiving //remote stop commands
ShutdownMonitor.register(this);
//Start a thread waiting to receive "stop" commands. ShutdownMonitor.getInstance().start(); // initialize
允许远程的将jetty的server关掉。
获取化线程池
// check size of thread pool
SizedThreadPool pool = getBean(SizedThreadPool.class);
QueuedThreadPool
设置selector数量
根据Connector数量进行累计 大部分情况下,只有一个ServerConnector,因为每个connector都有selector
for (Connector connector : _connectors) {
if (connector instanceof AbstractConnector) acceptors+=((AbstractConnector)connector).getAcceptors();
if (connector instanceof ServerConnector) selectors+=((ServerConnector)connector).getSelectorManager().getSelectorCount();
}
累计所有Connector的需求
计算所需的所有线程数量
int needed=1+selectors+acceptors;
如果大于默认的200则中断程序
if (max>0 && needed>max)
throw new IllegalStateException(String.format("Insufficient threads: max=%d < needed(acceptors=%d
+ selectors=%d + request=1)",max,acceptors,selectors));
因为如果光accept和selector线程都超过200说明性能很差了,没有必要再继续往下跑了。
维护Bean
启动QueuedThreadPool
doStart()
startThreads()建立需要的线程
创建线程
Thread thread = newThread(_runnable);
_runnable _jobs中取任务并执行
设置线程的属性
thread.setDaemon(isDaemon()); thread.setPriority(getThreadsPriority()); thread.setName(_name + “-” + thread.getId()); _threads.add(thread);
启动线程 thread.start();
启动WebAppContext
如果需要使用,在此处启动,去做servlet规范所做的内容,这里就不展开讲了。
启动Connector
取得ConnectionFactory
_defaultConnectionFactory = getConnectionFactory(_defaultProtocol);
创建selector线程并启动
for (int i = 0; i < _selectors.length; i++) {
ManagedSelector selector = newSelector(i); _selectors[i] = selector;
selector.start();
execute(new NonBlockingThread(selector));
}
newSelector()
protected ManagedSelector newSelector(int id) {
return new ManagedSelector(id);
}
创建Acceptor线程
//创建几个accept线程
_stopping=new CountDownLatch(_acceptors.length);
for (int i = 0; i < _acceptors.length; i++)
{
Acceptor a = new Acceptor(i);
addBean(a);
//执行
getExecutor().execute(a);
}
Acceptor
设置线程名字
final Thread thread = Thread.currentThread();
String name=thread.getName();
_name=String.format("%s-acceptor-%d@%x-
%s",name,_acceptor,hashCode(),AbstractConnector.this.toString()); thread.setName(_name);
设置优先级
将自己放入_acceptors数组
synchronized (AbstractConnector.this) {
_acceptors[_acceptor] = thread;
}
监听端口
try {
while (isAccepting()) {
try {
accept(_acceptor); }
catch (Throwable e) {
if (isAccepting())
LOG.warn(e);
else LOG.ignore(e);
}
}
} finally {
thread.setName(name);
if (_acceptorPriorityDelta!=0) thread.setPriority(priority);
synchronized (AbstractConnector.this) {
_acceptors[_acceptor] = null;
}
CountDownLatch stopping=_stopping;
if (stopping!=null)
stopping.countDown();
}
ServerConnector.accept()
public void accept(int acceptorID) throws IOException {
ServerSocketChannel serverChannel = _acceptChannel; if (serverChannel != null && serverChannel.isOpen()) {
SocketChannel channel =serverChannel.accept();
accepted(channel);
}
}
在accept的地方等待
没有Acceptor的情况
channle默认是blocking的
如果acceptor数量为0,没有安排线程专门进行accept,则设置为非阻塞模式 若是非0,有专门线程进行accept,因此,为阻塞模式
protected void doStart() throws Exception {
super.doStart();
if (getAcceptors()==0) {
_acceptChannel.configureBlocking(false);
_manager.acceptor(_acceptChannel);
}
}
启动完毕
Http请求
Accept成功
private void accepted(SocketChannel channel) throws IOException {
channel.configureBlocking(false);
Socket socket = channel.socket();
configure(socket);
_manager.accept(channel);
}
设置为非阻塞模式
channel.configureBlocking(false);
配置Socket
Socket socket = channel.socket();
configure(socket);
正式处理
SelectorManager _manager;
_manager.accept(channel);
选择可用的ManagedSelector线程
private ManagedSelector chooseSelector() {
//The ++ increment here not atomic, but does not matter,
// so long as the value chages sometimes,then connections will
//be distributed over the available selectors
long s = _selectorIndex++;
int index = (int)(s % getSelectorCount());
return _selectors[index];
}
这里有一个很有意思的注释,++并不是原子操作,但是不影响,因为这里只需要++就可以了,并不需要每次的数字搜不一样。
ManagedSelector处理
ManagedSelector 是一个线程 封装了Selector 的使用
提交任务
selector.submit(selector.new Accept(channel, attachment));
提交这个处理任务到ManagedSelector:
private final Queue<Runnable> _changes = new ConcurrentArrayQueue<>(); _changes.offer(change);
任务提交到了_chages这个队列中,ConcurrentArrayQueue这个队列是jetty中自己实现的。
ConcurrentArrayQueue
与ConcurrentLinkedQueue相似的性能,但直接保存元素 而不是node,因此需要更少的对象,更少的GC
因为ConcurrentLinkedQueue是一个链表,所以要有node,并且指向下一个node,而ConcurrentArrayQueue不是一个链表,所以需要更少的对象,更少的GC
请求处理
ManagedSelector.run()
while (isRunning())
select();
select()
发现有任务就执行
runChanges();
参见: 提交任务
private void runChanges() {
Runnable change;
while ((change = _changes.poll()) != null)
runChange(change);
}
将_changes中的任务拿出来做执行。
runChange()
change.run();
Accept.run
SelectionKey key = channel.register(_selector, 0, attachment);
EndPoint endpoint = createEndPoint(channel, key);
key.attach(endpoint);
注册selector并将endpoint传给后面。
select()
runChanges实质上是将selector和channel连接到一起,接下来就是等待read或者write准备好
int selected = _selector.select();
处理SelectionKey
当发现任何一个selector可用的时候就会处理SelectionKey
Set<SelectionKey> selectedKeys = _selector.selectedKeys(); for (SelectionKey key : selectedKeys)
{
if (key.isValid()) {
processKey(key);
}
else {
if (debug)
LOG.debug("Selector loop ignoring invalid key for channel {}", key.channel());
Object attachment = key.attachment();
if (attachment instanceof EndPoint)
((EndPoint)attachment).close();
}
}
selectedKeys.clear();
processKey()
private void processKey(SelectionKey key) {
Object attachment = key.attachment(); try
{
if (attachment instanceof SelectableEndPoint) {
((SelectableEndPoint)attachment).onSelected();
}
else if (key.isConnectable()) {
processConnect(key, (Connect)attachment);
}
else if (key.isAcceptable())
{
processAccept(key);
} else {
throw new IllegalStateException(); }
}
catch (CancelledKeyException x) {
LOG.debug("Ignoring cancelled key for channel {}", key.channel());
if (attachment instanceof EndPoint)
closeNoExceptions((EndPoint)attachment);
}
catch (Throwable x) {
LOG.warn("Could not process key for channel " + key.channel(), x);
if (attachment instanceof EndPoint)
closeNoExceptions((EndPoint)attachment);
}
}
onSelected()
@Override
public void onSelected() {
assert _selector.isSelectorThread();
int oldInterestOps = _key.interestOps();
int readyOps = _key.readyOps();
int newInterestOps = oldInterestOps & ~readyOps; setKeyInterests(oldInterestOps, newInterestOps);
updateLocalInterests(readyOps, false);
if (_key.isReadable())
getFillInterest().fillable(); if (_key.isWritable())
getWriteFlusher().completeWrite();
}
会使用新的线程进行HTTP业务处理 (提交到线程池)