Skip to content

Hprose 服务器

小马哥 edited this page Jul 1, 2016 · 44 revisions

概述

Hprose 2.0 for Java 支持多种底层网络协议绑定的服务器,比如:HTTP 服务器,TCP 服务器和 WebSocket 服务器。

HTTP 服务器支持在 HTTP、HTTPS 协议上通讯。

TCP 服务器支持在 TCP 协议上通讯,并且支持全双工和半双工两种模式。

WebSocket 服务器支持在 ws、wss 协议上通讯。

我们下面先从最常用的 HTTP 服务器说起。

直接使用 HproseServlet 发布服务

通过 HproseServlet 发布服务很简单,直接通过配置方式就可以,如果要发布的类是现成的,您不需要编写一行代码就可以完成发布。

发布的方法可以是静态方法,也可以是实例方法。但必须是 public 方法。您还可以同时发布多个类中的方法。

下面我们来看一下 HproseServlet 都有哪些可配置项。

class 配置项

<init-param>
    <param-name>class</param-name>
    <param-value>类1|类1的祖先类|名称空间1,类2|类2的祖先类|名称空间2,...</param-value>
</init-param>

上面选项中 param-value 部分比较复杂,我们来详细介绍一下。

通过 class 配置项可以发布的对象上的实例方法,对象会根据配置的类自动创建,可以同时发布多个对象,每个对象的配置通过逗号 , 分隔。

发布的多个对象可以是同一个类,也可以是不同类的对象,即上面所写的 类1类2 可以是相同的类,也可以是不同的类。

每个对象的配置分三个部分,这三个部分通过 | 来分隔。

第一部分表示创建对象的类,第二部分表示从创建的类到该祖先类上的方法全部发布,所指定的祖先类之上的父类中的方法不会被发布。

例如:

public class BaseService1 {
   public String foo() { return "foo" }
}

public class Service1 extends BaseService1 {
   public String bar() { return "bar" }
}

public class Service2 extends Service1 {
    public String foobar() { return "foobar" }
}

当配置为:

<init-param>
    <param-name>class</param-name>
    <param-value>Service2</param-value>
</init-param>

时,只发布 foobar 方法。

当配置为:

<init-param>
    <param-name>class</param-name>
    <param-value>Service2|Service1</param-value>
</init-param>

时,只发布 foobarbar 方法。

当配置为:

<init-param>
    <param-name>class</param-name>
    <param-value>Service2|BaseService1</param-value>
</init-param>

时,将发布 foobarbarfoo 方法。

之所有这样做,是因为所有的类都是从祖先类 Object 继承下来的,如果将继承来的 public 方法都发布的话,势必会将从 Object 继承来的方法也一起发布,这很可能不是用户所期望的结果。另一个原因,用户定义的类的层次可能比较深,而其定义的基类上的方法可能并不想被一起发布。因此,默认情况下,只发布直接在该类中声明的 public 方法,而不会发布继承来的方法。

从上面的几个例子中,我们还会发现,后两部分的配置是可选的。

名称空间的用处是可以将同一个服务的不同对象上的同名方法区分开,例如:

<init-param>
    <param-name>class</param-name>
    <param-value>Service2|BaseService1|service2,Service1||service1</param-value>
</init-param>

将会发布两个对象上的方法,第一个是 Service2 类的实例对象,它上面的 foobarbarfoo 方法都会发布。第二个是 Service1 类的实例对象,它上面的 bar 方法会被发布。因为加了名称空间,所以实际发布的方法名是:

  • service2_foobar
  • service2_bar
  • service2_foo
  • service1_bar

这样就把两个 bar 方法区分开了,在客户端调用时,同样可以指定名称空间(在客户端部分有介绍),或者直接使用全名调用。

从上面的配置中,我们还可以看到,配置的第二部分可以忽略,但是在有第三部分时, | 不能忽略。

staticClass 配置项

<init-param>
    <param-name>staticClass</param-name>
    <param-value>类1|名称空间1,类2|名称空间2,...</param-value>
</init-param>

通过 staticClass 配置项可以发布的类上的静态方法,每个类的配置通过逗号 , 分隔。类和名称空间之间用 | 分隔。

例如:

public class StaticService {
   public static String foo() { return "foo" }
   public static String bar() { return "bar" }
   public String foobar() { return "foobar" }
}

如果通过一下方式来发布:

<init-param>
    <param-name>staticClass</param-name>
    <param-value>StaticService</param-value>
</init-param>

将只发布 foobar 方法,因为 foobar 方法不是静态方法。

type 配置项

<init-param>
    <param-name>type</param-name>
    <param-value>类1|注册名1,类2|注册名2,...</param-value>
</init-param>

通过 type 配置项可以用来注册自定义类型,相当于通过 HproseClassManager 来注册。例如:

<init-param>
    <param-name>type</param-name>
    <param-value>hprose.example.io.User|User,hprose.example.io.Order|Order</param-value>
</init-param>

上面的配置相当于使用一下代码进行注册:

HproseClassManager.register(hprose.example.io.User.class, "User");
HproseClassManager.register(hprose.example.io.Order.class, "Order");

注册之后的类型,在传输时将使用注册名来代替类名与客户端进行交互。

mode 配置项

取值为有三个:

  • FieldMode
  • PropertyMode
  • MemberMode

配置时,大小写无关。该选项表示采用何种模式序列化自定义对象。默认值为 MemberMode,通常不需要配置该选项,采用默认方式就可以了。

配置方式如下:

<init-param>
    <param-name>mode</param-name>
    <param-value>propertyMode</param-value>
</init-param>

debug 配置项

<init-param>
    <param-name>debug</param-name>
    <param-value>true</param-value>
</init-param>

默认情况下,在调用过程中,服务器端发生错误时,只返回有限的错误信息。当打开调试开关后,服务器会将错误堆栈信息全部发送给客户端,这样,在客户端可以得到详细的错误信息。

crossDomain 配置项

<init-param>
    <param-name>crossDomain</param-name>
    <param-value>true</param-value>
</init-param>

Hprose 支持 JavaScript、ActionScript 和 SilverLight 客户端的跨域调用,对于 JavaScript 客户端来说,服务器提供了两种跨域方案,一种是 W3C 标准跨域方案(CORS 跨域资源共享),只要进行上面的设置就可以开启了。

另一种跨域方案同时适用于以上三种客户端,那就是通过设置跨域策略文件的方式。您只需要将 crossdomain.xml 文件放在网站发布的根目录上即可。

最后,Hprose 还提供了专门适用于 SilverLight 的跨域方案,那就是通过设置客户端访问策略文件的方式。做法跟 crossdomain.xml 类似,只需要将 clientaccesspolicy.xml 文件放在网站发布的根目录上即可。

关于 crossdomain.xmlclientaccesspolicy.xml 的更多内容请参阅:

origin 配置项

<init-param>
    <param-name>origin</param-name>
    <param-value>地址1,地址2,地址3...</param-value>
</init-param>

当开启 crossDomain 配置项后,是运行所有位置发出的跨域请求的,如果你需要限制跨域请求的来源,你可以通过配置该选项来实现。多个来源可以用 , 分隔。例如:

<init-param>
    <param-name>origin</param-name>
    <param-value>http://hprose.org,http://hprose.com</param-value>
</init-param>

就是只允许:

这两个来源下的请求。

注意,来源地址和请求页面地址并不相同,比如假设请求页面地址是:

http://hprose.com/test

它的来源地址是:

http://hprose.com

p3p 配置项

<init-param>
    <param-name>p3p</param-name>
    <param-value>true</param-value>
</init-param>

这个配置项决定是否发送 P3P 的 HTTP 头,这个头的作用是让 IE 允许跨域接收的 Cookie。当您的服务需要在浏览器中被跨域调用,并且希望传递 Cookie 时(例如通过 Cookie 来传递 Session ID),您可以考虑将这个开关打开。否则,无需开启此开关。此开关默认是关闭状态。

get 配置项

<init-param>
    <param-name>get</param-name>
    <param-value>false</param-value>
</init-param>

当该配置项设置为 false 时,关闭在 HTTP GET 请求时的发布列表显示功能。

发布列表的作用相当于 Web Service 的 WSDL,与 WSDL 不同的是,Hprose 的发布列表仅包含方法名,而不包含方法参数列表,返回结果类型,调用接口描述,数据类型描述等信息。这是因为 Hprose 是支持弱类型动态语言调用的,因此参数个数,参数类型,结果类型在发布期是不确定的,在调用期才会确定。所以,Hprose 与 Web Service 相比无论是服务的发布还是客户端的调用上都更加灵活。

但是有时候,您可能不希望用户直接通过浏览器就可以查看发布列表,这时只要将这个配置项设置为 false 就可以了。

event 配置项

<init-param>
    <param-name>event</param-name>
    <param-value>实现 HproseHttpServiceEvent 接口的事件类</param-value>
</init-param>

Hprose 服务器可以配置多个事件,对于 HTTP 服务器来说,这些事件定义在 HproseHttpServiceEvent 接口中,这些事件方法包括:

void onBeforeInvoke(String name, Object[] args, boolean byRef, HproseContext context) throws Throwable;
void onAfterInvoke(String name, Object[] args, boolean byRef, Object result, HproseContext context) throws Throwable;
Throwable onSendError(Throwable e, HproseContext context) throws Throwable;
void onServerError(Throwable e, HproseContext context);
void onSendHeader(HttpContext httpContext);

其中前 4 个继承自 HproseServiceEvent 接口,最后一个 onSendHeader 事件方法定义在 HproseHttpServiceEvent 接口中。

onBeforeInvoke 事件

当服务器端发布的方法被调用前,onBeforeInvoke 事件被触发。

name 为客户端所调用的方法名。

args 为方法的参数。

byRef 表示是否是引用参数传递的调用。

context 为服务上下文,你可以将其转型为:ServiceContextHttpContext 类型。通过该对象,你可以得到调用方法的更多信息,或者 HTTP 的各种上下文对象,例如:request,response, session 等。你还可以通过 HproseContext 上的方法来设置或获取一些你自己定义的一些数据。这些数据可以在各个事件,各个过滤器,各个中间件之间按执行顺序传递。

您可以在该事件中做用户身份验证,例如基于 session 的用户名密码验证,IP 验证等。也可以作日志记录。如果在该事件中想终止调用,抛出异常即可。

onAfterInvoke 事件

当服务器端发布的方法被成功调用后,onAfterInvoke 事件被触发,其中name, args, byRef, contextonBeforeInvoke 事件一致,参数 result 表示调用结果。

当调用发生错误时,onAfterInvoke 事件将不会被触发。如果在该事件中抛出异常,则调用结果不会被返回,客户端将收到此事件抛出的异常。

onSendError 事件

当服务器端调用抛出异常,或者在 onBeforeInvokeonAfterInvoke 事件中抛出异常时,该事件被触发。

您可以在该事件中作日志记录。

如果你希望返回客户端的异常与传入的异常参数不同,你可以通过返回或抛出一个新的异常来代替传入的异常,否则返回 null 即可。

onServerError 事件

当服务器端与客户端无法通讯时并抛出异常时,会触发该事件。

您可以在该事件中作日志记录。

该事件中不应抛出任何异常。

onSendHeader 事件

当服务器返回响应头部时,onSendHeader 事件会被触发。

在该事件中,您可以发送您自己的头信息,例如设置 Cookie,自定义头等等。

该事件中不应抛出任何异常。

filter 配置项

<init-param>
    <param-name>filter</param-name>
    <param-value>Filter1,Filter2,...</param-value>
</init-param>

通过该配置可以添加一个或多个 Filter,关于 Filter 的更多内容,请参见:Hprose 过滤器一章。

beforeFilter 配置项

<init-param>
    <param-name>beforeFilter</param-name>
    <param-value>Handler1,Handler1,...</param-value>
</init-param>

通过该配置可以添加一个或多个在 Filter 执行之前的输入输出中间件处理器,关于中间件的更多内容,请参见:Hprose 中间件一章。

afterFilter 配置项

<init-param>
    <param-name>afterFilter</param-name>
    <param-value>Handler1,Handler1,...</param-value>
</init-param>

通过该配置可以添加一个或多个在 Filter 执行之后的输入输出中间件处理器,关于中间件的更多内容,请参见:Hprose 中间件一章。

invoke 配置项

<init-param>
    <param-name>invoke</param-name>
    <param-value>Handler1,Handler1,...</param-value>
</init-param>

通过该配置可以添加一个或多个调用中间件处理器,关于中间件的更多内容,请参见:Hprose 中间件一章。

topic 配置项

<init-param>
    <param-name>topic</param-name>
    <param-value>topic1|timeout1|heartbeat1,topic2|timeout2|heartbeat2,...</param-value>
</init-param>

通过该配置项,可以用于发布推送主题。多个主题之间用 , 分隔,每个主题的参数之间用 | 分隔。timeoutheartbeat 参数都可以省略。

自己编写 Servlet 发布 Hprose 服务

如果您觉得直接使用 HproseServlet 发布服务还不能满足您的要求,您当然也可以自己编写一个 Servlet 来发布 Hprose 服务。这并不是什么难事,因为 Hprose 已经为您提供了很好的基础,您只需要对 HproseServlet 做一下扩展,或者直接使用 HproseHttpService 来构建自己的Servlet 即可。

扩展 HproseServlet

我们先来介绍最简单的方式,那就是以 HproseServlet 为基类,通过创建子类的方式来扩展 HproseServlet。使用这种方式,您可以更灵活的控制方法的发布。

在前面直接使用 HproseServlet 发布服务时,您会发现我们只能以类为单位来发布方法,而不能单独发布类或对象上的某一个或几个方法,另外,如果我们想对某一个方法指定别名,而不是对整个对象或类上所有方法增加名称空间(别名前缀)的话,也是做不到的。

上面这些需求您都可以通过扩展 HproseServlet 来实现。

创建 HproseServlet 的子类非常容易,HproseServlet 提供了多个可以覆盖的方法,其中最重要是:setGlobalMethods

protected void setGlobalMethods(HproseMethods methods);

该方法的参数类型是 HproseMethods 类型的,下面我们先来对 HproseMethods 类型做一下介绍。

HproseMethods 类

HproseMethods 类型用于表示发布方法的集合。它有以下几个方法:

  • addMethod
  • addMethods
  • addInstanceMethods
  • addStaticMethods
  • addMissingMethod

下面我们对这几个方法分别做一下介绍。

addMethod 方法

public void addMethod(String aliasName, HproseMethod method);

public void addMethod(HproseMethod method);

public void addMethod(Method method, Object obj, String aliasName);
public void addMethod(Method method, Object obj, String aliasName, HproseResultMode mode);
public void addMethod(Method method, Object obj, String aliasName, boolean simple);
public void addMethod(Method method, Object obj, String aliasName, HproseResultMode mode, boolean simple);
public void addMethod(Method method, Object obj, String aliasName, HproseResultMode mode, boolean simple, boolean oneway);

public void addMethod(Method method, Object obj);
public void addMethod(Method method, Object obj, HproseResultMode mode);
public void addMethod(Method method, Object obj, boolean simple);
public void addMethod(Method method, Object obj, HproseResultMode mode, boolean simple);
public void addMethod(Method method, Object obj, HproseResultMode mode, boolean simple, boolean oneway);

public void addMethod(String methodName, Object obj, Class<?>[] paramTypes, String aliasName) throws NoSuchMethodException;
public void addMethod(String methodName, Object obj, Class<?>[] paramTypes, String aliasName, HproseResultMode mode) throws NoSuchMethodException;
public void addMethod(String methodName, Object obj, Class<?>[] paramTypes, String aliasName, boolean simple) throws NoSuchMethodException;
public void addMethod(String methodName, Object obj, Class<?>[] paramTypes, String aliasName, HproseResultMode mode, boolean simple) throws NoSuchMethodException;
public void addMethod(String methodName, Object obj, Class<?>[] paramTypes, String aliasName, HproseResultMode mode, boolean simple, boolean oneway) throws NoSuchMethodException;

public void addMethod(String methodName, Class<?> type, Class<?>[] paramTypes, String aliasName) throws NoSuchMethodException;
public void addMethod(String methodName, Class<?> type, Class<?>[] paramTypes, String aliasName, HproseResultMode mode) throws NoSuchMethodException;
public void addMethod(String methodName, Class<?> type, Class<?>[] paramTypes, String aliasName, boolean simple) throws NoSuchMethodException;
public void addMethod(String methodName, Class<?> type, Class<?>[] paramTypes, String aliasName, HproseResultMode mode, boolean simple) throws NoSuchMethodException;
public void addMethod(String methodName, Class<?> type, Class<?>[] paramTypes, String aliasName, HproseResultMode mode, boolean simple, boolean oneway) throws NoSuchMethodException;

public void addMethod(String methodName, Object obj, Class<?>[] paramTypes) throws NoSuchMethodException;
public void addMethod(String methodName, Object obj, Class<?>[] paramTypes, HproseResultMode mode) throws NoSuchMethodException;
public void addMethod(String methodName, Object obj, Class<?>[] paramTypes, boolean simple) throws NoSuchMethodException;
public void addMethod(String methodName, Object obj, Class<?>[] paramTypes, HproseResultMode mode, boolean simple) throws NoSuchMethodException;
public void addMethod(String methodName, Object obj, Class<?>[] paramTypes, HproseResultMode mode, boolean simple, boolean oneway) throws NoSuchMethodException;

public void addMethod(String methodName, Class<?> type, Class<?>[] paramTypes) throws NoSuchMethodException;
public void addMethod(String methodName, Class<?> type, Class<?>[] paramTypes, HproseResultMode mode) throws NoSuchMethodException;
public void addMethod(String methodName, Class<?> type, Class<?>[] paramTypes, boolean simple) throws NoSuchMethodException;
public void addMethod(String methodName, Class<?> type, Class<?>[] paramTypes, HproseResultMode mode, boolean simple) throws NoSuchMethodException;
public void addMethod(String methodName, Class<?> type, Class<?>[] paramTypes, HproseResultMode mode, boolean simple, boolean oneway) throws NoSuchMethodException;

public void addMethod(String methodName, Object obj, String aliasName);
public void addMethod(String methodName, Object obj, String aliasName, HproseResultMode mode);
public void addMethod(String methodName, Object obj, String aliasName, boolean simple);
public void addMethod(String methodName, Object obj, String aliasName, HproseResultMode mode, boolean simple);
public void addMethod(String methodName, Object obj, String aliasName, HproseResultMode mode, boolean simple, boolean oneway);

public void addMethod(String methodName, Class<?> type, String aliasName);
public void addMethod(String methodName, Class<?> type, String aliasName, HproseResultMode mode);
public void addMethod(String methodName, Class<?> type, String aliasName, boolean simple);
public void addMethod(String methodName, Class<?> type, String aliasName, HproseResultMode mode, boolean simple);
public void addMethod(String methodName, Class<?> type, String aliasName, HproseResultMode mode, boolean simple, boolean oneway);

public void addMethod(String methodName, Object obj);
public void addMethod(String methodName, Object obj, HproseResultMode mode);
public void addMethod(String methodName, Object obj, boolean simple);
public void addMethod(String methodName, Object obj, HproseResultMode mode, boolean simple);
public void addMethod(String methodName, Object obj, HproseResultMode mode, boolean simple, boolean oneway);

public void addMethod(String methodName, Class<?> type);
public void addMethod(String methodName, Class<?> type, HproseResultMode mode);
public void addMethod(String methodName, Class<?> type, boolean simple);
public void addMethod(String methodName, Class<?> type, HproseResultMode mode, boolean simple);
public void addMethod(String methodName, Class<?> type, HproseResultMode mode, boolean simple, boolean oneway);

addMethod 用来控制单个方法的发布,它有许多的重载形式。通过它可以发布任意对象上的任意 public 的实例方法,或任意类上的任意 public 的静态方法。

参数 aliasName 可以为每个方法都指定别名。当您发布的方法具有相同个数参数的重载时,你需要为这些重载方法指定不同的别名。在 Hprose 2.0 中,除了用这种方式以外,还可以在定义方法时,在方法上加上 @MethodName(别名) 注解来标注该方法发布时的别名。

参数 mode 用来指明方法调用结果的类型。如果返回结果就是普通对象,那么不需要加这个参数,也就是默认值 HproseResultMode.Normal。如果返回结果是 Hprose 序列化之后的数据(byte[] 类型),那么设置该参数为 HproseResultMode.Serialized 可以避免该结果被二次序列化。如果返回结果是一个完整的响应,当这个响应结果不带 Hprose 结束符时,需要将该参数设置为 HproseResultMode.Raw,如果这个响应结果带Hprose 结束符,则设置这个参数为 HproseResultMode.RawWithEndTag。这个参数主要用于存储转发的 Hprose 代理服务器。通常我们不需要用到这个参数。在 Hprose 2.0 中,除了用这种方式以外,还可以在定义方法时,在方法上加上 @ResultMode(HproseResultMode 枚举值) 注解来标注该方法的调用结果类型。

参数 simple 表示结果在传输给客户端时,是否使用简单序列化方式。对于简单类型的返回结果,将该参数设置为 true 可以加快一点速度。在 Hprose 2.0 中,除了用这种方式以外,还可以在定义方法时,在方法上加上 @Simple 注解来标注该方法以简单类型返回结果。

参数 oneway 表示在收到调用时,是否立即返回空响应之后再执行调用。通常对于没有返回值的服务方法,而且又不需要保证跟后续调用有同步关系的情况下,可以将该参数设置为 true,这将加快客户端的响应速度。在 Hprose 2.0 中,除了用这种方式以外,还可以在定义方法时,在方法上加上 @Oneway 注解来标注该方法以简单类型返回结果。

参数 HproseMethod method 用来指定 Hprose 包装的服务方法,用户通常不需要使用该重载版本。

参数 Method method 用来指定通过反射获取到的服务方法,用户通常也不需要使用该重载版本。

参数 methodName 表示服务方法名,obj 表示方法所在的对象,type 表示方法所在的类。当发布实例方法时指定 obj 参数,当发布静态方法时,指定 type 参数。

参数 paramTypes 表示服务方法的参数类型列表,当指定该参数时,只有服务方法的参数类型跟 paramTypes 参数指定的类型一致时才会发布,通过该参数可以将同名重载方法区分发布。

addMethods 方法

public void addMethods(String[] methodNames, Object obj, String[] aliasNames);
public void addMethods(String[] methodNames, Object obj, String[] aliasNames, HproseResultMode mode);
public void addMethods(String[] methodNames, Object obj, String[] aliasNames, boolean simple);
public void addMethods(String[] methodNames, Object obj, String[] aliasNames, HproseResultMode mode, boolean simple);
public void addMethods(String[] methodNames, Object obj, String[] aliasNames, HproseResultMode mode, boolean simple, boolean oneway);

public void addMethods(String[] methodNames, Object obj, String aliasPrefix);
public void addMethods(String[] methodNames, Object obj, String aliasPrefix, HproseResultMode mode);
public void addMethods(String[] methodNames, Object obj, String aliasPrefix, boolean simple);
public void addMethods(String[] methodNames, Object obj, String aliasPrefix, HproseResultMode mode, boolean simple);
public void addMethods(String[] methodNames, Object obj, String aliasPrefix, HproseResultMode mode, boolean simple, boolean oneway);

public void addMethods(String[] methodNames, Object obj);
public void addMethods(String[] methodNames, Object obj, HproseResultMode mode);
public void addMethods(String[] methodNames, Object obj, boolean simple);
public void addMethods(String[] methodNames, Object obj, HproseResultMode mode, boolean simple);
public void addMethods(String[] methodNames, Object obj, HproseResultMode mode, boolean simple, boolean oneway);

public void addMethods(String[] methodNames, Class<?> type, String[] aliasNames);
public void addMethods(String[] methodNames, Class<?> type, String[] aliasNames, HproseResultMode mode);
public void addMethods(String[] methodNames, Class<?> type, String[] aliasNames, boolean simple);
public void addMethods(String[] methodNames, Class<?> type, String[] aliasNames, HproseResultMode mode, boolean simple);
public void addMethods(String[] methodNames, Class<?> type, String[] aliasNames, HproseResultMode mode, boolean simple, boolean oneway);

public void addMethods(String[] methodNames, Class<?> type, String aliasPrefix);
public void addMethods(String[] methodNames, Class<?> type, String aliasPrefix, HproseResultMode mode);
public void addMethods(String[] methodNames, Class<?> type, String aliasPrefix, boolean simple);
public void addMethods(String[] methodNames, Class<?> type, String aliasPrefix, HproseResultMode mode, boolean simple);
public void addMethods(String[] methodNames, Class<?> type, String aliasPrefix, HproseResultMode mode, boolean simple, boolean oneway);

public void addMethods(String[] methodNames, Class<?> type);
public void addMethods(String[] methodNames, Class<?> type, HproseResultMode mode);
public void addMethods(String[] methodNames, Class<?> type, boolean simple);
public void addMethods(String[] methodNames, Class<?> type, HproseResultMode mode, boolean simple);
public void addMethods(String[] methodNames, Class<?> type, HproseResultMode mode, boolean simple, boolean oneway);

addMethods 用来同时发布一组服务方法。如果您所发布的方法来自同一个对象(obj 参数),或是同一个类(type 参数),使用 addMethods 方法通常比直接使用 addMethod 方法更为方便。因为您不但可以为每个方法都指定别名(aliasNames 参数),还可以为这一组方法(methodNames 参数)指定同一个名称空间(aliasPrefix 参数)。但实际上我们很少情况会直接使用它。因为有更加简单的 addInstanceMethodsaddStaticMethods 方法。

addInstanceMethods 方法

public void addInstanceMethods(Object obj, Class<?> type, String aliasPrefix, HproseResultMode mode, boolean simple, boolean oneway);
public void addInstanceMethods(Object obj, Class<?> type, String aliasPrefix, HproseResultMode mode, boolean simple);
public void addInstanceMethods(Object obj, Class<?> type, String aliasPrefix, boolean simple);
public void addInstanceMethods(Object obj, Class<?> type, String aliasPrefix, HproseResultMode mode);
public void addInstanceMethods(Object obj, Class<?> type, String aliasPrefix);

public void addInstanceMethods(Object obj, Class<?> type, HproseResultMode mode, boolean simple, boolean oneway);
public void addInstanceMethods(Object obj, Class<?> type, HproseResultMode mode, boolean simple);
public void addInstanceMethods(Object obj, Class<?> type, boolean simple);
public void addInstanceMethods(Object obj, Class<?> type, HproseResultMode mode);
public void addInstanceMethods(Object obj, Class<?> type);

public void addInstanceMethods(Object obj, String aliasPrefix);
public void addInstanceMethods(Object obj, String aliasPrefix, HproseResultMode mode);
public void addInstanceMethods(Object obj, String aliasPrefix, boolean simple);
public void addInstanceMethods(Object obj, String aliasPrefix, HproseResultMode mode, boolean simple);
public void addInstanceMethods(Object obj, String aliasPrefix, HproseResultMode mode, boolean simple, boolean oneway);

public void addInstanceMethods(Object obj)
public void addInstanceMethods(Object obj, HproseResultMode mode);
public void addInstanceMethods(Object obj, boolean simple);
public void addInstanceMethods(Object obj, HproseResultMode mode, boolean simple);
public void addInstanceMethods(Object obj, HproseResultMode mode, boolean simple, boolean oneway);

addInstanceMethods 用来发布指定对象上的指定类层次上声明的所有 public 实例方法。如果您在使用 addInstanceMethods 方法时,不指定类层次(type 参数),则发布这个对象所在类上声明的所有 public 实例方法。这个方法也支持指定名称空间(aliasPrefix 参数)。

addStaticMethods 方法

public void addStaticMethods(Class<?> type, String aliasPrefix, HproseResultMode mode, boolean simple, boolean oneway);
public void addStaticMethods(Class<?> type, String aliasPrefix, HproseResultMode mode, boolean simple);
public void addStaticMethods(Class<?> type, String aliasPrefix, boolean simple);
public void addStaticMethods(Class<?> type, String aliasPrefix, HproseResultMode mode);
public void addStaticMethods(Class<?> type, String aliasPrefix);

public void addStaticMethods(Class<?> type, HproseResultMode mode, boolean simple, boolean oneway);
public void addStaticMethods(Class<?> type, HproseResultMode mode, boolean simple);
public void addStaticMethods(Class<?> type, boolean simple);
public void addStaticMethods(Class<?> type, HproseResultMode mode);
public void addStaticMethods(Class<?> type);

addStaticMethods 用来发布指定类(type 参数)上声明的所有 public 静态方法。这个方法也支持指定名称空间(aliasPrefix 参数)。

addMissingMethod 方法

public void addMissingMethod(String methodName, Object obj) throws NoSuchMethodException;
public void addMissingMethod(String methodName, Object obj, HproseResultMode mode) throws NoSuchMethodException;
public void addMissingMethod(String methodName, Object obj, boolean simple) throws NoSuchMethodException;
public void addMissingMethod(String methodName, Object obj, HproseResultMode mode, boolean simple) throws NoSuchMethodException;
public void addMissingMethod(String methodName, Object obj, HproseResultMode mode, boolean simple, boolean oneway) throws NoSuchMethodException;

public void addMissingMethod(String methodName, Class<?> type) throws NoSuchMethodException;
public void addMissingMethod(String methodName, Class<?> type, HproseResultMode mode) throws NoSuchMethodException;
public void addMissingMethod(String methodName, Class<?> type, boolean simple) throws NoSuchMethodException;
public void addMissingMethod(String methodName, Class<?> type, HproseResultMode mode, boolean simple) throws NoSuchMethodException;
public void addMissingMethod(String methodName, Class<?> type, HproseResultMode mode, boolean simple, boolean oneway) throws NoSuchMethodException;

这是一个很有意思的方法,它用来发布一个特定的方法,当客户端调用的方法在服务器发布的方法中没有查找到时,将调用这个特定的方法。

使用 addMissingMethod 发布的方法可以是实例方法,也可以是静态方法,但是只能发布一个。如果多次调用 addMissingMethod 方法,将只有最后一次发布的有效。

addMissingMethod 发布的方法应为以下形式:

public Object missingMethod(String name, Object[] args);

第一个参数表示客户端调用时指定的方法名,方法名在传入该方法时全部是小写的。

第二个参数表示客户端调用时传入的参数列表。例如客户端如果传入两个参数,则 args 的数组长度为 2,客户端的第一个参数为 args 的第一个元素,第二个参数为 args 的第二个元素。如果客户端调用的方法没有参数,则 args 为长度为 0 的数组。

现在,您对 HproseMethods 的以上方法应该有一定程度的认识了,下面我们再结合 HproseServletsetGlobalMethods 方法来看一下如何实际应用它们。

setGlobalMethods 方法

setGlobalMethods 方法是在 HproseServletinit 方法最后被调用的,其参数为全局发布方法的集合。当我们需要在全局发布方法时,就可以覆盖这个方法来实现。

例如:

package hprose.exam.server;
import hprose.common.HproseMethods;
import hprose.server.HproseServlet;
public class MyHproseServlet extends HproseServlet {
    public String hello(String name) {
        return "Hello " + name;
    }
    @Override
    protected void setGlobalMethods(HproseMethods methods) {
        methods.addMethod("hello", this);
    }
}

HproseServlet 代码并不复杂,它的主要代码几乎全是关于配置的加载,而功能实现部分是在 HproseHttpService 类中完成的,因此你也可以直接使用 HproseHttpService 来实现自己的 Servlet,或者通过它来使用 JSP 发布服务,又或者通过它来实现与 Spring 等框架的集成。

HproseHttpServiceHproseService 的一个子类。HproseService 还有另外两个子类实现:HproseTcpServerHproseWebSocketService

你可能会奇怪,为什么 HproseHttpServiceHproseWebSocketService 结尾都是 Service,而 HproseTcpServer 的结尾是 Server。这并不是拼写错误,而是因为 HproseHttpServiceHproseWebSocketService 并不能单独启动,它们需要运行环境的支持。而 HproseTcpServer 是一个可以单独启动的服务器。

HproseService 实现了 Hprose 服务器代码的公共部分。这些子类实现了具体的传输协议部分。所以在介绍 HproseHttpService 之前,我们先来看一下,HproseService 中都包含了那些属性和方法。

HproseService 类

getCurrentContext 静态方法

public static ServiceContext getCurrentContext();

该方法返回当前执行环境的上下文对象。

对于 HproseService 的三个子类,还分别有三个重载版本:

public static HttpContext getCurrentContext();       // HproseHttpService
public static TcpContext getCurrentContext();        // HproseTcpServer
public static WebSocketContext getCurrentContext();  // HproseWebSocketService

通过该方法获取到的是一个本地线程变量,因此你可以安全的存取它。

关于 Hprose 上下文对象的更多介绍,请查阅 Hprose 上下文 一章。

globalMethods 属性

public HproseMethods getGlobalMethods();
public void setGlobalMethods(HproseMethods methods);

该属性用来设置或获取全局服务方法集合。用户一般不需要自己来设置。不能在服务启动之后再设置该属性,否则会产生不可预测的结果。

mode 属性

public final HproseMode getMode();
public final void setMode(HproseMode mode);

该属性用来设置或获取服务器序列化模式。默认值为 HproseMode.MemberMode 通常不需要修改。

debugEnabled 属性

public final boolean isDebugEnabled();
public final void setDebugEnabled(boolean enabled);

该属性用来设置或获取服务器是否是工作在调试模式下,在调试模式下,当服务器端发生异常时,将会将详细的错误堆栈信息返回给客户端,否则,只返回错误信息。默认值为 false

errorDelay 属性

public int getErrorDelay();
public void setErrorDelay(int errorDelay);

该属性表示在调用执行时,如果发生异常,将延时一段时间后再返回给客户端。

在关闭该功能的情况下,如果某个服务因为编写错误抛出异常,客户端又反复重试该调用,可能会导致服务器不能正常处理其它业务请求而造成的假死机现象。使用该功能,可以避免这种问题发生。

如果你不需要该功能,设置为 0 就可以关闭它。

该属性默认值为 10000,单位是毫秒。

event 属性

public final HproseServiceEvent getEvent();
public final void setEvent(HproseServiceEvent event);

该属性用于设置或获取 Hprose 服务器端的事件,对于 HTTP 和 TCP 服务器还有两个子接口,HproseHttpServiceEventHproseTcpServiceEvent

关于 HproseServiceEventHproseHttpServiceEvent 接口在前面已经介绍过了。这里再介绍一下 HproseTcpServiceEvent 接口。

HproseTcpServiceEvent 接口中有两个事件方法:

void onAccept(TcpContext tcpContext);
void onClose(TcpContext tcpContext);

当服务器端接收到客户端连接时,onAccept 事件被触发。当客户端到服务器端的连接关闭时,onClose 事件被触发。

至于可以在这些事件里能够做什么,用户可以自由发挥。

filter 属性

public final HproseFilter getFilter();
public final void setFilter(HproseFilter filter);

该属性默认值为 null。

该属性的作用是可以设置一个 Filter 对象。关于 Filter 对象,我们将作为单独的章节进行介绍,这里暂且略过。

因为通过该属性只能设置一个 Filter 当你需要设置多个 Filter 时,应使用 addFilter 方法代替该属性。

addFilter 方法

public final void addFilter(HproseFilter filter);

该方法同设置 filter 属性类似。该方法用于添加一个 filter 对象到 Filter 链的末尾,并可以连续添加多个 filter

removeFilter 方法

public final boolean removeFilter(HproseFilter filter);

该方法同设置 filter 属性类似。该方法用于从 Filter 链中删除指定的 filter 对象。

add 方法

public final void add(Method method, Object obj, String aliasName);
public final void add(Method method, Object obj, String aliasName, HproseResultMode mode);
public final void add(Method method, Object obj, String aliasName, boolean simple);
public final void add(Method method, Object obj, String aliasName, HproseResultMode mode, boolean simple);
public final void add(Method method, Object obj, String aliasName, HproseResultMode mode, boolean simple, boolean oneway);

public final void add(Method method, Object obj);
public final void add(Method method, Object obj, HproseResultMode mode);
public final void add(Method method, Object obj, boolean simple);
public final void add(Method method, Object obj, HproseResultMode mode, boolean simple);
public final void add(Method method, Object obj, HproseResultMode mode, boolean simple, boolean oneway);

public final void add(String methodName, Object obj, Class<?>[] paramTypes, String aliasName) throws NoSuchMethodException;
public final void add(String methodName, Object obj, Class<?>[] paramTypes, String aliasName, HproseResultMode mode) throws NoSuchMethodException;
public final void add(String methodName, Object obj, Class<?>[] paramTypes, String aliasName, boolean simple) throws NoSuchMethodException;
public final void add(String methodName, Object obj, Class<?>[] paramTypes, String aliasName, HproseResultMode mode, boolean simple) throws NoSuchMethodException;
public final void add(String methodName, Object obj, Class<?>[] paramTypes, String aliasName, HproseResultMode mode, boolean simple, boolean oneway) throws NoSuchMethodException;

public final void add(String methodName, Class<?> type, Class<?>[] paramTypes, String aliasName) throws NoSuchMethodException;
public final void add(String methodName, Class<?> type, Class<?>[] paramTypes, String aliasName, HproseResultMode mode) throws NoSuchMethodException;
public final void add(String methodName, Class<?> type, Class<?>[] paramTypes, String aliasName, boolean simple) throws NoSuchMethodException;
public final void add(String methodName, Class<?> type, Class<?>[] paramTypes, String aliasName, HproseResultMode mode, boolean simple) throws NoSuchMethodException;
public final void add(String methodName, Class<?> type, Class<?>[] paramTypes, String aliasName, HproseResultMode mode, boolean simple, boolean oneway) throws NoSuchMethodException;

public final void add(String methodName, Object obj, Class<?>[] paramTypes) throws NoSuchMethodException;
public final void add(String methodName, Object obj, Class<?>[] paramTypes, HproseResultMode mode) throws NoSuchMethodException;
public final void add(String methodName, Object obj, Class<?>[] paramTypes, boolean simple) throws NoSuchMethodException;
public final void add(String methodName, Object obj, Class<?>[] paramTypes, HproseResultMode mode, boolean simple) throws NoSuchMethodException;
public final void add(String methodName, Object obj, Class<?>[] paramTypes, HproseResultMode mode, boolean simple, boolean oneway) throws NoSuchMethodException;

public final void add(String methodName, Class<?> type, Class<?>[] paramTypes) throws NoSuchMethodException;
public final void add(String methodName, Class<?> type, Class<?>[] paramTypes, HproseResultMode mode) throws NoSuchMethodException;
public final void add(String methodName, Class<?> type, Class<?>[] paramTypes, boolean simple) throws NoSuchMethodException;
public final void add(String methodName, Class<?> type, Class<?>[] paramTypes, HproseResultMode mode, boolean simple) throws NoSuchMethodException;
public final void add(String methodName, Class<?> type, Class<?>[] paramTypes, HproseResultMode mode, boolean simple, boolean oneway) throws NoSuchMethodException;

public final void add(String methodName, Object obj, String aliasName);
public final void add(String methodName, Object obj, String aliasName, HproseResultMode mode);
public final void add(String methodName, Object obj, String aliasName, boolean simple);
public final void add(String methodName, Object obj, String aliasName, HproseResultMode mode, boolean simple);
public final void add(String methodName, Object obj, String aliasName, HproseResultMode mode, boolean simple, boolean oneway);

public final void add(String methodName, Class<?> type, String aliasName);
public final void add(String methodName, Class<?> type, String aliasName, HproseResultMode mode);
public final void add(String methodName, Class<?> type, String aliasName, boolean simple);
public final void add(String methodName, Class<?> type, String aliasName, HproseResultMode mode, boolean simple);
public final void add(String methodName, Class<?> type, String aliasName, HproseResultMode mode, boolean simple, boolean oneway);

public final void add(String methodName, Object obj);
public final void add(String methodName, Object obj, HproseResultMode mode);
public final void add(String methodName, Object obj, boolean simple);
public final void add(String methodName, Object obj, HproseResultMode mode, boolean simple);
public final void add(String methodName, Object obj, HproseResultMode mode, boolean simple, boolean oneway);

public final void add(String methodName, Class<?> type);
public final void add(String methodName, Class<?> type, HproseResultMode mode);
public final void add(String methodName, Class<?> type, boolean simple);
public final void add(String methodName, Class<?> type, HproseResultMode mode, boolean simple);
public final void add(String methodName, Class<?> type, HproseResultMode mode, boolean simple, boolean oneway);

public final void add(String[] methodNames, Object obj, String[] aliasNames);
public final void add(String[] methodNames, Object obj, String[] aliasNames, HproseResultMode mode);
public final void add(String[] methodNames, Object obj, String[] aliasNames, boolean simple);
public final void add(String[] methodNames, Object obj, String[] aliasNames, HproseResultMode mode, boolean simple);
public final void add(String[] methodNames, Object obj, String[] aliasNames, HproseResultMode mode, boolean simple, boolean oneway);

public final void add(String[] methodNames, Object obj, String aliasPrefix);
public final void add(String[] methodNames, Object obj, String aliasPrefix, HproseResultMode mode);
public final void add(String[] methodNames, Object obj, String aliasPrefix, boolean simple);
public final void add(String[] methodNames, Object obj, String aliasPrefix, HproseResultMode mode, boolean simple);
public final void add(String[] methodNames, Object obj, String aliasPrefix, HproseResultMode mode, boolean simple, boolean oneway);

public final void add(String[] methodNames, Object obj);
public final void add(String[] methodNames, Object obj, HproseResultMode mode);
public final void add(String[] methodNames, Object obj, boolean simple);
public final void add(String[] methodNames, Object obj, HproseResultMode mode, boolean simple);
public final void add(String[] methodNames, Object obj, HproseResultMode mode, boolean simple, boolean oneway);

public final void add(String[] methodNames, Class<?> type, String[] aliasNames);
public final void add(String[] methodNames, Class<?> type, String[] aliasNames, HproseResultMode mode);
public final void add(String[] methodNames, Class<?> type, String[] aliasNames, boolean simple);
public final void add(String[] methodNames, Class<?> type, String[] aliasNames, HproseResultMode mode, boolean simple);
public final void add(String[] methodNames, Class<?> type, String[] aliasNames, HproseResultMode mode, boolean simple, boolean oneway);

public final void add(String[] methodNames, Class<?> type, String aliasPrefix);
public final void add(String[] methodNames, Class<?> type, String aliasPrefix, HproseResultMode mode);
public final void add(String[] methodNames, Class<?> type, String aliasPrefix, boolean simple);
public final void add(String[] methodNames, Class<?> type, String aliasPrefix, HproseResultMode mode, boolean simple);
public final void add(String[] methodNames, Class<?> type, String aliasPrefix, HproseResultMode mode, boolean simple, boolean oneway);

public final void add(String[] methodNames, Class<?> type);
public final void add(String[] methodNames, Class<?> type, HproseResultMode mode);
public final void add(String[] methodNames, Class<?> type, boolean simple);
public final void add(String[] methodNames, Class<?> type, HproseResultMode mode, boolean simple);
public final void add(String[] methodNames, Class<?> type, HproseResultMode mode, boolean simple, boolean oneway);

public final void add(Object obj, Class<?> type, String aliasPrefix);
public final void add(Object obj, Class<?> type, String aliasPrefix, HproseResultMode mode);
public final void add(Object obj, Class<?> type, String aliasPrefix, boolean simple);
public final void add(Object obj, Class<?> type, String aliasPrefix, HproseResultMode mode, boolean simple);
public final void add(Object obj, Class<?> type, String aliasPrefix, HproseResultMode mode, boolean simple, boolean oneway);

public final void add(Object obj, Class<?> type);
public final void add(Object obj, Class<?> type, HproseResultMode mode);
public final void add(Object obj, Class<?> type, boolean simple);
public final void add(Object obj, Class<?> type, HproseResultMode mode, boolean simple);
public final void add(Object obj, Class<?> type, HproseResultMode mode, boolean simple, boolean oneway);

public final void add(Object obj, String aliasPrefix);
public final void add(Object obj, String aliasPrefix, HproseResultMode mode);
public final void add(Object obj, String aliasPrefix, boolean simple);
public final void add(Object obj, String aliasPrefix, HproseResultMode mode, boolean simple);
public final void add(Object obj, String aliasPrefix, HproseResultMode mode, boolean simple, boolean oneway);

public final void add(Object obj);
public final void add(Object obj, HproseResultMode mode);
public final void add(Object obj, boolean simple);
public final void add(Object obj, HproseResultMode mode, boolean simple);
public final void add(Object obj, HproseResultMode mode, boolean simple, boolean oneway);

public final void add(Class<?> type, String aliasPrefix);
public final void add(Class<?> type, String aliasPrefix, HproseResultMode mode);
public final void add(Class<?> type, String aliasPrefix, boolean simple);
public final void add(Class<?> type, String aliasPrefix, HproseResultMode mode, boolean simple);
public final void add(Class<?> type, String aliasPrefix, HproseResultMode mode, boolean simple, boolean oneway);

public final void add(Class<?> type);
public final void add(Class<?> type, HproseResultMode mode);
public final void add(Class<?> type, boolean simple);
public final void add(Class<?> type, HproseResultMode mode, boolean simple);
public final void add(Class<?> type, HproseResultMode mode, boolean simple, boolean oneway);

add 有许多的重载形式。它其实是 HproseMethods 上的 addMethod, addMethods, addIntanceMethods, addStaticMethods 四个方法的简写,参数跟这四个的参数完全一致,只是不再区分方法名。对于记不住该用哪个方法名的用户,直接使用 add 是个不错的方式。

addMissingMethod 方法

public void addMissingMethod(String methodName, Object obj) throws NoSuchMethodException;
public void addMissingMethod(String methodName, Object obj, HproseResultMode mode) throws NoSuchMethodException;
public void addMissingMethod(String methodName, Object obj, boolean simple) throws NoSuchMethodException;
public void addMissingMethod(String methodName, Object obj, HproseResultMode mode, boolean simple) throws NoSuchMethodException;
public void addMissingMethod(String methodName, Object obj, HproseResultMode mode, boolean simple, boolean oneway) throws NoSuchMethodException;

public void addMissingMethod(String methodName, Class<?> type) throws NoSuchMethodException;
public void addMissingMethod(String methodName, Class<?> type, HproseResultMode mode) throws NoSuchMethodException;
public void addMissingMethod(String methodName, Class<?> type, boolean simple) throws NoSuchMethodException;
public void addMissingMethod(String methodName, Class<?> type, HproseResultMode mode, boolean simple) throws NoSuchMethodException;
public void addMissingMethod(String methodName, Class<?> type, HproseResultMode mode, boolean simple, boolean oneway) throws NoSuchMethodException;

HproseMethods 上的 addMissingMethod 功能一样。

推送服务

Hprose 2.0 最大的亮点就是增加了推送功能的支持,而且这个功能的增加是在不修改现有通讯协议的方式下实现的,因此,这里的推送服务,即使不是 Hprose 2.0 的客户端也可以使用。

当然,在旧版本的客户端调用推送服务,需要多写一些代码。所以,如果你所使用的语言支持 Hprose 2.0,那么推荐直接使用 Hprose 2.0 的推送 API 来做推送,这样会极大的减少你的工作量。

下面我们来介绍一下服务器端增加的关于推送的 API。

timeout 属性

public int getTimeout();
public void setTimeout(int timeout);

该属性设置推送空闲超时的。该属性默认值为 120000,单位是毫秒(ms),即 2 分钟。

当服务器发布了推送主题后(后面会专门介绍推送),客户端会跟服务器端保持一个长连接,如果达到超时时间,仍然没有任何消息推送给客户端,则返回 null,此时,如果客户端仍然在线的话,则会立即再次发送获取推送主题的请求。服务器端通过这个方式可以获知客户端是否还在线。

heartbeat 属性

public int getHeartbeat();
public void setHeartbeat(int heartbeat);

该属性用来设置推送的心跳检测间隔时间。该属性默认值为 3000,单位是毫秒,即 3 秒钟。

当服务器端推送数据给客户端后,如果客户端在 heartbeat 时间内没有取走推送数据,则服务器端认为客户端以掉线。对于以掉线的客户端,服务器端会清除为该客户端分配的内存空间,并将该客户端从推送列表中移除。

timeoutheartbeat 属性在检测客户端是否离线时是相互配合的,当服务器端没有向客户端推送任何消息时,服务器端需要至少 timeout + heartbeat 的时间才能检测到客户端以离线。当服务器端有向客户端推送消息时,则在推送消息之后经过 heartbeat 时间可以检测到客户端以掉线。

timeoutheartbeat 设置的时间越短,检测到客户端离线的时间就越短。但是需要注意以下几个问题:

timeout 时间越短,服务器端和客户端之间的用于检测是否掉线的通讯就越频繁,所以不应该将 timeout 设置的过短,否则会严重增加服务器的负担。

因此,timeout 的设置一般不应少于 30 秒。对于负载比较高的服务器,保持默认值就是一个不错的选项。

对于推送频繁的服务器来说,heartbeat 时间越长,对于已经离线的客户端,在服务器端存储的离线消息就越多,这会严重的占用服务器端的内存,因此,不宜将 heartbeat 的时间设置的过长。

如果 heartbeat 的时间设置的过短,客户端可能会因为网络原因导致不能及时取走推送消息,这就会导致错误的离线判断,当错误离线判断发生后,会丢失一些推送消息。

因此,heartbeat 的选择则应根据客户端的网络情况来决定,如果客户端都是来自局域网,并且客户端数量较少,设置为 1 秒甚至更短的时间也是可以的。而对于比较慢速且不太稳定的移动网络,设置为 5 秒或者 10 秒可能是一个比较合适的取值。对于普通的互联网客户端来说,保持默认值就可以了。

pushEvent 属性

public PushEvent getPushEvent();
public void setPushEvent(PushEvent pushEvent);

该属性用来设置推送事件。推送事件接口 PushEvent 中包含两个事件方法:

void subscribe(String topic, int id, HproseService service);
void unsubscribe(String topic, int id, HproseService service);

当编号为 id 的客户端订阅主题 topic 时,触发 subscribe 事件。

当编号为 id 的客户端退订主题 topic 时,触发 unsubscribe 事件。

publish 方法

public final void publish(String topic);
public final void publish(String topic, int timeout);
public final void publish(String topic, int timeout, int heartbeat);

public final void publish(String[] topics);
public final void publish(String[] topics, int timeout);
public final void publish(String[] topics, int timeout, int heartbeat);

该方法用于发布一个或一组推送主题。这个推送的主题实际上是一个自动生成的远程服务方法。它的功能就是实现推送。

topic 为主题名,topics 为一组主题名。

这里 timeoutheartbeat 参数在前面的属性介绍里已经说明过了,这里不再重复。

publish 方法仅仅是告诉客户端,现在有一个叫做 topic 的推送主题可以订阅。

而要真正推送数据给客户端,则需要使用以下几个方法。

广播

public final void broadcast(String topic, Object result) throws HproseException;
public final void broadcast(String topic, Object result, Action<Integer[]> callback) throws HproseException;

public final Promise<Integer[]> push(String topic, Object result) throws HproseException;

这里有两个推送方法:broadcastpush

这两个方法功能相同,但是 broadcast 方法支持回调,该回调方法的参数是一个整数数组,该数组中保存的是所有推送成功的客户端 id。而 push 方法是将这个数组作为一个 Promise<Integer[]> 结果返回。

一旦服务器启动,你可以在任何地方进行数据推送。比如在其它的服务方法中,在服务器事件中,甚至在服务器外的并行运行的函数中。例如:

时间推送服务器

package hprose.example.push;

import hprose.common.HproseException;
import hprose.server.HproseTcpServer;
import hprose.util.concurrent.Timer;
import java.io.IOException;
import java.net.URISyntaxException;

public class TimePushServer {
    public static void main(String[] args) throws URISyntaxException, IOException {
        HproseTcpServer server = new HproseTcpServer("tcp://0.0.0.0:8080");
        server.publish("time");
        server.start();
        Timer timer = new Timer(() -> {
            try {
                server.push("time", java.util.Calendar.getInstance());
            }
            catch (HproseException ex) {}
        });
        timer.setInterval(1000);
        System.out.println("START");
        System.in.read();
        server.stop();
        System.out.println("STOP");
    }
}

时间显示客户端

package hprose.example.push;

import hprose.client.HproseClient;
import java.io.IOException;
import java.net.URISyntaxException;
import java.util.Date;
import java.util.concurrent.CountDownLatch;

public class TimePushClient {
    public static void main(String[] args) throws IOException, URISyntaxException, Throwable {
        final HproseClient client = HproseClient.create("tcp://127.0.0.1:8080");
        final CountDownLatch counter = new CountDownLatch(10);
        client.subscribe("time", (Date time) -> {
            if (counter.getCount() > 0) {
                counter.countDown();
                System.out.println(time);
            }
            else {
                client.unsubscribe("time");
            }
        }, Date.class);
        Thread.sleep(12000);
    }
}

该程序运行结果为:

Thu Jun 30 22:43:46 CST 2016
Thu Jun 30 22:43:47 CST 2016
Thu Jun 30 22:43:48 CST 2016
Thu Jun 30 22:43:49 CST 2016
Thu Jun 30 22:43:50 CST 2016
Thu Jun 30 22:43:51 CST 2016
Thu Jun 30 22:43:52 CST 2016
Thu Jun 30 22:43:53 CST 2016
Thu Jun 30 22:43:54 CST 2016
Thu Jun 30 22:43:55 CST 2016

推送客户端同样支持使用接口方式调用,例如上面的时间显示客户端例子可以改为:

package hprose.example.push;

import hprose.client.HproseClient;
import hprose.util.concurrent.Action;
import java.io.IOException;
import java.net.URISyntaxException;
import java.util.Date;
import java.util.concurrent.CountDownLatch;

interface IPushTime {
    public void time(Action<Date> callback);
}

public class TimePushClient2 {
    public static void main(String[] args) throws IOException, URISyntaxException, Throwable {
        final HproseClient client = HproseClient.create("tcp://127.0.0.1:8080");
        IPushTime pushTime = client.useService(IPushTime.class);
        final CountDownLatch counter = new CountDownLatch(10);
        pushTime.time((Date time) -> {
            if (counter.getCount() > 0) {
                counter.countDown();
                System.out.println(time);
            }
            else {
                client.unsubscribe("time");
            }
        });
        Thread.sleep(12000);
    }
}

运行结果是一样的。通过接口方式调用推送时,方法名就是订阅的主题名,推送方法可以接收一个或两个参数,如果只有一个参数,那么参数应该为 Action<T> callback,如果有两个参数,那么第一个参数为 Integer id, 第二个参数为 Action<T> callback。推送方法的返回值类型应为 void。

如果需要设置超时,使用 @Timeout(timeout) 注解来标注即可。使用接口方式的好处是,如果推送的类型是一个嵌套的泛型,可以更准确的将该类型传递给客户端进行反序列化。

另外,你可能会注意到上面两个客户端的最后加上 Thread.sleep(12000);,这是因为推送是异步的,如果不加这个,程序会直接结束,看不到任何输出。如果是在图形界面程序中,就是完全不必要的了。

有时候,你可能想在某个服务方法中推送数据给客户端,但是该服务方法可能在其它文件中定义。因此,你得不到 server 对象。那这时还能进行推送吗?

答案是可以,没问题。我们前面说过,在服务方法中我们可以得到一个 context 参数,这个 context 参数中就包含有一个 clients 对象,这个对象上就包含了所有跟推送有关的方法,这些方法跟 server 对象上的推送方法是完全一样的。

Clone this wiki locally