申明:
我对node.js与java这两种开发技术不会有什么偏爱的想法,觉得他们都有自己擅长的地方,将一门技术用在其适合的位置才是最合适的,个人观点是没有绝对的好与坏之分,所以请不要说我是什么黑。。。而且我也不会给出任何结论。。只如实的贴出数据。。。。
一直觉得网上有很多类似的对比,但是个人觉得都不是很客观,要么node.js懂的比较多,要么java懂的比较多。。。
测试环境:
服务器:亚马逊虚拟机,large,美国东部、。。。。(具体配置可以上亚马逊网站上面查)
http访问测试机器(客户端):亚马逊虚拟机,medium,美国东部
测试工具:apache ab
版本信息:node.js是v0.10.16 jre版本是1.7.0_45 netty版本是4.0final(java的nio框架)
测试用例(1):纯粹的http访问测试,客户端提交http请求,服务器返回hello world
node.js代码如下:
var cluster = require('cluster');
var http = require('http');
var numCPUs = require('os').cpus().length;
var util = require("./util.js");
if (cluster.isMaster) {
// Fork workers.
for (var i = 0; i < numCPUs; i++) {
cluster.fork();
}
cluster.on('exit', function(worker, code, signal) {
console.log('worker ' + worker.process.pid + ' died');
});
} else {
// Workers can share any TCP connection
// In this case its a HTTP server
http.createServer(function(req, res) {
var buffer = new Buffer("hello world");
var length = Buffer.byteLength("hello world");
res.statusCode = 200;
res.setHeader('Content-Length', length);
res.end(buffer);
/*
util.getValue(function(reply){
var buffer = new Buffer(reply);
var length = Buffer.byteLength(reply);
res.statusCode = 200;
res.setHeader('Content-Length', length);
res.end(buffer);
});*/
}).listen(8009);
}
java代码如下:
package fjs;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpRequestDecoder;
import io.netty.handler.codec.http.HttpResponseEncoder;
public class Server {
private void start() throws InterruptedException {
EventLoopGroup bossGroup = new NioEventLoopGroup(1); //这个是用于serversocketchannel的eventloop
EventLoopGroup workerGroup = new NioEventLoopGroup(); //这个是用于处理accept到的channel
try {
ServerBootstrap b = new ServerBootstrap(); //构建serverbootstrap对象
b.group(bossGroup, workerGroup); //设置时间循环对象,前者用来处理accept事件,后者用于处理已经建立的连接的io
b.channel(NioServerSocketChannel.class); //用它来建立新accept的连接,用于构造serversocketchannel的工厂类
b.childHandler(new ChannelInitializer<SocketChannel>(){ //为accept channel的pipeline预添加的inboundhandler
@Override //当新连接accept的时候,这个方法会调用
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast("decoder", new HttpRequestDecoder()); //用于解析http报文的handler
ch.pipeline().addLast("aggregator", new HttpObjectAggregator(65536)); //用于将解析出来的数据封装成http对象,httprequest什么的
ch.pipeline().addLast("encoder", new HttpResponseEncoder()); //用于将response编码成httpresponse报文发送
ch.pipeline().addLast(new HttpHandler());
}
});
//bind方法会创建一个serverchannel,并且会将当前的channel注册到eventloop上面,
//会为其绑定本地端口,并对其进行初始化,为其的pipeline加一些默认的handler
ChannelFuture f = b.bind(8009).sync();
f.channel().closeFuture().sync(); //相当于在这里阻塞,直到serverchannel关闭
} finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
public static void main(String args[]) throws InterruptedException {
Server server = new Server();
server.start();
}
}
package fjs;
import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1;
import static io.netty.handler.codec.http.HttpHeaders.Names.CONNECTION;
import static io.netty.handler.codec.http.HttpHeaders.Names.CONTENT_LENGTH;
import static io.netty.handler.codec.http.HttpHeaders.isKeepAlive;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandler;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.HttpHeaders.Values;
import io.netty.handler.codec.http.HttpResponseStatus;
public class HttpHandler implements ChannelInboundHandler{
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
// TODO Auto-generated method stu
}
public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
// TODO Auto-generated method stub
}
public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
// TODO Auto-generated method stub
}
public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {
// TODO Auto-generated method stub
}
public void channelActive(ChannelHandlerContext ctx) throws Exception {
// TODO Auto-generated method stub
}
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
// TODO Auto-generated method stub
ctx.pipeline().close();
}
public void channelRead(ChannelHandlerContext ctx, Object req)
throws Exception {
// TODO Auto-generated method stub
FullHttpRequest r = (FullHttpRequest)req; //将其强制转化为httprequest
ByteBuf b = ctx.alloc().buffer();
//System.out.println(request.headers().get("Sec-WebSocket-Version"));
b.writeBytes("hello world".getBytes());
//b.writeBytes(Util.getValue().getBytes());
DefaultFullHttpResponse response = new DefaultFullHttpResponse( HTTP_1_1, HttpResponseStatus.OK, b);
response.headers().set(CONTENT_LENGTH, b.readableBytes());
boolean keepAlive = isKeepAlive(r); //判断当前的连接时否是keepalive的
if (keepAlive) {
response.headers().set(CONNECTION, Values.KEEP_ALIVE);
ctx.writeAndFlush(response);
} else {
ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
}
}
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
// TODO Auto-generated method stub
}
public void userEventTriggered(ChannelHandlerContext ctx, Object evt)
throws Exception {
// TODO Auto-generated method stub
}
public void channelWritabilityChanged(ChannelHandlerContext ctx)
throws Exception {
// TODO Auto-generated method stub
}
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
throws Exception {
// TODO Auto-generated method stub
ctx.pipeline().close();
System.out.println(cause);
}
}
测试结果如下:
(1)node.js
(2)java:
其实上面java和node.js的数据都挺低的,还没有我自己的台式机上服务端和客户端一起跑跑出来的数据高。。。但是毕竟是虚拟机嘛,不能要求太高。。。。。
(2)测试用例(2),其实服务器还是返回hello world,只不过这个hello world是从redis里面取出来的。。。
node.js代码如下:
var redis = require("redis");
var client;
client = redis.createClient(6379, "localhost");
client.auth('fjsfjs');
client.on("error", function (err) {
// console.log("error event - " + client.host + ":" + client.port + " - " + err);
console.log("aa" + err);
});
exports.getValue = function(cb) {
client.send_command('GET', ["node-netty-test"], function(err, reply){
cb(reply);
});
}
var cluster = require('cluster');
var http = require('http');
var numCPUs = require('os').cpus().length;
var util = require("./util.js");
if (cluster.isMaster) {
// Fork workers.
for (var i = 0; i < numCPUs; i++) {
cluster.fork();
}
cluster.on('exit', function(worker, code, signal) {
console.log('worker ' + worker.process.pid + ' died');
});
} else {
// Workers can share any TCP connection
// In this case its a HTTP server
http.createServer(function(req, res) {
/*
var buffer = new Buffer("hello world");
var length = Buffer.byteLength("hello world");
res.statusCode = 200;
res.setHeader('Content-Length', length);
res.end(buffer);*/
util.getValue(function(reply){
var buffer = new Buffer(reply);
var length = Buffer.byteLength(reply);
res.statusCode = 200;
res.setHeader('Content-Length', length);
res.end(buffer);
});
}).listen(8009);
}
java代码如下:
package fjs;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
import redis.clients.jedis.exceptions.JedisConnectionException;
public class Util {
private static JedisPool pool = new JedisPool(new JedisPoolConfig(), "localhost", 6379, 2000, "fjsfjs");
//private static JedisPool pool = new JedisPool(new JedisPoolConfig(), "localhost");
static {
Jedis jedis = pool.getResource();
try {
jedis.set("node-netty-test", "hello world");
} catch (JedisConnectionException e) {
pool.returnBrokenResource(jedis);
}finally {
pool.returnResource(jedis);
}
}
public static String getValue() {
Jedis jedis = pool.getResource();
try {
return jedis.get("node-netty-test");
} catch (JedisConnectionException e) {
pool.returnBrokenResource(jedis);
return "";
}finally {
pool.returnResource(jedis);
}
}
}
package fjs;
import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1;
import static io.netty.handler.codec.http.HttpHeaders.Names.CONNECTION;
import static io.netty.handler.codec.http.HttpHeaders.Names.CONTENT_LENGTH;
import static io.netty.handler.codec.http.HttpHeaders.isKeepAlive;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandler;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.HttpHeaders.Values;
import io.netty.handler.codec.http.HttpResponseStatus;
public class HttpHandler implements ChannelInboundHandler{
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
// TODO Auto-generated method stu
}
public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
// TODO Auto-generated method stub
}
public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
// TODO Auto-generated method stub
}
public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {
// TODO Auto-generated method stub
}
public void channelActive(ChannelHandlerContext ctx) throws Exception {
// TODO Auto-generated method stub
}
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
// TODO Auto-generated method stub
ctx.pipeline().close();
}
public void channelRead(ChannelHandlerContext ctx, Object req)
throws Exception {
// TODO Auto-generated method stub
FullHttpRequest r = (FullHttpRequest)req; //将其强制转化为httprequest
ByteBuf b = ctx.alloc().buffer();
//System.out.println(request.headers().get("Sec-WebSocket-Version"));
//b.writeBytes("hello world".getBytes());
b.writeBytes(Util.getValue().getBytes());//改成从redis里面获取hello world
DefaultFullHttpResponse response = new DefaultFullHttpResponse( HTTP_1_1, HttpResponseStatus.OK, b);
response.headers().set(CONTENT_LENGTH, b.readableBytes());
boolean keepAlive = isKeepAlive(r); //判断当前的连接时否是keepalive的
if (keepAlive) {
response.headers().set(CONNECTION, Values.KEEP_ALIVE);
ctx.writeAndFlush(response);
} else {
ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
}
}
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
// TODO Auto-generated method stub
}
public void userEventTriggered(ChannelHandlerContext ctx, Object evt)
throws Exception {
// TODO Auto-generated method stub
}
public void channelWritabilityChanged(ChannelHandlerContext ctx)
throws Exception {
// TODO Auto-generated method stub
}
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
throws Exception {
// TODO Auto-generated method stub
ctx.pipeline().close();
System.out.println(cause);
}
}
另外一个java源文件没有改变,
测试数据如下:
(1)node.js:
(2)java:
好了,所有的测试结果都已经贴出来了。。。
这里node.js考虑到了要绑定cpu的问题,但是后来实际发现这个影响不大。。。另外java对redis的访问因为采用的是连接池,官方文档本身就明确说这是线程安全的。。。。所以不会有问题。。。。
另外,因为服务器上有别的程序再跑,所以也有一定的影响,但这个影响不大,而且我贴出来的测试结果都是从好多结果中跳出来比较平均的结果贴出来的。。。。
另外对于测试用例以及其余的地方如果有什么不对的,还请指正。。。。。(没有测试 数据库,因为觉得太麻烦了,机器上没有装数据库。。。)
另外还要补充一下。。。node.js的cpu一般情况下是java的1.5-2 倍,内存都比较小,没啥对比的意思。。
好药补充一下。。java访问redis这里采用的是同步的,感觉影响也不大。。。毕竟redis访问性能太高了。。。所以对java的影响也不大。。。