WebSocket子协议STOMP详解

  • Post author:
  • Post category:其他



1. STOMP简介

STOMP即Simple (or Streaming) Text Orientated Messaging Protocol,它是一个简单的文本消息传输协议,提供了一个可互操作的连接格式,允许STOMP客户端与任意STOMP消息代理(Broker)进行交互。STOMP协议由于设计简单,易于开发客户端,因此在多种语言和多种平台上得到广泛地应用。


2. 协议支持

  • STOMP 1.0
  • STOMP 1.1 (including heart-beating)


3. 下载JavaScript客户端包文件

stomp.min.js,可直接使用于web项目中


4. STOMP API介绍


4.1 STOMP Frame(帧)

STOMP Over WebSocket 提供了一个直接从 STOMP frame到JavaScript object的映射。

Frame Object

Property

Type

Notes

command

String

name of the frame (“CONNECT”, “SEND”, etc.)

headers

JavaScript object

body

String

command和headers属性始终会被定义,但是当这个frame没有头部信息时,headers参数可以为空,用{}表示。若这个frame没有body(主体内容),body的值可以为null。


4.2 STOMP客户端创建(普通的WebSocket方式)

STOMP JavaScript客户端将使用URL为 ws:// 与STOMP server建立通信。

使用 Stomp.client(url) 来创建STOMP客户端的js对象,如:

var url = "ws://localhost:61614/stomp";
var client = Stomp.client(url);

使用Stomp.client(url, protocols)可以覆盖默认的subprotocols,如protocols为[‘v10.stomp’,’v11.stomp’] 基于STOMP 1.0 & 1.1规范,第二个参数可以为单独的一个String类型,也可以为一个String的数组类型。


4.3 STOMP客户端创建(自定义WebSocket方式)

web浏览器支持不同的WebSocket协议版本,但是一些老版本的浏览器不支持WebSocket方式创建的js脚本,可以使用stomp.js,stomp.js会使用浏览器原生的WebSocket class来创建WebSocket。

使用Stomp.over(ws)方法,ws参数可以指定其他类型的WebSocket(如SockJS包装的WebSocket)

<script src="http://cdn.sockjs.org/sockjs-0.3.min.js"></script>
<script>
    // 使用SockJS实现而不是浏览器的本地实现
    var ws = new SockJS(url);
    var client = Stomp.over(ws);
    [...]
</script>


4.4 在node.js应用中的使用

使用npm安装stompjs

> npm install stompjs

在node.js程序中通过require导入stompjs模块

var Stomp = require('stompjs');

建立一个基于TCP socket的连接到STOMP代理

var client = Stomp.overTCP('localhost', 61613);

建立一个基于Web Socket的连接到STOMP代理

var client = Stomp.overWS('ws://localhost:61614/stomp');

除了初始化不同,无论是浏览器还是node.js环境下,Stomp API都是相同的。


4.5 连接到服务端

当STOMP client创建好后,通过connect()方法进行与STOMP seerver的连接和认证,connect方法可接受多个参数来提供简单的API。

client.connect(login, passcode, connectCallback);
client.connect(login, passcode, connectCallback, errorCallback);
client.connect(login, passcode, connectCallback, errorCallback, host);

注:login与passcode是String类型,connectCallback与errorCallback是回调函数,host为String类型

如果需要附加一些其他的信息,可以通过传递一个headers参数。

client.connect(headers, connectCallback);
client.connect(headers, connectCallback, errorCallback);

注:headers为map类型, connectCallback与errorCallback为回调函数

如附加的headers参数

var headers = {
    login: 'mylogin',
    passcode: 'mypasscode',
    // additional header
    'client-id': 'my-client-id'
};
client.connect(headers, connectCallback);

也可使用{}来表示不附加任何headers参数

断开连接时,调用disconnect方法,这个方法也是异步的,当断开成功后会接收一个额外的回调函数的参数,如:

client.disconnect(function() {
   alert("See you next time!");
};

当客户端client断开连接后,不能再发送和接收任何messages。


4.6 心跳检测(heart-beating)

如果STOMP 代理接收的帧是STOMP 1.1协议时,默认心跳检测是开启的。

客户端client对象有一个字段 heartbeat ,通过改变incoming和outgoing数值,来配置心跳频率(默认频率值为:10000ms)

client.heartbeat.outgoing = 20000; // 客户端每20000ms发送一次心跳检测
client.heartbeat.incoming = 0;     // client不接收serever端的心跳检测

注:heart-beating是利用window.setInterval()去规律地发送heart-beats或者检查服务端的heart-beats


4.7 发送消息

当客户端与服务端连接成功后,可以调用send()方法来发送STOMP消息。这个方法必须有一个参数, 用来描述对应的STOMP的目的地。另外可以有两个可选的参数:headers,object类型,包含额外的信息头;body为一个String类型的参数。

client.send("/queue/test", {priority: 9}, "Hello, STOMP");

代码解释:客户端将发送一个STOMP的帧到 /queue/test 地址的目的地,携带的headers参数priority为9,消息体为Hello, STOMP

注:如果你想发送一个有消息体(body)的信息,也必须传递headers参数。如果没有headers需要传递,可以用{}来表示,如:

client.send(destination, {}, body);


4.8 订阅(Subscribe)和接收消息

浏览器接收一个消息,STOMP客户端首先必须订阅一个目标地址destination。

使用subscribe()方法订阅,该方法接收两个必选的参数destination String类型的目的地址和callback回调函数,还有一个可选的参数headers

var subscription = client.subscribe("/queue/test", callback);

subscribe()方法返回一个js对象,这个对象包含一个id属性,对应这个这个客户端的订阅id。而unsubscribe()可以用来取消客户端对这个目的地destination的订阅。

默认情况下,如果没有在headers参数中额外添加,这个库会默认构建一个唯一的id。在传递headers这个参数时,可以使用你自己的id如:

var mysubid = '...';
var subscription = client.subscribe(destination, callback, { id: mysubid });

客户端发送一个STOMP的订阅帧到服务端,并注册一个回调函数,每当服务端发送消息到客户端时,客户端都会调用回调函数,回调函数中的参数为STOMP帧对象。

callback = function(message) {
    // called when the client receives a STOMP message from the server
    if (message.body) {
      alert("got message with body " + message.body)
    } else {
      alert("got empty message");
    }
});

subscribe()方法接收一个可选的参数headers,当订阅一个目的地destination的时候可以附加该参数

var headers = {ack: 'client', 'selector': "location = 'Europe'"};
client.subscribe("/queue/test", message_callback, headers);

代码解释:该客户端只接收匹配selector location = ‘Europe’的消息,并且会确认接收到的message

注:如果想让客户端订阅多个目的地destinations,可以使用同一个回调函数来接收messages

onmessage = function(message) {
    // called every time the client receives a message
}
var sub1 = client.subscribe("queue/test", onmessage);
var sub2 = client.subscribe("queue/another", onmessage);

调用unsubscribe()方法可以终止接收messages

var subscription = client.subscribe(...);
  
...
  
subscription.unsubscribe();


4.9 JSON的支持

如果想发送和接收JSON对象的消息,可以通过JSON.stringify() 和 JSON.parse()来转换

var quote = {symbol: 'APPL', value: 195.46};
  client.send("/topic/stocks", {}, JSON.stringify(quote));

  client.subcribe("/topic/stocks", function(message) {
  var quote = JSON.parse(message.body);
  alert(quote.symbol + " is at " + quote.value);
};


4.10 消息确认

默认情况,在消息发送给客户端之前,服务端会自动确认。

客户端可以选择通过订阅一个目的地destination时设置一个ack,header为client或client-individual来处理消息确认。

在下面这个例子,客户端必须调用message.ack()来通知客户端它已经接收了消息。

var subscription = client.subscribe("/queue/test",
    function(message) {
      // do something with the message
      ...
      // and acknowledge it
      message.ack();
    },
    {ack: 'client'}
);

使用nack()方法可以通知STOMP 1.1 代理,客户端没有消费该消息,和ack()方法的参数相同


4.11 事务(Transactions)

消息的发送确认可以在一个事务中处理。

通过调用客户端自身的begin()方法来启动事务,该方法有一个可选的参数transaction,String类型,唯一标识一个事务,如果没有传递参数,那么stompjs库会自动生成。该方法会返回一个js对象,包含一个id属性,对应该事务的ID。

另外两个方法:

  • commit()提交事务
  • abort()终止事务

在一个事务中,客户端可以在发送/接受消息时指定transaction id来设置transaction。

// start the transaction
var tx = client.begin();
// send the message in a transaction
client.send("/queue/test", {transaction: tx.id}, "message in a transaction");
// commit the transaction to effectively send the message
tx.commit();

注:如果在调用send()方法发送消息时未指定transaction头信息,则该事务不起作用,如:

var txid = "unique_transaction_identifier";
// start the transaction
var tx = client.begin();
// oops! send the message outside the transaction
client.send("/queue/test", {}, "I thought I was in a transaction!");
tx.abort(); // Too late! the message has been sent


4.12 Debug调试

有一些测试代码能有助于你知道库发送或接收的是什么,从而来调试程序。

客户端可以将其debug属性设置为一个函数,传递一个字符串参数去观察库所有的debug语句。

client.debug = function(str) {
    // append the debug log to a #debug div somewhere in the page using JQuery:
    $("#debug").append(str + "\n");
};


转载于:https://my.oschina.net/feinik/blog/853875