博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Tomcat容器优化
阅读量:3927 次
发布时间:2019-05-23

本文共 7446 字,大约阅读时间需要 24 分钟。

TCP/UDP协议

OSI网络七层模型

为使不同计算机厂家的计算机能够互相通信,以便在更大的范围内建立计算机网络,有必要建立一个国际范围的网络体系结构。

各层的主要功能

低三层

  • 物理层: 使原始的数据比特流能在物理介质上传输。
  • 数据链路层: 通过校验、确认和反馈重发等手段,形成稳定的数据链路(01010101)。
  • 网络层: 进行路由选择和流量控制。(IP协议:定义到具体的某台机器)

目的是为了让我们屏蔽底层网络的复杂性,比如一些后端语言开发的人员,如果对底层不是非常了解也没有什么关系。

中间层

  • 传输层:提供可靠的端口端口的数据传输服务(TCP/UDP协议)

高三层

  • 会话层: 负责建立、管理和终止进程之间会话和数据交换。
  • 表示层: 负责数据格式转换、数据加密与解密、压缩与解压缩等。
  • 应用层: 为用户的应用进程提供网络服务。

HTTP服务器(基于TCP开发的,基于Web服务的一个协议)实现了高三层这些功能。

传输层控制协议TCP

是Internet一个重要的传输层协议。TCP提供面向连接、可靠、有序、字节流传输服务。应用程序在使用TCP之前,必须先建立TCP连接。

TCP数据报文

在这里插入图片描述

标志位说明:

  • URG:紧急指针
  • ACK: 确认序号
  • PSH:有DATA数据传输
  • RST:连接重置
  • SYN: 建立连接
  • FIN: 关闭连接

TCP握手机制

三次握手

三次握手的目的是最大程度上去检验网络是否通畅。就好比打电话的"喂喂喂",在正式发送数据之前做一个检验。

在这里插入图片描述

这并不是网络的建立连接,而是确立的机制。

四次挥手

在这里插入图片描述

用户数据报协议UDP

用户数据报协议UDP是Internet传输层协议。提供无连接、不可靠、数据报尽力传输服务。

UDP数据报文

在这里插入图片描述

开发应用人员在UDP上构建应用,关注以下几点:

  1. 应用进程更容易控制发送什么数据以及何时发送;
  2. 无需建立连接;
  3. 无连接状态;
  4. 首部开销小

UDP和TCP比较

TCP UDP
面向连接 无连接
提供可靠性 不可靠
资源占用多 资源占用少

但是应用开发大部分都是基于TCP的,保证数据可靠性;只有在对网络高性能和数据库可靠性不高的情况下,比如看直播,音视频播放,物联网中智慧城市的一些设备的日志上报、状态上报,这种情况就可以用到UDP开发协议。

Socket编程

  • Internet中应用最广泛的网络应用编程接口,实现3种底层协议接口:

    1. 数据报类型套接字SOCK_DGRAM(面向UDP接口)
    2. 流式套接字SOCK_STREAM(面向TCP接口)
    3. 原始套接字SOCK_RAW(面向网络层协议接口IP、ICMP等)
  • 主要socket API及其调用过程

    1. 创建套接字
    2. 端点绑定
    3. 发送数据
    4. 接收数据
    5. 释放套接字

Socket API函数定义

  • listen()、accept()函数只能用于服务器端;
  • connect()函数只能用于客户端;
  • socket()、bind()、send()、recv()、sendto()、recvfrom()、close()

BIO网络编程

当使用到Socket requeat = serverScoket.accept()Inputstream inputStream = request.getInputStream();OutputStream outputStream = request.getOutputStream();的时候,会产生阻塞,等待下一步命令之后才释放。

Http协议

请求数据包解析

在这里插入图片描述

  • 第一部分: 请求行,请求类型,资源路径以及HTTP版本。
  • 第二部分: 请求头部,紧接着请求行(即第一行之后的部分),用来说明服务器要使用的附加信息。
  • 第三部分(第三第四图中没有): 空行,请求头部后面的空行是必须的,请求头部和数据主体之间必须有换行。
  • 第四部分: 请求数据也叫主体,可以添加任意的数据。这个例子的请求数据为空。

响应数据包解析

在这里插入图片描述

  • 第一部分: 状态行。HTTP版本、状态码、状态消息。
  • 第二部分: 响应报头部,紧接着请求行(即第一行)之后的部分,用来说明服务器要使用的附加信息。
  • 第三部分: 空行,头部后面的空行是必须的,头部和数据主体之间必须有换行。
  • 第四部分: 响应正文。可以添加任意数据。这个例子的响应正文为"Hello World"

响应状态码

1xx(临时响应):表示临时响应并需要请求者继续执行操作的状态代码

2xx (成功):表示成功处理了请求的状态代码。

3xx(重定向):表示要完成请求,需要进一步操作。通常, 这些状态代码用来重定向。

4xx (请求错误):这些状态代码表示请求可能出错,妨碍了服务器的处理。

5xx (服务器错误):这些状态代码表示服务器在尝试处理请求时发生内部错误。这些错误可能是服务器本身的错误,而不是请求出错。

阻塞IO的含义

  • 阻塞(blocking) IO: 资源不可用时,IO请求一直阻塞,直到反馈结果(有数据或超时)。
  • 非阻塞(non-blocking) IO: 资源不可用时,IO请求离开返回,返回数据标识资源不可用。
  • 同步(synchronous) IO: 应用阻塞在发送或接收数据的状态,直到数据成功传输或返回失败。
  • 异步(asynchronous) IO: 应用发送或接收数据后立刻返回,实际处理是异步执行的。

阻塞/非阻塞是获取资源的方式,异步/同步是程序如何处理资源的逻辑设计。代码中使用的API:ServerSocket#accept、InputStream#read都是阻塞的API。操作系统底层API中,默认Socket操作都是Blocking型,send/recv等接口都是阻塞的。

阻塞导致在处理网络I/O时,一个线程只能处理一个网络连接。

NIO网络编程

始于Java 1.4,提供了新的JAVA IO操作非阻塞API。用意是替代Java IO和Java Networking相关的API。

NIO中有三个核心组件:Buffer缓冲区,Channel通道,Selector选择器

Buffer缓冲区

缓冲区本质上是一个可以写入数据的内存块(类似数组),然后可以再次读取。此内存块包含在NIO Buffer对象中,该对象提供了一组方法,可以更轻松地使用内存块。相比较直接对数组的操作,Buffer API更加容易操作和管理。

使用Buffer进行数据写入与读取,需要进行如下四个步骤:

  1. 将数据写入缓冲区;
  2. 调用buffer.flip(),转换为读取模式;
  3. 缓冲区读取数据;
  4. 调用buffer.clear()buffer.compact()清除缓冲区;

Buffer工作原理

Buffer三个重要属性:

  • capacity容量: 作为一个内存块,Buffer具有一定的固定大小,也称为"容量"。
  • position位置: 写入模式时,代表写数据的位置。读取模式时,代表读取数据的位置。
  • limit限制: 写入模式,限制等于buffer的容量。读取模式下,limit等于写入的数据量。

在这里插入图片描述

ByteBuffer内存类型

ByteBuffer为性能关键型代码提供了**直接内存(direct堆外)和非直接内存(heap堆)两种实现。**堆外内存获取的方式:ByteBuffer directByteBuffer = ByteBuffer.allocateDirect(noBytes);

好处:

  1. 进行网络IO或者文件IO时比heapBuffer少一次拷贝。(file/socket ---- OS memory ---- jvm heap) GC会移动对象内存,在写file或socket的过程中,JVM的实现中,会先把数据复制到堆外,再进行写入。
  2. GC范围之外,降低GC压力,但实现了自动管理。DirectByteBuffer中有一个Cleaner对象(PhantomReference),Cleaner被GC前会执行clean方法,触发DirectByteBuffer中定义的Deallocator。

建议:

  1. 性能确实可观的时候采取使用;分配给大型、长寿命;(网络传输、文件读写场景)
  2. 通过虚拟机参数MaxDirectMemorySize限制大小,防止耗尽整个机器的内存;

ByteBuffer的一些常用API

//创建一个字节为4的byte字节缓冲区,默认是写入模式ByteBuffer byteBuffer =ByteBuffer.allocate(4);//写入1字节的数据 byteBuffer.put((byte)1);//转为读取模式byteBuffer.flip();//读取到缓冲区中的数据byteBuffer.get();//清除整个缓冲区byteBuffer.clear();//仅清除已阅读的数据,转为写入模式byteBuffer.compact();//重置position为0byteBuffer.rewind();//标记position位置byteBuffer.mark();//重置position为上次mark的位置byteBuffer.reset();

Channel通道

在这里插入图片描述

Channel的API涵盖了UDP/TCP网络和文件IO;FileChannel;DatagramChannel;SocketChannel;ServerSocketChannel。

和标准IO Stream操作的区别:在一个通道内进行读取和写入,stream通常是单向的(input或output),可以非阻塞读取和写入通道,通道始终读取或写入缓冲区。

SocketChannel

SocketChannel用于建立TCP网络连接,类似java.net.Socket。有两种创建SocketChannel形式:

  1. 客户端主动发起和服务器的连接。
  2. 服务端获取的新连接。
//客户端主动发起连接的方式SocketChannel socketChannel = SocketChannel.open();//设置为非阻塞模式socketChannel.configureBlocking(false);socketChannel.connect(new InetSocketAddress("127.0.0.1",8080));channel.write(byteBuffer);//发送请求数据-向通道写入数据int bytesRead = socketChannel.read(byteBuffer);//读取服务端返回-读取缓冲区的数据socketChannel.close();//关闭连接
  • write:write()在尚未写入任何内容时就可能反回了。需要在循环中调用write()。
  • read:read()方法可能直接返回而根本不读取任何数据,根据返回的int值判断读取了多少字节。

ServerSocketChannel

ServerSocketChannel可以监听新建的TCP连接通道,类似ServerSocket。

//创建网络服务端ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();serverSocketChannel.configureBlocking(false);//设置为非阻塞模式serverSocketChannel.socket().bind(new InetSocketAddress(8080));//绑定端口while(true){
SocketChannel socketChannel = serverSocketChannel.accept();//获取新tcp连接通道 if(socketChannel != null){
//tcp请求 读取/响应 }}

serverSocketChannel.accept():如果该通道处于非阻塞模式,那么如果没有挂起的连接,该方法立即返回null。必须检查返回的SocketChannel是否为null。

Selector选择器

Selector是一个Java NIO组件,可以检查一个或多个NIO通道,并确定哪些通道已准备好进行读取或写入。实现单个线程可以管理多个通道,从而管理多个网络连接。

在这里插入图片描述

一个线程使用Selector监听多个channel的不同事件:四个事件分别对应SelectionKey四个常量。

  1. Connect 连接SelectionKey.OP_CONNECT
  2. Accept 准备就绪OP_ACCEPT
  3. Read 读取OP_READ
  4. Write 写入OP_WRITE

实现一个线程处理多个通道的核心概念理解:事件驱动机制

非阻塞的网络通道下,开发者通过Selector注册对于通道感兴趣的事件类型,线程通过监听事件来触发相应的代码执行。(拓展:更底层是操作系统的多路复用机制)

//放弃对channel的轮询,借助消息通知机制Selector selector = Selector.open();channel.configureBlocking(false);SelectionKey key = channel.register(selector,SelectionKey.OP_READ);//注册感兴趣的事件key.interestOps(SelectionKey.OP_ACCEPT);//对serverSocketChannel上面的accrpt事件感兴趣(serverSocketChannel只能支持accept操作)while(true){
//由accept轮询,变成了事件通知的方式 selector.select();//不再轮询通道,改用轮询事件的方式,select方法有阻塞效果,直到有时间通知才会有返回 int readChannels = selector.select();//select收到新的事件,方法才会返回 if(readyChannels == 0) continue; //获取事件 Set
selectedKeys = selector.selectedKeys(); //遍历查询结果 Iterator
keyIterator = selectedKeys.iterator(); while(keyIterator.hasNext()){
//被封装的查询结果 SelectionKey key = keyIterator.next(); //判断不同的事件类型,执行对应的逻辑处理 //key.isAcceptable();key.isConnectable();key.Readable();key.isWritable(); if(key.isAcceptable(){
ServerSocketChannel server = (ServerSocketChannel) key.attachment(); //将拿到客户端连接通道,注册到selector上面 SocketChannel clientSocketChannel = server.accept(); } keyIterator.remove(); }}

NIO对比BIO

BIO线程模型

在这里插入图片描述

  • 阻塞IO,线程等待时间长;
  • 一个线程负责一个连接处理;
  • 线程多切利用率低;

NIO线程模型

在这里插入图片描述

  • 非阻塞IO,线程利用率更高;
  • 一个线程处理多个连接事件;
  • 性能更强大;

如果程序需要支撑大量的连接,使用NIO时最好的方式。Tomcat8中,已经完全去除BIO相关的网络处理代码,默认采用NIO进行网络处理。

小结

实际企业中不会只用单线程进行操作,而会对NIO进行多线程的改进。

参考Doug Lea的著名文章《Scalable in Java》

NIO为开发者提供了功能丰富及强大的IO处理API,但是在应用于网络应用开发的过程中,直接使用JDK提供的API,比较繁琐。而且要想将性能进行提升,光有NIO还不够,还需要将多线程技术与之结合起来。

因为网络编程本身的复杂性,以及JDK API开发的使用难度较高,所以在开源社区中,涌出来很多对JDK NIO进行封装、增强后的网络编程框架,例如: Netty、 Mina等。

Tomcat网络处理线程模型

BIO+同步Servlet

一个请求,一个工作线程,CPU利用率低,新版本中不再使用。

在这里插入图片描述

APR+异步Servlet

apr (Apache Portable Runtime/Apache可移植运行库),是Apache HTTP服务器的支持库。JNI的形式调用Apache HTTP服务器的核心动态链接库来处理文件读取或网络传输操作Tomcat默认监听指定路径,如果有apr安装,则自动启用

在这里插入图片描述

NIO+异步Servlet

Tomcat8开始,默认NIO方式,非阻塞读取请求信息,非阻塞处理下一个请求,完全异步。

在这里插入图片描述

NIO处理流程

  1. 接收器接受套接字
  2. 接收器从缓存中检索nioChannel对象。
  3. Pollerthread将nioChannel注册到他的选择器IO事件
  4. 轮询器将nioChannel分配给一个work线程来处理请求
  5. SocketProcessor完成对请求的处理和返回

在这里插入图片描述

Tomcat参数调优

参数调优

配置项 默认 建议 注意
ConnectionTimeout 20s 减少
maxThread处理连接的最大线程数 200 增加 不是越大越好
acceptCount(backlog)等待接受accept的请求数量限制 100 增加 socket参数,min(accept,/proc/sys/net/core/somaxconn)
maxConnections最大连接处理数 nio 1w apr 8192 不变

在这里插入图片描述

转载地址:http://amugn.baihongyu.com/

你可能感兴趣的文章
hdu 4276 The Ghost Blows Light(树形DP+最短路+分组背包)好题。。。
查看>>
zoj 3537 Cake(区间DP+最优三角形剖分)待续
查看>>
poj 2955 Brackets(区间DP,经典问题)求有规律的括号的最大长度
查看>>
hdu 1754 I Hate It(线段树,单点替换,求区间最值)
查看>>
poj 2828 Buy Tickets(线段树中单点更新较难的题目)
查看>>
codeforces 395 B1. iwiwi(待续)
查看>>
hdu 4283 You Are the One(区间DP)题目转换难,状态难,。。。
查看>>
codeforces 397B. On Corruption and Numbers
查看>>
SqlMapConfig.xml中的setting属性设置
查看>>
hdu 3172 Virtual Friends(简单并查集)
查看>>
find the most comfortable road(并查集加贪心)
查看>>
Junk-Mail Filter(并查集,删除结点,虚父节点)
查看>>
A Bug's Life (并查集,同性恋问题,注意处理性别)
查看>>
选美大赛(线段树)
查看>>
超级玛丽(简单模拟超时)
查看>>
obex_io.c
查看>>
Linux程序开发基础概念
查看>>
Linux系统环境变量详谈
查看>>
sprintf函数用法
查看>>
make的常见错误信息
查看>>