仪表盘在监控系统和运行环境时是一种非常重要的工具.通常的做法是客户端实时更新来自服务端的数据.这种做法在网页上最常用的技术是ajax轮询.使用ajax时,客户端每隔一段时间就请求服务器以更新数据.
但是如果监控的目的是为了显示实时的数据信息,那么使用ajax轮询时就会有许多的问题.第一问题是性能问题.如果轮询的间隔时间设置的非常小,那么大量的请求将会增加服务器的复杂.当然还不仅仅是服务器,网络也会被这些大量的请求堵塞.然而如果我们把轮询的间隔时间设置的非常长,那么信息可能会有较大的延迟.另一个问题是服务端返回的信息可能并不包含任何数据.这种情况会导致一些无用的ajax轮询请求.
首先,我要引用我的一段代码,是关于如何使用Jetty和Gradle创建并 嵌入war 的。为了让这个引用简短些,我假设你已经读过了这些内容,或者你对于这些项目有充分的认知。
其次,在解释我们样例的开始阶段,我们将加入对于内嵌Jetty中websockets的支持。因此,你需要创建你自己的build文件,并命名为“ build.gradle ”,代码如下所示。请注意那些红色的代码行,他们负责导入所要的支持。
// Tells gradle it is a war project which will be imported into eclipse wtp apply plugin: 'war' apply plugin: 'eclipse-wtp' // Define souce code compatibility sourceCompatibility = 1.7 targetCompatibility = 1.7 // Use maven repository repositories { mavenCentral() } // Fills out all dependencies which are necessary to start the embedded jetty into our war file configurations { embeddedJetty } dependencies { embeddedJetty 'org.eclipse.jetty:jetty-servlet:+' embeddedJetty 'org.eclipse.jetty:jetty-webapp:+' embeddedJetty 'org.eclipse.jetty:jetty-jsp:+' embeddedJetty 'org.eclipse.jetty:jetty-annotations:+' // add support for jetty implementation of websockets embeddedJetty 'org.eclipse.jetty.websocket:websocket-server:+' compile 'org.glassfish:javax.json:+' } war.baseName = 'WebSockets' war { // unzip and add all jetty dependencies into the root of our war file from {configurations.embeddedJetty.collect { project.zipTree(it) } } // remove signature and unnecessary files exclude "META-INF/*.SF", "META-INF/*.RSA", "about.html", "about_files/**", "readme.txt", "plugin.properties", "jetty-dir.css" // include only the classes which will be used to start Embedded Jetty from "$buildDir/classes/main" exclude "com/myapp/" // tells the class to run when the generate war be executed using 'java -jar' manifest { attributes 'Main-Class': 'com.embedded.JettyStarter' } } // Once you will need some basic api (e.i. servlet api) for compilation, add embeddedJetty dependencies for compilation sourceSets.main.compileClasspath += configurations.embeddedJetty // the same for eclipse classpath, so you can use it to edit your java files eclipse { classpath { plusConfigurations += configurations.embeddedJetty } }
不幸的是,Jetty 9 到现在为止还不支持 JSR 356(Java API for WebSocket),因此我们这个例子会使用其目前所提供的 API (顺带插一句,这个 API 与 JSR 356 是很类似的)。对于其而言,使用类似于下面的代码去创建一个类用于实现 WebSocket。
import org.eclipse.jetty.websocket.api.Session; import org.eclipse.jetty.websocket.api.annotations.OnWebSocketClose; import org.eclipse.jetty.websocket.api.annotations.OnWebSocketConnect; import org.eclipse.jetty.websocket.api.annotations.WebSocket; @WebSocket public class DashboardWebSocket { @OnWebSocketClose public void onClose(final Session session, int x, String text) { EventGenerator.unregistry( new EventObserver(session) ); } @OnWebSocketConnect public void onOpen(final Session session) { EventGenerator.registry( new EventObserver(session) ); } }上面这个类最为重要的部分是 onOpen(...) 和 onClose(...) 两个方法,它们会在任何一个 WebSocket 连接打开和关闭的时候执行。在这个例子中,每当有用户进入这个应用时,会将会话信息作为一个事件观察者进行注册。这样做的目的,可以将服务端所发生的任何事件广播给已注册的会话。同样的方式,在用户关闭浏览器连接丢失时,会话信息将会从事件观察者列表中注销。
import org.eclipse.jetty.websocket.servlet.WebSocketServlet; import org.eclipse.jetty.websocket.servlet.WebSocketServletFactory; @SuppressWarnings("serial") @WebServlet(name = "Dashboard WebSocket Servlet", urlPatterns = { "/dashboard" }) public class DashboardServet extends WebSocketServlet { @Override public void configure(WebSocketServletFactory factory) { factory.register(DashboardWebSocket.class); } }一旦这些步骤完成,就需要使用JavaScript API来打开初始链接,然后等待从服务器过来的新消息。注意,下面的JS代码中,我们使用了 ‘ws’ 协议而不是 ‘http’ 协议。这是必须的,因为W ebSocket实际上是一个不同的协议。
<script> var load = function() { var socket = new WebSocket("ws://localhost:8081/dashboard"); socket.onmessage = function(event) { // show the event.data in your graph, table or whatever } } </script>上面的代码中,我们展示了一个非常简单的实现类,其中一个 WebSocket 被打开,并且对每个服务器事件,都能收到通过函数 onmessage(..) 的参数传递过来是数据。下图是本应用运行情况的一个截图展示。其中点的横坐标代表时间,纵坐标代表某时刻事件发生的数量 (为了例子更直观,我们使用了一个从0 到100.000的随机数) 。
评论删除后,数据将无法恢复
评论(1)