你还在用tomcat ?out了

简介: Netty作为高性能服务器讲解

大家好,我是三友~~

前言

tomcat一度是web容器的标准,但是tomcat的并发量却只有200-400之间,即使现在有了aio模式,也没有提升太多。所以现在大部分都是使用netty作为高性能服务器框架,在dubbo,vert.x,gateway等等开源项目中都使用了,那么netty为什么深受喜爱?

下文将带你寻找答案

阅读netty会发现netty对于java有着很多改进,并适配了不同版本,不用升jdk版本就实现了相关jdk功能,及时优化了Java的bug,epoll的cpu100%空转。netty也封装了java的nio,简化了代码操作。netty本身也提供很多的附加功能,比如流量整形,黑白名单,安全认证等,极大的方便了网络开发。

公众号:三友的java日记

IO模型

首先这个问题绕不开io,java有bio,nio,多路复用io,aio

image.png

同步异步区别:是否立即返回结果

阻塞非阻塞区别:线程是否需要等待任务完成。

多路复用:使用linux底层的select、poll、epoll非阻塞api进行调用

信号驱动:类似事件机制进行,向linux发送系统需要调用信号,系统返回调用信号准备结果,期间主线程还是能接收其他请求过来。当返回调用信号成功后,等待数据从内核态复制到用户态,最终完成

bio 、nio、多路复用的解释

在java的nio当中,当线程读取数据的时候,当没有数据可以读取的时候,会立即返回-1给线程,此时线程就知道现在没有多余的数据可以读了,线程就可以继续往下执行。但是在bio中,一旦没有数据可以读取,此时不会返回给线程结果,而是一直阻塞在那里,线程也就无法继续执行代码了。

nio 解决了线程阻塞的问题,就是一旦没有数据可以读,就可以往下执行,但是还是有个问题,就是虽然现在没数据可以读,但是你怎么知道接下来会没有数据读写呢,所以一般都是类似于死循环这种模式去读,读不到就进行下一次循环。虽然不是阻塞,但是还是基本上属于一个线程对一个socket读写的模式。

对于io多路复用,整体的大概是这样的,就是一个或几个线程可以管理一堆socket,socket一旦有读写请求,就会通知你,然后你就可以进行io读写操作了。这些都是依赖操作系统层面。在java中也这种模型api的封装,比如 Selector , SocketChannel ,SocketChannel 可以往 Selector 注册, 一个 Selector 可以管理一堆 SocketChannel ,其实底层最后都是基于操作系统的机制操作的。io多路复用的意思也就是 多个socket 复用一个或多个线程。

所以一般都是nio + io多路复用 一起使用的。当一个线程A来管理一堆Socket,不断去选择有可以进行读写操作的socket(Selector 的 select方法就是干这个事的) ,一旦发现有读写(上面说过,操作系统会通知你哪些socket有读写操作),就将socket交给其他的线程去处理,其他线程处理完了,发现没有读写了,因为是nio,所以不会阻塞,可以继续执行,由于有通知机制,所以这个线程不需要一直循环去判断有没有数据要读取,因为一旦这个socket将来还有数据,还是会通知到线程A,线程A会再次交给子线程处理。基于这种模型,可以实现一定数量的线程就可以完成一大堆socket的io读写操作。

面试补充:

select 缺点:

  • 用户态拷贝到内核态
  • 内核遍历fd(文件描述符号)
  • 支持的文件描述符限制1024

poll 改进:

  • fd(文件描述符号)没有限制

缺点:

  • 和select一样,poll返回后,需要遍历fd(文件描述符号)来获取就绪的描述符
  • 调用poll都需要大把大量客户端在一时刻可能只有很少的处于就绪状态,因此随着监视的描述符数量的增长,其效率也会线性下降

epoll 改进:

  • 用户态和内核态共享
  • 回调解决轮训

从表格上来看aio无疑是最佳选择,但是实际上aio并没有带来大量性能提升,而具有reactor的netty框架是当前的性能杀招。

image.png

上图是主从reactor的多线程的原理,通过boss和work作为分发器分发给线程池处理。

boss负责接受请求,什么叫接收请求,就是当有客户端需要跟服务端进行通信的时候,客户端需要跟服务端进行tcp三次握手,之后服务端会创建一个跟客户端通信的Socket,java中的api是叫SocketChannel,这些事都是boss负责的。

work负责读写处理,就是当客户端和服务端进行tcp三次握手之后,成功建立连接,此时客户端向服务端发来请求,work线程就会负责从连接中读取数据,处理请求,然后响应给客户端。

零拷贝

有了reactor,netty还有杀招-零拷贝

所谓零拷贝指的是零cpu拷贝,相比普通的拷贝,减少了用户态和内核态的切换,也减少了2次cpu拷贝。而用户态和内核态的切换是很好性能的。

正常的io会经过如下过程

image.png

而linux进行升级mmp,sendfile api,最终诞生了零拷贝,通过共享用户态避免了向用户态的切换。

使用文件描述符,代替了内核态的修改,只需传输标识地址,无需修改大量内容。

文件描述符号(简称呼fd):标识打开的文件的记录表

image.png

堆外内存

堆内存创建快,读写慢。堆外内存,创建慢,读写快。学过c和c++的都知道如何申请一块内存,堆外内存也是一样使用,一般可以安全的使用如下api申请。

ByteBuffer buffer = ByteBuffer.allocateDirect(10 1024 1024);

在netty中同样支持堆内存,堆外内存,池化,非池化的选择,这也是其高性能利器

高性能对象池

在java中对象池往往都非常重,但netty对于这块做了专门的优化,实现了Recycler做对象池,用来做池化内存。

Recycler提供了3个方法

get():获取一个对象。

recycle(T, Handle):回收一个对象,T为对象泛型。

newObject(Handle):当没有可用对象时创建对象的实现方法。

读取netty源码,发现recycler主要依靠DefaultHandler,WeakOrderQueue,Stack实现,如果threadlocal有就拿,没有就新建,DefaultHandler的pop是拿去的核心方法。

本文介绍了netty比tomcat的优势,本人也在工作中使用了netty作为服务器,那么后面将会介绍如何正确使用netty。

最后,如果本篇文章对你所有帮助,欢迎转发、点赞、收藏、在看,非常感谢。

相关文章
|
Java 应用服务中间件 负载均衡
Tomcat中的Out Of Memory错误
  tomcat中的outofmemory错误 1、PermGen space错误 PermGen space的全称是Permanent Generation space,是指内存的永久保存区域Out Of Memory Error: PermGen space 从表面上看就是内存益出,解决方法也一定是加大内存。
849 0
|
2月前
|
XML 应用服务中间件 Apache
Tomcat AJP连接器配置secretRequired=“true“,但是属性secret确实空或者空字符串,这样的组合是无效的。
Tomcat AJP连接器配置secretRequired=“true“,但是属性secret确实空或者空字符串,这样的组合是无效的。
|
22天前
|
前端开发 Java 应用服务中间件
Springboot对MVC、tomcat扩展配置
Springboot对MVC、tomcat扩展配置
|
7天前
|
XML Java 应用服务中间件
Tomcat_servlet部署、编译、配置、打包
Tomcat_servlet部署、编译、配置、打包
12 0
|
20天前
|
运维 Java 应用服务中间件
Tomcat详解(二)——tomcat安装与配置
Tomcat详解(二)——tomcat安装与配置
21 1
|
5天前
|
应用服务中间件
Idea中配置tomcat以及测试
Idea中配置tomcat以及测试
10 0
|
7天前
|
网络协议 Java 应用服务中间件
记录_centos7离线环境和虚拟机共享文件安装jdk和tomcat(配置环境变量)
记录_centos7离线环境和虚拟机共享文件安装jdk和tomcat(配置环境变量)
6 0
|
8天前
|
应用服务中间件
【SSM】如何在IDEA配置tomcat启动项目
【SSM】如何在IDEA配置tomcat启动项目
12 1
|
8天前
|
IDE JavaScript Java
如何配置tomcat
【4月更文挑战第15天】如何配置tomcat
16 2
|
8天前
|
负载均衡 Ubuntu 应用服务中间件
Apache(mod_proxy)+Tomcat负载均衡配置
Apache(mod_proxy)+Tomcat负载均衡配置