因为在实验室的项目中使用到了WAMP,所以去搜了各大网站有关于这个协议的信息,但是网上的文章少之可怜,最为权威以及正确的也就是WAMP官网啦。所以接下来的知识讲解,都是笔者结合自己的实际经验以及用那点略显拙劣的英文水平翻译官网总结出来的,如果有错误也希望大家积极的提出来,共同进步!!
同样,为了更好的验证代码的正确,依旧是贴GitHub地址(但是地址路由器地址全都改啦,这个就需要自己配置安装啦!!)

一、WAMP是什么?
1.1 介绍
WAMP(Web Application Messaging Protocol)是一种路由协议,所有组件都连接到WAMP路由器上,WAMP路由器在组件之间执行消息路由。其设计目的是为应用程序组件之间的实时消息交换提供开放标准。
WAMP需要一个可靠的,有序的全双工消息通道作为传输层,默认情况下使用 WebSocket。但是,真正的实现也可以使用与这些特性匹配的其他传输,比如原始套接字或HTTP长轮询与WAMP进行通信,或者使用RawSocket。
这里比较一下这两个传输协议:
- Websocket:使用Websocket传输,可以创建持久的双向连接。消息没有HTTP头,这可以最大限度的减少开销
- RawSocket:RawSocket上的WAMP使用的二进制消息,不需要事先HTTP握手,具有较小的开销。但是它最大的缺点就是浏览器不允许TCP连接。要在Web浏览器上使用客户端,必须安装浏览器扩展,同时另一个缺点是缺少传输级压缩。
1.2 传递模式
WAMP提供了两种消息传递模式:
- Publish & Subscribe(发布订阅)
- routed Remote Procedure Calls (rRPC)(路由远程过程调用)
发布和订阅
发布和订阅是一种已建立的消息传递模式。其中组件(订阅者)通知路由器它想要接收的主题的信息(也就是它要订阅主题),然后,另一个组件(发布者)可以发布关于某个主题的消息,最后。路由器将事件分发给所有订阅该主题的组件。
路由远程过程调用
路由远程过程调用依赖于发布和预定模式。一个组件(Callee)向路由器提供一个特定的过程。其他组件(Callers)可以调用该过程。路由器调用Callee上的过程,接收过程的结果,然后将此结果转发回Caller。
路由RPC和传统的客户端-服务端RPC的不同之处在于路由器充当呼叫者和被呼叫者之间的中介,传统的RPC直接调用(通俗讲:RPC 是指计算机 A 上的进程,调用另外一台计算机 B 上的进程,其中 A 上的调用进程被挂起,而 B 上的被调用进程开始执行,当值返回给 A 时,A 进程继续执行。)
路由RPC的优点:
- 因为所有消息都在WAMP路由上,因此不再需要在Callee和Caller之间建立直接网络连接或路径。
二、WAMP中的消息路由
WAMP在开放的WebSocket协议中提供统一的应用程序路由,该协议适用于不同的语言。
使用WAMP,可以从应用程序组件构建分布式系统,这些组件低耦合并实时通信。
先在这里列出WAMP的一些特点(优点)
- 低耦合
- 基于组件
- 实时通信
- 语言独立
2.1 低耦合
WAMP提供了我们称之为应用程序通信的统一应用程序路由:
- 发布和订阅者模式中的事件路由
- 远程过程调用模式中的呼叫路由
接下来让我们分别比较传统方式与WAMP中发布订阅模式和路由远程过程调用得出这些特点。
传统方式
先看一看传统方式“客户端-服务器”的模式,在这种模式中,远程过程调用直接从主叫方到被叫方:

WAMP中的发布-订阅模式
在该模式中,发布者将消息(主题)传递给事件路由,并且订阅者仅仅是通过对消息(主题)的订阅,在事件路由接收到该消息(主题)后,将消息(主题)传递给订阅者。
整个过程中,发布者和订阅者之间是间接的完成整个过程的,对方都不需要了解彼此

WAMP中的路由远程过程调用
这个过程和发布-订阅的模式类似,当调用者想要调用远程过程时,它会和呼叫路由通信,并且只提供调用过程的URI以及调用参数,然后呼叫路由会在注册的被调用者中进行查找并调用,之后将结果路由回来。两者之间没有直接的调用。

以上三种模式的比较可以得出,WAMP提供的两种模式都是通过添加路由器(或者通俗理解就是中介),免去调用者和被调用者之间的之间交流,以较低组件之间的耦合。这也是WAMP的优点之一了。
2.2基于组件
如果将事件路由(Broker--用于发布订阅)和呼叫路由(Dealer--用于路由远程过程调用)组合在一起就会得到WAMP中路由器。

如果将这两者组合成一个路由器,那么这一个路由器就可以担任起两个作用。
2.3 实时
WebSocket是一种新的Web协议,它可以在需要双向实时通信时克服HTTP的限制,并且它提供了与Web和浏览器兼容的双向实时消息传递,除此之外,还可以在费浏览器环境中运行WebSocket。
WAMP是一个正式注册的WebSocket子协议(运行在WebSocket之上),使用JSON作为消息序列化格式。
因为WAMP也支持实时双向通信。
2.4 语言无关
WAMP拥有许多常见和不常见的语言功能的一流支持。
这也就意味着WAMP可以实现及时使用--无需关心语言。

三、比较WAMP
根据以下六个标准对WAMP以及AJAX、Socket.IO和MQTT进行比较(其它技术没有使用过,在这不多阐述)
3.1 六个标准
- 是否支持发布订阅模式
- 是否支持远程过程调用
- 是否支持路由远程过程调用
- 是否适用于不同的编程语言
- 是否有开放式的官方标准
- 是否本身就运行在网络上
技术 | 发布订阅 | RPC | 路由RPC | 网络原生 | 跨语言 | 开放标准 |
---|---|---|---|---|---|---|
WAMP | √ | √ | √ | √ | √ | √ |
MQTT | √ | - | - | - | √ | √ |
Socket.IO | √ | - | - | √ | - | - |
AJAX | - | √ | - | √ | √ | - |
AJAX
AJAX既不是协议也不是API,而是浏览器中JavaScript的编程模式,它使用HTTP请求实现前端(浏览器)和后端之间类似RPC的通信。
即使使用AJAX实现了RPC机制,这也是点对点RPC,调用不是在不同服务器或应用程序至今路由,而是严格从点(浏览器)到点(浏览器连接到的服务器),除此之外,AJAX不提供发布订阅模式(可以说是没有解决服务器主动推送消息到服务器)。
因为AJAX使用普通的HTTP头信息,所以它的开销和限制会比较大。
Socket.IO
Socket.IO是一个用JavaScript编写的客户端-服务端(发布订阅)的服务实现。使用服务器端的Node.js和浏览器进行通信。
与WAMP相比,Socket.IO库允许订阅不同的主题,具有广播消息和消息命名空间的功能。二进制数据传递是可能的,但是需要双方的二外模块(Socket.io-stream)。
Socket.IO不提供远程过程调用。

四、实现
4.1 Library(库)
为了在不同语言上编写程序组件,需要选择适合的WAMP库。也就是说,我使用不同的语言,要选择该语言版本的库。
这里只说一下 AutobahnJS,该库可以运行在JavaScript、HTML5以及NodeJS环境中。
贴一个GitHub地址,详细介绍参看这个网址就可以了,其实它的使用理解下来与Socket.IO差不多,只不过在使用之前需要在服务器上安装路由器(下面会讲),这里就贴一下基本使用的代码。
模式一:发布订阅模式
publicer(发布者)代码:
//引入库
var autobahn = require('autobahn');
//创建连接
var connection = new autobahn.Connection({
url: 'ws://xxx.xxx.xxx:xxx/ws', //路由器的地址
realm: 'realm1' //域(类似于Socket.io中的命名空间)
});
//监听连接
connection.onopen = function (session) {
console.log('连接成功');
//发布消息,第一个参数为主题,第二个参数为发布内容
session.publish('wamptopic', ['Hello,World!']);
};
//打开连接
connection.open();
receiver(订阅者)代码:
//引入库
var autobahn = require('autobahn');
//创建连接
var connection = new autobahn.Connection({
url: 'ws://xxx.xxx.xxx.xxx:xxx/ws', //路由器的地址
realm: 'realm1' //域(类似于Socket.io中的命名空间)
});
//监听连接
connection.onopen = function (session) {
function onEvent(args) {
var msg = args[0];
console.log(" received: " + msg);
}
//订阅主题,第一个参数是主题,第二个参数是接收信息的回调函数
session.subscribe('wamptopic', onEvent)
};
//打开连接
connection.open();
注意
- 一定要保证订阅者和发布者所在的域是一样的(即要保证ream的值是一样的)
- 订阅者在收到的数据是一个数组(文档提供)
模式二:路由远程调用模式
callee(调用者)
//引入库
var autobahn = require('autobahn');
//创建连接
var connection = new autobahn.Connection({
url: 'ws://xxx.xxx.xxx.xxx:xxx/ws', //路由器的地址
realm: 'realm1' //域(类似于Socket.io中的命名空间)
});
//监听连接
connection.onopen = function (session) {
function onEvent(args) {
return args[0] + args[1];
}
//订阅主题,第一个参数是主题,第二个参数是接收、处理并返回信息的回调函数
session.register('wamptopic', onEvent)
};
//打开连接
connection.open();
caller(被调用者)
//引入库
var autobahn = require('autobahn');
//创建连接
var connection = new autobahn.Connection({
url: 'ws://xxx.xxx.xxx.xxx:xxx/ws', //路由器的地址
realm: 'realm1' //域(类似于Socket.io中的命名空间)
});
//监听连接
connection.onopen = function (session) {
console.log('连接成功');
//发布消息,第一个参数为主题,第二个参数为传递的参数,之后是接收调用的返回结果
session.call('wamptopic', [2, 3]).then((res) => {
console.log('结果', res)
})
};
//打开连接
connection.open();
4.2 Routers(路由器)
官网上提供了很多路由器的库,因为笔者实验室用到的是Crossbar,所以对于其他路由库的配置也不清楚。配置好之后的地址就是笔者在上面xxx.xxx.xxx.xxx:xx所替代的东西。 官网上的配置步骤非常的详细,即使如此,可能会遇到各种Bug,这就需要就不同的问题自己进行解决啦~~
五、与websocket有什么区别
讲到这里,其实WAMP的使用以及与Websocket的关系和区别,在上面几乎都已经说明,如果没有完全读懂,说明可能对WebSocket不太熟悉,那么这里笔者毛遂自荐了解Websocket及Socket.io去读一读,再来和这篇文章作对比,就会发现它们的关系和区别啦~~~
