Netty NIO 框架性能压测 – 长链接
压测准备
需要将ulimit -n 改大,否则nio链接开不大。
准备4台机器(1台netty服务器,3台压测机)
使用apache的ab做压测工具


开始干活
压测代码:

package org.dueam.sample.netty; 

package org.dueam.sample.netty; 


import java.io.BufferedReader; 

import java.io.InputStreamReader; 

import java.net.InetSocketAddress; 

import java.util.HashMap; 

import java.util.Map; 

import java.util.Random; 

import java.util.concurrent.Executors; 


import org.jboss.netty.bootstrap.ServerBootstrap; 

import org.jboss.netty.buffer.ChannelBuffer; 

import org.jboss.netty.buffer.DynamicChannelBuffer; 

import org.jboss.netty.channel.Channel; 

import org.jboss.netty.channel.ChannelFactory; 

import org.jboss.netty.channel.ChannelHandlerContext; 

import org.jboss.netty.channel.ChannelPipeline; 

import org.jboss.netty.channel.ChannelStateEvent; 

import org.jboss.netty.channel.ExceptionEvent; 

import org.jboss.netty.channel.MessageEvent; 

import org.jboss.netty.channel.SimpleChannelHandler; 

import org.jboss.netty.channel.ChannelHandler.Sharable; 

import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory; 


public class ChatServer { 


 public static void main(String[] args) throws Exception { 

 if(args.length <1){ 

 args = new String[]{"9876","true"}; 

 } 

 ChannelFactory factory = new NioServerSocketChannelFactory(Executors 

 .newCachedThreadPool(), Executors.newCachedThreadPool()); 


 ServerBootstrap bootstrap = new ServerBootstrap(factory); 


 ChatServerHandler handler = new ChatServerHandler(); 

 ChannelPipeline pipeline = bootstrap.getPipeline(); 

 pipeline.addLast("chat", handler); 


 bootstrap.setOption("child.tcpNoDelay", true); 

 bootstrap.setOption("child.keepAlive", true); 

 int port = Integer.valueOf(args[0]); 

 bootstrap.bind(new InetSocketAddress(port)); 


 boolean fillChat = "true".equals(args[1]); 

 if (fillChat) { 

 ChannelManagerThread cmt = new ChannelManagerThread(); 

 cmt.start(); 

 } 


 BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); 

 while (true) { 

 String command = br.readLine(); 

 if ("dump".equals(command)) { 

 System.out.println("当前活着的数量:" + channel.size()); 

 } else if ("help".equals(command)) { 

 System.out.println("命令列表:"); 

 System.out.println("dump:打印当前情况"); 

 System.out.println("help:帮助文档"); 

 } 

 } 


 } 

 final static Random random = new Random(); 

 static int max = 0; 

 static class ChannelManagerThread extends Thread { 

 @Override 

 public void run() { 

 while (true) { 

 try { 

 if(max < channel.size()){ 

 max = channel.size() ; 

 System.out.println("live:"+channel.size()); 

 } 


 for (Channel s : channel.values()) { 

 if (random.nextInt(100)>70) { 

 ChannelBuffer cb = new DynamicChannelBuffer(256); 

 cb.writeBytes("Hey!有人来找你了!".getBytes()); 

 s.write(cb); 

 } 

 } 

 sleep(500); 

 } catch (InterruptedException e) { 


 } 

 } 

 } 

 } 


 final static Map<Integer, Channel> channel = new HashMap<Integer, Channel>(); 


 static void log(String message) { 

 System.out.println(message); 

 } 


 @Sharable 

 static class ChatServerHandler extends SimpleChannelHandler { 

 @Override 

 public void channelConnected(ChannelHandlerContext ctx, 

 ChannelStateEvent e) { 

 Channel ch = e.getChannel(); 

 ChannelBuffer cb = new DynamicChannelBuffer(256); 

 cb.writeBytes("Hell!你来了啊!".getBytes()); 

 ch.write(cb); 

 channel.put(e.getChannel().getId(), e.getChannel()); 

 } 



 @Override 

 public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) { 

 } 


 @Override 

 public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) { 

 e.getCause().printStackTrace(); 

 channel.remove(e.getChannel().getId()); 

 log("remove channel by exception! id:" + e.getChannel().getId()); 


 e.getChannel().close(); 

 } 


 @Override 

 public void channelDisconnected(ChannelHandlerContext ctx, 

 ChannelStateEvent e) throws Exception { 

 channel.remove(e.getChannel().getId()); 

 log("remove channel by exception! id:" + e.getChannel().getId()); 


 } 

 }


}压测方式:

#加大超时和并发量,并使用keep-alive的方式保持住端口
./ab -n 20000 -c 20000 -k -t 999999999 -r http://192.168.216.30:9876/压测结果
内存损耗:

# free -k -t -s 10
-- 原始内存
total used free shared buffers cached
Mem: 4149076 189828 3959248 0 13196 95484
-/+ buffers/cache: 81148 4067928
Swap: 2096472 208 2096264
Total: 6245548 190036 6055512

-- 执行 chat server之后
total used free shared buffers cached
Mem: 4149076 207236 3941840 0 13216 96244
-/+ buffers/cache: 97776 4051300
Swap: 2096472 208 2096264
Total: 6245548 207444 6038104

-- 59471 个nio连接之后
total used free shared buffers cached
Mem: 4149076 474244 3674832 0 13328 96132
-/+ buffers/cache: 364784 3784292
Swap: 2096472 208 2096264
Total: 6245548 474452 5771096结论:


Netty nio 可以轻松将链接开到6W,每个线程大概损坏5k左右的系统内存
后续压测方案
编写Java客户端做内容实时双向推送
使用100台机器每台机器起1000个线程来模拟客户端进行压测