-
Notifications
You must be signed in to change notification settings - Fork 88
Hprose 服务器
Hprose 2.0 for Node.js 支持多种底层网络协议绑定的服务器,比如:HTTP 服务器,Socket 服务器和 WebSocket 服务器。
其中 HTTP 服务器支持在 HTTP、HTTPS 协议上通讯。
Socket 服务器支持在 TCP、Unix Socket 协议上通讯,并且支持全双工和半双工两种模式。
WebSocket 服务器支持在 ws、wss 协议上通讯。
尽管支持这么多不同的底层网络协议,但除了在对涉及到底层网络协议的参数设置上有所不同以外,其它的用法都完全相同。因此,我们在下面介绍 Hprose 服务器的功能时,若未涉及到底层网络协议的区别,就以 HTTP 服务器为例来进行说明。
Hprose 的服务器端的实现,分为 Service
和 Server
两部分。
其中 Service
部分是核心功能,包括接收请求,处理请求,服务调用,返回应答等整个服务的处理流程。
而 Server
则主要负责启动和关闭服务器,它包括设置服务地址和端口,设置服务器启动选项,启动服务器,接收来自客户端的连接然后传给 Service
进行处理。
之所以分开,是为了更方便的跟已有的库和框架结合,例如:connect、express 等,这些库和框架都提供了丰富的中间件,在这种情况下,只需要把 Service
作为这些库和框架的中间件来使用就可以了,在这种情况下,我们就不需要使用 Server
了。
分开的另外一个理由是,Server
部分的实现是很简单的,有时候开发者可能会希望把 hprose 服务结合到自己的某个服务器中去,而不是作为一个单独的服务器来运行,在这种情况下,也是直接使用 Service
就可以了。
当开发者没有什么特殊需求,只是希望启动一个独立的 hprose 服务器时,那使用 Server
就是一个最方便的选择了。
创建服务器有多种方式,我们先从最简单的方式说起。
var hprose = require("hprose");
function hello(name) {
return "Hello " + name + "!";
}
var server = new hprose.Server("http://0.0.0.0:8080");
server.add(hello);
server.start();
var hprose = require("hprose");
function hello(name) {
return "Hello " + name + "!";
}
var options = {
key: fs.readFileSync('server-key.pem'),
cert: fs.readFileSync('server-cert.pem')
};
var server = new hprose.Server("https://0.0.0.0:8080", options);
server.add(hello);
server.start();
var hprose = require("hprose");
function hello(name) {
return "Hello " + name + "!";
}
var server = new hprose.Server("tcp://0.0.0.0:8080");
server.add(hello);
server.start();
var hprose = require("hprose");
function hello(name) {
return "Hello " + name + "!";
}
var options = {
key: fs.readFileSync('server-key.pem'),
cert: fs.readFileSync('server-cert.pem'),
requestCert: true,
ca: [ fs.readFileSync('client-cert.pem') ]
};
var server = new hprose.Server("tls://0.0.0.0:8080", options);
server.add(hello);
server.start();
var hprose = require("hprose");
function hello(name) {
return "Hello " + name + "!";
}
var server = new hprose.Server("unix:/tmp/my.sock");
server.add(hello);
server.start();
var hprose = require("hprose");
function hello(name) {
return "Hello " + name + "!";
}
var server = new hprose.Server("ws://0.0.0.0:8080");
server.add(hello);
server.start();
var hprose = require("hprose");
function hello(name) {
return "Hello " + name + "!";
}
var options = {
key: fs.readFileSync('server-key.pem'),
cert: fs.readFileSync('server-cert.pem')
};
var server = new hprose.Server("wss://0.0.0.0:8080", options);
server.add(hello);
server.start();
该方法与 Server 构造器函数的参数完全一致,功能也一样。这里只举一例:
var hprose = require("hprose");
function hello(name) {
return "Hello " + name + "!";
}
var options = {
key: fs.readFileSync('server-key.pem'),
cert: fs.readFileSync('server-cert.pem')
};
var server = hprose.Server.create("https://0.0.0.0:8080", options);
server.add(hello);
server.start();
var hprose = require("hprose");
var connect = require('connect');
function hello(name) {
return "Hello " + name + "!";
}
var service = new hprose.HttpService();
service.add(hello);
var app = connect()
.use(service.handle)
.listen(8080);
var hprose = require("hprose");
var express = require('express');
function hello(name) {
return "Hello " + name + "!";
}
var service = new hprose.HttpService();
service.add(hello);
var app = express()
.use(service.handle)
.listen(8080);
const hprose = require("hprose");
const Koa = require('koa');
function hello(name) {
return "Hello " + name + "!";
}
const service = new hprose.HttpService();
service.add(hello);
const app = new Koa();
app.use(async ctx => {
await service.handle(ctx.req, ctx.res);
});
app.listen(8080);
另外,HttpServer
、SocketServer
、WebSocketServer
都有单独的构造器函数,但是参数跟 Server
的构造器参数不太一样,相对来说,这些构造器函数的参数更接近底层,所以我们通常不需要直接使用这些构造器。
SocketService
和 WebSocketService
也可以直接使用它们的构造器函数创建服务对象,然后跟其它库或框架结合使用。这里就不再一一举例。
启动服务可以使用以下两个方法:
server.start();
server.listen(...);
start
方法不需要传入参数,它会以默认设置启动服务,这个方法是最常用的。
listen
方法启动服务是需要自己传入参数的,它的参数跟 Node.js 的 http.Server.listen
、https.Server.listen
、net.Server.listen
等 lesten
方法的参数相同。通常不需要使用该方法。
关闭服务器也提供了两个方法:
server.stop();
server.close(callback);
stop
方法也不需要传入参数。
close
方法的参数 callback
跟 Node.js 的各种服务的 close
方法的 callback
参数相同。通常也不需要使用该方法。
hprose.Service
是所有服务的基类。在它上面提供了以下设置:
该属性为 Boolean 类型,默认值为 false。
用来设置服务器是否是工作在 debug 模式下,在该模式下,当服务器端发生异常时,将会将详细的错误堆栈信息返回给客户端,否则,只返回错误信息。
该属性为 Boolean 类型,默认值为 false。
该属性表示调用所返回的结果是否为简单数据。简单数据是指:null、数字(包括整数、长整数、浮点数)、Boolean 值、字符串、二进制数据、日期时间等基本类型的数据或者不包含引用的数组、Map 和对象。当该属性设置为 true 时,在进行序列化操作时,将忽略引用处理,加快序列化速度。但如果数据不是简单类型的情况下,将该属性设置为 true,可能会因为死循环导致堆栈溢出的错误。
简单的讲,用 JSON 可以表示的数据都是简单数据。但是对于比较复杂的 JSON 数据,设置 simple 为 true 可能不会加快速度,反而会减慢,比如对象数组。因为默认情况下,hprose 会对对象数组中的重复字符串的键值进行引用处理,这种引用处理可以对序列化起到优化作用。而关闭引用处理,也就关闭了这种优化。
你也可以针对某个服务函数/方法进行单独设置。
因为不同调用的数据可能差别很大,因此,建议不要修改默认设置,而是针对某个服务函数/方法进行单独设置。
该属性为 Boolean 类型,默认值为 false。
该属性表示在调用中是否将 context 自动作为最后一个参数传入调用方法。
你也可以针对某个服务函数/方法进行单独设置。
除非所有的服务方法的参数最后都定义了 context 参数。否则,建议不要修改默认设置,而是针对某个服务函数/方法进行单独设置。
该属性为整型值,默认值为 10000,单位是毫秒。
该属性表示在调用执行时,如果发生异常,将延时一段时间后再返回给客户端。
在关闭该功能的情况下,如果某个服务因为编写错误抛出异常,客户端又反复重试该调用,可能会导致服务器不能正常处理其它业务请求而造成的假死机现象。使用该功能,可以避免这种问题发生。
如果你不需要该功能,设置为 0 就可以关闭它。
该属性可以为对象类型或对象数组类型。默认值为 null。
该属性的作用是可以设置一个或多个 Filter 对象。关于 Filter 对象,我们将作为单独的章节进行介绍,这里暂且略过。
hprose 为发布服务提供了多个方法,这些方法可以随意组合,通过这种组合,你所发布的服务将不会局限于某一个对象,或某一个类,而是可以将不同的函数和方法随意重新组合成一个服务。
server.addFunction(func[, alias[, options]]);
该方法的功能上发布一个函数作为一个远程服务。
func
是要发布的函数,如果它是具名函数,则第二个参数 alias
可以省略。如果它是匿名函数,则第二个参数 alias
不可省略。
alias
是函数的别名,该别名是客户端调用时所使用的名字,别名中,你可以使用 _
分隔符。当客户端调用时,根据不同的语言,可以自动转换成 .
分隔的调用,或者 ->
分隔的调用。在别名中不要使用 .
分隔符。
对于具名函数,你也可以指定一个 alias
参数作为别名。
options
是一个对象,它里面包含了一些对该服务函数的特殊设置,有以下设置项可以设置:
mode
simple
oneway
async
useHarmonyMap
passContext
scope
该设置表示该服务函数返回的结果类型,它有4个取值,分别是:
-
hprose.Normal
(或hprose.ResultMode.Normal
) -
hprose.Serialized
(或hprose.ResultMode.Serialized
) -
hprose.Raw
(或hprose.ResultMode.Raw
) -
hprose.RawWithEndTag
(或hprose.ResultMode.RawWithEndTag
)
hprose.Normal
是默认值,表示返回正常的已被反序列化的结果。
hprose.Serialized
表示返回的结果保持序列化的格式。
hprose.Raw
表示返回原始数据。
hprose.RawWithEndTag
表示返回带有结束标记的原始数据。
这四种结果的形式在客户端的相关介绍中已有说明,这里不再重复。
不过要注意的是,这里的设置跟客户端的设置并没有直接关系,这里设置的是服务函数本身返回的数据格式,即使服务函数返回的是 hprose.RawWithEndTag
格式的数据,客户端仍然可以以其它三种格式来接收数据。
该设置通常用于做高性能的缓存或代理服务器。我们在后面介绍 addMissingFunction
方法时再举例说明。
该设置表示本服务函数所返回的结果是否为简单数据。默认值与全局设置一致。前面在属性介绍中已经进行了说明,这里就不在重复。
该设置表示本服务函数是否不需要等待返回值。当该设置为 true
时,调用会异步开始,并且不等待结果,立即返回 null 给客户端。默认值为 false
。
该设置表示本服务函数是否为异步函数,异步函数的最后一个参数是一个回调函数,用户需要在异步函数中调用该回调方法来传回返回值,例如:
var hprose = require("hprose");
function hello(name, callback) {
setTimeout(function() {
callback("Hello " + name + "!");
}, 10);
}
var server = hprose.Server.create("http://0.0.0.0:8080");
server.addFunction(hello, { async: true });
server.start();
该设置为 Boolean
类型,默认值为 false
。
该设置表示在接收服务函数的参数时,如果参数中包含有 Map
类型的数据,是否反序列化为 ECMAScript 6 中的 Map
类型对象。当该属性设置为 false
时(即默认值),Map
类型的数据将会被反序列化为 Object
实例对象的数据。
除非 Map
中的键不是字符串类型,否则没必要设置为 true
。
该设置与 server.passContext
属性的功能相同。但在这里它是针对该服务函数的单独设置。例如:
var hprose = require("hprose");
function hello(name, context) {
return 'Hello ' + name + '! -- ' + context.socket.remoteAddress;
}
var server = hprose.Server.create("http://0.0.0.0:8080");
server.addFunction(hello, { passContext: true });
server.start();
注意,当 passContext
和 async
同时设置为 true 的时候,服务函数的 context
参数应该放在 callback
参数之前,例如:
var hprose = require("hprose");
function hello(name, context, callback) {
setTimeout(function() {
callback('Hello ' + name + '! -- ' + context.socket.remoteAddress);
}, 10);
}
var server = hprose.Server.create("http://0.0.0.0:8080");
server.addFunction(hello, { passContext: true, async: true });
server.start();
该选项表示服务函数/方法执行时,函数/方法中所引用的 this
对象。默认为 undefined。
server.addAsyncFunction(func[, alias[, options]]);
该方法与 addFunction
功能相同,但是 async
选项被默认设置为 true
。也就是说,它是 addFunction
发布异步方法的简写形式。
server.addMissingFunction(func[, options]);
该方法用于发布一个用于处理客户端调用缺失服务的函数。缺失服务是指服务器端并没有明确发布的远程函数/方法。例如:
在服务器端没有发布 hello
函数时,在默认情况下,客户端调用该函数,服务器端会返回 `'Can't find this function hello().' 这样一个错误。
但是如果服务器端通过本方法发布了一个用于处理客户端调用缺失服务的 func
函数,则服务器端会返回这个 func
函数的返回值。
该方法还可以用于做代理服务器,例如:
'use strict';
var hprose = require('hprose');
var client = hprose.Client.create('http://www.hprose.com/example/', []);
function proxy(name, args) {
return client.invoke(name, args, { mode: hprose.RawWithEndTag });
}
var server = hprose.Server.create("tcp://0.0.0.0:1234");
server.addMissingFunction(proxy, { mode: hprose.RawWithEndTag });
server.start();
现在,客户端对这个服务器所发出的所有请求,都会通过 proxy
函数转发到 http://www.hprose.com/example/
这个服务上,并把结果直接按照原始方式返回。
另外,我们还知道 client.invoke
方法的返回值是一个 promise
对象,也就是说,服务函数/方法其实也可以直接返回 promise
对象,异步服务不一定非要用 callback 方式。
该方法与 addMissingFunction
功能相同,但是 async
选项被默认设置为 true
。也就是说,它是 addMissingFunction
发布异步方法的简写形式。
server.addFunctions(funcs[, aliases[, options]]);
如果你想同时发布多个方法,可以使用该方法。
funcs
是函数数组,数组元素必须为 function 类型的对象。
aliases
是别名数组,数组元素必须是字符串,并且需要与 funcs
数组中的元素个数一一对应。
当 funcs
中的函数全都是具名函数时,aliases
可以省略。
options
的选项值跟 addFunction
方法相同。
该方法与 addFunctions
功能相同,但是 async
选项被默认设置为 true
。也就是说,它是 addFunctions
发布异步方法的简写形式。
server.addMethod(method[, obj[, alias[, options]]]);
该方法跟 addFunction
类似,它的功能是添加方法。
method
是方法或者方法名,也就是说,可以是函数类型,也可以是字符串。
obj
是 method
所在的对象。如果省略 obj
,那么等同于调用:
server.addFunction(method[, alias[, options]]);
因此当省略 obj
时,method
不可以是字符串。
alias
是方法的别名。
options
选项值跟 addFunction
方法相同。
该方法与 addMethod
功能相同,但是 async
选项被默认设置为 true
。也就是说,它是 addMethod
发布异步方法的简写形式。
server.addMissingMethod(method[, obj[, options]])
该方法的功能与 addMissingMethod
类似。它们之前的区别跟 addMethod
和 addFunction
相同。这里就不详细介绍了。
该方法与 addMissingMethod
功能相同,但是 async
选项被默认设置为 true
。也就是说,它是 addMissingMethod
发布异步方法的简写形式。
server.addMethods(methods[, obj[, aliases, [options]]]);
该方法的功能与 addFunctions
类似。它们之前的区别跟 addMethod
和 addFunction
相同。这里就不详细介绍了。
该方法与 addMethods
功能相同,但是 async
选项被默认设置为 true
。也就是说,它是 addMethods
发布异步方法的简写形式。
server.addInstanceMethods(obj[, aliasPrefix[, options]]);
该方法用于发布 obj
上所有可以列举的方法(即可以通过 for in 循环得到的)。
aliasPrefix
是别名前缀,例如假设有一个 user
对象,该对象上包含有 add
,del
,update
,query
四个方法。那么当调用:
server.addInstanceMethods(user, 'user');
的方式来发布 user
对象上的这四个方法后,等同于这样的发布:
server.addMethods(['add','del','update','query'],
user,
['user_add','user_del','user_update','user_query']);
即在每个发布的方法名之前都添加了一个 user_
的前缀。注意这里前缀和方法名之间是使用 _
分隔的。
当省略 aliasPrefix
参数时,发布的方法名前不会增加任何前缀。
最后的 options
选项值跟 addFunction
方法相同。
该方法与 addInstanceMethods
功能相同,但是 async
选项被默认设置为 true
。也就是说,它是 addInstanceMethods
发布异步方法的简写形式。
上面如此之多的 addXXX
方法也许会把你搞晕,也许你不查阅本手册,都记不清该使用哪个方法来发布。
没关系,add
方法就是用来简化上面这些 addXXX
方法的。
add
方法不支持 options
参数。其它参数你只要按照上面任何一个方法的参数来写,add
方法都可以自动根据参数的个数和类型判断该调用哪个方法进行发布,当你不需要设置 options
参数时,它会大大简化你的工作量。
该方法与 add
功能相同,但是 async
选项被默认设置为 true
。也就是说,它是 add
发布异步方法的简写形式。
该方法与 add
功能相反。使用该方法可以移除已经发布的函数,方法或者推送主题。该方法的参数为发布的远程方法的别名。注意:该别名是大小写敏感的。