Netty: 大名鼎鼎, Javaer 人尽皆知的优秀框架,作为曾今的使用者对 netty 的作者也有无限的崇拜之情.对于 netty 的特点和优势这里不在多言,以免言多必失,大家可以问度娘.嘿嘿
Voovan: Voovan奉行简约、优雅,其核心功能则是异步通信,同时还包含了动态编译、反射、ORM、Http 服务和客户端、websocket 服务和客户端、JSON 的序列化以及日志等一款综合性工具框架。
既然 Voovan 和 Netty 都是已 socket 通信作为核心的通信框架.这里我们对 Voovan 和 Netty 做一个性能对比的测试,同时我们对编码也进行对比,来看看两款框架有什么神奇的区别。
异同点:
1.都支持 TLS、SSL 加密。
2.都支持 TCP、UDP 通信模型。
3.都使用异步事件驱动。
4.都实现了 HTTP、WEBSOCKET 协议的支持。
5.Voovan 比 Netty 多了一个消息分割类,具体看后面的介绍 。
6.Voovan 框架额外实现了一个 Web 服务。
7.文档方面两个框架都有着较为丰富的文档,只是 Netty 文档多为英文,而 Voovan 由于是国人开发,多为中文文档.
8.Netty 提供了较多的协议的默认实现.
9.Voovan 提供了额外的更多的工具类的支持.
一、主机环境
资源有限使用虚拟机进行性能测试
OS Ubuntu 16.04
CPU Intel(R) Core(TM) i5-6500 CPU @ 3.20GHz
CPUCount 4核
内存 512M
JDK OpenJDK 1.8.0_111-8u111-b14-2 @ ubuntu0.16.04.2-b14
命令 ab -c 100 -n 10000 http://10.0.0.102:2808?/
二、测试说明
测试目的: 对比 Voovan 和 Netty 在并发场景下能够支撑的并发连接数.
测试工具: Apache bench
原料:Netty 4.1.8 、 Voovan1.0-RC-1
为避免自己编码的压测工具带来各种风险,同时也为了使测试结果更加公平、真实、可信。测试方法如下:
1. 使用 ab 进行压力测试
2. Voovan 和 Netty 两个框架分别模拟 Http 服务,在接受到 HTTP 请求后不进行解析,直接返回相同的 Http 响应后关闭连接。
3. 每次响应都关闭连接,保证不会因 HTTP 的 KeepAlive 头带来的长连接影响测试结果的准确性。
三、测试代码
Voovan主类: AioPerformTest
public static void main(String[] args) throws IOException { AioServerSocket serverSocket = new AioServerSocket("0.0.0.0",28081,5000); serverSocket.filterChain().add(new StringFilter()); //配置过滤器 serverSocket.handler(new PerformTestHandler()); //配置业务类 serverSocket.messageSplitter(new PerformTestSpliter()); // 配置消息截断器 serverSocket.start(); //开启监听 } ........
Voovan业务类 PerformTestHandler:
public class PerformTestHandler implements IoHandler {
private String responseStr ;
public PerformTestHandler(){
responseStr = "HTTP/1.1 200 OK\\r\\n" +
"Date: Wed, 10 Jun 2009 11:22:58 GMT\\r\\n" +
"Server: Microsoft-IIS/6.0\\r\\n" +
"X-Powered-By: ASP.NET\\r\\n" +
"Content-Length: 2\\r\\n" +
"Content-Type: text/html\\r\\n" +
"Cache-control: private\\r\\n\\r\\n"+
"OK\\r\\n\\r\\n";
}
@Override
public Object onReceive(IoSession session, Object obj) {
return responseStr;
}
@Override
public void onSent(IoSession session, Object obj) {
session.close(); //关闭连接
}
........
}
Netty主类:
netty 的业务累使用了内联类的方式写入到主类中了,以下代码的主体来自 netty 官网
package org.voovan.test.netty;
import io.netty.bootstrap.ServerBootstrap; import io.netty.buffer.ByteBuf; import io.netty.channel.*; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioServerSocketChannel;
/** * 类文字命名 * * @author helyho *
* Voovan Framework. * WebSite: https://github.com/helyho/Voovan * Licence: Apache v2 License */ public class NettyServer {
public static void main(String\[\] args) throws InterruptedException {
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new DiscardServerHandler());
}
})
.option(ChannelOption.SO\_BACKLOG, 1024)
.childOption(ChannelOption.SO\_KEEPALIVE, true);
ChannelFuture f = b.bind(28080).sync();
f.channel().closeFuture().sync();
} finally {
workerGroup.shutdownGracefully();
bossGroup.shutdownGracefully();
}
}
public static class DiscardServerHandler extends ChannelInboundHandlerAdapter {
private String retVal;
public DiscardServerHandler(){
retVal = "HTTP/1.1 200 OK\\r\\n" +
"Server: Voovan-WebServer/V1.0-RC-1\\r\\n" +
"Connection: keep-alive\\r\\n" +
"Content-Length: 2\\r\\n" +
"Date: Thu, 05 Jan 2017 04:55:20 GMT\\r\\n" +
"Content-Type: text/html\\r\\n"+
"\\r\\n"+
"OK\\r\\n\\r\\n";
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
//byte\[\] buffer = new byte\[((ByteBuf) msg).readableBytes()\];
//((ByteBuf) msg).readBytes(buffer);
//System.out.println(new String(buffer));
//((ByteBuf) msg).clear();
((ByteBuf) msg).release();
ByteBuf bf = ctx.alloc().buffer(retVal.length());
bf.writeBytes(retVal.getBytes());
ctx.writeAndFlush(bf);
ctx.close();
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { // (4)
// Close the connection when an exception is raised.
cause.printStackTrace();
ctx.close();
}
}
}
以上完整的测试代码均在https://git.oschina.net/helyho/Voovan可以找到。
2017-07-01: 有网友质疑采用 ctx.alloc()方式没有从 Bytebuf 池中获取.经过调试截图如下:
PooledByteBufAllocator 对象是用来分配bytebuf池中非堆内存的,所以通ctx.alloc().buffer(retVal.length());的方式实际上是从bytebuf池中获取的 bytebuf。
四、代码比对
首先请以初学者的角度来看待以上两段代码:
1.Voovan 仅仅5行代码就可以完成对 Socket 的初始化工作,每行代码都有他独立的作用,结构清晰易懂.Netty 则最少需要8行代码来实现而且包含一个内联类,对于初学者来说可能理解有些难度多数初学者就是复制粘贴直接使用。
2.Voovan 框架通过 PerformTestSpliter 消息分割类将消息转换成你期望的类型,这样在 IoHandler的实现类中的OnRecive 事件中可以直接使用.Netty 则需要在接收事件中进行处理.其实提供消息分割器的目的是为了优雅的解决粘包的问题。
3.Voovan 和 Netty 都提供过滤链来对消息进行处理,但 Voovan 的过滤链是在消息分割类之后是可以有针对性的过滤一个有效且完成的报文。
4.Voovan 和 Netty 都采用了自管理线程池能根据业务情况自动扩充线程池的大小。
基于以上的代码做了一个简单的对比分析,两个框架在目的和功能上具有相通性,采用了不同的方法进行实现。
五、测试步骤
1.首先进行5次预热,以保证框架的线程池管理达到最优状态
2.接着,连续运行10次并记录结果。
3.首先测试 Voovan 框架,测试完成后,等待3分钟后进行 Netty 框架的测试。
由于机器是虚拟机所以各项ab的参数设置的并不高,但好在两个测试程序运行在同样的环境中,并使用同样的参数进行测试,横向对比的结果还是可靠的。
六、数据说话
下面我们来看看测试数据:
框架
第一次
第二次
第三次
第四次
第五次
第六次
第七次
第八次
第九次
第十次
平均
Voovan
18734.1
18573.97
18990.18
19394.95
18946.86
16115.36
14878.41
18410.92
21638.65
19569.17
18525.257
Netty
19382.62
18808.45
19255.73
12422.08
20086.29
20176.91
19604.92
19338.77
14812.84
16474.74
18036.335
通过上表可以看出:
Voovan 框架在10次测试后平均并发数据为: 18525
Netty 框架在10次测试后平均并发数据为: 18036
两个框架在并发性能上差异为:489,平均导10次每次的差异约为49,鉴于我们进行测试的总请求量是10000,这个差异可能是机器的各种不可控因素导致的差异,个人认为是可以忽略的.并不影响两个框架的整体性能。
性能曲线分析:
橙色的线条是 Netty 的数据
蓝色的线条是 Voovan 的数据
可以看到 Voovan 的出现了一次较大的向下波动,Netty 出现了两次较大的向下波动.具体波动原因不得而知,推测可能是虚拟机较弱,因 GC 导致,由于只有10次测试的样本并所以不能通过较大的波动次数来评判框架的好坏。
总的来说两个框架在并发性能上应该是处于相同级别的,或者说都具有优秀的并发性能。
最后各位童鞋,你们怎么看呢?
有兴趣了解 Voovan 的朋友可以移步【Voovan】