简介
有时候,我们可能会希望在远程过程调用中对通讯的一些细节有更多的控制,比如对传输中的数据进行加密、压缩、签名、跟踪、协议转换等等,但是又希望这些工作能够跟服务函数/方法本身可以解耦。这个时候,Hprose 过滤器就是一个不错的选择。
Hprose 过滤器是一个接口,它有两个方法:
- inputFilter(data, context)
- outputFilter(data, context)
其中 inputFilter
的作用是对输入数据进行处理,outputFilter
的作用是对输出数据进行处理。
data
参数就是输入输出数据,它是 Uint8Array
类型的。这两个方法的返回值也是 Uint8Array
类型的数据,它表示已经处理过的数据,如果你不打算对数据进行修改,你可以直接将 data
参数作为返回值返回。
context
参数是调用的上下文对象,我们在服务器和客户端的介绍中已经多次提到过它。
执行顺序
不论是客户端,还是服务器,都可以添加多个过滤器。假设我们按照添加的顺序把它们叫做 filter1
, filter2
, … filterN
。那么它们的执行顺序是这样的。
在客户端的执行顺序
+------------------- outputFilter -------------------+
| +-------+ +-------+ +-------+ |
| |filter1|----->|filter2|-----> ... ----->|filterN| |---------+
| +-------+ +-------+ +-------+ | v
+----------------------------------------------------+ +---------------+
| Hprose Server |
+-------------------- inputFilter -------------------+ +---------------+
| +-------+ +-------+ +-------+ | |
| |filter1|<-----|filter2|<----- ... <-----|filterN| |<--------+
| +-------+ +-------+ +-------+ |
+----------------------------------------------------+
在服务器端的执行顺序
+-------------------- inputFilter -------------------+
| +-------+ +-------+ +-------+ |
+-------->| |filterN|-----> ... ----->|filter2|----->|filter1| |
| | +-------+ +-------+ +-------+ |
+---------------+ +----------------------------------------------------+
| Hprose Client |
+---------------+ +------------------- outputFilter -------------------+
^ | +-------+ +-------+ +-------+ |
+---------| |filterN|<----- ... <-----|filter2|<-----|filter1| |
| +-------+ +-------+ +-------+ |
+----------------------------------------------------+
跟踪调试
有时候我们在调试过程中,可能会需要查看输入输出数据。用抓包工具抓取数据当然是一个办法,但是使用 过滤器可以更方便更直接的显示出输入输出数据。
logfilter.js
- var hprose = require('hprose');
- function log(data) {
- console.log(hprose.BytesIO.toString(data));
- return data;
- }
- module.exports = {
- inputFilter: log,
- outputFilter: log
- };
server.js
- var hprose = require("hprose");
- var logfilter = require("./logfilter.js");
- function hello(name) {
- return "Hello " + name + "!";
- }
- var server = hprose.Server.create("http://0.0.0.0:8080");
- server.addFilter(logfilter);
- server.add(hello);
- server.start();
client.js
- var hprose = require("hprose");
- var logfilter = require("./logfilter.js");
- var client = hprose.Client.create("http://127.0.0.1:8080/", ['hello']);
- client.addFilter(logfilter);
- client.hello("world", function(result) {
- console.log(result);
- });
然后分别启动服务器和客户端,就会看到如下输出:
服务器输出
Cs5"hello"a1{s5"world"}z
Rs12"Hello world!"z
客户端输出
Cs5"hello"a1{s5"world"}z
Rs12"Hello world!"z
Hello world!
压缩传输
上面的例子,我们只使用了一个过滤器。在本例中,我们展示多个过滤器组合使用的效果。
CompressFilter.js
- var hprose = require('hprose');
- var compressjs = require('compressjs');
- function CompressFilter(algorithmName) {
- this.algorithm = compressjs[algorithmName];
- }
- CompressFilter.prototype.inputFilter = function(data) {
- return this.algorithm.decompressFile(data);
- };
- CompressFilter.prototype.outputFilter = function(data) {
- return this.algorithm.compressFile(data);
- };
- module.exports = CompressFilter;
SizeFilter.js
- var hprose = require('hprose');
- function SizeFilter(message) {
- this.message = message;
- }
- SizeFilter.prototype.inputFilter = function(data) {
- console.log(this.message + ' input size: ' + data.length);
- return data;
- };
- SizeFilter.prototype.outputFilter = function(data) {
- console.log(this.message + ' output size: ' + data.length);
- return data;
- };
- module.exports = SizeFilter;
server.js
- var hprose = require("hprose");
- var CompressFilter = require("./CompressFilter.js");
- var SizeFilter = require("./SizeFilter.js");
- function echo(value) {
- return value;
- }
- var server = hprose.Server.create("http://0.0.0.0:8080");
- server.addFilter(new SizeFilter('Non compressed'));
- server.addFilter(new CompressFilter('Lzp3'));
- server.addFilter(new SizeFilter('Compressed'));
- server.add(echo);
- server.start();
client.js
- var hprose = require("hprose");
- var CompressFilter = require("./CompressFilter.js");
- var SizeFilter = require("./SizeFilter.js");
- var client = hprose.Client.create("http://127.0.0.1:8080/", ['echo']);
- client.addFilter(new SizeFilter('Non compressed'));
- client.addFilter(new CompressFilter('Lzp3'));
- client.addFilter(new SizeFilter('Compressed'));
- var value = [];
- for (var i = 0; i < 100000; i++) {
- value[i] = i;
- }
- client.echo(value, function(result) {
- console.log(result.length);
- });
然后分别启动服务器和客户端,就会看到如下输出:
服务器输出
Compressed input size: 127233
Non compressed input size: 688893
Non compressed output size: 688881
Compressed output size: 127217
客户端输出
Non compressed output size: 688893
Compressed output size: 127233
Compressed input size: 127217
Non compressed input size: 688881
100000
在这个例子中,压缩我们使用了 compressjs
这个库,运行前需要自行安装一下。这个库支持的压缩算法还有很多,读者有兴趣的话,可以将这个例子中的 Lzp3
算法换成其它几个算法进行一下对比。
加密跟这个类似,这里就不再单独举加密的例子了。
运行时间统计
有时候,我们希望能够对调用执行时间做一个统计,对于客户端来说,也就是客户端调用发出前,到客户端收到调用结果的时间统计。对于服务器来说,就是收到客户端调用请求到要发出调用结果的这一段时间的统计。这个功能,通过 过滤器也可以实现。
statfilter.js
- var hprose = require('hprose');
- function stat(data, context) {
- if ('starttime' in context.userdata) {
- var t = Date.now() - context.userdata.starttime;
- console.log('It takes ' + t + ' ms.');
- }
- else {
- context.userdata.starttime = Date.now();
- }
- return data;
- }
- module.exports = {
- inputFilter: stat,
- outputFilter: stat
- };
server.js
- var hprose = require("hprose");
- var statfilter = require("./statfilter.js");
- function echo(value) {
- return value;
- }
- var server = hprose.Server.create("http://0.0.0.0:8080");
- server.addFilter(statfilter);
- server.add(echo);
- server.start();
client.js
- var hprose = require("hprose");
- var statfilter = require("./statfilter.js");
- var client = hprose.Client.create("http://127.0.0.1:8080/", ['echo']);
- client.addFilter(statfilter);
- var value = [];
- for (var i = 0; i < 100000; i++) {
- value[i] = i;
- }
- client.echo(value, function(result) {
- console.log(result.length);
- });
然后分别启动服务器和客户端,就会看到如下输出:
服务器输出
It takes 190 ms.
客户端输出
It takes 234 ms.
100000
最后让我们把这个这个运行时间统计的例子跟上面的压缩例子结合一下,可以看到更详细的时间统计。
server.js
- var hprose = require("hprose");
- var CompressFilter = require("./CompressFilter.js");
- var SizeFilter = require("./SizeFilter.js");
- var statfilter = require("./statfilter.js");
- function echo(value) {
- return value;
- }
- var server = hprose.Server.create("http://0.0.0.0:8080");
- server.addFilter(statfilter);
- server.addFilter(new SizeFilter('Non compressed'));
- server.addFilter(new CompressFilter('Lzp3'));
- server.addFilter(new SizeFilter('Compressed'));
- server.addFilter(statfilter);
- server.add(echo);
- server.start();
client.js
- var hprose = require("hprose");
- var CompressFilter = require("./CompressFilter.js");
- var SizeFilter = require("./SizeFilter.js");
- var statfilter = require("./statfilter.js");
- var client = hprose.Client.create("http://127.0.0.1:8080/", ['echo']);
- client.addFilter(statfilter);
- client.addFilter(new SizeFilter('Non compressed'));
- client.addFilter(new CompressFilter('Lzp3'));
- client.addFilter(new SizeFilter('Compressed'));
- client.addFilter(statfilter);
- var value = [];
- for (var i = 0; i < 100000; i++) {
- value[i] = i;
- }
- client.echo(value, function(result) {
- console.log(result.length);
- });
然后分别启动服务器和客户端,就会看到如下输出:
服务器输出
Compressed input size: 127233
Non compressed input size: 688893
It takes 109 ms.
It takes 279 ms.
Non compressed output size: 688881
Compressed output size: 127217
It takes 428 ms.
客户端输出
Non compressed output size: 688893
Compressed output size: 127233
It takes 186 ms.
It takes 643 ms.
Compressed input size: 127217
Non compressed input size: 688881
It takes 740 ms.
100000
在这里,我们可以看到客户端和服务器端分别输出了三段用时。
服务器端输出:
第一个 109 ms
是解压缩输入数据的时间。
第二个 279 ms
是第一个阶段用时 + 反序列化 + 调用 + 序列化的总时间。
第三个 428 ms
是前两个阶段用时 + 压缩输出数据的时间。
客户端输出:
第一个 186 ms
是压缩输出数据的时间。
第二个 643 ms
是第一个阶段用时 + 从客户端调用发出到服务器端返回数据的总时间。
第三个 740 ms
是前两个阶段用时 + 解压缩输入数据的时间。
协议转换
Hprose 过滤器的功能不止于此,如果你对 Hprose 协议本身有所了解的话,你还可以直接在 过滤器中对输入输出数据进行解析转换。
在 Hprose for Node.js 中已经提供了一个现成的 JSONRPC 的过滤器。使用它,你可以将 Hprose 服务器变身为 Hprose + JSONRPC 双服务器。也可以将 Hprose 客户端变身为 JSONRPC 客户端。
JSONRPC 服务器
- var hprose = require('hprose');
- function hello(name) {
- return 'Hello ' + name + '!';
- }
- var server = hprose.Server.create("http://0.0.0.0:8080");
- server.addFilter(new hprose.JSONRPCServiceFilter());
- server.add(hello);
- server.start();
实现一个 JSONRPC 服务器就这么简单,只需要添加一个 JSONRPCServiceFilter
实例对象就可以了。而且这个服务器可以同时接收 Hprose 和 JSONRPC 两种请求。
JSONRPC 客户端
- var hprose = require("hprose");
- var client = hprose.Client.create("http://127.0.0.1:8080/", ['hello']);
- client.filter = new hprose.JSONRPCClientFilter();
- client.hello("world", function(result) {
- console.log(result);
- });
客户端也是同样的简单,只需要设置客户端的 filter
属性(或者用 addFilter 方法)为一个 JSONRPCClientFilter
实例对象,Hprose 客户端就马上变身为 JSONRPC 客户端了。不过需要注意一点,跟服务器不同,添加了 JSONRPCClientFilter
的客户端,是一个纯 JSONRPC 客户端,这个客户端只能跟 JSONRPC 服务器通讯,不能再跟纯 Hprose 服务器通讯了,但是跟 Hprose + JSONRPC 的双料服务器通讯是没问题的。
Hprose 过滤器的功能很强大,除了上面这些用法之外,你还可以结合服务器事件来实现更为复杂的功能。不过这里就不再继续举例说明了。
原文: https://github.com/hprose/hprose-nodejs/wiki/Hprose-%E8%BF%87%E6%BB%A4%E5%99%A8