使用 WebSockets 来实现 Dashboards 已翻译 100%

oschina 投递于 2013/06/25 07:45 (共 7 段, 翻译完成于 06-26)
阅读 1470
收藏 8
0
加载中

仪表盘在监控系统和运行环境时是一种非常重要的工具.通常的做法是客户端实时更新来自服务端的数据.这种做法在网页上最常用的技术是ajax轮询.使用ajax时,客户端每隔一段时间就请求服务器以更新数据.

但是如果监控的目的是为了显示实时的数据信息,那么使用ajax轮询时就会有许多的问题.第一问题是性能问题.如果轮询的间隔时间设置的非常小,那么大量的请求将会增加服务器的复杂.当然还不仅仅是服务器,网络也会被这些大量的请求堵塞.然而如果我们把轮询的间隔时间设置的非常长,那么信息可能会有较大的延迟.另一个问题是服务端返回的信息可能并不包含任何数据.这种情况会导致一些无用的ajax轮询请求.

酒逍遥
翻译于 2013/06/25 09:43
1

很幸运,2011年12月诞生了一种新的名叫WebSocket的协议。协议的规格说明写道:“此项技术的目的,在于提供一种机制,用于浏览器应用程序和服务器之间的双向通信,这种通信并不依赖打开多个HTTP连接。”换句话说,假如客户端建立了初始会话,WebSocket就可以让你能够发送从服务器到客户端的消息帧。

经过以上分析,显而易见的结论是,很多基于浏览器的程序,例如仪表板,聊天、游戏、看板、计划牌等等,都应当改用WebSocket实现。因此本帖将演示如何用WebSocket实现一个极简单的仪表板程序。

l
翻译于 2013/06/25 14:49
1

首先,我要引用我的一段代码,是关于如何使用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
       }
}
御豪同学
翻译于 2013/06/25 12:42
1

不幸的是,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 连接打开和关闭的时候执行。在这个例子中,每当有用户进入这个应用时,会将会话信息作为一个事件观察者进行注册。这样做的目的,可以将服务端所发生的任何事件广播给已注册的会话。同样的方式,在用户关闭浏览器连接丢失时,会话信息将会从事件观察者列表中注销。
frankiegao123
翻译于 2013/06/25 23:54
1
除了WebSocket本身,也要用到Jetty API,你需要实现一个WebSocketServlet接口来映射并注册WebSocket。在我们的例子中,每次“/dashboard”这个URI 被使用,都会被重定向到这个 servlet。
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的随机数) 。

lwei
翻译于 2013/06/26 09:12
1

简而言之,我要说WebSocket是建立现代web应用的非常重要的技术之一,在不久的将来,它将大量应用于企业软件以及一般的Web应用程序。然而,WebSockets成为取代长轮询功能的独特战略仍然是复杂的,因为还存在不理解或支持“ws“协议的防火墙和代理服务器。此外,在Java世界中,还只有少数对JSR 356标准提供支持的应用服务器。所以,我们仍要等待几个月来使用这种标准规定的API(注:GlassFish已经提供了这样的实现)。

赵亮-碧海情天
翻译于 2013/06/25 20:36
1
如果你想学习和/或运行该例子,你可以从 这里  下载全部源代码。要启动仪表盘,你必须打一个 war,然后使用下面的命令‘ java -jar WebSockets.war ’来运行它。 这会在8081端口开启一个Jetty服务,所以你可以在浏览器中访问如下地址 http://localhost:8081/ ,随意浏览。 

还有,如果你想了解websocket消息结构是如何发送到浏览器的,我建议你打开 ‘Chrome 开发者工具’  并选择 ‘网络(NetWork)’ 标签。在这里,你可以看到本应用示例将不会产生 ‘http’ 请求。而是只有一个 ‘ws’  链接。点击该 ‘ws’  链接,你将可以看到从服务器返回的消息结构,如下图所示


感谢您的阅读!
lwei
翻译于 2013/06/26 09:30
1
本文中的所有译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接。
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。
加载中

评论(1)

凡梦星尘
凡梦星尘
如何使用STOMP协议实现上述功能呢???
返回顶部
顶部
返回顶部
顶部