Jetty学习【五】 Connector

本文介绍了Jetty的Connector组件,深入讲解了NIO的概念及其应用,并详细剖析了Connector的实现,包括Blocking mode的工作原理以及SelectChannelConnector的具体实现,探讨了非阻塞模式下连接确认的必要性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1、NIO简介

JAVA中传统的IO模式,也就是BIO模式,如果在线程中调用read,而此时如果还没有消息到来,这个线程就会被挂起。为了处理多个连接,就需要启动相应数量的线程,这样才能保证每个线程之间互不打扰,不会因为某个线程的挂起,而导致其他连接得不到处理。



但是这样就会出现另外一个问题:N个工作线程中的大部分线程都因为等待消息而挂起,这些线程由于CPU任务调度算法,而频繁的被唤醒,但是又什么都没做又被挂起。在线程数少的时候,这种情况可以接受。但是当线程数多起来,就会浪费大量的CPU时间,如果按照如下方式计算吞吐量
吞吐量 = 实际处理消息时间 / (实际处理消息时间 + 线程切换总时间)
会发现,吞吐量会很低。正是因为这么多的问题,才会有NIO的诞生。如果想了解NIO的底层实现,可以去看看epoll的实现。这里超出了本文的范畴,不做介绍了~。

来看看NIO是如何工作的。由一个线程(该线程充当了观察者的角色)不断的去查看用户注册的感兴趣事件是否触发(这里感兴趣的事件比如某个连接是否有数据到达),一旦发现有感兴趣的事件到达,就可以将该事件交由工作线程去处理。而这个工作线程可以直接取出数据,或者写入数据,然后做处理。这样每个工作线程都专注于数据的读取和写入,和计算,保证了每次线程切换都是有意义的。按照上面的公式,线程的大部分时间都是用来处理消息,因此吞吐量相应也会提高很多。



下面来看几个术语,

1)Channel,NIO中的通道,相当于Socket连接的抽象,数据的源头或者数据的目的地。并且Channle支持异步I/O。
2)Selector,上面提到的观察者。轮询查找注册在该Selector上的Channel,查看关注的事件是否发生。在同一个Selector上可以注册多个通道,也就是说Selector一次select(),取出的SelectionKey可能归属于不同的Channel。
3)Buffer,和Channel配合使用。从Channel读取数据到Buffer,从Buffer将数据写入Channel。
4)SelectionKey,Selector在某个Channel上关注的事件。


2、NIO应用

Server:

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;

public class Server {
	public static void main(String[] args) {
		try {
			ServerSocketChannel ssc = ServerSocketChannel.open();
			// 绑定地址
			ssc.bind(new InetSocketAddress("127.0.0.1", 8765));
			// 设置非阻塞模式
			ssc.configureBlocking(false);
			
			// acceptor
			final Selector acceptor = Selector.open();
			ssc.register(acceptor, SelectionKey.OP_ACCEPT);
			// selector
			final Selector selector = Selector.open();
			
			// acceptor thread,关注accept事件
			new Thread(new Runnable() {
				
				@Override
				public void run() {
					while (true) {
						int i = 0;
						try {
							i = acceptor.select();
						} catch (IOException e) {
						}
						if (i != 0) {
							Set<SelectionKey> selected = acceptor.selectedKeys();
							Iterator it = selected.iterator();
							while (it.hasNext()) {
								SelectionKey cur = (SelectionKey) it.next();
								it.remove();
								if (cur.isAcceptable()){
									ServerSocketChannel ssc = (ServerSocketChannel) cur.channel();
									try {
										SocketChannel sc = ssc.accept();
										sc.configureBlocking(false);
										sc.register(selector, SelectionKey.OP_READ | SelectionKey.OP_WRITE);
									} catch (IOException e) {
									}
								}
								else{
									System.out.println("error");
								}
									
							}
						}
					}
				}
			}).start();
			
			// selector thread,关注read和write
			new Thread(new Runnable() {
				
				@Override
				public void run() {
					while (true) {
						int i = 0;
						try {
							i = selector.select(1000);
						} catch (IOException e) {
						}
						
						if (i != 0) {
							// 获取关注的事件
							Set<SelectionKey> selected = selector.selectedKeys();
							Iterator it = selected.iterator();
							while (it.hasNext()) {
								SelectionKey cur = (SelectionKey) it.next();
								it.remove();
								if (cur.isReadable()){
									// 获取产生该事件的channel
									SocketChannel sc = (SocketChannel) cur.channel();
									ByteBuffer bb = ByteBuffer.allocate(100);
									try {
										sc.read(bb);
										System.out.println(new String(bb.array()).trim());
									} catch (IOException e) {
									}
								}
								else if(cur.isWritable()){
									System.out.println("writable");
								}
								else{
									System.out.println("error");
								}
									
							}
						}
					}
				}
			}).start();
			
		} catch (IOException e) {
		}

	}
}

启动两个线程,一个用于accept,另一个用于select处理write和read事件。

Client:
/**
 * 
 */

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;

public class Client {

	public static void main(String[] args) throws IOException {
		SocketChannel channel = SocketChannel.open();
		channel.configureBlocking(false);
		Selector selector = Selector.open();
		
		channel.connect(new InetSocketAddress("127.0.0.1", 8765));
		channel.register(selector, SelectionKey.OP_CONNECT);
		while(!channel.finishConnect()){
			System.out.println("Not connected");
		}
		
		ByteBuffer byb = ByteBuffer.wrap("abc".getBytes());
		channel.write(byb);
		
	}

}

客户端连接服务器,并且写入abc字符串。

运行程序,服务器打印abc。

3、Connector的实现

3.1 类图



3.2 什么是Connecto

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值