Vert.x是一个轻量级的高性能JVM应用平台,基于它可开发各种移动,Web和企业应用程序。一个主要特点是可使用多种语言编写应用,如Java, JavaScript, CoffeeScript, Ruby, Python 或 Groovy等等,它的简单actor-like机制能帮助脱离直接基于多线程编程。它是基于Netty和Java 7的NIO2的编写的。
当前业界遭遇C10K问题,当并发连接超过10,000+以上时使用传统技术会引发暂停,移动设备或视频 声音如类似微信这样的实时聊天,都是属于长任务连接Long-lived。
通常Tomcat会在100个并发长请求(这个请求要求做很多事长任务)下堵塞,而Vertx将长任务委托给另外一个线程来执行,从而不会堵塞当前线程,与NodeJS的原理非常类似,如下图:
事件循环的线程和长任务工作线程之间是通过事件总线EventBus通讯的,这个事件总线是一个JVM之中,如下图:
图中每个Verticle是一个线程,符合单写原则,避免了写争夺。总线可以拓展到客户端边的Javascript,可以在客户端和服务器端分发事件,可以如下图在多个客户端或服务器端:
下图是在一个Web应用中,多层之间也可以通过EventBus通讯实现调用:
代码演示
下面是一个服务器的Java代码:
public class ServerExample extends Verticle {
public void start() {
vertx.createHttpServer().requestHandler(new Handler<HttpServerRequest>() {
public void handle(HttpServerRequest req) {
System.out.println("Got request: " + req.uri());
req.response().headers().set("Content-Type", "text/html; charset=UTF-8");
req.response().end("<html><body><h1>Hello from vert.x!</h1></body></html>");
}
}).listen(8080);
}
}
groovy版本:
package http
vertx.createHttpServer().requestHandler { req ->
req.response.end "<html><body><h1>Hello from vert.x!</h1></body></html>"
}.listen(8080, "localhost")
Javascript版本:
var vertx = require('vertx')
vertx.createHttpServer().requestHandler(function(req) {
req.response.end("<html><body><h1>Hello from vert.x!</h1></body></html>");
}).listen(8080);
WebScoket实现
WebSocket是Web服务器端向客户端推送信息的协议,使用vert.x实现如下:
vertx.createHttpServer().websocketHandler { ws ->
ws.dataHandler { data -> ws.writeTextFrame(data.toString()) }
}.requestHandler { req ->
if (req.uri == "/") req.response.sendFile "websockets/ws.html"
}.listen(8080)
浏览器客户端通过JS调用:
<script>
var socket;
if (window.WebSocket) {
socket = new WebSocket("ws://localhost:8080/myapp");
socket.onmessage = function(event) {
alert("Received data from websocket: " + event.data);
}
socket.onopen = function(event) {
alert("Web Socket opened!");
};
socket.onclose = function(event) {
alert("Web Socket closed.");
};
} else {
alert("Your browser does not support Websockets. (Use Chrome)");
}
function send(message) {
if (!window.WebSocket) {
return;
}
if (socket.readyState == WebSocket.OPEN) {
socket.send(message);
} else {
alert("The socket is not open.");
}
}
</script>
SocketJS是一种处理客户端和服务器通讯的框架,能够在客户端提供类似websocket的API,支持JSON-Polling, XHR-Polling/Streaming。
<html>
<head>
<title>SockJS Test</title>
<script src="http://cdn.sockjs.org/sockjs-0.3.4.min.js"></script>
</head>
<body>
<script>
var sock = new SockJS('http://localhost:8080/testapp');
sock.onopen = function() {
console.log('open');
};
sock.onmessage = function(e) {
console.log('message', e.data);
alert('received message echoed from server: ' + e.data);
};
sock.onclose = function() {
console.log('close');
};
function send(message) {
if (sock.readyState === SockJS.OPEN) {
console.log("sending message")
sock.send(message);
} else {
console.log("The socket is not open.");
}
}
</script>
<form οnsubmit="return false;">
<input type="text" name="message" value="Hello, World!"/>
<input type="button" value="Send SockJS data" οnclick="send(this.form.message.value)"/>
</form>
</body>
</html>
服务器端创建一个SocketJS的Server:
def server = vertx.createHttpServer()
// Serve the index page
server.requestHandler { req ->
if (req.uri == "/") req.response.sendFile 'sockjs/index.html'
}
// The handler for the SockJS app - we just echo data back
vertx.createSockJSServer(server).installApp(prefix: '/testapp') { sock ->
sock.dataHandler { buff ->
sock << buff
}
}
server.listen(8080)
消息模型
使用vert.x可以实现1:N和1:1的消息模型。Publish/subscribe如下:
JS代码如下:
var eb = require("vertx/event_bus");
var console = require("vertx/console");
var vertx = require("vertx")
vertx.setPeriodic(1000, function sendMessage() {
eb.publish('news-feed', 'some news!');
})
订阅者:
var eb = require("vertx/event_bus");
var console = require("vertx/console");
eb.registerHandler("news-feed", function(message) {
console.log('Received news ' + message);
});
一对一的队列方式:
发布者JS代码如下:
var eb = require("vertx/event_bus");
var console = require("vertx/console");
var vertx = require("vertx")
vertx.setPeriodic(1000, function sendMessage() {
eb.send('ping-address', 'ping!', function(reply) {
console.log("Received reply: " + reply);
});
})
订阅者代码如下:
var eb = require("vertx/event_bus");
var console = require("vertx/console");
eb.registerHandler("ping-address", function(message, replier) {
console.log('Received message ' + message);
// Now reply to it
replier('pong!');
});
共享数据
消息传递是非常有用的。然而,它并不总是在所有类型的应用程序解决并发的最好选择。缓存也是最流行的解决方式之一。如果只有一个Verticle有一一个缓存,这是非常低效的。当其他Verticles需要缓存,每个Verticle应该共享相同的缓存数据。 因此,Vert.x提供全局访问共享的MAP的方式,称为 Shared Map。 共享的是不变性数据。
与NodeJS比较
安装使用
vertx使用了invokeDynamic in JDK7.因此需要JDK7环境。从 http://vertx.io/downloads.html下载,解压,将bin目录加入环境PATH中。在windows环境可以执行vertx version
比如建设一个JS编写的服务器代码:
load('vertx.js');
vertx.createHttpServer().requestHandler(function(req) {
req.response.end("Hello World!");}) .listen(8080, 'localhost');
在命令行只有运行下面命令可启动这个服务器:
$ vertx run server.js
在浏览器打开http://localhost:8080,可看到hello World