ServerSocketChannel
面向流的侦听套接字的可选通道。
通过调用此类的open方法创建服务器套接字通道。 无法为任意预先存在的ServerSocket创建通道。 新创建的服务器套接字通道已打开但尚未绑定。 尝试调用未绑定的服务器套接字通道的accept方法将导致抛出NotYetBoundException 。 可以通过调用此类定义的bind方法之一来绑定服务器套接字通道。
accept
public abstract SocketChannel accept() throws IOException
接受与此通道套接字的连接。
如果此通道处于非阻塞模式,则如果没有挂起连接,则此方法将立即返回null
。 否则它将无限期地阻塞,直到新连接可用或发生I / O错误。
无论此通道的阻塞模式如何,此方法返回的套接字通道(如果有)都将处于阻塞模式。
serverSocket.bind()方法将ServerSocket类绑定到指定的地址,而ServerSocketChannel类也有bind() 方法,该方法public final ServerSocketChannel bind(SocketAddress local)的作用是将通道的套接字绑定到本地地址并侦听连接。
public abstract ServerSocketChannel bind(SocketAddress local; int backlog)
方法的作用是将通道的套接字绑定到本地地址并侦听连接,通过使用参数backlog来限制客户端连接的数量。
public abstract Socket Channel accept()方法的作用是接受此通道套接字的连接。如果此通道处于非阻塞模式,那么在不存在挂起的连接时,此方法将直接返回nul否则,在新的连接可用或者发生I/O错误之前会无限期地阻塞它。无论此通道的阻塞模式如何,此方法返回的套接字通道(如果有)将处于阻塞模式。
调用 ServerSocketChannel的 public final Selectable Channel configure Blocking( boolean block)方法即可。 public final Selectable Channel configure Blocking( boolean block)方法的作用是调整此通道的阻塞模式,传入tue是阻塞模式,传人 false是非阻塞模式。
可以把这个 Socketchannel通道注册到选择器中实现I/O多路复用,另外, Socketchannel通道使用缓冲区进行数据的读取操作。
返回此通道所支持的操作
public final int validOps()
方法的作用是返回一个操作集,标识此通道所支持的操作。因为服务器套接字通道仅支持接受新的连接,所以此方法返回 SelectionKey. OP ACCEPT
。
SocketChannel支持OP_CONNECT、OP_READ、OP_WRITE。
bind
public abstract ServerSocketChannel bind(SocketAddress local, int backlog) throws IOException
将通道的套接字绑定到本地地址并配置套接字以侦听连接。
此方法用于在套接字和本地地址之间建立关联。 一旦建立关联,则套接字保持绑定直到通道关闭。
backlog
参数是套接字上的最大挂起连接数。 它的确切语义是特定于实现的。 特别地,实现可以施加最大长度或者可以选择忽略参数altogther。 如果backlog
参数的值为0
或负值,则使用特定于实现的默认值。
Selector对象
由于Selector类是抽象类,需要调用 open方法获得 Selector对象。
Selector类的public static Selector open
方法的作用是打开1个选择器,使 Selectablechannel能将自身注册到这个选择器上。
执行注册操作与获得SelectionKey对象
Selectable Channel类的public final SelectionKey register( Selector sel, Int ops)方法的作用是向给定的选择器注册此通道,返回一个选择键( SelectionKey)。
参数sel代表要向其注册此通道的选择器,参数ops代表 register方法的返回值 SelectionKey的可用操作集,操作集是在 SelectionKey类中以常量的形式进行提供的:
OP_ACCEPT:用于套接字接受操作的操作集位
OP_CONNECT:用于套接字连接操作的操作集位
OP_READ:用于读取操作的操作集位
OP_WRITE:用于写入操作的操作集位
如果想把通道注册到选择器中,就必须将通道设置成非阻塞模式。
Tomcat的Poller对象
Poller(轮训器):实现Runnable接口,构造函数为this.selector = Selector.open();由一个SynchronizedQueue构成
Tomcat的Acceptor对象
实现了runable接口
Acceptor跑在一个单独的线程里,它在一个死循环里调用 accept方法来接收新连接,一旦有新的连接请求到来,accept方法返回一个 Channel 对象,接着把 Channel对象交给 Poller 去处理。
Poller 的本质是一个 Selector,也跑在单独线程里。Poller在内部维护一个 Channel数组,它在一个死循环里不断检测 Channel的数据就绪状态,一旦有 Channel可读,就生成一个 SocketProcessor任务对象扔给 Executor去处理。
Tomcat Connector总结
1、一个connector组件 可分为 protocolHandler 和 Adapter ,protocolHandler 用于处理 网络请求 , Adapter 将内部request , response 适配成 servlet的HttpServletReqeust , HttpServiceResponse
2、protocolHandler主要处理 网络连接 和 应用层协议 ,包含了两个重要部件 EndPoint 和 Processor, EndPoint 是用来实现 TCP/IP 协议数据读写的,本质调用操作系统的 socket 接口 , Processor用于处理socket, 转换应用层协议 ,封装内部 request 和 response
3、adapter 用于 内部 request 和 response 转换 成sevlet 规范的 HttpServletReqeust , HttpServiceResponse
4、EndPoint 一图以避之
5、
Processor 用来实现 HTTP 协议,Processor 接收来自 EndPoint 的 Socket,读取字节流解析成 Tomcat Request 和 Response 对象,并通过 Adapter 将其提交到容器处理,Processor 是对应用层协议的抽象
参考:tomcat的原理-组件connector - 简书
Tomcat - Request请求处理过程:Connector | Java 全栈知识体系
一个web请求访问tomcat会经过如下组件:
- accptor: 接收网络请求,发送事件给poller组件。
- poller: 接收I/O事件, 丢给业务线程池。
- processor: 处理socket。
- adapter :适配 httpServletReqeust 和 httpServletResponse
- 交给container 执行pipline。
SpringBoot支持的并发量
主要看其对Tomcat的设置。有最大线程数和最大连接数。
并发量指连接数还是线程数?连接数!
200个线程如何处理10000条连接?
Tomcat有两种处理连接的模式,一种是BIO,一个线程只处理一个Socket连接,另一种就是NIO,一个线程处理多个Socket连接。由于HTTP请求不会太耗时,而且多个连接一般不会同时来消息,所以一个线程处理多个连接没有太大问题。
为什么不开几个线程?
多开线程的代价就是,增加上下文切换的时间,浪费CPU时间,另外还有就是线程数增多,每个线程分配到的时间片就变少。多开线程≠提高处理效率。
那增大最大连接数呢?
增大最大连接数,支持的并发量确实可以上去。但是在没有改变硬件条件的情况下,这种并发量的提升必定以牺牲响应时间为代价。
tomcat7以下的版本都是BIO,就是一个请求是一个独立的线程。不能适用高并发的场景。
在8以上的版本,默认都是NIO。