26
BES 服务器推送机制分析 作者: 邓明轩

BES 服务器推送机制分析 - images.csdn.netimages.csdn.net/upimgs/lee/BBPDF/BESfwtsjzfx.pdf · 前言 数据推送是BlackBerry 应用平台的一大优势,在BlackBerry 应用平台上部署的应用可以

  • Upload
    ngonga

  • View
    239

  • Download
    2

Embed Size (px)

Citation preview

Page 1: BES 服务器推送机制分析 - images.csdn.netimages.csdn.net/upimgs/lee/BBPDF/BESfwtsjzfx.pdf · 前言 数据推送是BlackBerry 应用平台的一大优势,在BlackBerry 应用平台上部署的应用可以

BES 服务器推送机制分析

作者 邓明轩

目录

前言 3

MDS 推送介绍 3

MDS 推送架构 3

数据推送命令格式 4

数据推送关键代码 6

客户端接收关键代码 8

异步确认消息接收代码 11

推送细节分析 13

MDS 推送的整体时序 13

推送请求是否到达 MDSBES 服务器 14

推送请求检查 15

MDSBES 中的推送队列 17

手持设备端对推送数据的处理 20

异步确认消息(应用依赖和非应用依赖) 21

如何保证数据推送的可靠性 24

应用依赖和非应用依赖的比较和选择 24

MDS 接收线程限制的处理 24

MDS 推送队列限制的处理 25

手持设备端接收队列的处理 25

应用依赖的客户端 26

异步确认消息处理 26

前言

数据推送是 BlackBerry 应用平台的一大优势在 BlackBerry 应用平台上部署的应用可以

和 BlackBerry 推送邮件一样通过推送实时地将数据从服务器端推送到 BlackBerry 手持设备

端所以对于很多应用开发商而言BlackBerry 应用平台提供的推送功能是 BlackBerry 应

用集成必然会使用到的强大功能

然而因为种种原因有一些开发商发现使用 BlackBerry Enterprise Server (BES) 应用平

台的推送功能并不能保证数据到达 BlackBerry 设备导致应用层面的种种问题实际上

BlackBerry 应用平台提供了从多机制保证数据推送的成功只要我们充分了解 BES 中 MDS(以

下简称 MDS)的推送机制我们就可以利用平台提供的机制保证数据到达 BlackBerry 手持设

本章节的主要目的是详细分析 MDS 的推送机制让读者更好地了解 BlackBerry 应用平

台从而可以开发更加强壮更加稳定的 BlackBerry 应用

值得读者注意的是本章节的内容是结合少量的官方文档配合于大量的测试总结而来

的旨在为读者提供更多的线索以理解 MDS 推送机制如果本章节的内容与 RIM 提供的任

何官方文档有冲突的话请以官方文档为准同时不同版本的 BlackBerry 手持设备在不同场

景下也可能有不同的行为所以读者在生产环境实施 BlackBerry 应用时也要结合用户所使用

的 BlackBerry 设备进行测试才能最好地保证应用的稳定性

MDS 推送介绍

MDS 推送架构

在详细讲解 MDS 推送机制之前我们先从整体上了解 MDS 推送的架构下面是 MDS 推

送架构的示意图

从示意图中可以看到在 BlackBerry 应用平台上的数据推送从整体上可以分为六步按时

间顺序分别为

1 第一步应用服务器向 MDSBES 服务器发送推送请求所发送的请求为 HTTP 格式的请

求有关请求的详细格式在下一小节中有详细讲解

2 第二步MDSBES 服务器查询相关配置数据库确定应用服务器所发送的请求是否为合

法的请求此外MDSBES 服务器还会根据资源情况确定是否接收该请求对于是否接

收请求的判断在下一节内容中也有详细讨论

3 第三步MDSBES 服务器向应用服务器返回消息通知应用服务器是否接受该请求返

回消息以 HTTP 答复的方式返回给应用服务器

4 第四步MDSBES 服务器将数据推送到手持设备端

5 第五步手持设备端对数据进行处理后向 MDSBES 服务器返回确认消息

6 第六步MDSBES 根据手持设备端返回的消息决定向应用服务器返回什么异步消息这

一步并不是必然发生的根据推送请求的不同有可能不发生

从这里我们可以看到从应用服务器到手持设备端的推送通道是由多个不同的通道连接

而成的这其中有很多个连接点某一个连接点出现异常都可以导致推送的失败所以应用

开发商需要详细了解这些连接点可能出现的问题在应用开发过程中进行规避

在详细描述各个可能出现问题的关键点之前需要先了解推送的基本实现对于应用开

发人员而言完成数据推送需要完成的主要程序编写工作有应用服务器端发出推送请求的

程序手持设备端侦听接收推送数据的程序和服务器端侦听接收确认消息的程序如下图

橙色框图所显示的

以下章节将对这三个部分作详细讲解

数据推送命令格式

在 BlackBerry 应用平台上的数据推送是由应用服务器发起的应用服务器根据应用逻辑

进行判断发现有数据需要推送到用户的手持设备端时连接 MDSBES 服务器进行数据推送

应用服务器与 MDSBES 服务器的连接方式为 HTTP 连接通过 HTTP 连接发送一个 POST 请

求将需要推送的数据作为 POST 的内容

该 POST 请求的 URL 格式为

httpBES_HOSTBES_PORTpushDESTINATION=DESINTATION_ADDRESSampPORT=HANDHEL

D_APPLICATION_PORTampREQUESTURI=

下面是请求中各参数的说明

参数 描述

BES_HOST BlackBerry Enterprise Server 的服务器名或者是 IP

BES_PORT BlackBerry Enterprise Server 的推送端口一般是 8080

DESTINATION_ADDRESS 用户的邮件地址或者是设备码 (PIN)

HANDHELD_APPLICATION_PORT BlackBerry 手持设备需要帧听的端口

也就是说应用服务器通过 BES_HOST 和 BES_PORT 指定需要连接的 MDSBES 服务器和

需要连接的端口BES 服务器安装后缺省使用 8080 作为推送端口所以推送应用开发人员

一般情况下只需要了解 MDSBES 服务器的服务器名或者是 IP 地址就可以了如果 BES 管理

人员在安装服务器后修改过推送端口则需要告知开发人员使用新指定的端口

推送 URL 中 push后面的部分用于指定数据的接收者和手持设备需要帧听的端口

DESTINATION_ADDRESS 用于指定数据的接收者可以使用该用户的邮件地址或者是该用户手

持设备的 PIN 码HANDHELD_APPLICATION_PORT 用于指定手持设备需要帧听的端口这里

使用的端口只是一个约定要求在手持设备上运行的程序从这一端口中读取数据

除了 URL 参数以后在推送过程还可以使用一些规定好的 HTTP 头指定该推送的属性

HTTP 头参数表如下

HTTP 头 描述

X-RIM-Push-ID 用于指定该消息的 ID该 ID 需要是一个唯一值可以用于

取消推送或者是查看推送的状态

X-RIM-Push-NotifyURL 用于指定确认消息的接收 URL

X-RIM-Push-Reliability-Mode 用于指定推送的可靠性

X-RIM-Push-Deliver-Before 用于指定推送数据的最迟推送时间

X-RIM-Push-Priority 用于指定推送数据的优先级

X-RIM-Push-ID 属性一般建议使用 URL 和编号结合的方式如 123blackberrycom为

了更好地控制数据的推送一般不建议使用随机数作为推送 ID使用统一的 ID 生成方式更

有利于推送数据的取消和状态确定注意如果没有指定该参数的话MDSBES 服务器会自

动生成一个唯一的 ID这样就无法在应用服务器上使用这个 ID 对特定的推送数据进行处理

了此外推送 ID 不能以ppgrimcom 结束

X-RIM-Push-NotifyURL 属性通过 URL 的形式指定了确认消息的接收地址如

httptestingserver7778指定了这一属性后MDSBES 服务器会将推送的确认消息以 HTTP

请求的形式发送到指定的服务器也就是说应用开发者需要开发一个 HTTP 服务器端程序监

听指定服务器的指定端口(上例中则是服务器 testingserver 的 7778 端口)通过这个服务器

端程序获取确认消息在确认消息中会包含 HTTP 头 X-RIM-Push-ID 和 X-RIM-Push-Status通

过 X-RIM-Push-ID 告知监听者是哪条推送数据的确认消息通过 X-RIM-Push-Status 指明该数

据推送的结果200 代表推送成功400 代表推送失败现实环境中对于数据推送是否成功

不能简单地以结果 200 或者是 400 进行判断需要结合很多因素进行判断本文的后续章节

会详细描述

属性 X-RIM-Push-Reliability-Mode 用于指定推送的可靠性值可以是 TRANSPORT 或者是

APPLICATION另外有一个值 APPLICATIONPREFERRED 涉及更复杂的场景这里不做描述如

果指定可靠性为 TRANSPORT 则以数据到达手持设备准本文称之为非应用依赖如果指定

可靠性为 APPLICATION 则以数据被手持设备端应用接收为准本文称之为应用依赖对于不

同可靠性设置本文的后续章节会有详细描述总体而言非应用依赖的推送更适合于广播

性质的不要求应用一定收到数据的场景而应用依赖更适合于点对点的要求应用一定要收到

数据的场景

属性 X-RIM-Push-Deliver-Before 用于指定数据推送的最后时间如果在指定时间前该数

据仍无法被成功推送到手持设备端则该数据会被视作过期而被 MDSBES 服务器丢弃

属性 X-RIM-Push-Priority 用于指定推送数据的优先级指定推送数据的优先级并不能加

快数据的推送指定优先级的结果是推送数据到达手持设备端的行为不同可以选择的值有

none (缺省)low medium 和 high如果指定优先级为 lowmedium 或者是 high则用

户只是接收到数据如果指定优先级为 high则用户在接收到数据后还会看到提示对话框

数据推送关键代码

理论上讲应用服务器的数据推送代码可以用任何可以提供 HTTP 支持的语言编写需

要实现的不过是通过 HTTP 协议往指定的服务器和端口发送 POST 消息

下面以 java 代码为例讲解数据推送的关键代码部分本例的代码片段为一个 Java

Application 的部分代码该 Java Application 在服务器上以 J2SE 应用的形式运行启动一个

线程发送 HTTP 的 POST 请求以下代码为该线程的 run 函数

public void run()

Random random = new Random()

String pushId = pushID + randomnextInt()

String data = What need to be pushed

try

URL _pushURL = new URL(http testingBESabccom 8080

pushDESTINATION=user1abccomampPORT=55100ampREQUESTURI=)

HttpURLConnection conn = (HttpURLConnection) _pushURLopenConnection()

connsetDoInput(true)

connsetDoOutput(true)

connsetRequestMethod(POST)

connsetRequestProperty(X-RIM-PUSH-ID pushId)

connsetRequestProperty(X-RIM-Push-NotifyURL

httptestingserver7778)

connsetRequestProperty(X-RIM-Push-Reliability-Mode

APPLICATION)

OutputStream out = conngetOutputStream()

outwrite(datagetBytes())

outclose()

InputStream ins = conngetInputStream()

int contentLength = conngetContentLength()

int responseCode = conngetResponseCode()

Systemoutprintln(Response Code is + responseCode)

if (contentLength gt 0)

byte[] someArray = new byte[contentLength]

DataInputStream dins = new DataInputStream(ins)

dinsreadFully(someArray)

Systemoutprintln(new String(someArray))

else

Systemoutprintln(Content legth of response is 0)

conndisconnect()

catch (IOException e)

Systemerrprintln(Exception + e)

在以上代码中先通过 URL 类建立一个 HTTP 连接定义URL 类的构造函数中第一个参

数为协议类型这里使用ldquohttprdquo第二个参数为服务器名第三个参数为请求的端口号

第二个参数和第三个参数对应了数据推送请求中的 BES_HOST 和 BES_PORT 属性URL 类的

构造函数中第四个参数为相对 URL推送属性 DESTINATION 和 PORT 都放在相对 URL 中代

码如下

URL _pushURL = new URL(http testingBESabccom 8080

pushDESTINATION=user1abccomampPORT=55100ampREQUESTURI=)

建立了 URL 类的实例后可以通过该实例的 openConnection 函数打开连接返回的是

一个 HttpURLConnection 实例

HttpURLConnection conn = (HttpURLConnection) _pushURLopenConnection()

打开 http 连接后可以通过连接实例的 setRequestProperty 函数设置 http 头推送请求中

的 http 头属性在这里设置如

connsetRequestProperty(X-RIM-PUSH-ID pushId)

其中一个关键的 http 头属性是 X-RIM-Push-Reliability-Mode设置该属性为ldquoTRANSPORTrdquo

表示该推送是非应用依赖的推送设置该属性为ldquoAPPLICATIONrdquo表示该推送是应用依赖的

推送两者的具体区别在后面的章节会有详细的讨论以下代码设置该属性为

ldquoAPPLICATIONrdquo

connsetRequestProperty(X-RIM-Push-Reliability-Mode

APPLICATION)

特别要注意如果设置推送类型为应用依赖对于服务器的设置和手持设备有特定的要

求一方面是服务器端必须是 40 以上同时必须设置允许对所使用的端口进行应用依赖的

推送一方面是要手持设备的 ROM 版本也是 40 以上同时服务器必须知道手持设备能否

支持应用依赖的推送对于服务器的端口设置设置界面如下可以由 BES 管理员进行设置

如果是使用开发环境的 MDS-CS 模拟器模拟应用依赖推送的话需要修改开发环境的

MDSconfig目录中的 rimpublicproperty文件去除 pushapplicationreliableports一行的注释

同时加上需要推送的端口如ldquopushapplicationreliableports=10055100rdquo

对于服务器端是否知道手持设备端的版本需要通过手持设备端的操作完成只要手持

设备端的浏览器通过 BES 访问过网络服务器就会记录该手持设备是否支持应用依赖的推

送如果只是测试的话在测试前通过手持设备端的浏览器打开任何一个网站就可以了如

果是生产环境则需要考虑客户端应用在第一次启动的时候主动访问一次网络以保证客户

端都可以正常接收应用依赖的推送

如果以上设置没有完成的话对 MDSBES 服务器发送应用依赖的推送时会被拒绝服

务器端会出现ldquoThe specified delivery method is not possiblerdquo的错误应用服务器的推送程

序则会捕获以下异常

ldquojavaioIOException Server returned HTTP response code 400 for

URL httpapprdquo

设置属性后就需要获取 http连接的 OutputStream将需要推送的数据写入 OutputStream

中写完后关闭连接

需要注意的是 MDSBES 服务器是否接收该推送请求是通过 http 返回码实现的所以推

送应用中需要获取到 http 连接的返回码通过返回码判断推送是否成功代码如下

int responseCode = conngetResponseCode()

客户端接收关键代码

客户端数据接收程序运行在 BlackBerry 手持设备上必须通过 java 语言实现需要调用

BlackBerry 设备端的 API

客户端接收程序的主体就是启动一个线程开启 httpltportgt连接(其中 port 是客户

端需要侦听的端口号)然后从该连接中获取 InputStream从 InputStream 中获取服务器所

推送的数据

下面是侦听线程的 run 方法为保持程序的完整代码中使用到的类属性并没有修改

从该函数中看到的本函数没有定义的变量都是该类的类属性有关完整代码请参考

BlackBerry 开发环境所提供的标准样例

public void run()

StreamConnection stream = null

InputStream input = null

MDSPushInputStream pushInputStream=null

while (_stop)

try

synchronized(this)

_notify =

(StreamConnectionNotifier)Connectoropen(http55100)

while (_stop)

stream = _notifyacceptAndOpen()

try

input = streamopenInputStream()

pushInputStream= new

MDSPushInputStream((HttpServerConnection)stream input)

DataBuffer db = new DataBuffer()

byte[] data = new byte[256]

int chunk = 0

while ( -1 = (chunk = inputread(data)) )

dbwrite(data 0 chunk)

pushInputStreamaccept()

inputclose()

streamclose()

data = dbgetArray()

catch (IOException e1)

Systemerrprintln(e1toString())

if ( input = null )

try

inputclose()

catch (IOException e2)

if ( stream = null )

try

streamclose()

catch (IOException e2)

_notifyclose()

_notify = null

catch (IOException ioe)

if ( _notify = null )

try

_notifyclose()

_notify = null

catch ( IOException e )

以上代码中建立端口侦听的代码如下

_notify = (StreamConnectionNotifier)Connectoropen(http55100)

其中 URLldquohttp55100rdquo是用于侦听推送的特定格式55100 为需要侦听的端口两

个线程不能同时打开同一个端口的侦听所以在这里使用了 synchronized 对线程并发进行了

控制

建立侦听后可以通过函数 acceptAndOpen 开始侦听当线程调用该方法开始侦听的时候

线程会阻塞在这里直到有推送消息到达代码如下

stream = _notifyacceptAndOpen()

侦听之后的代码为获取连接的 InputStream从 InputStream中获取服务器端推送的数据

读取完数据后关闭 InputStream 和 StreamConnection然后重新调用 acceptAdnOpen 继续侦

听端口准备接收新的推送数据

开发人员如果希望使用应用依赖的推送在客户端必须将 InputStream 转换成

MDSPushInputStream同时在接收完数据后调用 MDSPushInputStream 的 accept 函数造知服

务器该数据已收到这也是应用依赖和非应用依赖在代码实现层面的最主要区别代码如下

pushInputStream= new

MDSPushInputStream((HttpServerConnection)stream input)

helliphellip

pushInputStreamaccept()

从良好的编程习惯来讲在连接使用后需要主动关闭所使用的连接和资源如

InputStreamStreamConnectionStreamConnectionNotifier 等所以在样例代码中也可以看

到该样例在异常处理阶段都对这些资源进行了关闭操作在关闭这些连接和资源的时候对

StreamConnectionNotifier 需要特别注意有可能在 StreamConnectionNotifier 中会保留有未

读取的推送数据需要调用 acceptAdnOpen 函数将保留的数据读取出来直到 acceptAdnOpen

处于阻塞状态

另外如果使用了应用依赖的推送客户端程序调用 MDSPushInputStream 的 accept 函

数后确认消息不会马上发出而是在对应用 InputStream 和 StreamConnection 关闭后才发出

所以对于应用依赖的推送保证 InputStream 和 StreamConnection 的关闭非常之重要

异步确认消息接收代码

异步消息的接收代码运行在服务器端在推送请求的 X-RIM-Push-NotifyURL 属性所指定

的服务器上侦听指定端口和应用服务器端的数据推送代码一样异步确认消息的接收程序

可以通过任何支持 HTTP 的语言编写主要的工作是侦听指定端口从指定端口获取 MDSBES

发送回来的确认消息根据 HTTP 协议获取相关属性并返回相关信息

下面以 java 代码为例对关键代码进行说明这段代码一个 java Application 的一部分是

侦听线程的 run 函数

public void run()

try

Systemoutprintln(Waiting for notification on port + 7778

+ )

while (true)

ServerSocket serverSocket = new ServerSocket(7778)

serverSocketsetSoTimeout(120000)

try

Socket clientSocket = serverSocketaccept()

InputStream input = clientSocketgetInputStream()

StringBuffer str = new StringBuffer()

int byteRead = inputread()

while ((byteRead = -1) ampamp (inputavailable() gt 0))

strappend((char) byteRead)

byteRead = inputread()

Systemoutprintln(strtoString())

OutputStream output = clientSocketgetOutputStream()

String response = HTTP10 200 OK

outputwrite(responsegetBytes())

outputflush()

outputclose()

inputclose()

clientSocketclose()

catch (SocketTimeoutException ste)

Systemoutprintln(Notification connection timeout

Restarting)

serverSocketclose()

catch (Exception exception)

exceptionprintStackTrace()

异步确认消息的接收代码比较简单基本上就是打开一个 Socket 侦听指定的端口本

例中是 7778然后不断地从该端口读取数据打开 Socket 侦听的代码如下

ServerSocket serverSocket = new ServerSocket(7778)

在异步消息接收过程中需要注意的是接收完 MDSBES 服务器的确认消息后应该给

MDSBES 服务器返回一个 200 的成功消息否则 MDSBES 服务器会认为发送确认消息失败

可能重次几次后再放弃这样异步消息接收程序有可能重复收到同一条确认消息返回成功

消息的代码如下

OutputStream output = clientSocketgetOutputStream()

String response = HTTP10 200 OK

outputwrite(responsegetBytes())

outputflush()

outputclose()

推送细节分析

了解基本的 MDS 推送知识是不足于编写一个可靠的数据推送程序的要保证数据推送

的可靠性必须了解整个推送过程的每一个细节本章节先从整体上描述了整个数据推送的

调用时序然后从不同的关键点详细讲解数据推送的细节

MDS 推送的整体时序

如上图所示数据推送主要是应用服务器MDS 服务器网络服务手机端几个组件

之间的交互在网络服务这一块我们可以假定这一层是稳定的不做考虑对于网络无法提

供服务等情况我们可以通过手持设备端关闭无线电或者是将手持设备放入无线屏蔽区等方

式模拟所以我们要考虑是应用服务器MDSBES 服务器手持设备这三部分

数据推送由应用服务器发生向 MDSBES 服务器发出 HTTP Post 请求第一个需要考虑

的是该 HTTP Post 请求是否能到达 MDSBES 服务器这一点在ldquo推送请求是否到达 MDSBES

服务器rdquo小节中有详细讨论

MDSBES 服务器在接收到 HTTP Post 请求后会对请求的格式进行判断如果格式错误

如指定目标不存在等则 MDSBES 服务器会返回 400 错误所以第二点要考虑是的如何保

证所有到达 MDSBES 服务器的请求都是合法的有关这一点在ldquo推送请求检查rdquo小节中有

详细描述

MDSBES 服务器发现推送数据格式正确以后还需要判断服务器上的队列是否有足够的

空间保存该数据如果没有发现空闲队列则丢弃该数据并向应用服务器返回 503 错误因此

第三点要考虑的是什么情况会导致服务器推送队列占满如何尽量避免这种情况有关这一

点在ldquoMDSBES 中的推送队列rdquo中有详细描述

MDSBES 服务器将数据推送到客户端以后由客户端程序将数据提取交由相应的应用

逻辑进行处理然后客户端应用在提取数据的时候并不一定能及时地将所有推送到的数据

获取所以第四点要考虑的是手持设备端是否也存在队列如何保证客户端程序将所有数据

都提取出来有关这一点在ldquo手持设备端对消息的处理rdquo小节中有详细描述

手持设备在接收推送数据后会通知 MDSBES 服务器MDSBES 服务器则向应用服务器

发送异步的确认消息通过返回数值 200 或者是 400 通知应用服务器推送数据是推送成功还

是推送失败异步确认消息对于应用开发者有特别的意义通过它可以确定数据是否到达客

户端从而决定是否再次推送数据然后很多开发商都发现异步确认消息的返回的ldquo200rdquo

和ldquo400rdquo并不能准备地反映数据的接收情况所以如何使用异步确认消息在不同的场

景下异步消息会包含什么不同值对于应用开发者而言非常重要有关这一点在小节ldquo异步

确认消息rdquo有详细描述

推送请求是否到达 MDSBES 服务器

有关推送请求是否到达 MDSBES 服务器 很多开发人员都会忽略认为推送请求是否

到达 MDSBES 服务器是由代码是否编写正确决定的一定会在代码测试阶段体现也来也

必须在代码编写阶段解决这个问题然而在生产环境确实有一些因素会导致应用服务器发送

的推送请求无法到达 MDSBES 服务器从而导致推送数据丢失的问题

推送请求无法到达 MDSBES 服务器的一个可能的直接原因是指定的服务器名不正确或

者是指定的端口号不正确原因可能是 BES 管理员修改了 BES 服务器的服务器名IP 地址或

者是推送监听端口

这种问题比较容易发现一方面是因为所有的 BlackBerry 用户此时都无法接收到推送数

据另一方面也因为应用服务器端会报网络连接的异常以 java 代码为例当指定的服务

器名或者是端口号不正确的情况下应用服务器端的推送程序会报以下异常

javanetConnectException Connection refused

或者是报域名无法解析等网络连接级别的异常

发现这种情况只需要检查代码中指定的服务器名和端口号是否和生产环境相同就可以

解决问题

推送请求无法到达 MDSBES 服务器的一个潜在可能性是推送请求的并发数量太大导致

MDSBES 服务器无法及时接收所有请求

这种问题比较难于发现因为部分用户仍然可以收到推送数据只有部分用户的部分数

据丢失了同时这种问题在功能性测试场景下不容易发生只有在大压力测试或者是存在

大量用户的生产环境中才容易出现

产生这种问题的根源是 MDSBES 服务器的并发接收限制MDS 服务器其实是运行在

Tomcat 服务器上一个服务Tomcat 服务器作为应用容器处理了低层的网络请求而 Tomcat

服务器在底层有并发网络请求的限制当网络连接达到服务器限制时新的推送连接会处理

等待状态如果在网络超时前仍无法建立连接则会出现网络超时异常

javanetConnectException Connection timed out connect

根据测试的结果不同版本的 BES 服务器在低层并发网络请求的数量上有差异以 200

个并发限制为例就是说同一时间点只能有 200 个连接可以向 MDSBES 服务器发送推送请

求第 201 个请求以后有所有请求都将进入等待状态如果在 TCPIP 超时限制到达之前

MDSBES 服务器仍无法释放前 200 个请求则等待状态的请求会因为网络超时而中断

当应用服务器采用多线程机制处理推送请求的情况下有可能出现以上情况如果因为业

务原因在短时间内出现大量等待推送的数据应用服务器则有可能启动多于 200 个的线程发

送推送请求如果需要推送的数据太大或者是推送线程需要动态对数据进行计算就有可

能导致线程所启动的连接在短时间内没有关闭当多于 200 个线程处于这种状态的时候

MDSBES 服务器就无法继续接收推送请求了这种问题的一个现象就是应用服务器端报网络

超时异常或者是连接被拒绝的异常

总的而言要保证数据推送的成功第一步需要保证推送数据到达 MDSBES 服务器

发现推送数据没有到达 MDSBES 服务器的情况时应该对应用服务器的日志进行检查进一

步确定问题的原因

推送请求检查

在 MDSBES 服务器接收到推送请求后MDSBES 服务器会对请求进行检查只有符合

推送格式的请求才被 MDSBES 服务器所接收当 MDSBES 服务器接收推送时会返回值为

200 的 HTTP 答复告知应用服务器已经接收该请求当 MDSBES 服务器拒绝推送请求时会

根据拒绝的原因返回相应的错误码可能返回的错误码包括 400403503 等下面对不同的

情况进行描述

第一种请求被拒绝的原因是推送请求的格式不对包括 URL 格式不正确和 HTTP 头不正

确两种情况两种情况 MDSBES 服务器都会返回 400 消息表示拒绝URL 格式不正确包括

URL 中不包含ldquoDESTINATIONrdquo参数或者是不包含ldquoPORTrdquo参数等HTTP 头不正确包括

PUSH ID 不唯一等因为推送请求中的 HTTP 头属性都有缺省值所以使用错误的 HTTP 头属

性并不会引发错误MDSBES 服务器只是忽略该参数如 HTTP 头ldquoX-RIM-PUSH-IDrdquo被误写

为ldquoX-RIMPUSHIDrdquoMDSBES 服务器并不会拒绝该请求只是使用ldquoX-RIM-PUSH-IDrdquo的缺

省值就是服务器随机生成一个唯一的 ID 给推送消息

对于 HTTP 协议而言服务器返回 400 代表服务器一般性错误所以返回消息会以异常

形式被捕获以 java 代码为类程序会捕获到以下网络异常

ldquojavaioIOException Server returned HTTP response code 400 for URL

httprdquo

第二种推送请求被拒绝的情况是推送请求指定的收件人不正确就是指应用服务器发送

推送请求时使用了正确的格式不过 DESTINATION 参数指定的收件人并不存在或者是该收件

人并不是 BlackBerry 用户此时 MDSBES 服务器会返回 403 错误读者需要注意 BES 服务

器 37 之前的版本对于这种情况会视作格式错误返回 400 错误

同样以 java 代码为例收件人不正确的情况下程序会捕获到以下网络异常

ldquojavaioIOException Server returned HTTP response code 403 for URL

httprdquo

第三种请求被拒绝的情况是 MDSBES 服务器上的推送队列已满无法再接收更多的推

送请求此时会返回 503 错误表示服务器暂时没有足够的资源处理该请求这种情况下 java

应用程序会捕获到以下异常

ldquojavaioIOException Server returned HTTP response code 503 for URL

httprdquo

这种情况与推送请求是否正确无关取决于 MDSBES 服务器运行情况关于 MDSBES

服务器的推送队列在下一节中我们才进行详细的讨论

如果数据推送请求通过了 MDSBES 服务器的消息检查则 MDSBES 服务器会将该数据

放入推送队列等待发送同时给应用服务器返回成功消息 200

成功消息是以 HTTP 的 ResponseCode 形式返回的开发人员可以通过 HTTP 连接的相应

方法获取在 java 代码中获取 ResponseCode 的函数是ldquo getResponseCoderdquo只有确认

MDSBES 服务器返回的 ResponseCode 是 200才能确定推送数据已经由 MDSBES 接收对

于其它的异常情况都需要由程序进行判断确定如何调整请求继续重试推送过程

有一点非常重要的是 MDSBES 接收该推送数据并不意味着该数据就一定能到达手持设

备端还有很多因素会导致手持设备端的应用接收不到该数据

MDSBES 中的推送队列

MDSBES 服务器有一个队列用于缓存推送请求因为手持设备并不是一定在线

MDSBES 服务器需要先将推送请求保留在服务端在合适的情况下将数据推送到手持设备端

后才从队列中将该请求移除

MDSBES 服务器上的推送队列是 MDS 推送的关键组件它的行为不仅仅影响应用服务

器端是否能够发送新的请求也影响到手持设备端是否能够接收到数据所以对于 MDS 推

送的开发人员必须了解 MDSBES 服务器上的推送队列

MDSBES 服务器上的推送队列有一个队列深度缺省为 1000也就是说 MDSBES 服务

器在同一时间只能缓存 1000 个推送请求该队列的深度可以由管理员在管理端修改

同时 MDSBES 服务器上的推送队列并不是永久保存推送数据它有一个过期时间缺

省为 10 分钟也就是说一个推送请求在 MDSBES 服务器上最多能够被保留 10 分钟同样

这个过期时间可以由管理员在管理端修改

推送队列的深度修改界面如下

推送队列的过期时间的修改界面如下(以毫秒为单位)

对于推送队列的进一步讨论是有关推送数据进入队列后的状态如何变化下面是推送数

据在队列中的状态变化图说明了推送数据在不同情况下的状态变化下图的各个状态中

淡蓝色的状态为起始状态深蓝色的状态为中间状态红色的状态为终止状态处于中间状

态的数据最终必然会转为终止状态的其中一种

当 MDSBES 服务器接收到推送数据后如果之前目标用户没有 5 个处于暂时失败的数

据包服务器会将数据放入等待发送的队列中推送的数据处于ldquo尝试发送rdquo状态处于ldquo尝

试发送rdquo会在很短的时间内占用 1000 个队列位置中的一个不管是成功还是失败它所占

用的队列位置很快被释放

然后 MDSBES 服务器尝试将数据推送到手持设备端如果此时对应的手持设备处于开

机状态网络一切正常则 MDSBES 服务器将数据发送给手持设备推送数据变成ldquo成功rdquo

状态MDSBES 服务器会向应用服务器发送异步的确认消息同时将数据从队列中移除注

意此时 MDSBES 服务器向应用服务器返回的确认消息并不一定是表示成功 200具体是

返回代表成功的 200 还是返回代表失败的 400 还取决于其它因素有关这一点我们在后面的

章节中详细讨论

如果 MDSBES 服务器尝试将数据推送到手持设备的时候手持设备处于关机状态则该

数据进入ldquo暂时失败rdquo状态处于ldquo暂时失败rdquo状态的数据不占用等待队列的队列位置某

一个目标用户最多只能有 5 条数据处于ldquo暂时失败rdquo状态目前没有明确的数据或者是文档

说明一个 MDSBES 服务器总共可以保留多少条ldquo暂时失败rdquo状态的数据

如果某一用户已有 5 条数据处于ldquo暂时失败rdquo状态MDSBES 服务器接收到该用户新

的推送请求时会直接将该数据转入ldquo等待rdquo状态每一条进入ldquo等待rdquo状态的推送数据会占

用推送队列中的一个位置当队列中的 1000 个位置都被占满的时候 MDSBES 服务器就停止

接收新的数据推送无论新的数据是推送给哪个用户

处于ldquo暂时失败rdquo和ldquo等待rdquo状态的数据会在服务器上保留 10 分钟(如上所述这一

时间可以由管理修改)10 分钟后处于这两种状态的数据将被丢弃进入ldquo过期rdquo状态同

时 MDSBES 服务器会向应用服务器发送异步消息这种情况下发送的异步确认消息一定是

代表失败的 400如果在 10 分钟内手持设备重新进入网络手持设备会发送消息到 MDSBES

服务器告知该手持设备已经上线MDSBES 服务器会将处理ldquo暂时失败rdquo和ldquo等待rdquo状态的

数据转成ldquo尝试发送状态rdquo重新发送该数据需要注意的是在 10 分钟的等待过程中

MDSBES 服务器如果没有接收到手持设备重新连接网络的消息则 MDSBES 服务器不会尝

试重新发送ldquo暂时失败rdquo和ldquo等待rdquo状态的数据

根据以上的分析可以对推出以下结果

1 当用户的手持设备在线时对该用户的数据推送会被马上处理该数据只在很短的时间

占用 1000 个等待队列中的一个位置

2 当用户的手持设备不在线时假设该服务器只有一个用户则服务器在 10 分钟内最多

可以接收该用户的 1005 条数据其中 5 条处于ldquo暂时失败rdquo状态1000 条在等待队列

3 如果用户的手持设备在数据发送给 MDSBES 服务器 10 分钟内重新接入网络MDSBES

服务器可以将该数据推送到手持设备端

4 如果 MDSBES 服务器的等待队列未满推送给新的用户的前 5 条数据不占用等待队列

5 如果 MDSBES 服务器的等待队列已满不管是由哪个用户的数据占满的MDSBES 服

务器都会停止接收所有用户的新的数据推送

总结而言在生产环境中保证 MDSBES 服务器正常工作的关键是要保证 MDSBES 服务

器的等待队列不被占满

手持设备端对推送数据的处理

MDSBES 服务器成功接收应用服务器推送的数据后会将数据推送到手持设备端在手持

设备端需要由客户端应用程序将推送的数据提取出来一个直观的猜测是手持设备端同样需

要一个队列保存数据否则客户端应用程序在提取前一条数据时新到达的数据就会直接丢

失需要验证的是这个队列有多深队列行为是否被系统隐藏经过测试证实手持设备端确

实也存在队列队列深度为 10而且可以反映在应用层其中最值得开发人员注意的是这

个队列是一个可溢出队列关于手持设备端推送数据队列的示意图如下

如图所示当客户端提取程序正在读取数据ldquoRrdquo如图中标有 R 的椭圆形所示如果

有更多的数据被推送到手持设备端所推送的数据会被放入设备的队列中一共有 10 条数

据可以被保留在设备队列中如椭圆形 1 到 10 所示此时如果有更多的数据被推送进来

设备队列已满则这些新的数据会溢出如椭圆形 111213 所示同时对于服务器端

而言数据 111213 已经被推送过一次服务器端不会再次尝试推送这些数据在这种

情况下数据 1 到 10 可以继续被客户端提取程序所提取而数据 111213 则丢失了

对于尚未推送到手持设备端的数据如数据 1415假设因为网络中断没有被推送到

手持设备端则仍保留在服务器端当网络恢复后数据 1415 仍可以被正常处理

对于确认消息(图中橙色圆形所示)的处理则更为复杂在异步确认消息一节中再作讨

从这一节的讨论得出的结果是数据到达手持设备端未必就能够被客户端提取程序提取

客户端提取程序有职责尽快提取数据防止推送数据的溢出溢出的数据在客户端无法再被

处理而且服务器端也不会尝试继续推送这些数据

异步确认消息(应用依赖和非应用依赖)

手持设备端在接收到推送数据后会通知 MDSBES 服务器这时 MDSBES 服务器会根据

最初的推送请求向指定的 URL 发送一个确认消息告知应用服务器某一条推送数据的最终

结果这就是异步确认消息异步确认消息以 http 协议发送给应用服务器消息 200 代表

数据推送成功400 代表数据推送失败

异步确认消息是应用服务器判断推送是否成功的有效工具但是很多开发人员因为对

异步确认消息的机制不了解错误地使用了异步确认消息从而导致数据的丢失所以我们

需要详细了解异步确认消息的工作机制

要讨论异步确认消息的工作机制必然涉及到应用依赖与非应用依赖应用依赖的推送

和非应用依赖的推送对应的异步确认消息工作机制是不同的

下面先讨论非应用依赖的场景这也是很多开发商使用的场景

如果推送是非应用依赖的数据是以到达手持设备为成功标志的只要数据到达了手持

设备MDSBES 服务器就认为该数据推送成功会给应用服务器发送异步确认消息 200

如果数据推送时手持设备不在线则 10 分钟内 MDSBES 服务器会在服务器上保留该数

据如果 10 分钟内手持设备重新接入网络则 MDSBES 服务器会尝试发送该数据一旦数

据到达手持设备MDSBES 服务器都会给应用服务器发送异步确认消息 200

如果 10 分钟内手设备没有接入网络则该推送数据过期MDSBES 服务器会丢弃该数

据同时给应用服务器发送异步确认消息 400

可以看出非应用依赖的推送方式并不能保证客户端应用程序接收到数据在非应用依

赖的推送方式下 MDSBES 返回 200 确认消息也不能认为客户端已接收到数据如果数据送

达手持设备的时候客户端应用程序没有启动所推送的数据会在手持设备端丢失而

MDSBES 服务器认为数据已成功送达或者是客户端应用程序已启动但是没有及时读取推

送数据在手持设备端溢出的数据也会丢失MDSBES 服务器同样认为数据已成功送达

所以在非应用依赖的情况下如果接收到 400 确认消息可以确定客户端没有收到数据

如果接收到 200 确认消息只能认为客户端可能收到了数据

从以上的讨论看非应用依赖的推送方式并不适合用于可靠性要求较高的应用对于可

靠性要求较高的应用需要知道客户端应用程序是否接收到了数据应用依赖的推送方式就

是为此而设计的如果推送是应用依赖的数据是以客户端应用程序确认为成功标志的只

要在应用程序确认后MDSBES 服务器才认为数据推送成功这时才给应用服务器发送异步

确认消息 200

有关客户端应用程序如何确认收到数据在ldquo客户端接收关键代码rdquo一节中有描述简

单而言就是客户端应用程序通过调用 MDSPushInputStream 实例的 accept 函数确认数据收

到如果客户端应用程序在接收到数据后没有调用 accept 函数MDSBES 服务器会认为数

据推送失败给应用服务器发送异步确认消息 400另外一点曾经强调过的是accept 函数

调用后只有 InputStream 和 StreamConnection 都关闭了确认消息才被发送给 MDSBES 服

务器在极端的情况下这一点会影响到异步确认消息的结果

下面具体分析各种情景

如果手持设备在线但是客户端应用程序没有启动推送数据到达手持设备端后没有程

序侦听推送端口当然也没有程序会调用 accept 函数此时 MDSBES 服务器会认为数据推

送失败向应用服务器发送异步确认消息 400

如果手持设备在线客户端应用程序在读取推送数据后没有调用 accept 函数直接关闭

连接此时 MDSBES 服务器同样会认为数据推送失败向应用服务器发送异步确认消息 400

在这种情景下即使客户端接收到了数据但应用服务器仍收到失败消息因为作为成功标

志的 accept 函数并没有调用

如果手持设备在线客户端应用程序在读取推送数据后调用了 accept 函数并关闭连

接此时 MDSBES 服务器会认为数据推送成功向应用服务器发送异步确认消息 200

对于手持设备不在线的情况结果与非应用依赖相似10 分钟内如果手持设备不接入

网络的话推送数据会在 MDSBES 服务器上过期MDSBES 服务器会向应用服务器发送异

步确认消息 400

如果 10 分钟内手持设备重新接入网络MDSBES 服务器会尝试发送数据至手持设备端

还是以 accept 函数的调用为推送成功的标志如果数据重新发送成功但是客户端没有调

用 accept 函数则 MDSBES 服务器会向应用服务器发送异步确认消息 200

对于推送到手持设备端但是溢出的数据因为没有程序能够读取该数据并针对该数据调

用 accept 函数MDSBES 服务器会一直等待到 10 分钟后向应用服务器发送异步确认消息

400认为数据过期

一个特殊的情况是客户端应用程序在接收到数据后调用了 accept但是没有关闭

InputStream 和 StreamConnection此时 MDSBES 服务器会认识客户端还在处理数据会继

续等待如果服务器等待时候超过 10 分钟MDSBES 服务器会认为数据推送失败向应用

服务器发送异步确认消息 400如果在 10 分钟内客户端主动关闭 InputStream 和

StreamConnection或者是网络异常中断MDSBES 服务器会认为数据推送成功向应用服

务器发送异步确认消息 200

下面再讨论一个异常情况下的特例

结合以上描述的各种因素在极端的情况下会出现一些特殊的情况如上图所示假设

MDSBES 服务器在推送了数据 R数据 1 到数据 13 后出现网络中断同时客户端应用程序

正在缓慢地读取数据 R此时数据 1 到数据 10 会被保留在手持设备队列中客户端应用程

序在读取数据 R 后可以继续读取数据 1 到数据 10如果客户端应用程序在读取了数据后都

调用了 accept 函数则会产生对应用确认消息如图中的确认消息 R确认消息 1 到确认消

息 10但是因为网络的中断这些确认消息都无法到达 MDSBES 服务器如果网络在 10

钟内没有恢复MDSBES 服务器上所有等待确认消息的记录都会认识是过期的MDSBES

会向应用服务器发送异步确认消息 400此后即使网络恢复确认消息 R确认消息 1 到确

认消息 10 都不再有效

在这种特殊情况下即使客户端应用程序接收到数据并调用了 accept 函数MDSBES

服务器同样认为数据没有推送成功

根据以上的讨论应用依赖的推送同样不能保证客户端应用一定能收到数据但是在

应用依赖的推送方式下 MDSBES 返回 200 确认消息可以确定客户端已接收到数据因为 200

确认消息是由客户端应用程序调用 accept 函数触发的客户端应用程序可以在确认数据收

到后才调用 accept 函数通知应用服务器该数据已成功接收甚至可以在接收到数据后根

据数据内容决定不调用 accept 函数从而通过应用服务器该数据没有被接收

所以在应用依赖的情况下如果接收到 200 确认消息可以确定客户端已经接收到数据

如果接收到 400 确认消息只能认为客户端可能没有收到数据

因此应用依赖的推送方式更适合可靠性要求高的应用如果应用服务器接收到 200

确认消息就可以认为推送成功继续处理其它事务如果应用服务器接收到 400 确认消息则

需要组织数据重发直到接收到 200 确认消息为止这样就可以保证客户端一定能够接收到

需要推送的数据

如何保证数据推送的可靠性

通过以上几个章节的讨论我们知道虽然 MDSBES 服务器提供了很多机制保证数据

推送的可靠性但是都无法完全保证数据被客户端应用程序所接收

对于数据推送的可靠性保证一方面开发人员需要了解各个可能导致故障的因素在应

用编写的时候尽量减少故障出现的可能性对故障点进行规避另一方面开发人员也需要实

现额外的机制进行数据重发在故障点无法规避的情况下通过数据重发消除故障点的影响

应用依赖和非应用依赖的比较和选择

在实现推送应用的时候首要考虑的是有关应用依赖与非应用依赖的选择

应用依赖的推送形式在可靠性方面更优于非应用依赖的推送形式但是应用依赖的推送

形式对 MDSBES 应用服务器的潜在压力较大客户端应用程序的实现也较为复杂

非应用依赖的推送形式优势在于实现简单对 MDSBES 应用服务器形成的压力相对较

非应用依赖的推送形式从某种程度上讲有点类似于广播协议着重点是从服务器向客户

端发送大量数据并不看重客户端的接收情况从数据的应用类型来分非应用依赖的推送

更适合于天气信息最新报价等数据这些数据在客户端应用关闭时被自然丢弃短时间内

的多条数据有丢失影响也不大必竞用户不会关注 11 点零六分的天气和 11 点零七分的天气

有何不同

应用依赖的推送形式则类似于点对点协议不仅从服务器端向客户端发送数据同时也

关注客户端的接收情况应用依赖的推送更适合于 OAERP 这种应用这种应用需要保证

新的文档或者是记录一定能到达客户端应用丢失一条待办信息可能就会导致整个审批流程

的停滞

再从两种推送方式的本质差别看应用依赖并没有额外的机制消除故障点它的优势在

于它的异步确认消息更可信所以在选择了应用依赖的推送方式后进一步还是需要实现

额外的数据重发机制根据 MDSBES 所发送的异步确认消息实施数据重发

应用依赖的推送再加上结合异步确认消息的数据重发机制是保证推送数据可靠性的

最好方法

MDS 接收线程限制的处理

不管使用什么方式的推送应用开发时都需要考虑到 MDS 接收线程的限制不过以

现在的服务器处理能力正常情况下的 HTTP 推送请求可以在几毫秒或者是几十毫内处理完

成少量的 HTTP 推送不足于构成网络阻塞

需要考虑的是应用服务器端的推送程序在推送过程中不要在建立连接后才动态组织数

据这样会浪费 http 连接资源好的建议是预先组织好数据在建立 HTTP 连接后快速写完

数据尽快关闭连接

此外对于应用服务器的推送线程也要加于控制避免在短时间内无限制地启动新的线

程进行连接否则也容易造成连接过多而阻塞的情况

从网络层面来看调整应用服务器与 MDSBES 服务器之间的网络超时设置也是避免这

种问题的一种方法在允许的范围内调大网络超时时间可以让应用服务器的推送线程等待更

长的时间从而让 MDSBES 有足够的时间释放原有的连接

最后无论使用了什么方法调优都需要对应用服务器的推送程序进行异常处理当发

现网络超时异常的时候可以组织数据重发当然数据重发的间隔时间也要考虑不要因为

重发数据再次造成网络阻塞

MDS 推送队列限制的处理

相对而言MDS 推送队列限制造成的推送失败比 MDS 接收队列造成的推送失败更常见

已经发现的一个实际案例是周未的时候用户大量关机导致大量推送数据处于推送队列中

填满了推送队列从而导致 MDSBES 服务器不再响应新的推送请求一旦过期时间到达

有新的队列位置空闲又有大量新的推送数据重新占满推送队列

对于 MDS 推送队列的限制直观的做法是对队列参数进行调整如之前讨论的管理

员可以对队列的深度和过期时间进行调整

然而出于对过期时间的不正确理解管理员倾向于将过期时间调大当过期时间被调

大后推送给关机用户的数据在服务器占用队列的时间就变长导致 MDS 推送队列更容易

被占满

所以如果对过期时间进行调整的话方向是将过期时间调小过期时间越小等待数

据占用队列的时间就越短MDS 推送队列被占满的可能性就越小但是过期时间也不能

过小过期时间过小可能会导致过多的过期数据使应用服务器需要处理更多的数据重发

比如将过期时间调成 1 分钟用户走过一个人行遂道都可能导致数据发送失败

对于队列深度的调整方向是调大队列深度如从缺省的 1000 调整到 2000此时

MDSBES 服务器在同一时间可以保留更多的等待数据减少了队列占满的机会不过调大队

列深度会让 MDSBES 服务器保留更多的数据从而占用更多的服务器内存对服务器产生

更大的压力所以调用队列深度需要根据服务器的运行能力进行调用

从应用服务器设计角度应用 MDS 队列限制的方法是尽量减少等待数据的发送如发

送第一条数据的时候发现数据过期对于该用户的其它数据可以调整发送时间在更长的时

间间隔后才尝试发送

对于 MDS 队列限制应用服务器端的推送应用需要对 MDSBES 服务器返回的 503 错误

进行处理对于出现 503 错的数据组织重发同样重发的间隔也需要考虑不要因为数据

重发占用更多的推送队列也可以根据目标用户进行判断对于可能关机的用户暂时不重发

数据

手持设备端接收队列的处理

手持设备端接收队列并未从文档中明确列出是根据测试结果分析得出的手持设备端

目前也没有发现设置可以调整该队列所以客户端程序设计的时候不应该依赖手持设备端队

开发人员需要意识到的是手持设备端的接收队列是可溢出队列一旦推送数据从接收队

列出溢出客户端程序就无法提取该数据了所以在客户端应用开始过程中要注意加快推送

数据的提取过程在提取过程中不要对数据进行复杂的计算提取完成后尽快读取下一条数

据对于接收数据的处理和计算可以交由另一个线程完成

因为溢出数据对客户端应用而言不可见所以数据溢出时在客户端应用中也不会有异

常开发人员需要通过服务器返回的过期消息才能知道该数据推送失败

对于这一点客户端应用除了尽量避免数据溢出外无法在客户端通过其它机制进行补

应用依赖的客户端

对于应用依赖的客户端应用关键点是决定何时调用 accept 函数同时保证 InputStream

和 StreamConnection 被关闭

如之前讨论的使用应用依赖的推送关键是希望使用 MDSBES 发送给应用服务器的异

步确认消息从而对数据是否到达进行判断所以应用依赖的客户端应用需要保证正确调用

accept 函数

另外如果 InputStream 和 StreamConnection 没有关闭的话accept 函数所发出的确认

消息无法被 MDSBES 服务器收到开发人员在代码中应该主动关闭 InputStream 和

StreamConnection同时在程序发生异常时也需要在 catch 代码段对 InputStream 和

StreamConnection 进行关闭处理

异步确认消息处理

异步确认消息用于判断数据是否被成功推送至客户端应用要使用异步确认消息首先

要保证成功地无遗漏地接收所有从 MDSBES 服务器发送过来的异步确认消息

异步确认消息是一个 HTTP 消息消息比较短接收过程的并发压力不大下面是一个

异步确认消息的样例

Received notificationGET HTTP11

User-Agent RIM MDSCS

Accept

X-RIM-Push-ID pushID2081773768

X-RIM-Push-Destination mmtestbjsearbcn

X-RIM-Push-Status 400

Host localhost7778

其中最关键是的获取属性 X-RIM-Push-Status根据该属性的值判断数据推送是成功

(200)还是失败(400)当然属性 X-RIM-Push-ID 也非常重要用于确定该确认消息是针

对哪条推送数据的

如果应用服务器端通过确认消息发现数据推送失败则需要根据属性 X-RIM-Push-ID 重

新组织数据进行重发

Page 2: BES 服务器推送机制分析 - images.csdn.netimages.csdn.net/upimgs/lee/BBPDF/BESfwtsjzfx.pdf · 前言 数据推送是BlackBerry 应用平台的一大优势,在BlackBerry 应用平台上部署的应用可以

目录

前言 3

MDS 推送介绍 3

MDS 推送架构 3

数据推送命令格式 4

数据推送关键代码 6

客户端接收关键代码 8

异步确认消息接收代码 11

推送细节分析 13

MDS 推送的整体时序 13

推送请求是否到达 MDSBES 服务器 14

推送请求检查 15

MDSBES 中的推送队列 17

手持设备端对推送数据的处理 20

异步确认消息(应用依赖和非应用依赖) 21

如何保证数据推送的可靠性 24

应用依赖和非应用依赖的比较和选择 24

MDS 接收线程限制的处理 24

MDS 推送队列限制的处理 25

手持设备端接收队列的处理 25

应用依赖的客户端 26

异步确认消息处理 26

前言

数据推送是 BlackBerry 应用平台的一大优势在 BlackBerry 应用平台上部署的应用可以

和 BlackBerry 推送邮件一样通过推送实时地将数据从服务器端推送到 BlackBerry 手持设备

端所以对于很多应用开发商而言BlackBerry 应用平台提供的推送功能是 BlackBerry 应

用集成必然会使用到的强大功能

然而因为种种原因有一些开发商发现使用 BlackBerry Enterprise Server (BES) 应用平

台的推送功能并不能保证数据到达 BlackBerry 设备导致应用层面的种种问题实际上

BlackBerry 应用平台提供了从多机制保证数据推送的成功只要我们充分了解 BES 中 MDS(以

下简称 MDS)的推送机制我们就可以利用平台提供的机制保证数据到达 BlackBerry 手持设

本章节的主要目的是详细分析 MDS 的推送机制让读者更好地了解 BlackBerry 应用平

台从而可以开发更加强壮更加稳定的 BlackBerry 应用

值得读者注意的是本章节的内容是结合少量的官方文档配合于大量的测试总结而来

的旨在为读者提供更多的线索以理解 MDS 推送机制如果本章节的内容与 RIM 提供的任

何官方文档有冲突的话请以官方文档为准同时不同版本的 BlackBerry 手持设备在不同场

景下也可能有不同的行为所以读者在生产环境实施 BlackBerry 应用时也要结合用户所使用

的 BlackBerry 设备进行测试才能最好地保证应用的稳定性

MDS 推送介绍

MDS 推送架构

在详细讲解 MDS 推送机制之前我们先从整体上了解 MDS 推送的架构下面是 MDS 推

送架构的示意图

从示意图中可以看到在 BlackBerry 应用平台上的数据推送从整体上可以分为六步按时

间顺序分别为

1 第一步应用服务器向 MDSBES 服务器发送推送请求所发送的请求为 HTTP 格式的请

求有关请求的详细格式在下一小节中有详细讲解

2 第二步MDSBES 服务器查询相关配置数据库确定应用服务器所发送的请求是否为合

法的请求此外MDSBES 服务器还会根据资源情况确定是否接收该请求对于是否接

收请求的判断在下一节内容中也有详细讨论

3 第三步MDSBES 服务器向应用服务器返回消息通知应用服务器是否接受该请求返

回消息以 HTTP 答复的方式返回给应用服务器

4 第四步MDSBES 服务器将数据推送到手持设备端

5 第五步手持设备端对数据进行处理后向 MDSBES 服务器返回确认消息

6 第六步MDSBES 根据手持设备端返回的消息决定向应用服务器返回什么异步消息这

一步并不是必然发生的根据推送请求的不同有可能不发生

从这里我们可以看到从应用服务器到手持设备端的推送通道是由多个不同的通道连接

而成的这其中有很多个连接点某一个连接点出现异常都可以导致推送的失败所以应用

开发商需要详细了解这些连接点可能出现的问题在应用开发过程中进行规避

在详细描述各个可能出现问题的关键点之前需要先了解推送的基本实现对于应用开

发人员而言完成数据推送需要完成的主要程序编写工作有应用服务器端发出推送请求的

程序手持设备端侦听接收推送数据的程序和服务器端侦听接收确认消息的程序如下图

橙色框图所显示的

以下章节将对这三个部分作详细讲解

数据推送命令格式

在 BlackBerry 应用平台上的数据推送是由应用服务器发起的应用服务器根据应用逻辑

进行判断发现有数据需要推送到用户的手持设备端时连接 MDSBES 服务器进行数据推送

应用服务器与 MDSBES 服务器的连接方式为 HTTP 连接通过 HTTP 连接发送一个 POST 请

求将需要推送的数据作为 POST 的内容

该 POST 请求的 URL 格式为

httpBES_HOSTBES_PORTpushDESTINATION=DESINTATION_ADDRESSampPORT=HANDHEL

D_APPLICATION_PORTampREQUESTURI=

下面是请求中各参数的说明

参数 描述

BES_HOST BlackBerry Enterprise Server 的服务器名或者是 IP

BES_PORT BlackBerry Enterprise Server 的推送端口一般是 8080

DESTINATION_ADDRESS 用户的邮件地址或者是设备码 (PIN)

HANDHELD_APPLICATION_PORT BlackBerry 手持设备需要帧听的端口

也就是说应用服务器通过 BES_HOST 和 BES_PORT 指定需要连接的 MDSBES 服务器和

需要连接的端口BES 服务器安装后缺省使用 8080 作为推送端口所以推送应用开发人员

一般情况下只需要了解 MDSBES 服务器的服务器名或者是 IP 地址就可以了如果 BES 管理

人员在安装服务器后修改过推送端口则需要告知开发人员使用新指定的端口

推送 URL 中 push后面的部分用于指定数据的接收者和手持设备需要帧听的端口

DESTINATION_ADDRESS 用于指定数据的接收者可以使用该用户的邮件地址或者是该用户手

持设备的 PIN 码HANDHELD_APPLICATION_PORT 用于指定手持设备需要帧听的端口这里

使用的端口只是一个约定要求在手持设备上运行的程序从这一端口中读取数据

除了 URL 参数以后在推送过程还可以使用一些规定好的 HTTP 头指定该推送的属性

HTTP 头参数表如下

HTTP 头 描述

X-RIM-Push-ID 用于指定该消息的 ID该 ID 需要是一个唯一值可以用于

取消推送或者是查看推送的状态

X-RIM-Push-NotifyURL 用于指定确认消息的接收 URL

X-RIM-Push-Reliability-Mode 用于指定推送的可靠性

X-RIM-Push-Deliver-Before 用于指定推送数据的最迟推送时间

X-RIM-Push-Priority 用于指定推送数据的优先级

X-RIM-Push-ID 属性一般建议使用 URL 和编号结合的方式如 123blackberrycom为

了更好地控制数据的推送一般不建议使用随机数作为推送 ID使用统一的 ID 生成方式更

有利于推送数据的取消和状态确定注意如果没有指定该参数的话MDSBES 服务器会自

动生成一个唯一的 ID这样就无法在应用服务器上使用这个 ID 对特定的推送数据进行处理

了此外推送 ID 不能以ppgrimcom 结束

X-RIM-Push-NotifyURL 属性通过 URL 的形式指定了确认消息的接收地址如

httptestingserver7778指定了这一属性后MDSBES 服务器会将推送的确认消息以 HTTP

请求的形式发送到指定的服务器也就是说应用开发者需要开发一个 HTTP 服务器端程序监

听指定服务器的指定端口(上例中则是服务器 testingserver 的 7778 端口)通过这个服务器

端程序获取确认消息在确认消息中会包含 HTTP 头 X-RIM-Push-ID 和 X-RIM-Push-Status通

过 X-RIM-Push-ID 告知监听者是哪条推送数据的确认消息通过 X-RIM-Push-Status 指明该数

据推送的结果200 代表推送成功400 代表推送失败现实环境中对于数据推送是否成功

不能简单地以结果 200 或者是 400 进行判断需要结合很多因素进行判断本文的后续章节

会详细描述

属性 X-RIM-Push-Reliability-Mode 用于指定推送的可靠性值可以是 TRANSPORT 或者是

APPLICATION另外有一个值 APPLICATIONPREFERRED 涉及更复杂的场景这里不做描述如

果指定可靠性为 TRANSPORT 则以数据到达手持设备准本文称之为非应用依赖如果指定

可靠性为 APPLICATION 则以数据被手持设备端应用接收为准本文称之为应用依赖对于不

同可靠性设置本文的后续章节会有详细描述总体而言非应用依赖的推送更适合于广播

性质的不要求应用一定收到数据的场景而应用依赖更适合于点对点的要求应用一定要收到

数据的场景

属性 X-RIM-Push-Deliver-Before 用于指定数据推送的最后时间如果在指定时间前该数

据仍无法被成功推送到手持设备端则该数据会被视作过期而被 MDSBES 服务器丢弃

属性 X-RIM-Push-Priority 用于指定推送数据的优先级指定推送数据的优先级并不能加

快数据的推送指定优先级的结果是推送数据到达手持设备端的行为不同可以选择的值有

none (缺省)low medium 和 high如果指定优先级为 lowmedium 或者是 high则用

户只是接收到数据如果指定优先级为 high则用户在接收到数据后还会看到提示对话框

数据推送关键代码

理论上讲应用服务器的数据推送代码可以用任何可以提供 HTTP 支持的语言编写需

要实现的不过是通过 HTTP 协议往指定的服务器和端口发送 POST 消息

下面以 java 代码为例讲解数据推送的关键代码部分本例的代码片段为一个 Java

Application 的部分代码该 Java Application 在服务器上以 J2SE 应用的形式运行启动一个

线程发送 HTTP 的 POST 请求以下代码为该线程的 run 函数

public void run()

Random random = new Random()

String pushId = pushID + randomnextInt()

String data = What need to be pushed

try

URL _pushURL = new URL(http testingBESabccom 8080

pushDESTINATION=user1abccomampPORT=55100ampREQUESTURI=)

HttpURLConnection conn = (HttpURLConnection) _pushURLopenConnection()

connsetDoInput(true)

connsetDoOutput(true)

connsetRequestMethod(POST)

connsetRequestProperty(X-RIM-PUSH-ID pushId)

connsetRequestProperty(X-RIM-Push-NotifyURL

httptestingserver7778)

connsetRequestProperty(X-RIM-Push-Reliability-Mode

APPLICATION)

OutputStream out = conngetOutputStream()

outwrite(datagetBytes())

outclose()

InputStream ins = conngetInputStream()

int contentLength = conngetContentLength()

int responseCode = conngetResponseCode()

Systemoutprintln(Response Code is + responseCode)

if (contentLength gt 0)

byte[] someArray = new byte[contentLength]

DataInputStream dins = new DataInputStream(ins)

dinsreadFully(someArray)

Systemoutprintln(new String(someArray))

else

Systemoutprintln(Content legth of response is 0)

conndisconnect()

catch (IOException e)

Systemerrprintln(Exception + e)

在以上代码中先通过 URL 类建立一个 HTTP 连接定义URL 类的构造函数中第一个参

数为协议类型这里使用ldquohttprdquo第二个参数为服务器名第三个参数为请求的端口号

第二个参数和第三个参数对应了数据推送请求中的 BES_HOST 和 BES_PORT 属性URL 类的

构造函数中第四个参数为相对 URL推送属性 DESTINATION 和 PORT 都放在相对 URL 中代

码如下

URL _pushURL = new URL(http testingBESabccom 8080

pushDESTINATION=user1abccomampPORT=55100ampREQUESTURI=)

建立了 URL 类的实例后可以通过该实例的 openConnection 函数打开连接返回的是

一个 HttpURLConnection 实例

HttpURLConnection conn = (HttpURLConnection) _pushURLopenConnection()

打开 http 连接后可以通过连接实例的 setRequestProperty 函数设置 http 头推送请求中

的 http 头属性在这里设置如

connsetRequestProperty(X-RIM-PUSH-ID pushId)

其中一个关键的 http 头属性是 X-RIM-Push-Reliability-Mode设置该属性为ldquoTRANSPORTrdquo

表示该推送是非应用依赖的推送设置该属性为ldquoAPPLICATIONrdquo表示该推送是应用依赖的

推送两者的具体区别在后面的章节会有详细的讨论以下代码设置该属性为

ldquoAPPLICATIONrdquo

connsetRequestProperty(X-RIM-Push-Reliability-Mode

APPLICATION)

特别要注意如果设置推送类型为应用依赖对于服务器的设置和手持设备有特定的要

求一方面是服务器端必须是 40 以上同时必须设置允许对所使用的端口进行应用依赖的

推送一方面是要手持设备的 ROM 版本也是 40 以上同时服务器必须知道手持设备能否

支持应用依赖的推送对于服务器的端口设置设置界面如下可以由 BES 管理员进行设置

如果是使用开发环境的 MDS-CS 模拟器模拟应用依赖推送的话需要修改开发环境的

MDSconfig目录中的 rimpublicproperty文件去除 pushapplicationreliableports一行的注释

同时加上需要推送的端口如ldquopushapplicationreliableports=10055100rdquo

对于服务器端是否知道手持设备端的版本需要通过手持设备端的操作完成只要手持

设备端的浏览器通过 BES 访问过网络服务器就会记录该手持设备是否支持应用依赖的推

送如果只是测试的话在测试前通过手持设备端的浏览器打开任何一个网站就可以了如

果是生产环境则需要考虑客户端应用在第一次启动的时候主动访问一次网络以保证客户

端都可以正常接收应用依赖的推送

如果以上设置没有完成的话对 MDSBES 服务器发送应用依赖的推送时会被拒绝服

务器端会出现ldquoThe specified delivery method is not possiblerdquo的错误应用服务器的推送程

序则会捕获以下异常

ldquojavaioIOException Server returned HTTP response code 400 for

URL httpapprdquo

设置属性后就需要获取 http连接的 OutputStream将需要推送的数据写入 OutputStream

中写完后关闭连接

需要注意的是 MDSBES 服务器是否接收该推送请求是通过 http 返回码实现的所以推

送应用中需要获取到 http 连接的返回码通过返回码判断推送是否成功代码如下

int responseCode = conngetResponseCode()

客户端接收关键代码

客户端数据接收程序运行在 BlackBerry 手持设备上必须通过 java 语言实现需要调用

BlackBerry 设备端的 API

客户端接收程序的主体就是启动一个线程开启 httpltportgt连接(其中 port 是客户

端需要侦听的端口号)然后从该连接中获取 InputStream从 InputStream 中获取服务器所

推送的数据

下面是侦听线程的 run 方法为保持程序的完整代码中使用到的类属性并没有修改

从该函数中看到的本函数没有定义的变量都是该类的类属性有关完整代码请参考

BlackBerry 开发环境所提供的标准样例

public void run()

StreamConnection stream = null

InputStream input = null

MDSPushInputStream pushInputStream=null

while (_stop)

try

synchronized(this)

_notify =

(StreamConnectionNotifier)Connectoropen(http55100)

while (_stop)

stream = _notifyacceptAndOpen()

try

input = streamopenInputStream()

pushInputStream= new

MDSPushInputStream((HttpServerConnection)stream input)

DataBuffer db = new DataBuffer()

byte[] data = new byte[256]

int chunk = 0

while ( -1 = (chunk = inputread(data)) )

dbwrite(data 0 chunk)

pushInputStreamaccept()

inputclose()

streamclose()

data = dbgetArray()

catch (IOException e1)

Systemerrprintln(e1toString())

if ( input = null )

try

inputclose()

catch (IOException e2)

if ( stream = null )

try

streamclose()

catch (IOException e2)

_notifyclose()

_notify = null

catch (IOException ioe)

if ( _notify = null )

try

_notifyclose()

_notify = null

catch ( IOException e )

以上代码中建立端口侦听的代码如下

_notify = (StreamConnectionNotifier)Connectoropen(http55100)

其中 URLldquohttp55100rdquo是用于侦听推送的特定格式55100 为需要侦听的端口两

个线程不能同时打开同一个端口的侦听所以在这里使用了 synchronized 对线程并发进行了

控制

建立侦听后可以通过函数 acceptAndOpen 开始侦听当线程调用该方法开始侦听的时候

线程会阻塞在这里直到有推送消息到达代码如下

stream = _notifyacceptAndOpen()

侦听之后的代码为获取连接的 InputStream从 InputStream中获取服务器端推送的数据

读取完数据后关闭 InputStream 和 StreamConnection然后重新调用 acceptAdnOpen 继续侦

听端口准备接收新的推送数据

开发人员如果希望使用应用依赖的推送在客户端必须将 InputStream 转换成

MDSPushInputStream同时在接收完数据后调用 MDSPushInputStream 的 accept 函数造知服

务器该数据已收到这也是应用依赖和非应用依赖在代码实现层面的最主要区别代码如下

pushInputStream= new

MDSPushInputStream((HttpServerConnection)stream input)

helliphellip

pushInputStreamaccept()

从良好的编程习惯来讲在连接使用后需要主动关闭所使用的连接和资源如

InputStreamStreamConnectionStreamConnectionNotifier 等所以在样例代码中也可以看

到该样例在异常处理阶段都对这些资源进行了关闭操作在关闭这些连接和资源的时候对

StreamConnectionNotifier 需要特别注意有可能在 StreamConnectionNotifier 中会保留有未

读取的推送数据需要调用 acceptAdnOpen 函数将保留的数据读取出来直到 acceptAdnOpen

处于阻塞状态

另外如果使用了应用依赖的推送客户端程序调用 MDSPushInputStream 的 accept 函

数后确认消息不会马上发出而是在对应用 InputStream 和 StreamConnection 关闭后才发出

所以对于应用依赖的推送保证 InputStream 和 StreamConnection 的关闭非常之重要

异步确认消息接收代码

异步消息的接收代码运行在服务器端在推送请求的 X-RIM-Push-NotifyURL 属性所指定

的服务器上侦听指定端口和应用服务器端的数据推送代码一样异步确认消息的接收程序

可以通过任何支持 HTTP 的语言编写主要的工作是侦听指定端口从指定端口获取 MDSBES

发送回来的确认消息根据 HTTP 协议获取相关属性并返回相关信息

下面以 java 代码为例对关键代码进行说明这段代码一个 java Application 的一部分是

侦听线程的 run 函数

public void run()

try

Systemoutprintln(Waiting for notification on port + 7778

+ )

while (true)

ServerSocket serverSocket = new ServerSocket(7778)

serverSocketsetSoTimeout(120000)

try

Socket clientSocket = serverSocketaccept()

InputStream input = clientSocketgetInputStream()

StringBuffer str = new StringBuffer()

int byteRead = inputread()

while ((byteRead = -1) ampamp (inputavailable() gt 0))

strappend((char) byteRead)

byteRead = inputread()

Systemoutprintln(strtoString())

OutputStream output = clientSocketgetOutputStream()

String response = HTTP10 200 OK

outputwrite(responsegetBytes())

outputflush()

outputclose()

inputclose()

clientSocketclose()

catch (SocketTimeoutException ste)

Systemoutprintln(Notification connection timeout

Restarting)

serverSocketclose()

catch (Exception exception)

exceptionprintStackTrace()

异步确认消息的接收代码比较简单基本上就是打开一个 Socket 侦听指定的端口本

例中是 7778然后不断地从该端口读取数据打开 Socket 侦听的代码如下

ServerSocket serverSocket = new ServerSocket(7778)

在异步消息接收过程中需要注意的是接收完 MDSBES 服务器的确认消息后应该给

MDSBES 服务器返回一个 200 的成功消息否则 MDSBES 服务器会认为发送确认消息失败

可能重次几次后再放弃这样异步消息接收程序有可能重复收到同一条确认消息返回成功

消息的代码如下

OutputStream output = clientSocketgetOutputStream()

String response = HTTP10 200 OK

outputwrite(responsegetBytes())

outputflush()

outputclose()

推送细节分析

了解基本的 MDS 推送知识是不足于编写一个可靠的数据推送程序的要保证数据推送

的可靠性必须了解整个推送过程的每一个细节本章节先从整体上描述了整个数据推送的

调用时序然后从不同的关键点详细讲解数据推送的细节

MDS 推送的整体时序

如上图所示数据推送主要是应用服务器MDS 服务器网络服务手机端几个组件

之间的交互在网络服务这一块我们可以假定这一层是稳定的不做考虑对于网络无法提

供服务等情况我们可以通过手持设备端关闭无线电或者是将手持设备放入无线屏蔽区等方

式模拟所以我们要考虑是应用服务器MDSBES 服务器手持设备这三部分

数据推送由应用服务器发生向 MDSBES 服务器发出 HTTP Post 请求第一个需要考虑

的是该 HTTP Post 请求是否能到达 MDSBES 服务器这一点在ldquo推送请求是否到达 MDSBES

服务器rdquo小节中有详细讨论

MDSBES 服务器在接收到 HTTP Post 请求后会对请求的格式进行判断如果格式错误

如指定目标不存在等则 MDSBES 服务器会返回 400 错误所以第二点要考虑是的如何保

证所有到达 MDSBES 服务器的请求都是合法的有关这一点在ldquo推送请求检查rdquo小节中有

详细描述

MDSBES 服务器发现推送数据格式正确以后还需要判断服务器上的队列是否有足够的

空间保存该数据如果没有发现空闲队列则丢弃该数据并向应用服务器返回 503 错误因此

第三点要考虑的是什么情况会导致服务器推送队列占满如何尽量避免这种情况有关这一

点在ldquoMDSBES 中的推送队列rdquo中有详细描述

MDSBES 服务器将数据推送到客户端以后由客户端程序将数据提取交由相应的应用

逻辑进行处理然后客户端应用在提取数据的时候并不一定能及时地将所有推送到的数据

获取所以第四点要考虑的是手持设备端是否也存在队列如何保证客户端程序将所有数据

都提取出来有关这一点在ldquo手持设备端对消息的处理rdquo小节中有详细描述

手持设备在接收推送数据后会通知 MDSBES 服务器MDSBES 服务器则向应用服务器

发送异步的确认消息通过返回数值 200 或者是 400 通知应用服务器推送数据是推送成功还

是推送失败异步确认消息对于应用开发者有特别的意义通过它可以确定数据是否到达客

户端从而决定是否再次推送数据然后很多开发商都发现异步确认消息的返回的ldquo200rdquo

和ldquo400rdquo并不能准备地反映数据的接收情况所以如何使用异步确认消息在不同的场

景下异步消息会包含什么不同值对于应用开发者而言非常重要有关这一点在小节ldquo异步

确认消息rdquo有详细描述

推送请求是否到达 MDSBES 服务器

有关推送请求是否到达 MDSBES 服务器 很多开发人员都会忽略认为推送请求是否

到达 MDSBES 服务器是由代码是否编写正确决定的一定会在代码测试阶段体现也来也

必须在代码编写阶段解决这个问题然而在生产环境确实有一些因素会导致应用服务器发送

的推送请求无法到达 MDSBES 服务器从而导致推送数据丢失的问题

推送请求无法到达 MDSBES 服务器的一个可能的直接原因是指定的服务器名不正确或

者是指定的端口号不正确原因可能是 BES 管理员修改了 BES 服务器的服务器名IP 地址或

者是推送监听端口

这种问题比较容易发现一方面是因为所有的 BlackBerry 用户此时都无法接收到推送数

据另一方面也因为应用服务器端会报网络连接的异常以 java 代码为例当指定的服务

器名或者是端口号不正确的情况下应用服务器端的推送程序会报以下异常

javanetConnectException Connection refused

或者是报域名无法解析等网络连接级别的异常

发现这种情况只需要检查代码中指定的服务器名和端口号是否和生产环境相同就可以

解决问题

推送请求无法到达 MDSBES 服务器的一个潜在可能性是推送请求的并发数量太大导致

MDSBES 服务器无法及时接收所有请求

这种问题比较难于发现因为部分用户仍然可以收到推送数据只有部分用户的部分数

据丢失了同时这种问题在功能性测试场景下不容易发生只有在大压力测试或者是存在

大量用户的生产环境中才容易出现

产生这种问题的根源是 MDSBES 服务器的并发接收限制MDS 服务器其实是运行在

Tomcat 服务器上一个服务Tomcat 服务器作为应用容器处理了低层的网络请求而 Tomcat

服务器在底层有并发网络请求的限制当网络连接达到服务器限制时新的推送连接会处理

等待状态如果在网络超时前仍无法建立连接则会出现网络超时异常

javanetConnectException Connection timed out connect

根据测试的结果不同版本的 BES 服务器在低层并发网络请求的数量上有差异以 200

个并发限制为例就是说同一时间点只能有 200 个连接可以向 MDSBES 服务器发送推送请

求第 201 个请求以后有所有请求都将进入等待状态如果在 TCPIP 超时限制到达之前

MDSBES 服务器仍无法释放前 200 个请求则等待状态的请求会因为网络超时而中断

当应用服务器采用多线程机制处理推送请求的情况下有可能出现以上情况如果因为业

务原因在短时间内出现大量等待推送的数据应用服务器则有可能启动多于 200 个的线程发

送推送请求如果需要推送的数据太大或者是推送线程需要动态对数据进行计算就有可

能导致线程所启动的连接在短时间内没有关闭当多于 200 个线程处于这种状态的时候

MDSBES 服务器就无法继续接收推送请求了这种问题的一个现象就是应用服务器端报网络

超时异常或者是连接被拒绝的异常

总的而言要保证数据推送的成功第一步需要保证推送数据到达 MDSBES 服务器

发现推送数据没有到达 MDSBES 服务器的情况时应该对应用服务器的日志进行检查进一

步确定问题的原因

推送请求检查

在 MDSBES 服务器接收到推送请求后MDSBES 服务器会对请求进行检查只有符合

推送格式的请求才被 MDSBES 服务器所接收当 MDSBES 服务器接收推送时会返回值为

200 的 HTTP 答复告知应用服务器已经接收该请求当 MDSBES 服务器拒绝推送请求时会

根据拒绝的原因返回相应的错误码可能返回的错误码包括 400403503 等下面对不同的

情况进行描述

第一种请求被拒绝的原因是推送请求的格式不对包括 URL 格式不正确和 HTTP 头不正

确两种情况两种情况 MDSBES 服务器都会返回 400 消息表示拒绝URL 格式不正确包括

URL 中不包含ldquoDESTINATIONrdquo参数或者是不包含ldquoPORTrdquo参数等HTTP 头不正确包括

PUSH ID 不唯一等因为推送请求中的 HTTP 头属性都有缺省值所以使用错误的 HTTP 头属

性并不会引发错误MDSBES 服务器只是忽略该参数如 HTTP 头ldquoX-RIM-PUSH-IDrdquo被误写

为ldquoX-RIMPUSHIDrdquoMDSBES 服务器并不会拒绝该请求只是使用ldquoX-RIM-PUSH-IDrdquo的缺

省值就是服务器随机生成一个唯一的 ID 给推送消息

对于 HTTP 协议而言服务器返回 400 代表服务器一般性错误所以返回消息会以异常

形式被捕获以 java 代码为类程序会捕获到以下网络异常

ldquojavaioIOException Server returned HTTP response code 400 for URL

httprdquo

第二种推送请求被拒绝的情况是推送请求指定的收件人不正确就是指应用服务器发送

推送请求时使用了正确的格式不过 DESTINATION 参数指定的收件人并不存在或者是该收件

人并不是 BlackBerry 用户此时 MDSBES 服务器会返回 403 错误读者需要注意 BES 服务

器 37 之前的版本对于这种情况会视作格式错误返回 400 错误

同样以 java 代码为例收件人不正确的情况下程序会捕获到以下网络异常

ldquojavaioIOException Server returned HTTP response code 403 for URL

httprdquo

第三种请求被拒绝的情况是 MDSBES 服务器上的推送队列已满无法再接收更多的推

送请求此时会返回 503 错误表示服务器暂时没有足够的资源处理该请求这种情况下 java

应用程序会捕获到以下异常

ldquojavaioIOException Server returned HTTP response code 503 for URL

httprdquo

这种情况与推送请求是否正确无关取决于 MDSBES 服务器运行情况关于 MDSBES

服务器的推送队列在下一节中我们才进行详细的讨论

如果数据推送请求通过了 MDSBES 服务器的消息检查则 MDSBES 服务器会将该数据

放入推送队列等待发送同时给应用服务器返回成功消息 200

成功消息是以 HTTP 的 ResponseCode 形式返回的开发人员可以通过 HTTP 连接的相应

方法获取在 java 代码中获取 ResponseCode 的函数是ldquo getResponseCoderdquo只有确认

MDSBES 服务器返回的 ResponseCode 是 200才能确定推送数据已经由 MDSBES 接收对

于其它的异常情况都需要由程序进行判断确定如何调整请求继续重试推送过程

有一点非常重要的是 MDSBES 接收该推送数据并不意味着该数据就一定能到达手持设

备端还有很多因素会导致手持设备端的应用接收不到该数据

MDSBES 中的推送队列

MDSBES 服务器有一个队列用于缓存推送请求因为手持设备并不是一定在线

MDSBES 服务器需要先将推送请求保留在服务端在合适的情况下将数据推送到手持设备端

后才从队列中将该请求移除

MDSBES 服务器上的推送队列是 MDS 推送的关键组件它的行为不仅仅影响应用服务

器端是否能够发送新的请求也影响到手持设备端是否能够接收到数据所以对于 MDS 推

送的开发人员必须了解 MDSBES 服务器上的推送队列

MDSBES 服务器上的推送队列有一个队列深度缺省为 1000也就是说 MDSBES 服务

器在同一时间只能缓存 1000 个推送请求该队列的深度可以由管理员在管理端修改

同时 MDSBES 服务器上的推送队列并不是永久保存推送数据它有一个过期时间缺

省为 10 分钟也就是说一个推送请求在 MDSBES 服务器上最多能够被保留 10 分钟同样

这个过期时间可以由管理员在管理端修改

推送队列的深度修改界面如下

推送队列的过期时间的修改界面如下(以毫秒为单位)

对于推送队列的进一步讨论是有关推送数据进入队列后的状态如何变化下面是推送数

据在队列中的状态变化图说明了推送数据在不同情况下的状态变化下图的各个状态中

淡蓝色的状态为起始状态深蓝色的状态为中间状态红色的状态为终止状态处于中间状

态的数据最终必然会转为终止状态的其中一种

当 MDSBES 服务器接收到推送数据后如果之前目标用户没有 5 个处于暂时失败的数

据包服务器会将数据放入等待发送的队列中推送的数据处于ldquo尝试发送rdquo状态处于ldquo尝

试发送rdquo会在很短的时间内占用 1000 个队列位置中的一个不管是成功还是失败它所占

用的队列位置很快被释放

然后 MDSBES 服务器尝试将数据推送到手持设备端如果此时对应的手持设备处于开

机状态网络一切正常则 MDSBES 服务器将数据发送给手持设备推送数据变成ldquo成功rdquo

状态MDSBES 服务器会向应用服务器发送异步的确认消息同时将数据从队列中移除注

意此时 MDSBES 服务器向应用服务器返回的确认消息并不一定是表示成功 200具体是

返回代表成功的 200 还是返回代表失败的 400 还取决于其它因素有关这一点我们在后面的

章节中详细讨论

如果 MDSBES 服务器尝试将数据推送到手持设备的时候手持设备处于关机状态则该

数据进入ldquo暂时失败rdquo状态处于ldquo暂时失败rdquo状态的数据不占用等待队列的队列位置某

一个目标用户最多只能有 5 条数据处于ldquo暂时失败rdquo状态目前没有明确的数据或者是文档

说明一个 MDSBES 服务器总共可以保留多少条ldquo暂时失败rdquo状态的数据

如果某一用户已有 5 条数据处于ldquo暂时失败rdquo状态MDSBES 服务器接收到该用户新

的推送请求时会直接将该数据转入ldquo等待rdquo状态每一条进入ldquo等待rdquo状态的推送数据会占

用推送队列中的一个位置当队列中的 1000 个位置都被占满的时候 MDSBES 服务器就停止

接收新的数据推送无论新的数据是推送给哪个用户

处于ldquo暂时失败rdquo和ldquo等待rdquo状态的数据会在服务器上保留 10 分钟(如上所述这一

时间可以由管理修改)10 分钟后处于这两种状态的数据将被丢弃进入ldquo过期rdquo状态同

时 MDSBES 服务器会向应用服务器发送异步消息这种情况下发送的异步确认消息一定是

代表失败的 400如果在 10 分钟内手持设备重新进入网络手持设备会发送消息到 MDSBES

服务器告知该手持设备已经上线MDSBES 服务器会将处理ldquo暂时失败rdquo和ldquo等待rdquo状态的

数据转成ldquo尝试发送状态rdquo重新发送该数据需要注意的是在 10 分钟的等待过程中

MDSBES 服务器如果没有接收到手持设备重新连接网络的消息则 MDSBES 服务器不会尝

试重新发送ldquo暂时失败rdquo和ldquo等待rdquo状态的数据

根据以上的分析可以对推出以下结果

1 当用户的手持设备在线时对该用户的数据推送会被马上处理该数据只在很短的时间

占用 1000 个等待队列中的一个位置

2 当用户的手持设备不在线时假设该服务器只有一个用户则服务器在 10 分钟内最多

可以接收该用户的 1005 条数据其中 5 条处于ldquo暂时失败rdquo状态1000 条在等待队列

3 如果用户的手持设备在数据发送给 MDSBES 服务器 10 分钟内重新接入网络MDSBES

服务器可以将该数据推送到手持设备端

4 如果 MDSBES 服务器的等待队列未满推送给新的用户的前 5 条数据不占用等待队列

5 如果 MDSBES 服务器的等待队列已满不管是由哪个用户的数据占满的MDSBES 服

务器都会停止接收所有用户的新的数据推送

总结而言在生产环境中保证 MDSBES 服务器正常工作的关键是要保证 MDSBES 服务

器的等待队列不被占满

手持设备端对推送数据的处理

MDSBES 服务器成功接收应用服务器推送的数据后会将数据推送到手持设备端在手持

设备端需要由客户端应用程序将推送的数据提取出来一个直观的猜测是手持设备端同样需

要一个队列保存数据否则客户端应用程序在提取前一条数据时新到达的数据就会直接丢

失需要验证的是这个队列有多深队列行为是否被系统隐藏经过测试证实手持设备端确

实也存在队列队列深度为 10而且可以反映在应用层其中最值得开发人员注意的是这

个队列是一个可溢出队列关于手持设备端推送数据队列的示意图如下

如图所示当客户端提取程序正在读取数据ldquoRrdquo如图中标有 R 的椭圆形所示如果

有更多的数据被推送到手持设备端所推送的数据会被放入设备的队列中一共有 10 条数

据可以被保留在设备队列中如椭圆形 1 到 10 所示此时如果有更多的数据被推送进来

设备队列已满则这些新的数据会溢出如椭圆形 111213 所示同时对于服务器端

而言数据 111213 已经被推送过一次服务器端不会再次尝试推送这些数据在这种

情况下数据 1 到 10 可以继续被客户端提取程序所提取而数据 111213 则丢失了

对于尚未推送到手持设备端的数据如数据 1415假设因为网络中断没有被推送到

手持设备端则仍保留在服务器端当网络恢复后数据 1415 仍可以被正常处理

对于确认消息(图中橙色圆形所示)的处理则更为复杂在异步确认消息一节中再作讨

从这一节的讨论得出的结果是数据到达手持设备端未必就能够被客户端提取程序提取

客户端提取程序有职责尽快提取数据防止推送数据的溢出溢出的数据在客户端无法再被

处理而且服务器端也不会尝试继续推送这些数据

异步确认消息(应用依赖和非应用依赖)

手持设备端在接收到推送数据后会通知 MDSBES 服务器这时 MDSBES 服务器会根据

最初的推送请求向指定的 URL 发送一个确认消息告知应用服务器某一条推送数据的最终

结果这就是异步确认消息异步确认消息以 http 协议发送给应用服务器消息 200 代表

数据推送成功400 代表数据推送失败

异步确认消息是应用服务器判断推送是否成功的有效工具但是很多开发人员因为对

异步确认消息的机制不了解错误地使用了异步确认消息从而导致数据的丢失所以我们

需要详细了解异步确认消息的工作机制

要讨论异步确认消息的工作机制必然涉及到应用依赖与非应用依赖应用依赖的推送

和非应用依赖的推送对应的异步确认消息工作机制是不同的

下面先讨论非应用依赖的场景这也是很多开发商使用的场景

如果推送是非应用依赖的数据是以到达手持设备为成功标志的只要数据到达了手持

设备MDSBES 服务器就认为该数据推送成功会给应用服务器发送异步确认消息 200

如果数据推送时手持设备不在线则 10 分钟内 MDSBES 服务器会在服务器上保留该数

据如果 10 分钟内手持设备重新接入网络则 MDSBES 服务器会尝试发送该数据一旦数

据到达手持设备MDSBES 服务器都会给应用服务器发送异步确认消息 200

如果 10 分钟内手设备没有接入网络则该推送数据过期MDSBES 服务器会丢弃该数

据同时给应用服务器发送异步确认消息 400

可以看出非应用依赖的推送方式并不能保证客户端应用程序接收到数据在非应用依

赖的推送方式下 MDSBES 返回 200 确认消息也不能认为客户端已接收到数据如果数据送

达手持设备的时候客户端应用程序没有启动所推送的数据会在手持设备端丢失而

MDSBES 服务器认为数据已成功送达或者是客户端应用程序已启动但是没有及时读取推

送数据在手持设备端溢出的数据也会丢失MDSBES 服务器同样认为数据已成功送达

所以在非应用依赖的情况下如果接收到 400 确认消息可以确定客户端没有收到数据

如果接收到 200 确认消息只能认为客户端可能收到了数据

从以上的讨论看非应用依赖的推送方式并不适合用于可靠性要求较高的应用对于可

靠性要求较高的应用需要知道客户端应用程序是否接收到了数据应用依赖的推送方式就

是为此而设计的如果推送是应用依赖的数据是以客户端应用程序确认为成功标志的只

要在应用程序确认后MDSBES 服务器才认为数据推送成功这时才给应用服务器发送异步

确认消息 200

有关客户端应用程序如何确认收到数据在ldquo客户端接收关键代码rdquo一节中有描述简

单而言就是客户端应用程序通过调用 MDSPushInputStream 实例的 accept 函数确认数据收

到如果客户端应用程序在接收到数据后没有调用 accept 函数MDSBES 服务器会认为数

据推送失败给应用服务器发送异步确认消息 400另外一点曾经强调过的是accept 函数

调用后只有 InputStream 和 StreamConnection 都关闭了确认消息才被发送给 MDSBES 服

务器在极端的情况下这一点会影响到异步确认消息的结果

下面具体分析各种情景

如果手持设备在线但是客户端应用程序没有启动推送数据到达手持设备端后没有程

序侦听推送端口当然也没有程序会调用 accept 函数此时 MDSBES 服务器会认为数据推

送失败向应用服务器发送异步确认消息 400

如果手持设备在线客户端应用程序在读取推送数据后没有调用 accept 函数直接关闭

连接此时 MDSBES 服务器同样会认为数据推送失败向应用服务器发送异步确认消息 400

在这种情景下即使客户端接收到了数据但应用服务器仍收到失败消息因为作为成功标

志的 accept 函数并没有调用

如果手持设备在线客户端应用程序在读取推送数据后调用了 accept 函数并关闭连

接此时 MDSBES 服务器会认为数据推送成功向应用服务器发送异步确认消息 200

对于手持设备不在线的情况结果与非应用依赖相似10 分钟内如果手持设备不接入

网络的话推送数据会在 MDSBES 服务器上过期MDSBES 服务器会向应用服务器发送异

步确认消息 400

如果 10 分钟内手持设备重新接入网络MDSBES 服务器会尝试发送数据至手持设备端

还是以 accept 函数的调用为推送成功的标志如果数据重新发送成功但是客户端没有调

用 accept 函数则 MDSBES 服务器会向应用服务器发送异步确认消息 200

对于推送到手持设备端但是溢出的数据因为没有程序能够读取该数据并针对该数据调

用 accept 函数MDSBES 服务器会一直等待到 10 分钟后向应用服务器发送异步确认消息

400认为数据过期

一个特殊的情况是客户端应用程序在接收到数据后调用了 accept但是没有关闭

InputStream 和 StreamConnection此时 MDSBES 服务器会认识客户端还在处理数据会继

续等待如果服务器等待时候超过 10 分钟MDSBES 服务器会认为数据推送失败向应用

服务器发送异步确认消息 400如果在 10 分钟内客户端主动关闭 InputStream 和

StreamConnection或者是网络异常中断MDSBES 服务器会认为数据推送成功向应用服

务器发送异步确认消息 200

下面再讨论一个异常情况下的特例

结合以上描述的各种因素在极端的情况下会出现一些特殊的情况如上图所示假设

MDSBES 服务器在推送了数据 R数据 1 到数据 13 后出现网络中断同时客户端应用程序

正在缓慢地读取数据 R此时数据 1 到数据 10 会被保留在手持设备队列中客户端应用程

序在读取数据 R 后可以继续读取数据 1 到数据 10如果客户端应用程序在读取了数据后都

调用了 accept 函数则会产生对应用确认消息如图中的确认消息 R确认消息 1 到确认消

息 10但是因为网络的中断这些确认消息都无法到达 MDSBES 服务器如果网络在 10

钟内没有恢复MDSBES 服务器上所有等待确认消息的记录都会认识是过期的MDSBES

会向应用服务器发送异步确认消息 400此后即使网络恢复确认消息 R确认消息 1 到确

认消息 10 都不再有效

在这种特殊情况下即使客户端应用程序接收到数据并调用了 accept 函数MDSBES

服务器同样认为数据没有推送成功

根据以上的讨论应用依赖的推送同样不能保证客户端应用一定能收到数据但是在

应用依赖的推送方式下 MDSBES 返回 200 确认消息可以确定客户端已接收到数据因为 200

确认消息是由客户端应用程序调用 accept 函数触发的客户端应用程序可以在确认数据收

到后才调用 accept 函数通知应用服务器该数据已成功接收甚至可以在接收到数据后根

据数据内容决定不调用 accept 函数从而通过应用服务器该数据没有被接收

所以在应用依赖的情况下如果接收到 200 确认消息可以确定客户端已经接收到数据

如果接收到 400 确认消息只能认为客户端可能没有收到数据

因此应用依赖的推送方式更适合可靠性要求高的应用如果应用服务器接收到 200

确认消息就可以认为推送成功继续处理其它事务如果应用服务器接收到 400 确认消息则

需要组织数据重发直到接收到 200 确认消息为止这样就可以保证客户端一定能够接收到

需要推送的数据

如何保证数据推送的可靠性

通过以上几个章节的讨论我们知道虽然 MDSBES 服务器提供了很多机制保证数据

推送的可靠性但是都无法完全保证数据被客户端应用程序所接收

对于数据推送的可靠性保证一方面开发人员需要了解各个可能导致故障的因素在应

用编写的时候尽量减少故障出现的可能性对故障点进行规避另一方面开发人员也需要实

现额外的机制进行数据重发在故障点无法规避的情况下通过数据重发消除故障点的影响

应用依赖和非应用依赖的比较和选择

在实现推送应用的时候首要考虑的是有关应用依赖与非应用依赖的选择

应用依赖的推送形式在可靠性方面更优于非应用依赖的推送形式但是应用依赖的推送

形式对 MDSBES 应用服务器的潜在压力较大客户端应用程序的实现也较为复杂

非应用依赖的推送形式优势在于实现简单对 MDSBES 应用服务器形成的压力相对较

非应用依赖的推送形式从某种程度上讲有点类似于广播协议着重点是从服务器向客户

端发送大量数据并不看重客户端的接收情况从数据的应用类型来分非应用依赖的推送

更适合于天气信息最新报价等数据这些数据在客户端应用关闭时被自然丢弃短时间内

的多条数据有丢失影响也不大必竞用户不会关注 11 点零六分的天气和 11 点零七分的天气

有何不同

应用依赖的推送形式则类似于点对点协议不仅从服务器端向客户端发送数据同时也

关注客户端的接收情况应用依赖的推送更适合于 OAERP 这种应用这种应用需要保证

新的文档或者是记录一定能到达客户端应用丢失一条待办信息可能就会导致整个审批流程

的停滞

再从两种推送方式的本质差别看应用依赖并没有额外的机制消除故障点它的优势在

于它的异步确认消息更可信所以在选择了应用依赖的推送方式后进一步还是需要实现

额外的数据重发机制根据 MDSBES 所发送的异步确认消息实施数据重发

应用依赖的推送再加上结合异步确认消息的数据重发机制是保证推送数据可靠性的

最好方法

MDS 接收线程限制的处理

不管使用什么方式的推送应用开发时都需要考虑到 MDS 接收线程的限制不过以

现在的服务器处理能力正常情况下的 HTTP 推送请求可以在几毫秒或者是几十毫内处理完

成少量的 HTTP 推送不足于构成网络阻塞

需要考虑的是应用服务器端的推送程序在推送过程中不要在建立连接后才动态组织数

据这样会浪费 http 连接资源好的建议是预先组织好数据在建立 HTTP 连接后快速写完

数据尽快关闭连接

此外对于应用服务器的推送线程也要加于控制避免在短时间内无限制地启动新的线

程进行连接否则也容易造成连接过多而阻塞的情况

从网络层面来看调整应用服务器与 MDSBES 服务器之间的网络超时设置也是避免这

种问题的一种方法在允许的范围内调大网络超时时间可以让应用服务器的推送线程等待更

长的时间从而让 MDSBES 有足够的时间释放原有的连接

最后无论使用了什么方法调优都需要对应用服务器的推送程序进行异常处理当发

现网络超时异常的时候可以组织数据重发当然数据重发的间隔时间也要考虑不要因为

重发数据再次造成网络阻塞

MDS 推送队列限制的处理

相对而言MDS 推送队列限制造成的推送失败比 MDS 接收队列造成的推送失败更常见

已经发现的一个实际案例是周未的时候用户大量关机导致大量推送数据处于推送队列中

填满了推送队列从而导致 MDSBES 服务器不再响应新的推送请求一旦过期时间到达

有新的队列位置空闲又有大量新的推送数据重新占满推送队列

对于 MDS 推送队列的限制直观的做法是对队列参数进行调整如之前讨论的管理

员可以对队列的深度和过期时间进行调整

然而出于对过期时间的不正确理解管理员倾向于将过期时间调大当过期时间被调

大后推送给关机用户的数据在服务器占用队列的时间就变长导致 MDS 推送队列更容易

被占满

所以如果对过期时间进行调整的话方向是将过期时间调小过期时间越小等待数

据占用队列的时间就越短MDS 推送队列被占满的可能性就越小但是过期时间也不能

过小过期时间过小可能会导致过多的过期数据使应用服务器需要处理更多的数据重发

比如将过期时间调成 1 分钟用户走过一个人行遂道都可能导致数据发送失败

对于队列深度的调整方向是调大队列深度如从缺省的 1000 调整到 2000此时

MDSBES 服务器在同一时间可以保留更多的等待数据减少了队列占满的机会不过调大队

列深度会让 MDSBES 服务器保留更多的数据从而占用更多的服务器内存对服务器产生

更大的压力所以调用队列深度需要根据服务器的运行能力进行调用

从应用服务器设计角度应用 MDS 队列限制的方法是尽量减少等待数据的发送如发

送第一条数据的时候发现数据过期对于该用户的其它数据可以调整发送时间在更长的时

间间隔后才尝试发送

对于 MDS 队列限制应用服务器端的推送应用需要对 MDSBES 服务器返回的 503 错误

进行处理对于出现 503 错的数据组织重发同样重发的间隔也需要考虑不要因为数据

重发占用更多的推送队列也可以根据目标用户进行判断对于可能关机的用户暂时不重发

数据

手持设备端接收队列的处理

手持设备端接收队列并未从文档中明确列出是根据测试结果分析得出的手持设备端

目前也没有发现设置可以调整该队列所以客户端程序设计的时候不应该依赖手持设备端队

开发人员需要意识到的是手持设备端的接收队列是可溢出队列一旦推送数据从接收队

列出溢出客户端程序就无法提取该数据了所以在客户端应用开始过程中要注意加快推送

数据的提取过程在提取过程中不要对数据进行复杂的计算提取完成后尽快读取下一条数

据对于接收数据的处理和计算可以交由另一个线程完成

因为溢出数据对客户端应用而言不可见所以数据溢出时在客户端应用中也不会有异

常开发人员需要通过服务器返回的过期消息才能知道该数据推送失败

对于这一点客户端应用除了尽量避免数据溢出外无法在客户端通过其它机制进行补

应用依赖的客户端

对于应用依赖的客户端应用关键点是决定何时调用 accept 函数同时保证 InputStream

和 StreamConnection 被关闭

如之前讨论的使用应用依赖的推送关键是希望使用 MDSBES 发送给应用服务器的异

步确认消息从而对数据是否到达进行判断所以应用依赖的客户端应用需要保证正确调用

accept 函数

另外如果 InputStream 和 StreamConnection 没有关闭的话accept 函数所发出的确认

消息无法被 MDSBES 服务器收到开发人员在代码中应该主动关闭 InputStream 和

StreamConnection同时在程序发生异常时也需要在 catch 代码段对 InputStream 和

StreamConnection 进行关闭处理

异步确认消息处理

异步确认消息用于判断数据是否被成功推送至客户端应用要使用异步确认消息首先

要保证成功地无遗漏地接收所有从 MDSBES 服务器发送过来的异步确认消息

异步确认消息是一个 HTTP 消息消息比较短接收过程的并发压力不大下面是一个

异步确认消息的样例

Received notificationGET HTTP11

User-Agent RIM MDSCS

Accept

X-RIM-Push-ID pushID2081773768

X-RIM-Push-Destination mmtestbjsearbcn

X-RIM-Push-Status 400

Host localhost7778

其中最关键是的获取属性 X-RIM-Push-Status根据该属性的值判断数据推送是成功

(200)还是失败(400)当然属性 X-RIM-Push-ID 也非常重要用于确定该确认消息是针

对哪条推送数据的

如果应用服务器端通过确认消息发现数据推送失败则需要根据属性 X-RIM-Push-ID 重

新组织数据进行重发

Page 3: BES 服务器推送机制分析 - images.csdn.netimages.csdn.net/upimgs/lee/BBPDF/BESfwtsjzfx.pdf · 前言 数据推送是BlackBerry 应用平台的一大优势,在BlackBerry 应用平台上部署的应用可以

前言

数据推送是 BlackBerry 应用平台的一大优势在 BlackBerry 应用平台上部署的应用可以

和 BlackBerry 推送邮件一样通过推送实时地将数据从服务器端推送到 BlackBerry 手持设备

端所以对于很多应用开发商而言BlackBerry 应用平台提供的推送功能是 BlackBerry 应

用集成必然会使用到的强大功能

然而因为种种原因有一些开发商发现使用 BlackBerry Enterprise Server (BES) 应用平

台的推送功能并不能保证数据到达 BlackBerry 设备导致应用层面的种种问题实际上

BlackBerry 应用平台提供了从多机制保证数据推送的成功只要我们充分了解 BES 中 MDS(以

下简称 MDS)的推送机制我们就可以利用平台提供的机制保证数据到达 BlackBerry 手持设

本章节的主要目的是详细分析 MDS 的推送机制让读者更好地了解 BlackBerry 应用平

台从而可以开发更加强壮更加稳定的 BlackBerry 应用

值得读者注意的是本章节的内容是结合少量的官方文档配合于大量的测试总结而来

的旨在为读者提供更多的线索以理解 MDS 推送机制如果本章节的内容与 RIM 提供的任

何官方文档有冲突的话请以官方文档为准同时不同版本的 BlackBerry 手持设备在不同场

景下也可能有不同的行为所以读者在生产环境实施 BlackBerry 应用时也要结合用户所使用

的 BlackBerry 设备进行测试才能最好地保证应用的稳定性

MDS 推送介绍

MDS 推送架构

在详细讲解 MDS 推送机制之前我们先从整体上了解 MDS 推送的架构下面是 MDS 推

送架构的示意图

从示意图中可以看到在 BlackBerry 应用平台上的数据推送从整体上可以分为六步按时

间顺序分别为

1 第一步应用服务器向 MDSBES 服务器发送推送请求所发送的请求为 HTTP 格式的请

求有关请求的详细格式在下一小节中有详细讲解

2 第二步MDSBES 服务器查询相关配置数据库确定应用服务器所发送的请求是否为合

法的请求此外MDSBES 服务器还会根据资源情况确定是否接收该请求对于是否接

收请求的判断在下一节内容中也有详细讨论

3 第三步MDSBES 服务器向应用服务器返回消息通知应用服务器是否接受该请求返

回消息以 HTTP 答复的方式返回给应用服务器

4 第四步MDSBES 服务器将数据推送到手持设备端

5 第五步手持设备端对数据进行处理后向 MDSBES 服务器返回确认消息

6 第六步MDSBES 根据手持设备端返回的消息决定向应用服务器返回什么异步消息这

一步并不是必然发生的根据推送请求的不同有可能不发生

从这里我们可以看到从应用服务器到手持设备端的推送通道是由多个不同的通道连接

而成的这其中有很多个连接点某一个连接点出现异常都可以导致推送的失败所以应用

开发商需要详细了解这些连接点可能出现的问题在应用开发过程中进行规避

在详细描述各个可能出现问题的关键点之前需要先了解推送的基本实现对于应用开

发人员而言完成数据推送需要完成的主要程序编写工作有应用服务器端发出推送请求的

程序手持设备端侦听接收推送数据的程序和服务器端侦听接收确认消息的程序如下图

橙色框图所显示的

以下章节将对这三个部分作详细讲解

数据推送命令格式

在 BlackBerry 应用平台上的数据推送是由应用服务器发起的应用服务器根据应用逻辑

进行判断发现有数据需要推送到用户的手持设备端时连接 MDSBES 服务器进行数据推送

应用服务器与 MDSBES 服务器的连接方式为 HTTP 连接通过 HTTP 连接发送一个 POST 请

求将需要推送的数据作为 POST 的内容

该 POST 请求的 URL 格式为

httpBES_HOSTBES_PORTpushDESTINATION=DESINTATION_ADDRESSampPORT=HANDHEL

D_APPLICATION_PORTampREQUESTURI=

下面是请求中各参数的说明

参数 描述

BES_HOST BlackBerry Enterprise Server 的服务器名或者是 IP

BES_PORT BlackBerry Enterprise Server 的推送端口一般是 8080

DESTINATION_ADDRESS 用户的邮件地址或者是设备码 (PIN)

HANDHELD_APPLICATION_PORT BlackBerry 手持设备需要帧听的端口

也就是说应用服务器通过 BES_HOST 和 BES_PORT 指定需要连接的 MDSBES 服务器和

需要连接的端口BES 服务器安装后缺省使用 8080 作为推送端口所以推送应用开发人员

一般情况下只需要了解 MDSBES 服务器的服务器名或者是 IP 地址就可以了如果 BES 管理

人员在安装服务器后修改过推送端口则需要告知开发人员使用新指定的端口

推送 URL 中 push后面的部分用于指定数据的接收者和手持设备需要帧听的端口

DESTINATION_ADDRESS 用于指定数据的接收者可以使用该用户的邮件地址或者是该用户手

持设备的 PIN 码HANDHELD_APPLICATION_PORT 用于指定手持设备需要帧听的端口这里

使用的端口只是一个约定要求在手持设备上运行的程序从这一端口中读取数据

除了 URL 参数以后在推送过程还可以使用一些规定好的 HTTP 头指定该推送的属性

HTTP 头参数表如下

HTTP 头 描述

X-RIM-Push-ID 用于指定该消息的 ID该 ID 需要是一个唯一值可以用于

取消推送或者是查看推送的状态

X-RIM-Push-NotifyURL 用于指定确认消息的接收 URL

X-RIM-Push-Reliability-Mode 用于指定推送的可靠性

X-RIM-Push-Deliver-Before 用于指定推送数据的最迟推送时间

X-RIM-Push-Priority 用于指定推送数据的优先级

X-RIM-Push-ID 属性一般建议使用 URL 和编号结合的方式如 123blackberrycom为

了更好地控制数据的推送一般不建议使用随机数作为推送 ID使用统一的 ID 生成方式更

有利于推送数据的取消和状态确定注意如果没有指定该参数的话MDSBES 服务器会自

动生成一个唯一的 ID这样就无法在应用服务器上使用这个 ID 对特定的推送数据进行处理

了此外推送 ID 不能以ppgrimcom 结束

X-RIM-Push-NotifyURL 属性通过 URL 的形式指定了确认消息的接收地址如

httptestingserver7778指定了这一属性后MDSBES 服务器会将推送的确认消息以 HTTP

请求的形式发送到指定的服务器也就是说应用开发者需要开发一个 HTTP 服务器端程序监

听指定服务器的指定端口(上例中则是服务器 testingserver 的 7778 端口)通过这个服务器

端程序获取确认消息在确认消息中会包含 HTTP 头 X-RIM-Push-ID 和 X-RIM-Push-Status通

过 X-RIM-Push-ID 告知监听者是哪条推送数据的确认消息通过 X-RIM-Push-Status 指明该数

据推送的结果200 代表推送成功400 代表推送失败现实环境中对于数据推送是否成功

不能简单地以结果 200 或者是 400 进行判断需要结合很多因素进行判断本文的后续章节

会详细描述

属性 X-RIM-Push-Reliability-Mode 用于指定推送的可靠性值可以是 TRANSPORT 或者是

APPLICATION另外有一个值 APPLICATIONPREFERRED 涉及更复杂的场景这里不做描述如

果指定可靠性为 TRANSPORT 则以数据到达手持设备准本文称之为非应用依赖如果指定

可靠性为 APPLICATION 则以数据被手持设备端应用接收为准本文称之为应用依赖对于不

同可靠性设置本文的后续章节会有详细描述总体而言非应用依赖的推送更适合于广播

性质的不要求应用一定收到数据的场景而应用依赖更适合于点对点的要求应用一定要收到

数据的场景

属性 X-RIM-Push-Deliver-Before 用于指定数据推送的最后时间如果在指定时间前该数

据仍无法被成功推送到手持设备端则该数据会被视作过期而被 MDSBES 服务器丢弃

属性 X-RIM-Push-Priority 用于指定推送数据的优先级指定推送数据的优先级并不能加

快数据的推送指定优先级的结果是推送数据到达手持设备端的行为不同可以选择的值有

none (缺省)low medium 和 high如果指定优先级为 lowmedium 或者是 high则用

户只是接收到数据如果指定优先级为 high则用户在接收到数据后还会看到提示对话框

数据推送关键代码

理论上讲应用服务器的数据推送代码可以用任何可以提供 HTTP 支持的语言编写需

要实现的不过是通过 HTTP 协议往指定的服务器和端口发送 POST 消息

下面以 java 代码为例讲解数据推送的关键代码部分本例的代码片段为一个 Java

Application 的部分代码该 Java Application 在服务器上以 J2SE 应用的形式运行启动一个

线程发送 HTTP 的 POST 请求以下代码为该线程的 run 函数

public void run()

Random random = new Random()

String pushId = pushID + randomnextInt()

String data = What need to be pushed

try

URL _pushURL = new URL(http testingBESabccom 8080

pushDESTINATION=user1abccomampPORT=55100ampREQUESTURI=)

HttpURLConnection conn = (HttpURLConnection) _pushURLopenConnection()

connsetDoInput(true)

connsetDoOutput(true)

connsetRequestMethod(POST)

connsetRequestProperty(X-RIM-PUSH-ID pushId)

connsetRequestProperty(X-RIM-Push-NotifyURL

httptestingserver7778)

connsetRequestProperty(X-RIM-Push-Reliability-Mode

APPLICATION)

OutputStream out = conngetOutputStream()

outwrite(datagetBytes())

outclose()

InputStream ins = conngetInputStream()

int contentLength = conngetContentLength()

int responseCode = conngetResponseCode()

Systemoutprintln(Response Code is + responseCode)

if (contentLength gt 0)

byte[] someArray = new byte[contentLength]

DataInputStream dins = new DataInputStream(ins)

dinsreadFully(someArray)

Systemoutprintln(new String(someArray))

else

Systemoutprintln(Content legth of response is 0)

conndisconnect()

catch (IOException e)

Systemerrprintln(Exception + e)

在以上代码中先通过 URL 类建立一个 HTTP 连接定义URL 类的构造函数中第一个参

数为协议类型这里使用ldquohttprdquo第二个参数为服务器名第三个参数为请求的端口号

第二个参数和第三个参数对应了数据推送请求中的 BES_HOST 和 BES_PORT 属性URL 类的

构造函数中第四个参数为相对 URL推送属性 DESTINATION 和 PORT 都放在相对 URL 中代

码如下

URL _pushURL = new URL(http testingBESabccom 8080

pushDESTINATION=user1abccomampPORT=55100ampREQUESTURI=)

建立了 URL 类的实例后可以通过该实例的 openConnection 函数打开连接返回的是

一个 HttpURLConnection 实例

HttpURLConnection conn = (HttpURLConnection) _pushURLopenConnection()

打开 http 连接后可以通过连接实例的 setRequestProperty 函数设置 http 头推送请求中

的 http 头属性在这里设置如

connsetRequestProperty(X-RIM-PUSH-ID pushId)

其中一个关键的 http 头属性是 X-RIM-Push-Reliability-Mode设置该属性为ldquoTRANSPORTrdquo

表示该推送是非应用依赖的推送设置该属性为ldquoAPPLICATIONrdquo表示该推送是应用依赖的

推送两者的具体区别在后面的章节会有详细的讨论以下代码设置该属性为

ldquoAPPLICATIONrdquo

connsetRequestProperty(X-RIM-Push-Reliability-Mode

APPLICATION)

特别要注意如果设置推送类型为应用依赖对于服务器的设置和手持设备有特定的要

求一方面是服务器端必须是 40 以上同时必须设置允许对所使用的端口进行应用依赖的

推送一方面是要手持设备的 ROM 版本也是 40 以上同时服务器必须知道手持设备能否

支持应用依赖的推送对于服务器的端口设置设置界面如下可以由 BES 管理员进行设置

如果是使用开发环境的 MDS-CS 模拟器模拟应用依赖推送的话需要修改开发环境的

MDSconfig目录中的 rimpublicproperty文件去除 pushapplicationreliableports一行的注释

同时加上需要推送的端口如ldquopushapplicationreliableports=10055100rdquo

对于服务器端是否知道手持设备端的版本需要通过手持设备端的操作完成只要手持

设备端的浏览器通过 BES 访问过网络服务器就会记录该手持设备是否支持应用依赖的推

送如果只是测试的话在测试前通过手持设备端的浏览器打开任何一个网站就可以了如

果是生产环境则需要考虑客户端应用在第一次启动的时候主动访问一次网络以保证客户

端都可以正常接收应用依赖的推送

如果以上设置没有完成的话对 MDSBES 服务器发送应用依赖的推送时会被拒绝服

务器端会出现ldquoThe specified delivery method is not possiblerdquo的错误应用服务器的推送程

序则会捕获以下异常

ldquojavaioIOException Server returned HTTP response code 400 for

URL httpapprdquo

设置属性后就需要获取 http连接的 OutputStream将需要推送的数据写入 OutputStream

中写完后关闭连接

需要注意的是 MDSBES 服务器是否接收该推送请求是通过 http 返回码实现的所以推

送应用中需要获取到 http 连接的返回码通过返回码判断推送是否成功代码如下

int responseCode = conngetResponseCode()

客户端接收关键代码

客户端数据接收程序运行在 BlackBerry 手持设备上必须通过 java 语言实现需要调用

BlackBerry 设备端的 API

客户端接收程序的主体就是启动一个线程开启 httpltportgt连接(其中 port 是客户

端需要侦听的端口号)然后从该连接中获取 InputStream从 InputStream 中获取服务器所

推送的数据

下面是侦听线程的 run 方法为保持程序的完整代码中使用到的类属性并没有修改

从该函数中看到的本函数没有定义的变量都是该类的类属性有关完整代码请参考

BlackBerry 开发环境所提供的标准样例

public void run()

StreamConnection stream = null

InputStream input = null

MDSPushInputStream pushInputStream=null

while (_stop)

try

synchronized(this)

_notify =

(StreamConnectionNotifier)Connectoropen(http55100)

while (_stop)

stream = _notifyacceptAndOpen()

try

input = streamopenInputStream()

pushInputStream= new

MDSPushInputStream((HttpServerConnection)stream input)

DataBuffer db = new DataBuffer()

byte[] data = new byte[256]

int chunk = 0

while ( -1 = (chunk = inputread(data)) )

dbwrite(data 0 chunk)

pushInputStreamaccept()

inputclose()

streamclose()

data = dbgetArray()

catch (IOException e1)

Systemerrprintln(e1toString())

if ( input = null )

try

inputclose()

catch (IOException e2)

if ( stream = null )

try

streamclose()

catch (IOException e2)

_notifyclose()

_notify = null

catch (IOException ioe)

if ( _notify = null )

try

_notifyclose()

_notify = null

catch ( IOException e )

以上代码中建立端口侦听的代码如下

_notify = (StreamConnectionNotifier)Connectoropen(http55100)

其中 URLldquohttp55100rdquo是用于侦听推送的特定格式55100 为需要侦听的端口两

个线程不能同时打开同一个端口的侦听所以在这里使用了 synchronized 对线程并发进行了

控制

建立侦听后可以通过函数 acceptAndOpen 开始侦听当线程调用该方法开始侦听的时候

线程会阻塞在这里直到有推送消息到达代码如下

stream = _notifyacceptAndOpen()

侦听之后的代码为获取连接的 InputStream从 InputStream中获取服务器端推送的数据

读取完数据后关闭 InputStream 和 StreamConnection然后重新调用 acceptAdnOpen 继续侦

听端口准备接收新的推送数据

开发人员如果希望使用应用依赖的推送在客户端必须将 InputStream 转换成

MDSPushInputStream同时在接收完数据后调用 MDSPushInputStream 的 accept 函数造知服

务器该数据已收到这也是应用依赖和非应用依赖在代码实现层面的最主要区别代码如下

pushInputStream= new

MDSPushInputStream((HttpServerConnection)stream input)

helliphellip

pushInputStreamaccept()

从良好的编程习惯来讲在连接使用后需要主动关闭所使用的连接和资源如

InputStreamStreamConnectionStreamConnectionNotifier 等所以在样例代码中也可以看

到该样例在异常处理阶段都对这些资源进行了关闭操作在关闭这些连接和资源的时候对

StreamConnectionNotifier 需要特别注意有可能在 StreamConnectionNotifier 中会保留有未

读取的推送数据需要调用 acceptAdnOpen 函数将保留的数据读取出来直到 acceptAdnOpen

处于阻塞状态

另外如果使用了应用依赖的推送客户端程序调用 MDSPushInputStream 的 accept 函

数后确认消息不会马上发出而是在对应用 InputStream 和 StreamConnection 关闭后才发出

所以对于应用依赖的推送保证 InputStream 和 StreamConnection 的关闭非常之重要

异步确认消息接收代码

异步消息的接收代码运行在服务器端在推送请求的 X-RIM-Push-NotifyURL 属性所指定

的服务器上侦听指定端口和应用服务器端的数据推送代码一样异步确认消息的接收程序

可以通过任何支持 HTTP 的语言编写主要的工作是侦听指定端口从指定端口获取 MDSBES

发送回来的确认消息根据 HTTP 协议获取相关属性并返回相关信息

下面以 java 代码为例对关键代码进行说明这段代码一个 java Application 的一部分是

侦听线程的 run 函数

public void run()

try

Systemoutprintln(Waiting for notification on port + 7778

+ )

while (true)

ServerSocket serverSocket = new ServerSocket(7778)

serverSocketsetSoTimeout(120000)

try

Socket clientSocket = serverSocketaccept()

InputStream input = clientSocketgetInputStream()

StringBuffer str = new StringBuffer()

int byteRead = inputread()

while ((byteRead = -1) ampamp (inputavailable() gt 0))

strappend((char) byteRead)

byteRead = inputread()

Systemoutprintln(strtoString())

OutputStream output = clientSocketgetOutputStream()

String response = HTTP10 200 OK

outputwrite(responsegetBytes())

outputflush()

outputclose()

inputclose()

clientSocketclose()

catch (SocketTimeoutException ste)

Systemoutprintln(Notification connection timeout

Restarting)

serverSocketclose()

catch (Exception exception)

exceptionprintStackTrace()

异步确认消息的接收代码比较简单基本上就是打开一个 Socket 侦听指定的端口本

例中是 7778然后不断地从该端口读取数据打开 Socket 侦听的代码如下

ServerSocket serverSocket = new ServerSocket(7778)

在异步消息接收过程中需要注意的是接收完 MDSBES 服务器的确认消息后应该给

MDSBES 服务器返回一个 200 的成功消息否则 MDSBES 服务器会认为发送确认消息失败

可能重次几次后再放弃这样异步消息接收程序有可能重复收到同一条确认消息返回成功

消息的代码如下

OutputStream output = clientSocketgetOutputStream()

String response = HTTP10 200 OK

outputwrite(responsegetBytes())

outputflush()

outputclose()

推送细节分析

了解基本的 MDS 推送知识是不足于编写一个可靠的数据推送程序的要保证数据推送

的可靠性必须了解整个推送过程的每一个细节本章节先从整体上描述了整个数据推送的

调用时序然后从不同的关键点详细讲解数据推送的细节

MDS 推送的整体时序

如上图所示数据推送主要是应用服务器MDS 服务器网络服务手机端几个组件

之间的交互在网络服务这一块我们可以假定这一层是稳定的不做考虑对于网络无法提

供服务等情况我们可以通过手持设备端关闭无线电或者是将手持设备放入无线屏蔽区等方

式模拟所以我们要考虑是应用服务器MDSBES 服务器手持设备这三部分

数据推送由应用服务器发生向 MDSBES 服务器发出 HTTP Post 请求第一个需要考虑

的是该 HTTP Post 请求是否能到达 MDSBES 服务器这一点在ldquo推送请求是否到达 MDSBES

服务器rdquo小节中有详细讨论

MDSBES 服务器在接收到 HTTP Post 请求后会对请求的格式进行判断如果格式错误

如指定目标不存在等则 MDSBES 服务器会返回 400 错误所以第二点要考虑是的如何保

证所有到达 MDSBES 服务器的请求都是合法的有关这一点在ldquo推送请求检查rdquo小节中有

详细描述

MDSBES 服务器发现推送数据格式正确以后还需要判断服务器上的队列是否有足够的

空间保存该数据如果没有发现空闲队列则丢弃该数据并向应用服务器返回 503 错误因此

第三点要考虑的是什么情况会导致服务器推送队列占满如何尽量避免这种情况有关这一

点在ldquoMDSBES 中的推送队列rdquo中有详细描述

MDSBES 服务器将数据推送到客户端以后由客户端程序将数据提取交由相应的应用

逻辑进行处理然后客户端应用在提取数据的时候并不一定能及时地将所有推送到的数据

获取所以第四点要考虑的是手持设备端是否也存在队列如何保证客户端程序将所有数据

都提取出来有关这一点在ldquo手持设备端对消息的处理rdquo小节中有详细描述

手持设备在接收推送数据后会通知 MDSBES 服务器MDSBES 服务器则向应用服务器

发送异步的确认消息通过返回数值 200 或者是 400 通知应用服务器推送数据是推送成功还

是推送失败异步确认消息对于应用开发者有特别的意义通过它可以确定数据是否到达客

户端从而决定是否再次推送数据然后很多开发商都发现异步确认消息的返回的ldquo200rdquo

和ldquo400rdquo并不能准备地反映数据的接收情况所以如何使用异步确认消息在不同的场

景下异步消息会包含什么不同值对于应用开发者而言非常重要有关这一点在小节ldquo异步

确认消息rdquo有详细描述

推送请求是否到达 MDSBES 服务器

有关推送请求是否到达 MDSBES 服务器 很多开发人员都会忽略认为推送请求是否

到达 MDSBES 服务器是由代码是否编写正确决定的一定会在代码测试阶段体现也来也

必须在代码编写阶段解决这个问题然而在生产环境确实有一些因素会导致应用服务器发送

的推送请求无法到达 MDSBES 服务器从而导致推送数据丢失的问题

推送请求无法到达 MDSBES 服务器的一个可能的直接原因是指定的服务器名不正确或

者是指定的端口号不正确原因可能是 BES 管理员修改了 BES 服务器的服务器名IP 地址或

者是推送监听端口

这种问题比较容易发现一方面是因为所有的 BlackBerry 用户此时都无法接收到推送数

据另一方面也因为应用服务器端会报网络连接的异常以 java 代码为例当指定的服务

器名或者是端口号不正确的情况下应用服务器端的推送程序会报以下异常

javanetConnectException Connection refused

或者是报域名无法解析等网络连接级别的异常

发现这种情况只需要检查代码中指定的服务器名和端口号是否和生产环境相同就可以

解决问题

推送请求无法到达 MDSBES 服务器的一个潜在可能性是推送请求的并发数量太大导致

MDSBES 服务器无法及时接收所有请求

这种问题比较难于发现因为部分用户仍然可以收到推送数据只有部分用户的部分数

据丢失了同时这种问题在功能性测试场景下不容易发生只有在大压力测试或者是存在

大量用户的生产环境中才容易出现

产生这种问题的根源是 MDSBES 服务器的并发接收限制MDS 服务器其实是运行在

Tomcat 服务器上一个服务Tomcat 服务器作为应用容器处理了低层的网络请求而 Tomcat

服务器在底层有并发网络请求的限制当网络连接达到服务器限制时新的推送连接会处理

等待状态如果在网络超时前仍无法建立连接则会出现网络超时异常

javanetConnectException Connection timed out connect

根据测试的结果不同版本的 BES 服务器在低层并发网络请求的数量上有差异以 200

个并发限制为例就是说同一时间点只能有 200 个连接可以向 MDSBES 服务器发送推送请

求第 201 个请求以后有所有请求都将进入等待状态如果在 TCPIP 超时限制到达之前

MDSBES 服务器仍无法释放前 200 个请求则等待状态的请求会因为网络超时而中断

当应用服务器采用多线程机制处理推送请求的情况下有可能出现以上情况如果因为业

务原因在短时间内出现大量等待推送的数据应用服务器则有可能启动多于 200 个的线程发

送推送请求如果需要推送的数据太大或者是推送线程需要动态对数据进行计算就有可

能导致线程所启动的连接在短时间内没有关闭当多于 200 个线程处于这种状态的时候

MDSBES 服务器就无法继续接收推送请求了这种问题的一个现象就是应用服务器端报网络

超时异常或者是连接被拒绝的异常

总的而言要保证数据推送的成功第一步需要保证推送数据到达 MDSBES 服务器

发现推送数据没有到达 MDSBES 服务器的情况时应该对应用服务器的日志进行检查进一

步确定问题的原因

推送请求检查

在 MDSBES 服务器接收到推送请求后MDSBES 服务器会对请求进行检查只有符合

推送格式的请求才被 MDSBES 服务器所接收当 MDSBES 服务器接收推送时会返回值为

200 的 HTTP 答复告知应用服务器已经接收该请求当 MDSBES 服务器拒绝推送请求时会

根据拒绝的原因返回相应的错误码可能返回的错误码包括 400403503 等下面对不同的

情况进行描述

第一种请求被拒绝的原因是推送请求的格式不对包括 URL 格式不正确和 HTTP 头不正

确两种情况两种情况 MDSBES 服务器都会返回 400 消息表示拒绝URL 格式不正确包括

URL 中不包含ldquoDESTINATIONrdquo参数或者是不包含ldquoPORTrdquo参数等HTTP 头不正确包括

PUSH ID 不唯一等因为推送请求中的 HTTP 头属性都有缺省值所以使用错误的 HTTP 头属

性并不会引发错误MDSBES 服务器只是忽略该参数如 HTTP 头ldquoX-RIM-PUSH-IDrdquo被误写

为ldquoX-RIMPUSHIDrdquoMDSBES 服务器并不会拒绝该请求只是使用ldquoX-RIM-PUSH-IDrdquo的缺

省值就是服务器随机生成一个唯一的 ID 给推送消息

对于 HTTP 协议而言服务器返回 400 代表服务器一般性错误所以返回消息会以异常

形式被捕获以 java 代码为类程序会捕获到以下网络异常

ldquojavaioIOException Server returned HTTP response code 400 for URL

httprdquo

第二种推送请求被拒绝的情况是推送请求指定的收件人不正确就是指应用服务器发送

推送请求时使用了正确的格式不过 DESTINATION 参数指定的收件人并不存在或者是该收件

人并不是 BlackBerry 用户此时 MDSBES 服务器会返回 403 错误读者需要注意 BES 服务

器 37 之前的版本对于这种情况会视作格式错误返回 400 错误

同样以 java 代码为例收件人不正确的情况下程序会捕获到以下网络异常

ldquojavaioIOException Server returned HTTP response code 403 for URL

httprdquo

第三种请求被拒绝的情况是 MDSBES 服务器上的推送队列已满无法再接收更多的推

送请求此时会返回 503 错误表示服务器暂时没有足够的资源处理该请求这种情况下 java

应用程序会捕获到以下异常

ldquojavaioIOException Server returned HTTP response code 503 for URL

httprdquo

这种情况与推送请求是否正确无关取决于 MDSBES 服务器运行情况关于 MDSBES

服务器的推送队列在下一节中我们才进行详细的讨论

如果数据推送请求通过了 MDSBES 服务器的消息检查则 MDSBES 服务器会将该数据

放入推送队列等待发送同时给应用服务器返回成功消息 200

成功消息是以 HTTP 的 ResponseCode 形式返回的开发人员可以通过 HTTP 连接的相应

方法获取在 java 代码中获取 ResponseCode 的函数是ldquo getResponseCoderdquo只有确认

MDSBES 服务器返回的 ResponseCode 是 200才能确定推送数据已经由 MDSBES 接收对

于其它的异常情况都需要由程序进行判断确定如何调整请求继续重试推送过程

有一点非常重要的是 MDSBES 接收该推送数据并不意味着该数据就一定能到达手持设

备端还有很多因素会导致手持设备端的应用接收不到该数据

MDSBES 中的推送队列

MDSBES 服务器有一个队列用于缓存推送请求因为手持设备并不是一定在线

MDSBES 服务器需要先将推送请求保留在服务端在合适的情况下将数据推送到手持设备端

后才从队列中将该请求移除

MDSBES 服务器上的推送队列是 MDS 推送的关键组件它的行为不仅仅影响应用服务

器端是否能够发送新的请求也影响到手持设备端是否能够接收到数据所以对于 MDS 推

送的开发人员必须了解 MDSBES 服务器上的推送队列

MDSBES 服务器上的推送队列有一个队列深度缺省为 1000也就是说 MDSBES 服务

器在同一时间只能缓存 1000 个推送请求该队列的深度可以由管理员在管理端修改

同时 MDSBES 服务器上的推送队列并不是永久保存推送数据它有一个过期时间缺

省为 10 分钟也就是说一个推送请求在 MDSBES 服务器上最多能够被保留 10 分钟同样

这个过期时间可以由管理员在管理端修改

推送队列的深度修改界面如下

推送队列的过期时间的修改界面如下(以毫秒为单位)

对于推送队列的进一步讨论是有关推送数据进入队列后的状态如何变化下面是推送数

据在队列中的状态变化图说明了推送数据在不同情况下的状态变化下图的各个状态中

淡蓝色的状态为起始状态深蓝色的状态为中间状态红色的状态为终止状态处于中间状

态的数据最终必然会转为终止状态的其中一种

当 MDSBES 服务器接收到推送数据后如果之前目标用户没有 5 个处于暂时失败的数

据包服务器会将数据放入等待发送的队列中推送的数据处于ldquo尝试发送rdquo状态处于ldquo尝

试发送rdquo会在很短的时间内占用 1000 个队列位置中的一个不管是成功还是失败它所占

用的队列位置很快被释放

然后 MDSBES 服务器尝试将数据推送到手持设备端如果此时对应的手持设备处于开

机状态网络一切正常则 MDSBES 服务器将数据发送给手持设备推送数据变成ldquo成功rdquo

状态MDSBES 服务器会向应用服务器发送异步的确认消息同时将数据从队列中移除注

意此时 MDSBES 服务器向应用服务器返回的确认消息并不一定是表示成功 200具体是

返回代表成功的 200 还是返回代表失败的 400 还取决于其它因素有关这一点我们在后面的

章节中详细讨论

如果 MDSBES 服务器尝试将数据推送到手持设备的时候手持设备处于关机状态则该

数据进入ldquo暂时失败rdquo状态处于ldquo暂时失败rdquo状态的数据不占用等待队列的队列位置某

一个目标用户最多只能有 5 条数据处于ldquo暂时失败rdquo状态目前没有明确的数据或者是文档

说明一个 MDSBES 服务器总共可以保留多少条ldquo暂时失败rdquo状态的数据

如果某一用户已有 5 条数据处于ldquo暂时失败rdquo状态MDSBES 服务器接收到该用户新

的推送请求时会直接将该数据转入ldquo等待rdquo状态每一条进入ldquo等待rdquo状态的推送数据会占

用推送队列中的一个位置当队列中的 1000 个位置都被占满的时候 MDSBES 服务器就停止

接收新的数据推送无论新的数据是推送给哪个用户

处于ldquo暂时失败rdquo和ldquo等待rdquo状态的数据会在服务器上保留 10 分钟(如上所述这一

时间可以由管理修改)10 分钟后处于这两种状态的数据将被丢弃进入ldquo过期rdquo状态同

时 MDSBES 服务器会向应用服务器发送异步消息这种情况下发送的异步确认消息一定是

代表失败的 400如果在 10 分钟内手持设备重新进入网络手持设备会发送消息到 MDSBES

服务器告知该手持设备已经上线MDSBES 服务器会将处理ldquo暂时失败rdquo和ldquo等待rdquo状态的

数据转成ldquo尝试发送状态rdquo重新发送该数据需要注意的是在 10 分钟的等待过程中

MDSBES 服务器如果没有接收到手持设备重新连接网络的消息则 MDSBES 服务器不会尝

试重新发送ldquo暂时失败rdquo和ldquo等待rdquo状态的数据

根据以上的分析可以对推出以下结果

1 当用户的手持设备在线时对该用户的数据推送会被马上处理该数据只在很短的时间

占用 1000 个等待队列中的一个位置

2 当用户的手持设备不在线时假设该服务器只有一个用户则服务器在 10 分钟内最多

可以接收该用户的 1005 条数据其中 5 条处于ldquo暂时失败rdquo状态1000 条在等待队列

3 如果用户的手持设备在数据发送给 MDSBES 服务器 10 分钟内重新接入网络MDSBES

服务器可以将该数据推送到手持设备端

4 如果 MDSBES 服务器的等待队列未满推送给新的用户的前 5 条数据不占用等待队列

5 如果 MDSBES 服务器的等待队列已满不管是由哪个用户的数据占满的MDSBES 服

务器都会停止接收所有用户的新的数据推送

总结而言在生产环境中保证 MDSBES 服务器正常工作的关键是要保证 MDSBES 服务

器的等待队列不被占满

手持设备端对推送数据的处理

MDSBES 服务器成功接收应用服务器推送的数据后会将数据推送到手持设备端在手持

设备端需要由客户端应用程序将推送的数据提取出来一个直观的猜测是手持设备端同样需

要一个队列保存数据否则客户端应用程序在提取前一条数据时新到达的数据就会直接丢

失需要验证的是这个队列有多深队列行为是否被系统隐藏经过测试证实手持设备端确

实也存在队列队列深度为 10而且可以反映在应用层其中最值得开发人员注意的是这

个队列是一个可溢出队列关于手持设备端推送数据队列的示意图如下

如图所示当客户端提取程序正在读取数据ldquoRrdquo如图中标有 R 的椭圆形所示如果

有更多的数据被推送到手持设备端所推送的数据会被放入设备的队列中一共有 10 条数

据可以被保留在设备队列中如椭圆形 1 到 10 所示此时如果有更多的数据被推送进来

设备队列已满则这些新的数据会溢出如椭圆形 111213 所示同时对于服务器端

而言数据 111213 已经被推送过一次服务器端不会再次尝试推送这些数据在这种

情况下数据 1 到 10 可以继续被客户端提取程序所提取而数据 111213 则丢失了

对于尚未推送到手持设备端的数据如数据 1415假设因为网络中断没有被推送到

手持设备端则仍保留在服务器端当网络恢复后数据 1415 仍可以被正常处理

对于确认消息(图中橙色圆形所示)的处理则更为复杂在异步确认消息一节中再作讨

从这一节的讨论得出的结果是数据到达手持设备端未必就能够被客户端提取程序提取

客户端提取程序有职责尽快提取数据防止推送数据的溢出溢出的数据在客户端无法再被

处理而且服务器端也不会尝试继续推送这些数据

异步确认消息(应用依赖和非应用依赖)

手持设备端在接收到推送数据后会通知 MDSBES 服务器这时 MDSBES 服务器会根据

最初的推送请求向指定的 URL 发送一个确认消息告知应用服务器某一条推送数据的最终

结果这就是异步确认消息异步确认消息以 http 协议发送给应用服务器消息 200 代表

数据推送成功400 代表数据推送失败

异步确认消息是应用服务器判断推送是否成功的有效工具但是很多开发人员因为对

异步确认消息的机制不了解错误地使用了异步确认消息从而导致数据的丢失所以我们

需要详细了解异步确认消息的工作机制

要讨论异步确认消息的工作机制必然涉及到应用依赖与非应用依赖应用依赖的推送

和非应用依赖的推送对应的异步确认消息工作机制是不同的

下面先讨论非应用依赖的场景这也是很多开发商使用的场景

如果推送是非应用依赖的数据是以到达手持设备为成功标志的只要数据到达了手持

设备MDSBES 服务器就认为该数据推送成功会给应用服务器发送异步确认消息 200

如果数据推送时手持设备不在线则 10 分钟内 MDSBES 服务器会在服务器上保留该数

据如果 10 分钟内手持设备重新接入网络则 MDSBES 服务器会尝试发送该数据一旦数

据到达手持设备MDSBES 服务器都会给应用服务器发送异步确认消息 200

如果 10 分钟内手设备没有接入网络则该推送数据过期MDSBES 服务器会丢弃该数

据同时给应用服务器发送异步确认消息 400

可以看出非应用依赖的推送方式并不能保证客户端应用程序接收到数据在非应用依

赖的推送方式下 MDSBES 返回 200 确认消息也不能认为客户端已接收到数据如果数据送

达手持设备的时候客户端应用程序没有启动所推送的数据会在手持设备端丢失而

MDSBES 服务器认为数据已成功送达或者是客户端应用程序已启动但是没有及时读取推

送数据在手持设备端溢出的数据也会丢失MDSBES 服务器同样认为数据已成功送达

所以在非应用依赖的情况下如果接收到 400 确认消息可以确定客户端没有收到数据

如果接收到 200 确认消息只能认为客户端可能收到了数据

从以上的讨论看非应用依赖的推送方式并不适合用于可靠性要求较高的应用对于可

靠性要求较高的应用需要知道客户端应用程序是否接收到了数据应用依赖的推送方式就

是为此而设计的如果推送是应用依赖的数据是以客户端应用程序确认为成功标志的只

要在应用程序确认后MDSBES 服务器才认为数据推送成功这时才给应用服务器发送异步

确认消息 200

有关客户端应用程序如何确认收到数据在ldquo客户端接收关键代码rdquo一节中有描述简

单而言就是客户端应用程序通过调用 MDSPushInputStream 实例的 accept 函数确认数据收

到如果客户端应用程序在接收到数据后没有调用 accept 函数MDSBES 服务器会认为数

据推送失败给应用服务器发送异步确认消息 400另外一点曾经强调过的是accept 函数

调用后只有 InputStream 和 StreamConnection 都关闭了确认消息才被发送给 MDSBES 服

务器在极端的情况下这一点会影响到异步确认消息的结果

下面具体分析各种情景

如果手持设备在线但是客户端应用程序没有启动推送数据到达手持设备端后没有程

序侦听推送端口当然也没有程序会调用 accept 函数此时 MDSBES 服务器会认为数据推

送失败向应用服务器发送异步确认消息 400

如果手持设备在线客户端应用程序在读取推送数据后没有调用 accept 函数直接关闭

连接此时 MDSBES 服务器同样会认为数据推送失败向应用服务器发送异步确认消息 400

在这种情景下即使客户端接收到了数据但应用服务器仍收到失败消息因为作为成功标

志的 accept 函数并没有调用

如果手持设备在线客户端应用程序在读取推送数据后调用了 accept 函数并关闭连

接此时 MDSBES 服务器会认为数据推送成功向应用服务器发送异步确认消息 200

对于手持设备不在线的情况结果与非应用依赖相似10 分钟内如果手持设备不接入

网络的话推送数据会在 MDSBES 服务器上过期MDSBES 服务器会向应用服务器发送异

步确认消息 400

如果 10 分钟内手持设备重新接入网络MDSBES 服务器会尝试发送数据至手持设备端

还是以 accept 函数的调用为推送成功的标志如果数据重新发送成功但是客户端没有调

用 accept 函数则 MDSBES 服务器会向应用服务器发送异步确认消息 200

对于推送到手持设备端但是溢出的数据因为没有程序能够读取该数据并针对该数据调

用 accept 函数MDSBES 服务器会一直等待到 10 分钟后向应用服务器发送异步确认消息

400认为数据过期

一个特殊的情况是客户端应用程序在接收到数据后调用了 accept但是没有关闭

InputStream 和 StreamConnection此时 MDSBES 服务器会认识客户端还在处理数据会继

续等待如果服务器等待时候超过 10 分钟MDSBES 服务器会认为数据推送失败向应用

服务器发送异步确认消息 400如果在 10 分钟内客户端主动关闭 InputStream 和

StreamConnection或者是网络异常中断MDSBES 服务器会认为数据推送成功向应用服

务器发送异步确认消息 200

下面再讨论一个异常情况下的特例

结合以上描述的各种因素在极端的情况下会出现一些特殊的情况如上图所示假设

MDSBES 服务器在推送了数据 R数据 1 到数据 13 后出现网络中断同时客户端应用程序

正在缓慢地读取数据 R此时数据 1 到数据 10 会被保留在手持设备队列中客户端应用程

序在读取数据 R 后可以继续读取数据 1 到数据 10如果客户端应用程序在读取了数据后都

调用了 accept 函数则会产生对应用确认消息如图中的确认消息 R确认消息 1 到确认消

息 10但是因为网络的中断这些确认消息都无法到达 MDSBES 服务器如果网络在 10

钟内没有恢复MDSBES 服务器上所有等待确认消息的记录都会认识是过期的MDSBES

会向应用服务器发送异步确认消息 400此后即使网络恢复确认消息 R确认消息 1 到确

认消息 10 都不再有效

在这种特殊情况下即使客户端应用程序接收到数据并调用了 accept 函数MDSBES

服务器同样认为数据没有推送成功

根据以上的讨论应用依赖的推送同样不能保证客户端应用一定能收到数据但是在

应用依赖的推送方式下 MDSBES 返回 200 确认消息可以确定客户端已接收到数据因为 200

确认消息是由客户端应用程序调用 accept 函数触发的客户端应用程序可以在确认数据收

到后才调用 accept 函数通知应用服务器该数据已成功接收甚至可以在接收到数据后根

据数据内容决定不调用 accept 函数从而通过应用服务器该数据没有被接收

所以在应用依赖的情况下如果接收到 200 确认消息可以确定客户端已经接收到数据

如果接收到 400 确认消息只能认为客户端可能没有收到数据

因此应用依赖的推送方式更适合可靠性要求高的应用如果应用服务器接收到 200

确认消息就可以认为推送成功继续处理其它事务如果应用服务器接收到 400 确认消息则

需要组织数据重发直到接收到 200 确认消息为止这样就可以保证客户端一定能够接收到

需要推送的数据

如何保证数据推送的可靠性

通过以上几个章节的讨论我们知道虽然 MDSBES 服务器提供了很多机制保证数据

推送的可靠性但是都无法完全保证数据被客户端应用程序所接收

对于数据推送的可靠性保证一方面开发人员需要了解各个可能导致故障的因素在应

用编写的时候尽量减少故障出现的可能性对故障点进行规避另一方面开发人员也需要实

现额外的机制进行数据重发在故障点无法规避的情况下通过数据重发消除故障点的影响

应用依赖和非应用依赖的比较和选择

在实现推送应用的时候首要考虑的是有关应用依赖与非应用依赖的选择

应用依赖的推送形式在可靠性方面更优于非应用依赖的推送形式但是应用依赖的推送

形式对 MDSBES 应用服务器的潜在压力较大客户端应用程序的实现也较为复杂

非应用依赖的推送形式优势在于实现简单对 MDSBES 应用服务器形成的压力相对较

非应用依赖的推送形式从某种程度上讲有点类似于广播协议着重点是从服务器向客户

端发送大量数据并不看重客户端的接收情况从数据的应用类型来分非应用依赖的推送

更适合于天气信息最新报价等数据这些数据在客户端应用关闭时被自然丢弃短时间内

的多条数据有丢失影响也不大必竞用户不会关注 11 点零六分的天气和 11 点零七分的天气

有何不同

应用依赖的推送形式则类似于点对点协议不仅从服务器端向客户端发送数据同时也

关注客户端的接收情况应用依赖的推送更适合于 OAERP 这种应用这种应用需要保证

新的文档或者是记录一定能到达客户端应用丢失一条待办信息可能就会导致整个审批流程

的停滞

再从两种推送方式的本质差别看应用依赖并没有额外的机制消除故障点它的优势在

于它的异步确认消息更可信所以在选择了应用依赖的推送方式后进一步还是需要实现

额外的数据重发机制根据 MDSBES 所发送的异步确认消息实施数据重发

应用依赖的推送再加上结合异步确认消息的数据重发机制是保证推送数据可靠性的

最好方法

MDS 接收线程限制的处理

不管使用什么方式的推送应用开发时都需要考虑到 MDS 接收线程的限制不过以

现在的服务器处理能力正常情况下的 HTTP 推送请求可以在几毫秒或者是几十毫内处理完

成少量的 HTTP 推送不足于构成网络阻塞

需要考虑的是应用服务器端的推送程序在推送过程中不要在建立连接后才动态组织数

据这样会浪费 http 连接资源好的建议是预先组织好数据在建立 HTTP 连接后快速写完

数据尽快关闭连接

此外对于应用服务器的推送线程也要加于控制避免在短时间内无限制地启动新的线

程进行连接否则也容易造成连接过多而阻塞的情况

从网络层面来看调整应用服务器与 MDSBES 服务器之间的网络超时设置也是避免这

种问题的一种方法在允许的范围内调大网络超时时间可以让应用服务器的推送线程等待更

长的时间从而让 MDSBES 有足够的时间释放原有的连接

最后无论使用了什么方法调优都需要对应用服务器的推送程序进行异常处理当发

现网络超时异常的时候可以组织数据重发当然数据重发的间隔时间也要考虑不要因为

重发数据再次造成网络阻塞

MDS 推送队列限制的处理

相对而言MDS 推送队列限制造成的推送失败比 MDS 接收队列造成的推送失败更常见

已经发现的一个实际案例是周未的时候用户大量关机导致大量推送数据处于推送队列中

填满了推送队列从而导致 MDSBES 服务器不再响应新的推送请求一旦过期时间到达

有新的队列位置空闲又有大量新的推送数据重新占满推送队列

对于 MDS 推送队列的限制直观的做法是对队列参数进行调整如之前讨论的管理

员可以对队列的深度和过期时间进行调整

然而出于对过期时间的不正确理解管理员倾向于将过期时间调大当过期时间被调

大后推送给关机用户的数据在服务器占用队列的时间就变长导致 MDS 推送队列更容易

被占满

所以如果对过期时间进行调整的话方向是将过期时间调小过期时间越小等待数

据占用队列的时间就越短MDS 推送队列被占满的可能性就越小但是过期时间也不能

过小过期时间过小可能会导致过多的过期数据使应用服务器需要处理更多的数据重发

比如将过期时间调成 1 分钟用户走过一个人行遂道都可能导致数据发送失败

对于队列深度的调整方向是调大队列深度如从缺省的 1000 调整到 2000此时

MDSBES 服务器在同一时间可以保留更多的等待数据减少了队列占满的机会不过调大队

列深度会让 MDSBES 服务器保留更多的数据从而占用更多的服务器内存对服务器产生

更大的压力所以调用队列深度需要根据服务器的运行能力进行调用

从应用服务器设计角度应用 MDS 队列限制的方法是尽量减少等待数据的发送如发

送第一条数据的时候发现数据过期对于该用户的其它数据可以调整发送时间在更长的时

间间隔后才尝试发送

对于 MDS 队列限制应用服务器端的推送应用需要对 MDSBES 服务器返回的 503 错误

进行处理对于出现 503 错的数据组织重发同样重发的间隔也需要考虑不要因为数据

重发占用更多的推送队列也可以根据目标用户进行判断对于可能关机的用户暂时不重发

数据

手持设备端接收队列的处理

手持设备端接收队列并未从文档中明确列出是根据测试结果分析得出的手持设备端

目前也没有发现设置可以调整该队列所以客户端程序设计的时候不应该依赖手持设备端队

开发人员需要意识到的是手持设备端的接收队列是可溢出队列一旦推送数据从接收队

列出溢出客户端程序就无法提取该数据了所以在客户端应用开始过程中要注意加快推送

数据的提取过程在提取过程中不要对数据进行复杂的计算提取完成后尽快读取下一条数

据对于接收数据的处理和计算可以交由另一个线程完成

因为溢出数据对客户端应用而言不可见所以数据溢出时在客户端应用中也不会有异

常开发人员需要通过服务器返回的过期消息才能知道该数据推送失败

对于这一点客户端应用除了尽量避免数据溢出外无法在客户端通过其它机制进行补

应用依赖的客户端

对于应用依赖的客户端应用关键点是决定何时调用 accept 函数同时保证 InputStream

和 StreamConnection 被关闭

如之前讨论的使用应用依赖的推送关键是希望使用 MDSBES 发送给应用服务器的异

步确认消息从而对数据是否到达进行判断所以应用依赖的客户端应用需要保证正确调用

accept 函数

另外如果 InputStream 和 StreamConnection 没有关闭的话accept 函数所发出的确认

消息无法被 MDSBES 服务器收到开发人员在代码中应该主动关闭 InputStream 和

StreamConnection同时在程序发生异常时也需要在 catch 代码段对 InputStream 和

StreamConnection 进行关闭处理

异步确认消息处理

异步确认消息用于判断数据是否被成功推送至客户端应用要使用异步确认消息首先

要保证成功地无遗漏地接收所有从 MDSBES 服务器发送过来的异步确认消息

异步确认消息是一个 HTTP 消息消息比较短接收过程的并发压力不大下面是一个

异步确认消息的样例

Received notificationGET HTTP11

User-Agent RIM MDSCS

Accept

X-RIM-Push-ID pushID2081773768

X-RIM-Push-Destination mmtestbjsearbcn

X-RIM-Push-Status 400

Host localhost7778

其中最关键是的获取属性 X-RIM-Push-Status根据该属性的值判断数据推送是成功

(200)还是失败(400)当然属性 X-RIM-Push-ID 也非常重要用于确定该确认消息是针

对哪条推送数据的

如果应用服务器端通过确认消息发现数据推送失败则需要根据属性 X-RIM-Push-ID 重

新组织数据进行重发

Page 4: BES 服务器推送机制分析 - images.csdn.netimages.csdn.net/upimgs/lee/BBPDF/BESfwtsjzfx.pdf · 前言 数据推送是BlackBerry 应用平台的一大优势,在BlackBerry 应用平台上部署的应用可以

从示意图中可以看到在 BlackBerry 应用平台上的数据推送从整体上可以分为六步按时

间顺序分别为

1 第一步应用服务器向 MDSBES 服务器发送推送请求所发送的请求为 HTTP 格式的请

求有关请求的详细格式在下一小节中有详细讲解

2 第二步MDSBES 服务器查询相关配置数据库确定应用服务器所发送的请求是否为合

法的请求此外MDSBES 服务器还会根据资源情况确定是否接收该请求对于是否接

收请求的判断在下一节内容中也有详细讨论

3 第三步MDSBES 服务器向应用服务器返回消息通知应用服务器是否接受该请求返

回消息以 HTTP 答复的方式返回给应用服务器

4 第四步MDSBES 服务器将数据推送到手持设备端

5 第五步手持设备端对数据进行处理后向 MDSBES 服务器返回确认消息

6 第六步MDSBES 根据手持设备端返回的消息决定向应用服务器返回什么异步消息这

一步并不是必然发生的根据推送请求的不同有可能不发生

从这里我们可以看到从应用服务器到手持设备端的推送通道是由多个不同的通道连接

而成的这其中有很多个连接点某一个连接点出现异常都可以导致推送的失败所以应用

开发商需要详细了解这些连接点可能出现的问题在应用开发过程中进行规避

在详细描述各个可能出现问题的关键点之前需要先了解推送的基本实现对于应用开

发人员而言完成数据推送需要完成的主要程序编写工作有应用服务器端发出推送请求的

程序手持设备端侦听接收推送数据的程序和服务器端侦听接收确认消息的程序如下图

橙色框图所显示的

以下章节将对这三个部分作详细讲解

数据推送命令格式

在 BlackBerry 应用平台上的数据推送是由应用服务器发起的应用服务器根据应用逻辑

进行判断发现有数据需要推送到用户的手持设备端时连接 MDSBES 服务器进行数据推送

应用服务器与 MDSBES 服务器的连接方式为 HTTP 连接通过 HTTP 连接发送一个 POST 请

求将需要推送的数据作为 POST 的内容

该 POST 请求的 URL 格式为

httpBES_HOSTBES_PORTpushDESTINATION=DESINTATION_ADDRESSampPORT=HANDHEL

D_APPLICATION_PORTampREQUESTURI=

下面是请求中各参数的说明

参数 描述

BES_HOST BlackBerry Enterprise Server 的服务器名或者是 IP

BES_PORT BlackBerry Enterprise Server 的推送端口一般是 8080

DESTINATION_ADDRESS 用户的邮件地址或者是设备码 (PIN)

HANDHELD_APPLICATION_PORT BlackBerry 手持设备需要帧听的端口

也就是说应用服务器通过 BES_HOST 和 BES_PORT 指定需要连接的 MDSBES 服务器和

需要连接的端口BES 服务器安装后缺省使用 8080 作为推送端口所以推送应用开发人员

一般情况下只需要了解 MDSBES 服务器的服务器名或者是 IP 地址就可以了如果 BES 管理

人员在安装服务器后修改过推送端口则需要告知开发人员使用新指定的端口

推送 URL 中 push后面的部分用于指定数据的接收者和手持设备需要帧听的端口

DESTINATION_ADDRESS 用于指定数据的接收者可以使用该用户的邮件地址或者是该用户手

持设备的 PIN 码HANDHELD_APPLICATION_PORT 用于指定手持设备需要帧听的端口这里

使用的端口只是一个约定要求在手持设备上运行的程序从这一端口中读取数据

除了 URL 参数以后在推送过程还可以使用一些规定好的 HTTP 头指定该推送的属性

HTTP 头参数表如下

HTTP 头 描述

X-RIM-Push-ID 用于指定该消息的 ID该 ID 需要是一个唯一值可以用于

取消推送或者是查看推送的状态

X-RIM-Push-NotifyURL 用于指定确认消息的接收 URL

X-RIM-Push-Reliability-Mode 用于指定推送的可靠性

X-RIM-Push-Deliver-Before 用于指定推送数据的最迟推送时间

X-RIM-Push-Priority 用于指定推送数据的优先级

X-RIM-Push-ID 属性一般建议使用 URL 和编号结合的方式如 123blackberrycom为

了更好地控制数据的推送一般不建议使用随机数作为推送 ID使用统一的 ID 生成方式更

有利于推送数据的取消和状态确定注意如果没有指定该参数的话MDSBES 服务器会自

动生成一个唯一的 ID这样就无法在应用服务器上使用这个 ID 对特定的推送数据进行处理

了此外推送 ID 不能以ppgrimcom 结束

X-RIM-Push-NotifyURL 属性通过 URL 的形式指定了确认消息的接收地址如

httptestingserver7778指定了这一属性后MDSBES 服务器会将推送的确认消息以 HTTP

请求的形式发送到指定的服务器也就是说应用开发者需要开发一个 HTTP 服务器端程序监

听指定服务器的指定端口(上例中则是服务器 testingserver 的 7778 端口)通过这个服务器

端程序获取确认消息在确认消息中会包含 HTTP 头 X-RIM-Push-ID 和 X-RIM-Push-Status通

过 X-RIM-Push-ID 告知监听者是哪条推送数据的确认消息通过 X-RIM-Push-Status 指明该数

据推送的结果200 代表推送成功400 代表推送失败现实环境中对于数据推送是否成功

不能简单地以结果 200 或者是 400 进行判断需要结合很多因素进行判断本文的后续章节

会详细描述

属性 X-RIM-Push-Reliability-Mode 用于指定推送的可靠性值可以是 TRANSPORT 或者是

APPLICATION另外有一个值 APPLICATIONPREFERRED 涉及更复杂的场景这里不做描述如

果指定可靠性为 TRANSPORT 则以数据到达手持设备准本文称之为非应用依赖如果指定

可靠性为 APPLICATION 则以数据被手持设备端应用接收为准本文称之为应用依赖对于不

同可靠性设置本文的后续章节会有详细描述总体而言非应用依赖的推送更适合于广播

性质的不要求应用一定收到数据的场景而应用依赖更适合于点对点的要求应用一定要收到

数据的场景

属性 X-RIM-Push-Deliver-Before 用于指定数据推送的最后时间如果在指定时间前该数

据仍无法被成功推送到手持设备端则该数据会被视作过期而被 MDSBES 服务器丢弃

属性 X-RIM-Push-Priority 用于指定推送数据的优先级指定推送数据的优先级并不能加

快数据的推送指定优先级的结果是推送数据到达手持设备端的行为不同可以选择的值有

none (缺省)low medium 和 high如果指定优先级为 lowmedium 或者是 high则用

户只是接收到数据如果指定优先级为 high则用户在接收到数据后还会看到提示对话框

数据推送关键代码

理论上讲应用服务器的数据推送代码可以用任何可以提供 HTTP 支持的语言编写需

要实现的不过是通过 HTTP 协议往指定的服务器和端口发送 POST 消息

下面以 java 代码为例讲解数据推送的关键代码部分本例的代码片段为一个 Java

Application 的部分代码该 Java Application 在服务器上以 J2SE 应用的形式运行启动一个

线程发送 HTTP 的 POST 请求以下代码为该线程的 run 函数

public void run()

Random random = new Random()

String pushId = pushID + randomnextInt()

String data = What need to be pushed

try

URL _pushURL = new URL(http testingBESabccom 8080

pushDESTINATION=user1abccomampPORT=55100ampREQUESTURI=)

HttpURLConnection conn = (HttpURLConnection) _pushURLopenConnection()

connsetDoInput(true)

connsetDoOutput(true)

connsetRequestMethod(POST)

connsetRequestProperty(X-RIM-PUSH-ID pushId)

connsetRequestProperty(X-RIM-Push-NotifyURL

httptestingserver7778)

connsetRequestProperty(X-RIM-Push-Reliability-Mode

APPLICATION)

OutputStream out = conngetOutputStream()

outwrite(datagetBytes())

outclose()

InputStream ins = conngetInputStream()

int contentLength = conngetContentLength()

int responseCode = conngetResponseCode()

Systemoutprintln(Response Code is + responseCode)

if (contentLength gt 0)

byte[] someArray = new byte[contentLength]

DataInputStream dins = new DataInputStream(ins)

dinsreadFully(someArray)

Systemoutprintln(new String(someArray))

else

Systemoutprintln(Content legth of response is 0)

conndisconnect()

catch (IOException e)

Systemerrprintln(Exception + e)

在以上代码中先通过 URL 类建立一个 HTTP 连接定义URL 类的构造函数中第一个参

数为协议类型这里使用ldquohttprdquo第二个参数为服务器名第三个参数为请求的端口号

第二个参数和第三个参数对应了数据推送请求中的 BES_HOST 和 BES_PORT 属性URL 类的

构造函数中第四个参数为相对 URL推送属性 DESTINATION 和 PORT 都放在相对 URL 中代

码如下

URL _pushURL = new URL(http testingBESabccom 8080

pushDESTINATION=user1abccomampPORT=55100ampREQUESTURI=)

建立了 URL 类的实例后可以通过该实例的 openConnection 函数打开连接返回的是

一个 HttpURLConnection 实例

HttpURLConnection conn = (HttpURLConnection) _pushURLopenConnection()

打开 http 连接后可以通过连接实例的 setRequestProperty 函数设置 http 头推送请求中

的 http 头属性在这里设置如

connsetRequestProperty(X-RIM-PUSH-ID pushId)

其中一个关键的 http 头属性是 X-RIM-Push-Reliability-Mode设置该属性为ldquoTRANSPORTrdquo

表示该推送是非应用依赖的推送设置该属性为ldquoAPPLICATIONrdquo表示该推送是应用依赖的

推送两者的具体区别在后面的章节会有详细的讨论以下代码设置该属性为

ldquoAPPLICATIONrdquo

connsetRequestProperty(X-RIM-Push-Reliability-Mode

APPLICATION)

特别要注意如果设置推送类型为应用依赖对于服务器的设置和手持设备有特定的要

求一方面是服务器端必须是 40 以上同时必须设置允许对所使用的端口进行应用依赖的

推送一方面是要手持设备的 ROM 版本也是 40 以上同时服务器必须知道手持设备能否

支持应用依赖的推送对于服务器的端口设置设置界面如下可以由 BES 管理员进行设置

如果是使用开发环境的 MDS-CS 模拟器模拟应用依赖推送的话需要修改开发环境的

MDSconfig目录中的 rimpublicproperty文件去除 pushapplicationreliableports一行的注释

同时加上需要推送的端口如ldquopushapplicationreliableports=10055100rdquo

对于服务器端是否知道手持设备端的版本需要通过手持设备端的操作完成只要手持

设备端的浏览器通过 BES 访问过网络服务器就会记录该手持设备是否支持应用依赖的推

送如果只是测试的话在测试前通过手持设备端的浏览器打开任何一个网站就可以了如

果是生产环境则需要考虑客户端应用在第一次启动的时候主动访问一次网络以保证客户

端都可以正常接收应用依赖的推送

如果以上设置没有完成的话对 MDSBES 服务器发送应用依赖的推送时会被拒绝服

务器端会出现ldquoThe specified delivery method is not possiblerdquo的错误应用服务器的推送程

序则会捕获以下异常

ldquojavaioIOException Server returned HTTP response code 400 for

URL httpapprdquo

设置属性后就需要获取 http连接的 OutputStream将需要推送的数据写入 OutputStream

中写完后关闭连接

需要注意的是 MDSBES 服务器是否接收该推送请求是通过 http 返回码实现的所以推

送应用中需要获取到 http 连接的返回码通过返回码判断推送是否成功代码如下

int responseCode = conngetResponseCode()

客户端接收关键代码

客户端数据接收程序运行在 BlackBerry 手持设备上必须通过 java 语言实现需要调用

BlackBerry 设备端的 API

客户端接收程序的主体就是启动一个线程开启 httpltportgt连接(其中 port 是客户

端需要侦听的端口号)然后从该连接中获取 InputStream从 InputStream 中获取服务器所

推送的数据

下面是侦听线程的 run 方法为保持程序的完整代码中使用到的类属性并没有修改

从该函数中看到的本函数没有定义的变量都是该类的类属性有关完整代码请参考

BlackBerry 开发环境所提供的标准样例

public void run()

StreamConnection stream = null

InputStream input = null

MDSPushInputStream pushInputStream=null

while (_stop)

try

synchronized(this)

_notify =

(StreamConnectionNotifier)Connectoropen(http55100)

while (_stop)

stream = _notifyacceptAndOpen()

try

input = streamopenInputStream()

pushInputStream= new

MDSPushInputStream((HttpServerConnection)stream input)

DataBuffer db = new DataBuffer()

byte[] data = new byte[256]

int chunk = 0

while ( -1 = (chunk = inputread(data)) )

dbwrite(data 0 chunk)

pushInputStreamaccept()

inputclose()

streamclose()

data = dbgetArray()

catch (IOException e1)

Systemerrprintln(e1toString())

if ( input = null )

try

inputclose()

catch (IOException e2)

if ( stream = null )

try

streamclose()

catch (IOException e2)

_notifyclose()

_notify = null

catch (IOException ioe)

if ( _notify = null )

try

_notifyclose()

_notify = null

catch ( IOException e )

以上代码中建立端口侦听的代码如下

_notify = (StreamConnectionNotifier)Connectoropen(http55100)

其中 URLldquohttp55100rdquo是用于侦听推送的特定格式55100 为需要侦听的端口两

个线程不能同时打开同一个端口的侦听所以在这里使用了 synchronized 对线程并发进行了

控制

建立侦听后可以通过函数 acceptAndOpen 开始侦听当线程调用该方法开始侦听的时候

线程会阻塞在这里直到有推送消息到达代码如下

stream = _notifyacceptAndOpen()

侦听之后的代码为获取连接的 InputStream从 InputStream中获取服务器端推送的数据

读取完数据后关闭 InputStream 和 StreamConnection然后重新调用 acceptAdnOpen 继续侦

听端口准备接收新的推送数据

开发人员如果希望使用应用依赖的推送在客户端必须将 InputStream 转换成

MDSPushInputStream同时在接收完数据后调用 MDSPushInputStream 的 accept 函数造知服

务器该数据已收到这也是应用依赖和非应用依赖在代码实现层面的最主要区别代码如下

pushInputStream= new

MDSPushInputStream((HttpServerConnection)stream input)

helliphellip

pushInputStreamaccept()

从良好的编程习惯来讲在连接使用后需要主动关闭所使用的连接和资源如

InputStreamStreamConnectionStreamConnectionNotifier 等所以在样例代码中也可以看

到该样例在异常处理阶段都对这些资源进行了关闭操作在关闭这些连接和资源的时候对

StreamConnectionNotifier 需要特别注意有可能在 StreamConnectionNotifier 中会保留有未

读取的推送数据需要调用 acceptAdnOpen 函数将保留的数据读取出来直到 acceptAdnOpen

处于阻塞状态

另外如果使用了应用依赖的推送客户端程序调用 MDSPushInputStream 的 accept 函

数后确认消息不会马上发出而是在对应用 InputStream 和 StreamConnection 关闭后才发出

所以对于应用依赖的推送保证 InputStream 和 StreamConnection 的关闭非常之重要

异步确认消息接收代码

异步消息的接收代码运行在服务器端在推送请求的 X-RIM-Push-NotifyURL 属性所指定

的服务器上侦听指定端口和应用服务器端的数据推送代码一样异步确认消息的接收程序

可以通过任何支持 HTTP 的语言编写主要的工作是侦听指定端口从指定端口获取 MDSBES

发送回来的确认消息根据 HTTP 协议获取相关属性并返回相关信息

下面以 java 代码为例对关键代码进行说明这段代码一个 java Application 的一部分是

侦听线程的 run 函数

public void run()

try

Systemoutprintln(Waiting for notification on port + 7778

+ )

while (true)

ServerSocket serverSocket = new ServerSocket(7778)

serverSocketsetSoTimeout(120000)

try

Socket clientSocket = serverSocketaccept()

InputStream input = clientSocketgetInputStream()

StringBuffer str = new StringBuffer()

int byteRead = inputread()

while ((byteRead = -1) ampamp (inputavailable() gt 0))

strappend((char) byteRead)

byteRead = inputread()

Systemoutprintln(strtoString())

OutputStream output = clientSocketgetOutputStream()

String response = HTTP10 200 OK

outputwrite(responsegetBytes())

outputflush()

outputclose()

inputclose()

clientSocketclose()

catch (SocketTimeoutException ste)

Systemoutprintln(Notification connection timeout

Restarting)

serverSocketclose()

catch (Exception exception)

exceptionprintStackTrace()

异步确认消息的接收代码比较简单基本上就是打开一个 Socket 侦听指定的端口本

例中是 7778然后不断地从该端口读取数据打开 Socket 侦听的代码如下

ServerSocket serverSocket = new ServerSocket(7778)

在异步消息接收过程中需要注意的是接收完 MDSBES 服务器的确认消息后应该给

MDSBES 服务器返回一个 200 的成功消息否则 MDSBES 服务器会认为发送确认消息失败

可能重次几次后再放弃这样异步消息接收程序有可能重复收到同一条确认消息返回成功

消息的代码如下

OutputStream output = clientSocketgetOutputStream()

String response = HTTP10 200 OK

outputwrite(responsegetBytes())

outputflush()

outputclose()

推送细节分析

了解基本的 MDS 推送知识是不足于编写一个可靠的数据推送程序的要保证数据推送

的可靠性必须了解整个推送过程的每一个细节本章节先从整体上描述了整个数据推送的

调用时序然后从不同的关键点详细讲解数据推送的细节

MDS 推送的整体时序

如上图所示数据推送主要是应用服务器MDS 服务器网络服务手机端几个组件

之间的交互在网络服务这一块我们可以假定这一层是稳定的不做考虑对于网络无法提

供服务等情况我们可以通过手持设备端关闭无线电或者是将手持设备放入无线屏蔽区等方

式模拟所以我们要考虑是应用服务器MDSBES 服务器手持设备这三部分

数据推送由应用服务器发生向 MDSBES 服务器发出 HTTP Post 请求第一个需要考虑

的是该 HTTP Post 请求是否能到达 MDSBES 服务器这一点在ldquo推送请求是否到达 MDSBES

服务器rdquo小节中有详细讨论

MDSBES 服务器在接收到 HTTP Post 请求后会对请求的格式进行判断如果格式错误

如指定目标不存在等则 MDSBES 服务器会返回 400 错误所以第二点要考虑是的如何保

证所有到达 MDSBES 服务器的请求都是合法的有关这一点在ldquo推送请求检查rdquo小节中有

详细描述

MDSBES 服务器发现推送数据格式正确以后还需要判断服务器上的队列是否有足够的

空间保存该数据如果没有发现空闲队列则丢弃该数据并向应用服务器返回 503 错误因此

第三点要考虑的是什么情况会导致服务器推送队列占满如何尽量避免这种情况有关这一

点在ldquoMDSBES 中的推送队列rdquo中有详细描述

MDSBES 服务器将数据推送到客户端以后由客户端程序将数据提取交由相应的应用

逻辑进行处理然后客户端应用在提取数据的时候并不一定能及时地将所有推送到的数据

获取所以第四点要考虑的是手持设备端是否也存在队列如何保证客户端程序将所有数据

都提取出来有关这一点在ldquo手持设备端对消息的处理rdquo小节中有详细描述

手持设备在接收推送数据后会通知 MDSBES 服务器MDSBES 服务器则向应用服务器

发送异步的确认消息通过返回数值 200 或者是 400 通知应用服务器推送数据是推送成功还

是推送失败异步确认消息对于应用开发者有特别的意义通过它可以确定数据是否到达客

户端从而决定是否再次推送数据然后很多开发商都发现异步确认消息的返回的ldquo200rdquo

和ldquo400rdquo并不能准备地反映数据的接收情况所以如何使用异步确认消息在不同的场

景下异步消息会包含什么不同值对于应用开发者而言非常重要有关这一点在小节ldquo异步

确认消息rdquo有详细描述

推送请求是否到达 MDSBES 服务器

有关推送请求是否到达 MDSBES 服务器 很多开发人员都会忽略认为推送请求是否

到达 MDSBES 服务器是由代码是否编写正确决定的一定会在代码测试阶段体现也来也

必须在代码编写阶段解决这个问题然而在生产环境确实有一些因素会导致应用服务器发送

的推送请求无法到达 MDSBES 服务器从而导致推送数据丢失的问题

推送请求无法到达 MDSBES 服务器的一个可能的直接原因是指定的服务器名不正确或

者是指定的端口号不正确原因可能是 BES 管理员修改了 BES 服务器的服务器名IP 地址或

者是推送监听端口

这种问题比较容易发现一方面是因为所有的 BlackBerry 用户此时都无法接收到推送数

据另一方面也因为应用服务器端会报网络连接的异常以 java 代码为例当指定的服务

器名或者是端口号不正确的情况下应用服务器端的推送程序会报以下异常

javanetConnectException Connection refused

或者是报域名无法解析等网络连接级别的异常

发现这种情况只需要检查代码中指定的服务器名和端口号是否和生产环境相同就可以

解决问题

推送请求无法到达 MDSBES 服务器的一个潜在可能性是推送请求的并发数量太大导致

MDSBES 服务器无法及时接收所有请求

这种问题比较难于发现因为部分用户仍然可以收到推送数据只有部分用户的部分数

据丢失了同时这种问题在功能性测试场景下不容易发生只有在大压力测试或者是存在

大量用户的生产环境中才容易出现

产生这种问题的根源是 MDSBES 服务器的并发接收限制MDS 服务器其实是运行在

Tomcat 服务器上一个服务Tomcat 服务器作为应用容器处理了低层的网络请求而 Tomcat

服务器在底层有并发网络请求的限制当网络连接达到服务器限制时新的推送连接会处理

等待状态如果在网络超时前仍无法建立连接则会出现网络超时异常

javanetConnectException Connection timed out connect

根据测试的结果不同版本的 BES 服务器在低层并发网络请求的数量上有差异以 200

个并发限制为例就是说同一时间点只能有 200 个连接可以向 MDSBES 服务器发送推送请

求第 201 个请求以后有所有请求都将进入等待状态如果在 TCPIP 超时限制到达之前

MDSBES 服务器仍无法释放前 200 个请求则等待状态的请求会因为网络超时而中断

当应用服务器采用多线程机制处理推送请求的情况下有可能出现以上情况如果因为业

务原因在短时间内出现大量等待推送的数据应用服务器则有可能启动多于 200 个的线程发

送推送请求如果需要推送的数据太大或者是推送线程需要动态对数据进行计算就有可

能导致线程所启动的连接在短时间内没有关闭当多于 200 个线程处于这种状态的时候

MDSBES 服务器就无法继续接收推送请求了这种问题的一个现象就是应用服务器端报网络

超时异常或者是连接被拒绝的异常

总的而言要保证数据推送的成功第一步需要保证推送数据到达 MDSBES 服务器

发现推送数据没有到达 MDSBES 服务器的情况时应该对应用服务器的日志进行检查进一

步确定问题的原因

推送请求检查

在 MDSBES 服务器接收到推送请求后MDSBES 服务器会对请求进行检查只有符合

推送格式的请求才被 MDSBES 服务器所接收当 MDSBES 服务器接收推送时会返回值为

200 的 HTTP 答复告知应用服务器已经接收该请求当 MDSBES 服务器拒绝推送请求时会

根据拒绝的原因返回相应的错误码可能返回的错误码包括 400403503 等下面对不同的

情况进行描述

第一种请求被拒绝的原因是推送请求的格式不对包括 URL 格式不正确和 HTTP 头不正

确两种情况两种情况 MDSBES 服务器都会返回 400 消息表示拒绝URL 格式不正确包括

URL 中不包含ldquoDESTINATIONrdquo参数或者是不包含ldquoPORTrdquo参数等HTTP 头不正确包括

PUSH ID 不唯一等因为推送请求中的 HTTP 头属性都有缺省值所以使用错误的 HTTP 头属

性并不会引发错误MDSBES 服务器只是忽略该参数如 HTTP 头ldquoX-RIM-PUSH-IDrdquo被误写

为ldquoX-RIMPUSHIDrdquoMDSBES 服务器并不会拒绝该请求只是使用ldquoX-RIM-PUSH-IDrdquo的缺

省值就是服务器随机生成一个唯一的 ID 给推送消息

对于 HTTP 协议而言服务器返回 400 代表服务器一般性错误所以返回消息会以异常

形式被捕获以 java 代码为类程序会捕获到以下网络异常

ldquojavaioIOException Server returned HTTP response code 400 for URL

httprdquo

第二种推送请求被拒绝的情况是推送请求指定的收件人不正确就是指应用服务器发送

推送请求时使用了正确的格式不过 DESTINATION 参数指定的收件人并不存在或者是该收件

人并不是 BlackBerry 用户此时 MDSBES 服务器会返回 403 错误读者需要注意 BES 服务

器 37 之前的版本对于这种情况会视作格式错误返回 400 错误

同样以 java 代码为例收件人不正确的情况下程序会捕获到以下网络异常

ldquojavaioIOException Server returned HTTP response code 403 for URL

httprdquo

第三种请求被拒绝的情况是 MDSBES 服务器上的推送队列已满无法再接收更多的推

送请求此时会返回 503 错误表示服务器暂时没有足够的资源处理该请求这种情况下 java

应用程序会捕获到以下异常

ldquojavaioIOException Server returned HTTP response code 503 for URL

httprdquo

这种情况与推送请求是否正确无关取决于 MDSBES 服务器运行情况关于 MDSBES

服务器的推送队列在下一节中我们才进行详细的讨论

如果数据推送请求通过了 MDSBES 服务器的消息检查则 MDSBES 服务器会将该数据

放入推送队列等待发送同时给应用服务器返回成功消息 200

成功消息是以 HTTP 的 ResponseCode 形式返回的开发人员可以通过 HTTP 连接的相应

方法获取在 java 代码中获取 ResponseCode 的函数是ldquo getResponseCoderdquo只有确认

MDSBES 服务器返回的 ResponseCode 是 200才能确定推送数据已经由 MDSBES 接收对

于其它的异常情况都需要由程序进行判断确定如何调整请求继续重试推送过程

有一点非常重要的是 MDSBES 接收该推送数据并不意味着该数据就一定能到达手持设

备端还有很多因素会导致手持设备端的应用接收不到该数据

MDSBES 中的推送队列

MDSBES 服务器有一个队列用于缓存推送请求因为手持设备并不是一定在线

MDSBES 服务器需要先将推送请求保留在服务端在合适的情况下将数据推送到手持设备端

后才从队列中将该请求移除

MDSBES 服务器上的推送队列是 MDS 推送的关键组件它的行为不仅仅影响应用服务

器端是否能够发送新的请求也影响到手持设备端是否能够接收到数据所以对于 MDS 推

送的开发人员必须了解 MDSBES 服务器上的推送队列

MDSBES 服务器上的推送队列有一个队列深度缺省为 1000也就是说 MDSBES 服务

器在同一时间只能缓存 1000 个推送请求该队列的深度可以由管理员在管理端修改

同时 MDSBES 服务器上的推送队列并不是永久保存推送数据它有一个过期时间缺

省为 10 分钟也就是说一个推送请求在 MDSBES 服务器上最多能够被保留 10 分钟同样

这个过期时间可以由管理员在管理端修改

推送队列的深度修改界面如下

推送队列的过期时间的修改界面如下(以毫秒为单位)

对于推送队列的进一步讨论是有关推送数据进入队列后的状态如何变化下面是推送数

据在队列中的状态变化图说明了推送数据在不同情况下的状态变化下图的各个状态中

淡蓝色的状态为起始状态深蓝色的状态为中间状态红色的状态为终止状态处于中间状

态的数据最终必然会转为终止状态的其中一种

当 MDSBES 服务器接收到推送数据后如果之前目标用户没有 5 个处于暂时失败的数

据包服务器会将数据放入等待发送的队列中推送的数据处于ldquo尝试发送rdquo状态处于ldquo尝

试发送rdquo会在很短的时间内占用 1000 个队列位置中的一个不管是成功还是失败它所占

用的队列位置很快被释放

然后 MDSBES 服务器尝试将数据推送到手持设备端如果此时对应的手持设备处于开

机状态网络一切正常则 MDSBES 服务器将数据发送给手持设备推送数据变成ldquo成功rdquo

状态MDSBES 服务器会向应用服务器发送异步的确认消息同时将数据从队列中移除注

意此时 MDSBES 服务器向应用服务器返回的确认消息并不一定是表示成功 200具体是

返回代表成功的 200 还是返回代表失败的 400 还取决于其它因素有关这一点我们在后面的

章节中详细讨论

如果 MDSBES 服务器尝试将数据推送到手持设备的时候手持设备处于关机状态则该

数据进入ldquo暂时失败rdquo状态处于ldquo暂时失败rdquo状态的数据不占用等待队列的队列位置某

一个目标用户最多只能有 5 条数据处于ldquo暂时失败rdquo状态目前没有明确的数据或者是文档

说明一个 MDSBES 服务器总共可以保留多少条ldquo暂时失败rdquo状态的数据

如果某一用户已有 5 条数据处于ldquo暂时失败rdquo状态MDSBES 服务器接收到该用户新

的推送请求时会直接将该数据转入ldquo等待rdquo状态每一条进入ldquo等待rdquo状态的推送数据会占

用推送队列中的一个位置当队列中的 1000 个位置都被占满的时候 MDSBES 服务器就停止

接收新的数据推送无论新的数据是推送给哪个用户

处于ldquo暂时失败rdquo和ldquo等待rdquo状态的数据会在服务器上保留 10 分钟(如上所述这一

时间可以由管理修改)10 分钟后处于这两种状态的数据将被丢弃进入ldquo过期rdquo状态同

时 MDSBES 服务器会向应用服务器发送异步消息这种情况下发送的异步确认消息一定是

代表失败的 400如果在 10 分钟内手持设备重新进入网络手持设备会发送消息到 MDSBES

服务器告知该手持设备已经上线MDSBES 服务器会将处理ldquo暂时失败rdquo和ldquo等待rdquo状态的

数据转成ldquo尝试发送状态rdquo重新发送该数据需要注意的是在 10 分钟的等待过程中

MDSBES 服务器如果没有接收到手持设备重新连接网络的消息则 MDSBES 服务器不会尝

试重新发送ldquo暂时失败rdquo和ldquo等待rdquo状态的数据

根据以上的分析可以对推出以下结果

1 当用户的手持设备在线时对该用户的数据推送会被马上处理该数据只在很短的时间

占用 1000 个等待队列中的一个位置

2 当用户的手持设备不在线时假设该服务器只有一个用户则服务器在 10 分钟内最多

可以接收该用户的 1005 条数据其中 5 条处于ldquo暂时失败rdquo状态1000 条在等待队列

3 如果用户的手持设备在数据发送给 MDSBES 服务器 10 分钟内重新接入网络MDSBES

服务器可以将该数据推送到手持设备端

4 如果 MDSBES 服务器的等待队列未满推送给新的用户的前 5 条数据不占用等待队列

5 如果 MDSBES 服务器的等待队列已满不管是由哪个用户的数据占满的MDSBES 服

务器都会停止接收所有用户的新的数据推送

总结而言在生产环境中保证 MDSBES 服务器正常工作的关键是要保证 MDSBES 服务

器的等待队列不被占满

手持设备端对推送数据的处理

MDSBES 服务器成功接收应用服务器推送的数据后会将数据推送到手持设备端在手持

设备端需要由客户端应用程序将推送的数据提取出来一个直观的猜测是手持设备端同样需

要一个队列保存数据否则客户端应用程序在提取前一条数据时新到达的数据就会直接丢

失需要验证的是这个队列有多深队列行为是否被系统隐藏经过测试证实手持设备端确

实也存在队列队列深度为 10而且可以反映在应用层其中最值得开发人员注意的是这

个队列是一个可溢出队列关于手持设备端推送数据队列的示意图如下

如图所示当客户端提取程序正在读取数据ldquoRrdquo如图中标有 R 的椭圆形所示如果

有更多的数据被推送到手持设备端所推送的数据会被放入设备的队列中一共有 10 条数

据可以被保留在设备队列中如椭圆形 1 到 10 所示此时如果有更多的数据被推送进来

设备队列已满则这些新的数据会溢出如椭圆形 111213 所示同时对于服务器端

而言数据 111213 已经被推送过一次服务器端不会再次尝试推送这些数据在这种

情况下数据 1 到 10 可以继续被客户端提取程序所提取而数据 111213 则丢失了

对于尚未推送到手持设备端的数据如数据 1415假设因为网络中断没有被推送到

手持设备端则仍保留在服务器端当网络恢复后数据 1415 仍可以被正常处理

对于确认消息(图中橙色圆形所示)的处理则更为复杂在异步确认消息一节中再作讨

从这一节的讨论得出的结果是数据到达手持设备端未必就能够被客户端提取程序提取

客户端提取程序有职责尽快提取数据防止推送数据的溢出溢出的数据在客户端无法再被

处理而且服务器端也不会尝试继续推送这些数据

异步确认消息(应用依赖和非应用依赖)

手持设备端在接收到推送数据后会通知 MDSBES 服务器这时 MDSBES 服务器会根据

最初的推送请求向指定的 URL 发送一个确认消息告知应用服务器某一条推送数据的最终

结果这就是异步确认消息异步确认消息以 http 协议发送给应用服务器消息 200 代表

数据推送成功400 代表数据推送失败

异步确认消息是应用服务器判断推送是否成功的有效工具但是很多开发人员因为对

异步确认消息的机制不了解错误地使用了异步确认消息从而导致数据的丢失所以我们

需要详细了解异步确认消息的工作机制

要讨论异步确认消息的工作机制必然涉及到应用依赖与非应用依赖应用依赖的推送

和非应用依赖的推送对应的异步确认消息工作机制是不同的

下面先讨论非应用依赖的场景这也是很多开发商使用的场景

如果推送是非应用依赖的数据是以到达手持设备为成功标志的只要数据到达了手持

设备MDSBES 服务器就认为该数据推送成功会给应用服务器发送异步确认消息 200

如果数据推送时手持设备不在线则 10 分钟内 MDSBES 服务器会在服务器上保留该数

据如果 10 分钟内手持设备重新接入网络则 MDSBES 服务器会尝试发送该数据一旦数

据到达手持设备MDSBES 服务器都会给应用服务器发送异步确认消息 200

如果 10 分钟内手设备没有接入网络则该推送数据过期MDSBES 服务器会丢弃该数

据同时给应用服务器发送异步确认消息 400

可以看出非应用依赖的推送方式并不能保证客户端应用程序接收到数据在非应用依

赖的推送方式下 MDSBES 返回 200 确认消息也不能认为客户端已接收到数据如果数据送

达手持设备的时候客户端应用程序没有启动所推送的数据会在手持设备端丢失而

MDSBES 服务器认为数据已成功送达或者是客户端应用程序已启动但是没有及时读取推

送数据在手持设备端溢出的数据也会丢失MDSBES 服务器同样认为数据已成功送达

所以在非应用依赖的情况下如果接收到 400 确认消息可以确定客户端没有收到数据

如果接收到 200 确认消息只能认为客户端可能收到了数据

从以上的讨论看非应用依赖的推送方式并不适合用于可靠性要求较高的应用对于可

靠性要求较高的应用需要知道客户端应用程序是否接收到了数据应用依赖的推送方式就

是为此而设计的如果推送是应用依赖的数据是以客户端应用程序确认为成功标志的只

要在应用程序确认后MDSBES 服务器才认为数据推送成功这时才给应用服务器发送异步

确认消息 200

有关客户端应用程序如何确认收到数据在ldquo客户端接收关键代码rdquo一节中有描述简

单而言就是客户端应用程序通过调用 MDSPushInputStream 实例的 accept 函数确认数据收

到如果客户端应用程序在接收到数据后没有调用 accept 函数MDSBES 服务器会认为数

据推送失败给应用服务器发送异步确认消息 400另外一点曾经强调过的是accept 函数

调用后只有 InputStream 和 StreamConnection 都关闭了确认消息才被发送给 MDSBES 服

务器在极端的情况下这一点会影响到异步确认消息的结果

下面具体分析各种情景

如果手持设备在线但是客户端应用程序没有启动推送数据到达手持设备端后没有程

序侦听推送端口当然也没有程序会调用 accept 函数此时 MDSBES 服务器会认为数据推

送失败向应用服务器发送异步确认消息 400

如果手持设备在线客户端应用程序在读取推送数据后没有调用 accept 函数直接关闭

连接此时 MDSBES 服务器同样会认为数据推送失败向应用服务器发送异步确认消息 400

在这种情景下即使客户端接收到了数据但应用服务器仍收到失败消息因为作为成功标

志的 accept 函数并没有调用

如果手持设备在线客户端应用程序在读取推送数据后调用了 accept 函数并关闭连

接此时 MDSBES 服务器会认为数据推送成功向应用服务器发送异步确认消息 200

对于手持设备不在线的情况结果与非应用依赖相似10 分钟内如果手持设备不接入

网络的话推送数据会在 MDSBES 服务器上过期MDSBES 服务器会向应用服务器发送异

步确认消息 400

如果 10 分钟内手持设备重新接入网络MDSBES 服务器会尝试发送数据至手持设备端

还是以 accept 函数的调用为推送成功的标志如果数据重新发送成功但是客户端没有调

用 accept 函数则 MDSBES 服务器会向应用服务器发送异步确认消息 200

对于推送到手持设备端但是溢出的数据因为没有程序能够读取该数据并针对该数据调

用 accept 函数MDSBES 服务器会一直等待到 10 分钟后向应用服务器发送异步确认消息

400认为数据过期

一个特殊的情况是客户端应用程序在接收到数据后调用了 accept但是没有关闭

InputStream 和 StreamConnection此时 MDSBES 服务器会认识客户端还在处理数据会继

续等待如果服务器等待时候超过 10 分钟MDSBES 服务器会认为数据推送失败向应用

服务器发送异步确认消息 400如果在 10 分钟内客户端主动关闭 InputStream 和

StreamConnection或者是网络异常中断MDSBES 服务器会认为数据推送成功向应用服

务器发送异步确认消息 200

下面再讨论一个异常情况下的特例

结合以上描述的各种因素在极端的情况下会出现一些特殊的情况如上图所示假设

MDSBES 服务器在推送了数据 R数据 1 到数据 13 后出现网络中断同时客户端应用程序

正在缓慢地读取数据 R此时数据 1 到数据 10 会被保留在手持设备队列中客户端应用程

序在读取数据 R 后可以继续读取数据 1 到数据 10如果客户端应用程序在读取了数据后都

调用了 accept 函数则会产生对应用确认消息如图中的确认消息 R确认消息 1 到确认消

息 10但是因为网络的中断这些确认消息都无法到达 MDSBES 服务器如果网络在 10

钟内没有恢复MDSBES 服务器上所有等待确认消息的记录都会认识是过期的MDSBES

会向应用服务器发送异步确认消息 400此后即使网络恢复确认消息 R确认消息 1 到确

认消息 10 都不再有效

在这种特殊情况下即使客户端应用程序接收到数据并调用了 accept 函数MDSBES

服务器同样认为数据没有推送成功

根据以上的讨论应用依赖的推送同样不能保证客户端应用一定能收到数据但是在

应用依赖的推送方式下 MDSBES 返回 200 确认消息可以确定客户端已接收到数据因为 200

确认消息是由客户端应用程序调用 accept 函数触发的客户端应用程序可以在确认数据收

到后才调用 accept 函数通知应用服务器该数据已成功接收甚至可以在接收到数据后根

据数据内容决定不调用 accept 函数从而通过应用服务器该数据没有被接收

所以在应用依赖的情况下如果接收到 200 确认消息可以确定客户端已经接收到数据

如果接收到 400 确认消息只能认为客户端可能没有收到数据

因此应用依赖的推送方式更适合可靠性要求高的应用如果应用服务器接收到 200

确认消息就可以认为推送成功继续处理其它事务如果应用服务器接收到 400 确认消息则

需要组织数据重发直到接收到 200 确认消息为止这样就可以保证客户端一定能够接收到

需要推送的数据

如何保证数据推送的可靠性

通过以上几个章节的讨论我们知道虽然 MDSBES 服务器提供了很多机制保证数据

推送的可靠性但是都无法完全保证数据被客户端应用程序所接收

对于数据推送的可靠性保证一方面开发人员需要了解各个可能导致故障的因素在应

用编写的时候尽量减少故障出现的可能性对故障点进行规避另一方面开发人员也需要实

现额外的机制进行数据重发在故障点无法规避的情况下通过数据重发消除故障点的影响

应用依赖和非应用依赖的比较和选择

在实现推送应用的时候首要考虑的是有关应用依赖与非应用依赖的选择

应用依赖的推送形式在可靠性方面更优于非应用依赖的推送形式但是应用依赖的推送

形式对 MDSBES 应用服务器的潜在压力较大客户端应用程序的实现也较为复杂

非应用依赖的推送形式优势在于实现简单对 MDSBES 应用服务器形成的压力相对较

非应用依赖的推送形式从某种程度上讲有点类似于广播协议着重点是从服务器向客户

端发送大量数据并不看重客户端的接收情况从数据的应用类型来分非应用依赖的推送

更适合于天气信息最新报价等数据这些数据在客户端应用关闭时被自然丢弃短时间内

的多条数据有丢失影响也不大必竞用户不会关注 11 点零六分的天气和 11 点零七分的天气

有何不同

应用依赖的推送形式则类似于点对点协议不仅从服务器端向客户端发送数据同时也

关注客户端的接收情况应用依赖的推送更适合于 OAERP 这种应用这种应用需要保证

新的文档或者是记录一定能到达客户端应用丢失一条待办信息可能就会导致整个审批流程

的停滞

再从两种推送方式的本质差别看应用依赖并没有额外的机制消除故障点它的优势在

于它的异步确认消息更可信所以在选择了应用依赖的推送方式后进一步还是需要实现

额外的数据重发机制根据 MDSBES 所发送的异步确认消息实施数据重发

应用依赖的推送再加上结合异步确认消息的数据重发机制是保证推送数据可靠性的

最好方法

MDS 接收线程限制的处理

不管使用什么方式的推送应用开发时都需要考虑到 MDS 接收线程的限制不过以

现在的服务器处理能力正常情况下的 HTTP 推送请求可以在几毫秒或者是几十毫内处理完

成少量的 HTTP 推送不足于构成网络阻塞

需要考虑的是应用服务器端的推送程序在推送过程中不要在建立连接后才动态组织数

据这样会浪费 http 连接资源好的建议是预先组织好数据在建立 HTTP 连接后快速写完

数据尽快关闭连接

此外对于应用服务器的推送线程也要加于控制避免在短时间内无限制地启动新的线

程进行连接否则也容易造成连接过多而阻塞的情况

从网络层面来看调整应用服务器与 MDSBES 服务器之间的网络超时设置也是避免这

种问题的一种方法在允许的范围内调大网络超时时间可以让应用服务器的推送线程等待更

长的时间从而让 MDSBES 有足够的时间释放原有的连接

最后无论使用了什么方法调优都需要对应用服务器的推送程序进行异常处理当发

现网络超时异常的时候可以组织数据重发当然数据重发的间隔时间也要考虑不要因为

重发数据再次造成网络阻塞

MDS 推送队列限制的处理

相对而言MDS 推送队列限制造成的推送失败比 MDS 接收队列造成的推送失败更常见

已经发现的一个实际案例是周未的时候用户大量关机导致大量推送数据处于推送队列中

填满了推送队列从而导致 MDSBES 服务器不再响应新的推送请求一旦过期时间到达

有新的队列位置空闲又有大量新的推送数据重新占满推送队列

对于 MDS 推送队列的限制直观的做法是对队列参数进行调整如之前讨论的管理

员可以对队列的深度和过期时间进行调整

然而出于对过期时间的不正确理解管理员倾向于将过期时间调大当过期时间被调

大后推送给关机用户的数据在服务器占用队列的时间就变长导致 MDS 推送队列更容易

被占满

所以如果对过期时间进行调整的话方向是将过期时间调小过期时间越小等待数

据占用队列的时间就越短MDS 推送队列被占满的可能性就越小但是过期时间也不能

过小过期时间过小可能会导致过多的过期数据使应用服务器需要处理更多的数据重发

比如将过期时间调成 1 分钟用户走过一个人行遂道都可能导致数据发送失败

对于队列深度的调整方向是调大队列深度如从缺省的 1000 调整到 2000此时

MDSBES 服务器在同一时间可以保留更多的等待数据减少了队列占满的机会不过调大队

列深度会让 MDSBES 服务器保留更多的数据从而占用更多的服务器内存对服务器产生

更大的压力所以调用队列深度需要根据服务器的运行能力进行调用

从应用服务器设计角度应用 MDS 队列限制的方法是尽量减少等待数据的发送如发

送第一条数据的时候发现数据过期对于该用户的其它数据可以调整发送时间在更长的时

间间隔后才尝试发送

对于 MDS 队列限制应用服务器端的推送应用需要对 MDSBES 服务器返回的 503 错误

进行处理对于出现 503 错的数据组织重发同样重发的间隔也需要考虑不要因为数据

重发占用更多的推送队列也可以根据目标用户进行判断对于可能关机的用户暂时不重发

数据

手持设备端接收队列的处理

手持设备端接收队列并未从文档中明确列出是根据测试结果分析得出的手持设备端

目前也没有发现设置可以调整该队列所以客户端程序设计的时候不应该依赖手持设备端队

开发人员需要意识到的是手持设备端的接收队列是可溢出队列一旦推送数据从接收队

列出溢出客户端程序就无法提取该数据了所以在客户端应用开始过程中要注意加快推送

数据的提取过程在提取过程中不要对数据进行复杂的计算提取完成后尽快读取下一条数

据对于接收数据的处理和计算可以交由另一个线程完成

因为溢出数据对客户端应用而言不可见所以数据溢出时在客户端应用中也不会有异

常开发人员需要通过服务器返回的过期消息才能知道该数据推送失败

对于这一点客户端应用除了尽量避免数据溢出外无法在客户端通过其它机制进行补

应用依赖的客户端

对于应用依赖的客户端应用关键点是决定何时调用 accept 函数同时保证 InputStream

和 StreamConnection 被关闭

如之前讨论的使用应用依赖的推送关键是希望使用 MDSBES 发送给应用服务器的异

步确认消息从而对数据是否到达进行判断所以应用依赖的客户端应用需要保证正确调用

accept 函数

另外如果 InputStream 和 StreamConnection 没有关闭的话accept 函数所发出的确认

消息无法被 MDSBES 服务器收到开发人员在代码中应该主动关闭 InputStream 和

StreamConnection同时在程序发生异常时也需要在 catch 代码段对 InputStream 和

StreamConnection 进行关闭处理

异步确认消息处理

异步确认消息用于判断数据是否被成功推送至客户端应用要使用异步确认消息首先

要保证成功地无遗漏地接收所有从 MDSBES 服务器发送过来的异步确认消息

异步确认消息是一个 HTTP 消息消息比较短接收过程的并发压力不大下面是一个

异步确认消息的样例

Received notificationGET HTTP11

User-Agent RIM MDSCS

Accept

X-RIM-Push-ID pushID2081773768

X-RIM-Push-Destination mmtestbjsearbcn

X-RIM-Push-Status 400

Host localhost7778

其中最关键是的获取属性 X-RIM-Push-Status根据该属性的值判断数据推送是成功

(200)还是失败(400)当然属性 X-RIM-Push-ID 也非常重要用于确定该确认消息是针

对哪条推送数据的

如果应用服务器端通过确认消息发现数据推送失败则需要根据属性 X-RIM-Push-ID 重

新组织数据进行重发

Page 5: BES 服务器推送机制分析 - images.csdn.netimages.csdn.net/upimgs/lee/BBPDF/BESfwtsjzfx.pdf · 前言 数据推送是BlackBerry 应用平台的一大优势,在BlackBerry 应用平台上部署的应用可以

D_APPLICATION_PORTampREQUESTURI=

下面是请求中各参数的说明

参数 描述

BES_HOST BlackBerry Enterprise Server 的服务器名或者是 IP

BES_PORT BlackBerry Enterprise Server 的推送端口一般是 8080

DESTINATION_ADDRESS 用户的邮件地址或者是设备码 (PIN)

HANDHELD_APPLICATION_PORT BlackBerry 手持设备需要帧听的端口

也就是说应用服务器通过 BES_HOST 和 BES_PORT 指定需要连接的 MDSBES 服务器和

需要连接的端口BES 服务器安装后缺省使用 8080 作为推送端口所以推送应用开发人员

一般情况下只需要了解 MDSBES 服务器的服务器名或者是 IP 地址就可以了如果 BES 管理

人员在安装服务器后修改过推送端口则需要告知开发人员使用新指定的端口

推送 URL 中 push后面的部分用于指定数据的接收者和手持设备需要帧听的端口

DESTINATION_ADDRESS 用于指定数据的接收者可以使用该用户的邮件地址或者是该用户手

持设备的 PIN 码HANDHELD_APPLICATION_PORT 用于指定手持设备需要帧听的端口这里

使用的端口只是一个约定要求在手持设备上运行的程序从这一端口中读取数据

除了 URL 参数以后在推送过程还可以使用一些规定好的 HTTP 头指定该推送的属性

HTTP 头参数表如下

HTTP 头 描述

X-RIM-Push-ID 用于指定该消息的 ID该 ID 需要是一个唯一值可以用于

取消推送或者是查看推送的状态

X-RIM-Push-NotifyURL 用于指定确认消息的接收 URL

X-RIM-Push-Reliability-Mode 用于指定推送的可靠性

X-RIM-Push-Deliver-Before 用于指定推送数据的最迟推送时间

X-RIM-Push-Priority 用于指定推送数据的优先级

X-RIM-Push-ID 属性一般建议使用 URL 和编号结合的方式如 123blackberrycom为

了更好地控制数据的推送一般不建议使用随机数作为推送 ID使用统一的 ID 生成方式更

有利于推送数据的取消和状态确定注意如果没有指定该参数的话MDSBES 服务器会自

动生成一个唯一的 ID这样就无法在应用服务器上使用这个 ID 对特定的推送数据进行处理

了此外推送 ID 不能以ppgrimcom 结束

X-RIM-Push-NotifyURL 属性通过 URL 的形式指定了确认消息的接收地址如

httptestingserver7778指定了这一属性后MDSBES 服务器会将推送的确认消息以 HTTP

请求的形式发送到指定的服务器也就是说应用开发者需要开发一个 HTTP 服务器端程序监

听指定服务器的指定端口(上例中则是服务器 testingserver 的 7778 端口)通过这个服务器

端程序获取确认消息在确认消息中会包含 HTTP 头 X-RIM-Push-ID 和 X-RIM-Push-Status通

过 X-RIM-Push-ID 告知监听者是哪条推送数据的确认消息通过 X-RIM-Push-Status 指明该数

据推送的结果200 代表推送成功400 代表推送失败现实环境中对于数据推送是否成功

不能简单地以结果 200 或者是 400 进行判断需要结合很多因素进行判断本文的后续章节

会详细描述

属性 X-RIM-Push-Reliability-Mode 用于指定推送的可靠性值可以是 TRANSPORT 或者是

APPLICATION另外有一个值 APPLICATIONPREFERRED 涉及更复杂的场景这里不做描述如

果指定可靠性为 TRANSPORT 则以数据到达手持设备准本文称之为非应用依赖如果指定

可靠性为 APPLICATION 则以数据被手持设备端应用接收为准本文称之为应用依赖对于不

同可靠性设置本文的后续章节会有详细描述总体而言非应用依赖的推送更适合于广播

性质的不要求应用一定收到数据的场景而应用依赖更适合于点对点的要求应用一定要收到

数据的场景

属性 X-RIM-Push-Deliver-Before 用于指定数据推送的最后时间如果在指定时间前该数

据仍无法被成功推送到手持设备端则该数据会被视作过期而被 MDSBES 服务器丢弃

属性 X-RIM-Push-Priority 用于指定推送数据的优先级指定推送数据的优先级并不能加

快数据的推送指定优先级的结果是推送数据到达手持设备端的行为不同可以选择的值有

none (缺省)low medium 和 high如果指定优先级为 lowmedium 或者是 high则用

户只是接收到数据如果指定优先级为 high则用户在接收到数据后还会看到提示对话框

数据推送关键代码

理论上讲应用服务器的数据推送代码可以用任何可以提供 HTTP 支持的语言编写需

要实现的不过是通过 HTTP 协议往指定的服务器和端口发送 POST 消息

下面以 java 代码为例讲解数据推送的关键代码部分本例的代码片段为一个 Java

Application 的部分代码该 Java Application 在服务器上以 J2SE 应用的形式运行启动一个

线程发送 HTTP 的 POST 请求以下代码为该线程的 run 函数

public void run()

Random random = new Random()

String pushId = pushID + randomnextInt()

String data = What need to be pushed

try

URL _pushURL = new URL(http testingBESabccom 8080

pushDESTINATION=user1abccomampPORT=55100ampREQUESTURI=)

HttpURLConnection conn = (HttpURLConnection) _pushURLopenConnection()

connsetDoInput(true)

connsetDoOutput(true)

connsetRequestMethod(POST)

connsetRequestProperty(X-RIM-PUSH-ID pushId)

connsetRequestProperty(X-RIM-Push-NotifyURL

httptestingserver7778)

connsetRequestProperty(X-RIM-Push-Reliability-Mode

APPLICATION)

OutputStream out = conngetOutputStream()

outwrite(datagetBytes())

outclose()

InputStream ins = conngetInputStream()

int contentLength = conngetContentLength()

int responseCode = conngetResponseCode()

Systemoutprintln(Response Code is + responseCode)

if (contentLength gt 0)

byte[] someArray = new byte[contentLength]

DataInputStream dins = new DataInputStream(ins)

dinsreadFully(someArray)

Systemoutprintln(new String(someArray))

else

Systemoutprintln(Content legth of response is 0)

conndisconnect()

catch (IOException e)

Systemerrprintln(Exception + e)

在以上代码中先通过 URL 类建立一个 HTTP 连接定义URL 类的构造函数中第一个参

数为协议类型这里使用ldquohttprdquo第二个参数为服务器名第三个参数为请求的端口号

第二个参数和第三个参数对应了数据推送请求中的 BES_HOST 和 BES_PORT 属性URL 类的

构造函数中第四个参数为相对 URL推送属性 DESTINATION 和 PORT 都放在相对 URL 中代

码如下

URL _pushURL = new URL(http testingBESabccom 8080

pushDESTINATION=user1abccomampPORT=55100ampREQUESTURI=)

建立了 URL 类的实例后可以通过该实例的 openConnection 函数打开连接返回的是

一个 HttpURLConnection 实例

HttpURLConnection conn = (HttpURLConnection) _pushURLopenConnection()

打开 http 连接后可以通过连接实例的 setRequestProperty 函数设置 http 头推送请求中

的 http 头属性在这里设置如

connsetRequestProperty(X-RIM-PUSH-ID pushId)

其中一个关键的 http 头属性是 X-RIM-Push-Reliability-Mode设置该属性为ldquoTRANSPORTrdquo

表示该推送是非应用依赖的推送设置该属性为ldquoAPPLICATIONrdquo表示该推送是应用依赖的

推送两者的具体区别在后面的章节会有详细的讨论以下代码设置该属性为

ldquoAPPLICATIONrdquo

connsetRequestProperty(X-RIM-Push-Reliability-Mode

APPLICATION)

特别要注意如果设置推送类型为应用依赖对于服务器的设置和手持设备有特定的要

求一方面是服务器端必须是 40 以上同时必须设置允许对所使用的端口进行应用依赖的

推送一方面是要手持设备的 ROM 版本也是 40 以上同时服务器必须知道手持设备能否

支持应用依赖的推送对于服务器的端口设置设置界面如下可以由 BES 管理员进行设置

如果是使用开发环境的 MDS-CS 模拟器模拟应用依赖推送的话需要修改开发环境的

MDSconfig目录中的 rimpublicproperty文件去除 pushapplicationreliableports一行的注释

同时加上需要推送的端口如ldquopushapplicationreliableports=10055100rdquo

对于服务器端是否知道手持设备端的版本需要通过手持设备端的操作完成只要手持

设备端的浏览器通过 BES 访问过网络服务器就会记录该手持设备是否支持应用依赖的推

送如果只是测试的话在测试前通过手持设备端的浏览器打开任何一个网站就可以了如

果是生产环境则需要考虑客户端应用在第一次启动的时候主动访问一次网络以保证客户

端都可以正常接收应用依赖的推送

如果以上设置没有完成的话对 MDSBES 服务器发送应用依赖的推送时会被拒绝服

务器端会出现ldquoThe specified delivery method is not possiblerdquo的错误应用服务器的推送程

序则会捕获以下异常

ldquojavaioIOException Server returned HTTP response code 400 for

URL httpapprdquo

设置属性后就需要获取 http连接的 OutputStream将需要推送的数据写入 OutputStream

中写完后关闭连接

需要注意的是 MDSBES 服务器是否接收该推送请求是通过 http 返回码实现的所以推

送应用中需要获取到 http 连接的返回码通过返回码判断推送是否成功代码如下

int responseCode = conngetResponseCode()

客户端接收关键代码

客户端数据接收程序运行在 BlackBerry 手持设备上必须通过 java 语言实现需要调用

BlackBerry 设备端的 API

客户端接收程序的主体就是启动一个线程开启 httpltportgt连接(其中 port 是客户

端需要侦听的端口号)然后从该连接中获取 InputStream从 InputStream 中获取服务器所

推送的数据

下面是侦听线程的 run 方法为保持程序的完整代码中使用到的类属性并没有修改

从该函数中看到的本函数没有定义的变量都是该类的类属性有关完整代码请参考

BlackBerry 开发环境所提供的标准样例

public void run()

StreamConnection stream = null

InputStream input = null

MDSPushInputStream pushInputStream=null

while (_stop)

try

synchronized(this)

_notify =

(StreamConnectionNotifier)Connectoropen(http55100)

while (_stop)

stream = _notifyacceptAndOpen()

try

input = streamopenInputStream()

pushInputStream= new

MDSPushInputStream((HttpServerConnection)stream input)

DataBuffer db = new DataBuffer()

byte[] data = new byte[256]

int chunk = 0

while ( -1 = (chunk = inputread(data)) )

dbwrite(data 0 chunk)

pushInputStreamaccept()

inputclose()

streamclose()

data = dbgetArray()

catch (IOException e1)

Systemerrprintln(e1toString())

if ( input = null )

try

inputclose()

catch (IOException e2)

if ( stream = null )

try

streamclose()

catch (IOException e2)

_notifyclose()

_notify = null

catch (IOException ioe)

if ( _notify = null )

try

_notifyclose()

_notify = null

catch ( IOException e )

以上代码中建立端口侦听的代码如下

_notify = (StreamConnectionNotifier)Connectoropen(http55100)

其中 URLldquohttp55100rdquo是用于侦听推送的特定格式55100 为需要侦听的端口两

个线程不能同时打开同一个端口的侦听所以在这里使用了 synchronized 对线程并发进行了

控制

建立侦听后可以通过函数 acceptAndOpen 开始侦听当线程调用该方法开始侦听的时候

线程会阻塞在这里直到有推送消息到达代码如下

stream = _notifyacceptAndOpen()

侦听之后的代码为获取连接的 InputStream从 InputStream中获取服务器端推送的数据

读取完数据后关闭 InputStream 和 StreamConnection然后重新调用 acceptAdnOpen 继续侦

听端口准备接收新的推送数据

开发人员如果希望使用应用依赖的推送在客户端必须将 InputStream 转换成

MDSPushInputStream同时在接收完数据后调用 MDSPushInputStream 的 accept 函数造知服

务器该数据已收到这也是应用依赖和非应用依赖在代码实现层面的最主要区别代码如下

pushInputStream= new

MDSPushInputStream((HttpServerConnection)stream input)

helliphellip

pushInputStreamaccept()

从良好的编程习惯来讲在连接使用后需要主动关闭所使用的连接和资源如

InputStreamStreamConnectionStreamConnectionNotifier 等所以在样例代码中也可以看

到该样例在异常处理阶段都对这些资源进行了关闭操作在关闭这些连接和资源的时候对

StreamConnectionNotifier 需要特别注意有可能在 StreamConnectionNotifier 中会保留有未

读取的推送数据需要调用 acceptAdnOpen 函数将保留的数据读取出来直到 acceptAdnOpen

处于阻塞状态

另外如果使用了应用依赖的推送客户端程序调用 MDSPushInputStream 的 accept 函

数后确认消息不会马上发出而是在对应用 InputStream 和 StreamConnection 关闭后才发出

所以对于应用依赖的推送保证 InputStream 和 StreamConnection 的关闭非常之重要

异步确认消息接收代码

异步消息的接收代码运行在服务器端在推送请求的 X-RIM-Push-NotifyURL 属性所指定

的服务器上侦听指定端口和应用服务器端的数据推送代码一样异步确认消息的接收程序

可以通过任何支持 HTTP 的语言编写主要的工作是侦听指定端口从指定端口获取 MDSBES

发送回来的确认消息根据 HTTP 协议获取相关属性并返回相关信息

下面以 java 代码为例对关键代码进行说明这段代码一个 java Application 的一部分是

侦听线程的 run 函数

public void run()

try

Systemoutprintln(Waiting for notification on port + 7778

+ )

while (true)

ServerSocket serverSocket = new ServerSocket(7778)

serverSocketsetSoTimeout(120000)

try

Socket clientSocket = serverSocketaccept()

InputStream input = clientSocketgetInputStream()

StringBuffer str = new StringBuffer()

int byteRead = inputread()

while ((byteRead = -1) ampamp (inputavailable() gt 0))

strappend((char) byteRead)

byteRead = inputread()

Systemoutprintln(strtoString())

OutputStream output = clientSocketgetOutputStream()

String response = HTTP10 200 OK

outputwrite(responsegetBytes())

outputflush()

outputclose()

inputclose()

clientSocketclose()

catch (SocketTimeoutException ste)

Systemoutprintln(Notification connection timeout

Restarting)

serverSocketclose()

catch (Exception exception)

exceptionprintStackTrace()

异步确认消息的接收代码比较简单基本上就是打开一个 Socket 侦听指定的端口本

例中是 7778然后不断地从该端口读取数据打开 Socket 侦听的代码如下

ServerSocket serverSocket = new ServerSocket(7778)

在异步消息接收过程中需要注意的是接收完 MDSBES 服务器的确认消息后应该给

MDSBES 服务器返回一个 200 的成功消息否则 MDSBES 服务器会认为发送确认消息失败

可能重次几次后再放弃这样异步消息接收程序有可能重复收到同一条确认消息返回成功

消息的代码如下

OutputStream output = clientSocketgetOutputStream()

String response = HTTP10 200 OK

outputwrite(responsegetBytes())

outputflush()

outputclose()

推送细节分析

了解基本的 MDS 推送知识是不足于编写一个可靠的数据推送程序的要保证数据推送

的可靠性必须了解整个推送过程的每一个细节本章节先从整体上描述了整个数据推送的

调用时序然后从不同的关键点详细讲解数据推送的细节

MDS 推送的整体时序

如上图所示数据推送主要是应用服务器MDS 服务器网络服务手机端几个组件

之间的交互在网络服务这一块我们可以假定这一层是稳定的不做考虑对于网络无法提

供服务等情况我们可以通过手持设备端关闭无线电或者是将手持设备放入无线屏蔽区等方

式模拟所以我们要考虑是应用服务器MDSBES 服务器手持设备这三部分

数据推送由应用服务器发生向 MDSBES 服务器发出 HTTP Post 请求第一个需要考虑

的是该 HTTP Post 请求是否能到达 MDSBES 服务器这一点在ldquo推送请求是否到达 MDSBES

服务器rdquo小节中有详细讨论

MDSBES 服务器在接收到 HTTP Post 请求后会对请求的格式进行判断如果格式错误

如指定目标不存在等则 MDSBES 服务器会返回 400 错误所以第二点要考虑是的如何保

证所有到达 MDSBES 服务器的请求都是合法的有关这一点在ldquo推送请求检查rdquo小节中有

详细描述

MDSBES 服务器发现推送数据格式正确以后还需要判断服务器上的队列是否有足够的

空间保存该数据如果没有发现空闲队列则丢弃该数据并向应用服务器返回 503 错误因此

第三点要考虑的是什么情况会导致服务器推送队列占满如何尽量避免这种情况有关这一

点在ldquoMDSBES 中的推送队列rdquo中有详细描述

MDSBES 服务器将数据推送到客户端以后由客户端程序将数据提取交由相应的应用

逻辑进行处理然后客户端应用在提取数据的时候并不一定能及时地将所有推送到的数据

获取所以第四点要考虑的是手持设备端是否也存在队列如何保证客户端程序将所有数据

都提取出来有关这一点在ldquo手持设备端对消息的处理rdquo小节中有详细描述

手持设备在接收推送数据后会通知 MDSBES 服务器MDSBES 服务器则向应用服务器

发送异步的确认消息通过返回数值 200 或者是 400 通知应用服务器推送数据是推送成功还

是推送失败异步确认消息对于应用开发者有特别的意义通过它可以确定数据是否到达客

户端从而决定是否再次推送数据然后很多开发商都发现异步确认消息的返回的ldquo200rdquo

和ldquo400rdquo并不能准备地反映数据的接收情况所以如何使用异步确认消息在不同的场

景下异步消息会包含什么不同值对于应用开发者而言非常重要有关这一点在小节ldquo异步

确认消息rdquo有详细描述

推送请求是否到达 MDSBES 服务器

有关推送请求是否到达 MDSBES 服务器 很多开发人员都会忽略认为推送请求是否

到达 MDSBES 服务器是由代码是否编写正确决定的一定会在代码测试阶段体现也来也

必须在代码编写阶段解决这个问题然而在生产环境确实有一些因素会导致应用服务器发送

的推送请求无法到达 MDSBES 服务器从而导致推送数据丢失的问题

推送请求无法到达 MDSBES 服务器的一个可能的直接原因是指定的服务器名不正确或

者是指定的端口号不正确原因可能是 BES 管理员修改了 BES 服务器的服务器名IP 地址或

者是推送监听端口

这种问题比较容易发现一方面是因为所有的 BlackBerry 用户此时都无法接收到推送数

据另一方面也因为应用服务器端会报网络连接的异常以 java 代码为例当指定的服务

器名或者是端口号不正确的情况下应用服务器端的推送程序会报以下异常

javanetConnectException Connection refused

或者是报域名无法解析等网络连接级别的异常

发现这种情况只需要检查代码中指定的服务器名和端口号是否和生产环境相同就可以

解决问题

推送请求无法到达 MDSBES 服务器的一个潜在可能性是推送请求的并发数量太大导致

MDSBES 服务器无法及时接收所有请求

这种问题比较难于发现因为部分用户仍然可以收到推送数据只有部分用户的部分数

据丢失了同时这种问题在功能性测试场景下不容易发生只有在大压力测试或者是存在

大量用户的生产环境中才容易出现

产生这种问题的根源是 MDSBES 服务器的并发接收限制MDS 服务器其实是运行在

Tomcat 服务器上一个服务Tomcat 服务器作为应用容器处理了低层的网络请求而 Tomcat

服务器在底层有并发网络请求的限制当网络连接达到服务器限制时新的推送连接会处理

等待状态如果在网络超时前仍无法建立连接则会出现网络超时异常

javanetConnectException Connection timed out connect

根据测试的结果不同版本的 BES 服务器在低层并发网络请求的数量上有差异以 200

个并发限制为例就是说同一时间点只能有 200 个连接可以向 MDSBES 服务器发送推送请

求第 201 个请求以后有所有请求都将进入等待状态如果在 TCPIP 超时限制到达之前

MDSBES 服务器仍无法释放前 200 个请求则等待状态的请求会因为网络超时而中断

当应用服务器采用多线程机制处理推送请求的情况下有可能出现以上情况如果因为业

务原因在短时间内出现大量等待推送的数据应用服务器则有可能启动多于 200 个的线程发

送推送请求如果需要推送的数据太大或者是推送线程需要动态对数据进行计算就有可

能导致线程所启动的连接在短时间内没有关闭当多于 200 个线程处于这种状态的时候

MDSBES 服务器就无法继续接收推送请求了这种问题的一个现象就是应用服务器端报网络

超时异常或者是连接被拒绝的异常

总的而言要保证数据推送的成功第一步需要保证推送数据到达 MDSBES 服务器

发现推送数据没有到达 MDSBES 服务器的情况时应该对应用服务器的日志进行检查进一

步确定问题的原因

推送请求检查

在 MDSBES 服务器接收到推送请求后MDSBES 服务器会对请求进行检查只有符合

推送格式的请求才被 MDSBES 服务器所接收当 MDSBES 服务器接收推送时会返回值为

200 的 HTTP 答复告知应用服务器已经接收该请求当 MDSBES 服务器拒绝推送请求时会

根据拒绝的原因返回相应的错误码可能返回的错误码包括 400403503 等下面对不同的

情况进行描述

第一种请求被拒绝的原因是推送请求的格式不对包括 URL 格式不正确和 HTTP 头不正

确两种情况两种情况 MDSBES 服务器都会返回 400 消息表示拒绝URL 格式不正确包括

URL 中不包含ldquoDESTINATIONrdquo参数或者是不包含ldquoPORTrdquo参数等HTTP 头不正确包括

PUSH ID 不唯一等因为推送请求中的 HTTP 头属性都有缺省值所以使用错误的 HTTP 头属

性并不会引发错误MDSBES 服务器只是忽略该参数如 HTTP 头ldquoX-RIM-PUSH-IDrdquo被误写

为ldquoX-RIMPUSHIDrdquoMDSBES 服务器并不会拒绝该请求只是使用ldquoX-RIM-PUSH-IDrdquo的缺

省值就是服务器随机生成一个唯一的 ID 给推送消息

对于 HTTP 协议而言服务器返回 400 代表服务器一般性错误所以返回消息会以异常

形式被捕获以 java 代码为类程序会捕获到以下网络异常

ldquojavaioIOException Server returned HTTP response code 400 for URL

httprdquo

第二种推送请求被拒绝的情况是推送请求指定的收件人不正确就是指应用服务器发送

推送请求时使用了正确的格式不过 DESTINATION 参数指定的收件人并不存在或者是该收件

人并不是 BlackBerry 用户此时 MDSBES 服务器会返回 403 错误读者需要注意 BES 服务

器 37 之前的版本对于这种情况会视作格式错误返回 400 错误

同样以 java 代码为例收件人不正确的情况下程序会捕获到以下网络异常

ldquojavaioIOException Server returned HTTP response code 403 for URL

httprdquo

第三种请求被拒绝的情况是 MDSBES 服务器上的推送队列已满无法再接收更多的推

送请求此时会返回 503 错误表示服务器暂时没有足够的资源处理该请求这种情况下 java

应用程序会捕获到以下异常

ldquojavaioIOException Server returned HTTP response code 503 for URL

httprdquo

这种情况与推送请求是否正确无关取决于 MDSBES 服务器运行情况关于 MDSBES

服务器的推送队列在下一节中我们才进行详细的讨论

如果数据推送请求通过了 MDSBES 服务器的消息检查则 MDSBES 服务器会将该数据

放入推送队列等待发送同时给应用服务器返回成功消息 200

成功消息是以 HTTP 的 ResponseCode 形式返回的开发人员可以通过 HTTP 连接的相应

方法获取在 java 代码中获取 ResponseCode 的函数是ldquo getResponseCoderdquo只有确认

MDSBES 服务器返回的 ResponseCode 是 200才能确定推送数据已经由 MDSBES 接收对

于其它的异常情况都需要由程序进行判断确定如何调整请求继续重试推送过程

有一点非常重要的是 MDSBES 接收该推送数据并不意味着该数据就一定能到达手持设

备端还有很多因素会导致手持设备端的应用接收不到该数据

MDSBES 中的推送队列

MDSBES 服务器有一个队列用于缓存推送请求因为手持设备并不是一定在线

MDSBES 服务器需要先将推送请求保留在服务端在合适的情况下将数据推送到手持设备端

后才从队列中将该请求移除

MDSBES 服务器上的推送队列是 MDS 推送的关键组件它的行为不仅仅影响应用服务

器端是否能够发送新的请求也影响到手持设备端是否能够接收到数据所以对于 MDS 推

送的开发人员必须了解 MDSBES 服务器上的推送队列

MDSBES 服务器上的推送队列有一个队列深度缺省为 1000也就是说 MDSBES 服务

器在同一时间只能缓存 1000 个推送请求该队列的深度可以由管理员在管理端修改

同时 MDSBES 服务器上的推送队列并不是永久保存推送数据它有一个过期时间缺

省为 10 分钟也就是说一个推送请求在 MDSBES 服务器上最多能够被保留 10 分钟同样

这个过期时间可以由管理员在管理端修改

推送队列的深度修改界面如下

推送队列的过期时间的修改界面如下(以毫秒为单位)

对于推送队列的进一步讨论是有关推送数据进入队列后的状态如何变化下面是推送数

据在队列中的状态变化图说明了推送数据在不同情况下的状态变化下图的各个状态中

淡蓝色的状态为起始状态深蓝色的状态为中间状态红色的状态为终止状态处于中间状

态的数据最终必然会转为终止状态的其中一种

当 MDSBES 服务器接收到推送数据后如果之前目标用户没有 5 个处于暂时失败的数

据包服务器会将数据放入等待发送的队列中推送的数据处于ldquo尝试发送rdquo状态处于ldquo尝

试发送rdquo会在很短的时间内占用 1000 个队列位置中的一个不管是成功还是失败它所占

用的队列位置很快被释放

然后 MDSBES 服务器尝试将数据推送到手持设备端如果此时对应的手持设备处于开

机状态网络一切正常则 MDSBES 服务器将数据发送给手持设备推送数据变成ldquo成功rdquo

状态MDSBES 服务器会向应用服务器发送异步的确认消息同时将数据从队列中移除注

意此时 MDSBES 服务器向应用服务器返回的确认消息并不一定是表示成功 200具体是

返回代表成功的 200 还是返回代表失败的 400 还取决于其它因素有关这一点我们在后面的

章节中详细讨论

如果 MDSBES 服务器尝试将数据推送到手持设备的时候手持设备处于关机状态则该

数据进入ldquo暂时失败rdquo状态处于ldquo暂时失败rdquo状态的数据不占用等待队列的队列位置某

一个目标用户最多只能有 5 条数据处于ldquo暂时失败rdquo状态目前没有明确的数据或者是文档

说明一个 MDSBES 服务器总共可以保留多少条ldquo暂时失败rdquo状态的数据

如果某一用户已有 5 条数据处于ldquo暂时失败rdquo状态MDSBES 服务器接收到该用户新

的推送请求时会直接将该数据转入ldquo等待rdquo状态每一条进入ldquo等待rdquo状态的推送数据会占

用推送队列中的一个位置当队列中的 1000 个位置都被占满的时候 MDSBES 服务器就停止

接收新的数据推送无论新的数据是推送给哪个用户

处于ldquo暂时失败rdquo和ldquo等待rdquo状态的数据会在服务器上保留 10 分钟(如上所述这一

时间可以由管理修改)10 分钟后处于这两种状态的数据将被丢弃进入ldquo过期rdquo状态同

时 MDSBES 服务器会向应用服务器发送异步消息这种情况下发送的异步确认消息一定是

代表失败的 400如果在 10 分钟内手持设备重新进入网络手持设备会发送消息到 MDSBES

服务器告知该手持设备已经上线MDSBES 服务器会将处理ldquo暂时失败rdquo和ldquo等待rdquo状态的

数据转成ldquo尝试发送状态rdquo重新发送该数据需要注意的是在 10 分钟的等待过程中

MDSBES 服务器如果没有接收到手持设备重新连接网络的消息则 MDSBES 服务器不会尝

试重新发送ldquo暂时失败rdquo和ldquo等待rdquo状态的数据

根据以上的分析可以对推出以下结果

1 当用户的手持设备在线时对该用户的数据推送会被马上处理该数据只在很短的时间

占用 1000 个等待队列中的一个位置

2 当用户的手持设备不在线时假设该服务器只有一个用户则服务器在 10 分钟内最多

可以接收该用户的 1005 条数据其中 5 条处于ldquo暂时失败rdquo状态1000 条在等待队列

3 如果用户的手持设备在数据发送给 MDSBES 服务器 10 分钟内重新接入网络MDSBES

服务器可以将该数据推送到手持设备端

4 如果 MDSBES 服务器的等待队列未满推送给新的用户的前 5 条数据不占用等待队列

5 如果 MDSBES 服务器的等待队列已满不管是由哪个用户的数据占满的MDSBES 服

务器都会停止接收所有用户的新的数据推送

总结而言在生产环境中保证 MDSBES 服务器正常工作的关键是要保证 MDSBES 服务

器的等待队列不被占满

手持设备端对推送数据的处理

MDSBES 服务器成功接收应用服务器推送的数据后会将数据推送到手持设备端在手持

设备端需要由客户端应用程序将推送的数据提取出来一个直观的猜测是手持设备端同样需

要一个队列保存数据否则客户端应用程序在提取前一条数据时新到达的数据就会直接丢

失需要验证的是这个队列有多深队列行为是否被系统隐藏经过测试证实手持设备端确

实也存在队列队列深度为 10而且可以反映在应用层其中最值得开发人员注意的是这

个队列是一个可溢出队列关于手持设备端推送数据队列的示意图如下

如图所示当客户端提取程序正在读取数据ldquoRrdquo如图中标有 R 的椭圆形所示如果

有更多的数据被推送到手持设备端所推送的数据会被放入设备的队列中一共有 10 条数

据可以被保留在设备队列中如椭圆形 1 到 10 所示此时如果有更多的数据被推送进来

设备队列已满则这些新的数据会溢出如椭圆形 111213 所示同时对于服务器端

而言数据 111213 已经被推送过一次服务器端不会再次尝试推送这些数据在这种

情况下数据 1 到 10 可以继续被客户端提取程序所提取而数据 111213 则丢失了

对于尚未推送到手持设备端的数据如数据 1415假设因为网络中断没有被推送到

手持设备端则仍保留在服务器端当网络恢复后数据 1415 仍可以被正常处理

对于确认消息(图中橙色圆形所示)的处理则更为复杂在异步确认消息一节中再作讨

从这一节的讨论得出的结果是数据到达手持设备端未必就能够被客户端提取程序提取

客户端提取程序有职责尽快提取数据防止推送数据的溢出溢出的数据在客户端无法再被

处理而且服务器端也不会尝试继续推送这些数据

异步确认消息(应用依赖和非应用依赖)

手持设备端在接收到推送数据后会通知 MDSBES 服务器这时 MDSBES 服务器会根据

最初的推送请求向指定的 URL 发送一个确认消息告知应用服务器某一条推送数据的最终

结果这就是异步确认消息异步确认消息以 http 协议发送给应用服务器消息 200 代表

数据推送成功400 代表数据推送失败

异步确认消息是应用服务器判断推送是否成功的有效工具但是很多开发人员因为对

异步确认消息的机制不了解错误地使用了异步确认消息从而导致数据的丢失所以我们

需要详细了解异步确认消息的工作机制

要讨论异步确认消息的工作机制必然涉及到应用依赖与非应用依赖应用依赖的推送

和非应用依赖的推送对应的异步确认消息工作机制是不同的

下面先讨论非应用依赖的场景这也是很多开发商使用的场景

如果推送是非应用依赖的数据是以到达手持设备为成功标志的只要数据到达了手持

设备MDSBES 服务器就认为该数据推送成功会给应用服务器发送异步确认消息 200

如果数据推送时手持设备不在线则 10 分钟内 MDSBES 服务器会在服务器上保留该数

据如果 10 分钟内手持设备重新接入网络则 MDSBES 服务器会尝试发送该数据一旦数

据到达手持设备MDSBES 服务器都会给应用服务器发送异步确认消息 200

如果 10 分钟内手设备没有接入网络则该推送数据过期MDSBES 服务器会丢弃该数

据同时给应用服务器发送异步确认消息 400

可以看出非应用依赖的推送方式并不能保证客户端应用程序接收到数据在非应用依

赖的推送方式下 MDSBES 返回 200 确认消息也不能认为客户端已接收到数据如果数据送

达手持设备的时候客户端应用程序没有启动所推送的数据会在手持设备端丢失而

MDSBES 服务器认为数据已成功送达或者是客户端应用程序已启动但是没有及时读取推

送数据在手持设备端溢出的数据也会丢失MDSBES 服务器同样认为数据已成功送达

所以在非应用依赖的情况下如果接收到 400 确认消息可以确定客户端没有收到数据

如果接收到 200 确认消息只能认为客户端可能收到了数据

从以上的讨论看非应用依赖的推送方式并不适合用于可靠性要求较高的应用对于可

靠性要求较高的应用需要知道客户端应用程序是否接收到了数据应用依赖的推送方式就

是为此而设计的如果推送是应用依赖的数据是以客户端应用程序确认为成功标志的只

要在应用程序确认后MDSBES 服务器才认为数据推送成功这时才给应用服务器发送异步

确认消息 200

有关客户端应用程序如何确认收到数据在ldquo客户端接收关键代码rdquo一节中有描述简

单而言就是客户端应用程序通过调用 MDSPushInputStream 实例的 accept 函数确认数据收

到如果客户端应用程序在接收到数据后没有调用 accept 函数MDSBES 服务器会认为数

据推送失败给应用服务器发送异步确认消息 400另外一点曾经强调过的是accept 函数

调用后只有 InputStream 和 StreamConnection 都关闭了确认消息才被发送给 MDSBES 服

务器在极端的情况下这一点会影响到异步确认消息的结果

下面具体分析各种情景

如果手持设备在线但是客户端应用程序没有启动推送数据到达手持设备端后没有程

序侦听推送端口当然也没有程序会调用 accept 函数此时 MDSBES 服务器会认为数据推

送失败向应用服务器发送异步确认消息 400

如果手持设备在线客户端应用程序在读取推送数据后没有调用 accept 函数直接关闭

连接此时 MDSBES 服务器同样会认为数据推送失败向应用服务器发送异步确认消息 400

在这种情景下即使客户端接收到了数据但应用服务器仍收到失败消息因为作为成功标

志的 accept 函数并没有调用

如果手持设备在线客户端应用程序在读取推送数据后调用了 accept 函数并关闭连

接此时 MDSBES 服务器会认为数据推送成功向应用服务器发送异步确认消息 200

对于手持设备不在线的情况结果与非应用依赖相似10 分钟内如果手持设备不接入

网络的话推送数据会在 MDSBES 服务器上过期MDSBES 服务器会向应用服务器发送异

步确认消息 400

如果 10 分钟内手持设备重新接入网络MDSBES 服务器会尝试发送数据至手持设备端

还是以 accept 函数的调用为推送成功的标志如果数据重新发送成功但是客户端没有调

用 accept 函数则 MDSBES 服务器会向应用服务器发送异步确认消息 200

对于推送到手持设备端但是溢出的数据因为没有程序能够读取该数据并针对该数据调

用 accept 函数MDSBES 服务器会一直等待到 10 分钟后向应用服务器发送异步确认消息

400认为数据过期

一个特殊的情况是客户端应用程序在接收到数据后调用了 accept但是没有关闭

InputStream 和 StreamConnection此时 MDSBES 服务器会认识客户端还在处理数据会继

续等待如果服务器等待时候超过 10 分钟MDSBES 服务器会认为数据推送失败向应用

服务器发送异步确认消息 400如果在 10 分钟内客户端主动关闭 InputStream 和

StreamConnection或者是网络异常中断MDSBES 服务器会认为数据推送成功向应用服

务器发送异步确认消息 200

下面再讨论一个异常情况下的特例

结合以上描述的各种因素在极端的情况下会出现一些特殊的情况如上图所示假设

MDSBES 服务器在推送了数据 R数据 1 到数据 13 后出现网络中断同时客户端应用程序

正在缓慢地读取数据 R此时数据 1 到数据 10 会被保留在手持设备队列中客户端应用程

序在读取数据 R 后可以继续读取数据 1 到数据 10如果客户端应用程序在读取了数据后都

调用了 accept 函数则会产生对应用确认消息如图中的确认消息 R确认消息 1 到确认消

息 10但是因为网络的中断这些确认消息都无法到达 MDSBES 服务器如果网络在 10

钟内没有恢复MDSBES 服务器上所有等待确认消息的记录都会认识是过期的MDSBES

会向应用服务器发送异步确认消息 400此后即使网络恢复确认消息 R确认消息 1 到确

认消息 10 都不再有效

在这种特殊情况下即使客户端应用程序接收到数据并调用了 accept 函数MDSBES

服务器同样认为数据没有推送成功

根据以上的讨论应用依赖的推送同样不能保证客户端应用一定能收到数据但是在

应用依赖的推送方式下 MDSBES 返回 200 确认消息可以确定客户端已接收到数据因为 200

确认消息是由客户端应用程序调用 accept 函数触发的客户端应用程序可以在确认数据收

到后才调用 accept 函数通知应用服务器该数据已成功接收甚至可以在接收到数据后根

据数据内容决定不调用 accept 函数从而通过应用服务器该数据没有被接收

所以在应用依赖的情况下如果接收到 200 确认消息可以确定客户端已经接收到数据

如果接收到 400 确认消息只能认为客户端可能没有收到数据

因此应用依赖的推送方式更适合可靠性要求高的应用如果应用服务器接收到 200

确认消息就可以认为推送成功继续处理其它事务如果应用服务器接收到 400 确认消息则

需要组织数据重发直到接收到 200 确认消息为止这样就可以保证客户端一定能够接收到

需要推送的数据

如何保证数据推送的可靠性

通过以上几个章节的讨论我们知道虽然 MDSBES 服务器提供了很多机制保证数据

推送的可靠性但是都无法完全保证数据被客户端应用程序所接收

对于数据推送的可靠性保证一方面开发人员需要了解各个可能导致故障的因素在应

用编写的时候尽量减少故障出现的可能性对故障点进行规避另一方面开发人员也需要实

现额外的机制进行数据重发在故障点无法规避的情况下通过数据重发消除故障点的影响

应用依赖和非应用依赖的比较和选择

在实现推送应用的时候首要考虑的是有关应用依赖与非应用依赖的选择

应用依赖的推送形式在可靠性方面更优于非应用依赖的推送形式但是应用依赖的推送

形式对 MDSBES 应用服务器的潜在压力较大客户端应用程序的实现也较为复杂

非应用依赖的推送形式优势在于实现简单对 MDSBES 应用服务器形成的压力相对较

非应用依赖的推送形式从某种程度上讲有点类似于广播协议着重点是从服务器向客户

端发送大量数据并不看重客户端的接收情况从数据的应用类型来分非应用依赖的推送

更适合于天气信息最新报价等数据这些数据在客户端应用关闭时被自然丢弃短时间内

的多条数据有丢失影响也不大必竞用户不会关注 11 点零六分的天气和 11 点零七分的天气

有何不同

应用依赖的推送形式则类似于点对点协议不仅从服务器端向客户端发送数据同时也

关注客户端的接收情况应用依赖的推送更适合于 OAERP 这种应用这种应用需要保证

新的文档或者是记录一定能到达客户端应用丢失一条待办信息可能就会导致整个审批流程

的停滞

再从两种推送方式的本质差别看应用依赖并没有额外的机制消除故障点它的优势在

于它的异步确认消息更可信所以在选择了应用依赖的推送方式后进一步还是需要实现

额外的数据重发机制根据 MDSBES 所发送的异步确认消息实施数据重发

应用依赖的推送再加上结合异步确认消息的数据重发机制是保证推送数据可靠性的

最好方法

MDS 接收线程限制的处理

不管使用什么方式的推送应用开发时都需要考虑到 MDS 接收线程的限制不过以

现在的服务器处理能力正常情况下的 HTTP 推送请求可以在几毫秒或者是几十毫内处理完

成少量的 HTTP 推送不足于构成网络阻塞

需要考虑的是应用服务器端的推送程序在推送过程中不要在建立连接后才动态组织数

据这样会浪费 http 连接资源好的建议是预先组织好数据在建立 HTTP 连接后快速写完

数据尽快关闭连接

此外对于应用服务器的推送线程也要加于控制避免在短时间内无限制地启动新的线

程进行连接否则也容易造成连接过多而阻塞的情况

从网络层面来看调整应用服务器与 MDSBES 服务器之间的网络超时设置也是避免这

种问题的一种方法在允许的范围内调大网络超时时间可以让应用服务器的推送线程等待更

长的时间从而让 MDSBES 有足够的时间释放原有的连接

最后无论使用了什么方法调优都需要对应用服务器的推送程序进行异常处理当发

现网络超时异常的时候可以组织数据重发当然数据重发的间隔时间也要考虑不要因为

重发数据再次造成网络阻塞

MDS 推送队列限制的处理

相对而言MDS 推送队列限制造成的推送失败比 MDS 接收队列造成的推送失败更常见

已经发现的一个实际案例是周未的时候用户大量关机导致大量推送数据处于推送队列中

填满了推送队列从而导致 MDSBES 服务器不再响应新的推送请求一旦过期时间到达

有新的队列位置空闲又有大量新的推送数据重新占满推送队列

对于 MDS 推送队列的限制直观的做法是对队列参数进行调整如之前讨论的管理

员可以对队列的深度和过期时间进行调整

然而出于对过期时间的不正确理解管理员倾向于将过期时间调大当过期时间被调

大后推送给关机用户的数据在服务器占用队列的时间就变长导致 MDS 推送队列更容易

被占满

所以如果对过期时间进行调整的话方向是将过期时间调小过期时间越小等待数

据占用队列的时间就越短MDS 推送队列被占满的可能性就越小但是过期时间也不能

过小过期时间过小可能会导致过多的过期数据使应用服务器需要处理更多的数据重发

比如将过期时间调成 1 分钟用户走过一个人行遂道都可能导致数据发送失败

对于队列深度的调整方向是调大队列深度如从缺省的 1000 调整到 2000此时

MDSBES 服务器在同一时间可以保留更多的等待数据减少了队列占满的机会不过调大队

列深度会让 MDSBES 服务器保留更多的数据从而占用更多的服务器内存对服务器产生

更大的压力所以调用队列深度需要根据服务器的运行能力进行调用

从应用服务器设计角度应用 MDS 队列限制的方法是尽量减少等待数据的发送如发

送第一条数据的时候发现数据过期对于该用户的其它数据可以调整发送时间在更长的时

间间隔后才尝试发送

对于 MDS 队列限制应用服务器端的推送应用需要对 MDSBES 服务器返回的 503 错误

进行处理对于出现 503 错的数据组织重发同样重发的间隔也需要考虑不要因为数据

重发占用更多的推送队列也可以根据目标用户进行判断对于可能关机的用户暂时不重发

数据

手持设备端接收队列的处理

手持设备端接收队列并未从文档中明确列出是根据测试结果分析得出的手持设备端

目前也没有发现设置可以调整该队列所以客户端程序设计的时候不应该依赖手持设备端队

开发人员需要意识到的是手持设备端的接收队列是可溢出队列一旦推送数据从接收队

列出溢出客户端程序就无法提取该数据了所以在客户端应用开始过程中要注意加快推送

数据的提取过程在提取过程中不要对数据进行复杂的计算提取完成后尽快读取下一条数

据对于接收数据的处理和计算可以交由另一个线程完成

因为溢出数据对客户端应用而言不可见所以数据溢出时在客户端应用中也不会有异

常开发人员需要通过服务器返回的过期消息才能知道该数据推送失败

对于这一点客户端应用除了尽量避免数据溢出外无法在客户端通过其它机制进行补

应用依赖的客户端

对于应用依赖的客户端应用关键点是决定何时调用 accept 函数同时保证 InputStream

和 StreamConnection 被关闭

如之前讨论的使用应用依赖的推送关键是希望使用 MDSBES 发送给应用服务器的异

步确认消息从而对数据是否到达进行判断所以应用依赖的客户端应用需要保证正确调用

accept 函数

另外如果 InputStream 和 StreamConnection 没有关闭的话accept 函数所发出的确认

消息无法被 MDSBES 服务器收到开发人员在代码中应该主动关闭 InputStream 和

StreamConnection同时在程序发生异常时也需要在 catch 代码段对 InputStream 和

StreamConnection 进行关闭处理

异步确认消息处理

异步确认消息用于判断数据是否被成功推送至客户端应用要使用异步确认消息首先

要保证成功地无遗漏地接收所有从 MDSBES 服务器发送过来的异步确认消息

异步确认消息是一个 HTTP 消息消息比较短接收过程的并发压力不大下面是一个

异步确认消息的样例

Received notificationGET HTTP11

User-Agent RIM MDSCS

Accept

X-RIM-Push-ID pushID2081773768

X-RIM-Push-Destination mmtestbjsearbcn

X-RIM-Push-Status 400

Host localhost7778

其中最关键是的获取属性 X-RIM-Push-Status根据该属性的值判断数据推送是成功

(200)还是失败(400)当然属性 X-RIM-Push-ID 也非常重要用于确定该确认消息是针

对哪条推送数据的

如果应用服务器端通过确认消息发现数据推送失败则需要根据属性 X-RIM-Push-ID 重

新组织数据进行重发

Page 6: BES 服务器推送机制分析 - images.csdn.netimages.csdn.net/upimgs/lee/BBPDF/BESfwtsjzfx.pdf · 前言 数据推送是BlackBerry 应用平台的一大优势,在BlackBerry 应用平台上部署的应用可以

不能简单地以结果 200 或者是 400 进行判断需要结合很多因素进行判断本文的后续章节

会详细描述

属性 X-RIM-Push-Reliability-Mode 用于指定推送的可靠性值可以是 TRANSPORT 或者是

APPLICATION另外有一个值 APPLICATIONPREFERRED 涉及更复杂的场景这里不做描述如

果指定可靠性为 TRANSPORT 则以数据到达手持设备准本文称之为非应用依赖如果指定

可靠性为 APPLICATION 则以数据被手持设备端应用接收为准本文称之为应用依赖对于不

同可靠性设置本文的后续章节会有详细描述总体而言非应用依赖的推送更适合于广播

性质的不要求应用一定收到数据的场景而应用依赖更适合于点对点的要求应用一定要收到

数据的场景

属性 X-RIM-Push-Deliver-Before 用于指定数据推送的最后时间如果在指定时间前该数

据仍无法被成功推送到手持设备端则该数据会被视作过期而被 MDSBES 服务器丢弃

属性 X-RIM-Push-Priority 用于指定推送数据的优先级指定推送数据的优先级并不能加

快数据的推送指定优先级的结果是推送数据到达手持设备端的行为不同可以选择的值有

none (缺省)low medium 和 high如果指定优先级为 lowmedium 或者是 high则用

户只是接收到数据如果指定优先级为 high则用户在接收到数据后还会看到提示对话框

数据推送关键代码

理论上讲应用服务器的数据推送代码可以用任何可以提供 HTTP 支持的语言编写需

要实现的不过是通过 HTTP 协议往指定的服务器和端口发送 POST 消息

下面以 java 代码为例讲解数据推送的关键代码部分本例的代码片段为一个 Java

Application 的部分代码该 Java Application 在服务器上以 J2SE 应用的形式运行启动一个

线程发送 HTTP 的 POST 请求以下代码为该线程的 run 函数

public void run()

Random random = new Random()

String pushId = pushID + randomnextInt()

String data = What need to be pushed

try

URL _pushURL = new URL(http testingBESabccom 8080

pushDESTINATION=user1abccomampPORT=55100ampREQUESTURI=)

HttpURLConnection conn = (HttpURLConnection) _pushURLopenConnection()

connsetDoInput(true)

connsetDoOutput(true)

connsetRequestMethod(POST)

connsetRequestProperty(X-RIM-PUSH-ID pushId)

connsetRequestProperty(X-RIM-Push-NotifyURL

httptestingserver7778)

connsetRequestProperty(X-RIM-Push-Reliability-Mode

APPLICATION)

OutputStream out = conngetOutputStream()

outwrite(datagetBytes())

outclose()

InputStream ins = conngetInputStream()

int contentLength = conngetContentLength()

int responseCode = conngetResponseCode()

Systemoutprintln(Response Code is + responseCode)

if (contentLength gt 0)

byte[] someArray = new byte[contentLength]

DataInputStream dins = new DataInputStream(ins)

dinsreadFully(someArray)

Systemoutprintln(new String(someArray))

else

Systemoutprintln(Content legth of response is 0)

conndisconnect()

catch (IOException e)

Systemerrprintln(Exception + e)

在以上代码中先通过 URL 类建立一个 HTTP 连接定义URL 类的构造函数中第一个参

数为协议类型这里使用ldquohttprdquo第二个参数为服务器名第三个参数为请求的端口号

第二个参数和第三个参数对应了数据推送请求中的 BES_HOST 和 BES_PORT 属性URL 类的

构造函数中第四个参数为相对 URL推送属性 DESTINATION 和 PORT 都放在相对 URL 中代

码如下

URL _pushURL = new URL(http testingBESabccom 8080

pushDESTINATION=user1abccomampPORT=55100ampREQUESTURI=)

建立了 URL 类的实例后可以通过该实例的 openConnection 函数打开连接返回的是

一个 HttpURLConnection 实例

HttpURLConnection conn = (HttpURLConnection) _pushURLopenConnection()

打开 http 连接后可以通过连接实例的 setRequestProperty 函数设置 http 头推送请求中

的 http 头属性在这里设置如

connsetRequestProperty(X-RIM-PUSH-ID pushId)

其中一个关键的 http 头属性是 X-RIM-Push-Reliability-Mode设置该属性为ldquoTRANSPORTrdquo

表示该推送是非应用依赖的推送设置该属性为ldquoAPPLICATIONrdquo表示该推送是应用依赖的

推送两者的具体区别在后面的章节会有详细的讨论以下代码设置该属性为

ldquoAPPLICATIONrdquo

connsetRequestProperty(X-RIM-Push-Reliability-Mode

APPLICATION)

特别要注意如果设置推送类型为应用依赖对于服务器的设置和手持设备有特定的要

求一方面是服务器端必须是 40 以上同时必须设置允许对所使用的端口进行应用依赖的

推送一方面是要手持设备的 ROM 版本也是 40 以上同时服务器必须知道手持设备能否

支持应用依赖的推送对于服务器的端口设置设置界面如下可以由 BES 管理员进行设置

如果是使用开发环境的 MDS-CS 模拟器模拟应用依赖推送的话需要修改开发环境的

MDSconfig目录中的 rimpublicproperty文件去除 pushapplicationreliableports一行的注释

同时加上需要推送的端口如ldquopushapplicationreliableports=10055100rdquo

对于服务器端是否知道手持设备端的版本需要通过手持设备端的操作完成只要手持

设备端的浏览器通过 BES 访问过网络服务器就会记录该手持设备是否支持应用依赖的推

送如果只是测试的话在测试前通过手持设备端的浏览器打开任何一个网站就可以了如

果是生产环境则需要考虑客户端应用在第一次启动的时候主动访问一次网络以保证客户

端都可以正常接收应用依赖的推送

如果以上设置没有完成的话对 MDSBES 服务器发送应用依赖的推送时会被拒绝服

务器端会出现ldquoThe specified delivery method is not possiblerdquo的错误应用服务器的推送程

序则会捕获以下异常

ldquojavaioIOException Server returned HTTP response code 400 for

URL httpapprdquo

设置属性后就需要获取 http连接的 OutputStream将需要推送的数据写入 OutputStream

中写完后关闭连接

需要注意的是 MDSBES 服务器是否接收该推送请求是通过 http 返回码实现的所以推

送应用中需要获取到 http 连接的返回码通过返回码判断推送是否成功代码如下

int responseCode = conngetResponseCode()

客户端接收关键代码

客户端数据接收程序运行在 BlackBerry 手持设备上必须通过 java 语言实现需要调用

BlackBerry 设备端的 API

客户端接收程序的主体就是启动一个线程开启 httpltportgt连接(其中 port 是客户

端需要侦听的端口号)然后从该连接中获取 InputStream从 InputStream 中获取服务器所

推送的数据

下面是侦听线程的 run 方法为保持程序的完整代码中使用到的类属性并没有修改

从该函数中看到的本函数没有定义的变量都是该类的类属性有关完整代码请参考

BlackBerry 开发环境所提供的标准样例

public void run()

StreamConnection stream = null

InputStream input = null

MDSPushInputStream pushInputStream=null

while (_stop)

try

synchronized(this)

_notify =

(StreamConnectionNotifier)Connectoropen(http55100)

while (_stop)

stream = _notifyacceptAndOpen()

try

input = streamopenInputStream()

pushInputStream= new

MDSPushInputStream((HttpServerConnection)stream input)

DataBuffer db = new DataBuffer()

byte[] data = new byte[256]

int chunk = 0

while ( -1 = (chunk = inputread(data)) )

dbwrite(data 0 chunk)

pushInputStreamaccept()

inputclose()

streamclose()

data = dbgetArray()

catch (IOException e1)

Systemerrprintln(e1toString())

if ( input = null )

try

inputclose()

catch (IOException e2)

if ( stream = null )

try

streamclose()

catch (IOException e2)

_notifyclose()

_notify = null

catch (IOException ioe)

if ( _notify = null )

try

_notifyclose()

_notify = null

catch ( IOException e )

以上代码中建立端口侦听的代码如下

_notify = (StreamConnectionNotifier)Connectoropen(http55100)

其中 URLldquohttp55100rdquo是用于侦听推送的特定格式55100 为需要侦听的端口两

个线程不能同时打开同一个端口的侦听所以在这里使用了 synchronized 对线程并发进行了

控制

建立侦听后可以通过函数 acceptAndOpen 开始侦听当线程调用该方法开始侦听的时候

线程会阻塞在这里直到有推送消息到达代码如下

stream = _notifyacceptAndOpen()

侦听之后的代码为获取连接的 InputStream从 InputStream中获取服务器端推送的数据

读取完数据后关闭 InputStream 和 StreamConnection然后重新调用 acceptAdnOpen 继续侦

听端口准备接收新的推送数据

开发人员如果希望使用应用依赖的推送在客户端必须将 InputStream 转换成

MDSPushInputStream同时在接收完数据后调用 MDSPushInputStream 的 accept 函数造知服

务器该数据已收到这也是应用依赖和非应用依赖在代码实现层面的最主要区别代码如下

pushInputStream= new

MDSPushInputStream((HttpServerConnection)stream input)

helliphellip

pushInputStreamaccept()

从良好的编程习惯来讲在连接使用后需要主动关闭所使用的连接和资源如

InputStreamStreamConnectionStreamConnectionNotifier 等所以在样例代码中也可以看

到该样例在异常处理阶段都对这些资源进行了关闭操作在关闭这些连接和资源的时候对

StreamConnectionNotifier 需要特别注意有可能在 StreamConnectionNotifier 中会保留有未

读取的推送数据需要调用 acceptAdnOpen 函数将保留的数据读取出来直到 acceptAdnOpen

处于阻塞状态

另外如果使用了应用依赖的推送客户端程序调用 MDSPushInputStream 的 accept 函

数后确认消息不会马上发出而是在对应用 InputStream 和 StreamConnection 关闭后才发出

所以对于应用依赖的推送保证 InputStream 和 StreamConnection 的关闭非常之重要

异步确认消息接收代码

异步消息的接收代码运行在服务器端在推送请求的 X-RIM-Push-NotifyURL 属性所指定

的服务器上侦听指定端口和应用服务器端的数据推送代码一样异步确认消息的接收程序

可以通过任何支持 HTTP 的语言编写主要的工作是侦听指定端口从指定端口获取 MDSBES

发送回来的确认消息根据 HTTP 协议获取相关属性并返回相关信息

下面以 java 代码为例对关键代码进行说明这段代码一个 java Application 的一部分是

侦听线程的 run 函数

public void run()

try

Systemoutprintln(Waiting for notification on port + 7778

+ )

while (true)

ServerSocket serverSocket = new ServerSocket(7778)

serverSocketsetSoTimeout(120000)

try

Socket clientSocket = serverSocketaccept()

InputStream input = clientSocketgetInputStream()

StringBuffer str = new StringBuffer()

int byteRead = inputread()

while ((byteRead = -1) ampamp (inputavailable() gt 0))

strappend((char) byteRead)

byteRead = inputread()

Systemoutprintln(strtoString())

OutputStream output = clientSocketgetOutputStream()

String response = HTTP10 200 OK

outputwrite(responsegetBytes())

outputflush()

outputclose()

inputclose()

clientSocketclose()

catch (SocketTimeoutException ste)

Systemoutprintln(Notification connection timeout

Restarting)

serverSocketclose()

catch (Exception exception)

exceptionprintStackTrace()

异步确认消息的接收代码比较简单基本上就是打开一个 Socket 侦听指定的端口本

例中是 7778然后不断地从该端口读取数据打开 Socket 侦听的代码如下

ServerSocket serverSocket = new ServerSocket(7778)

在异步消息接收过程中需要注意的是接收完 MDSBES 服务器的确认消息后应该给

MDSBES 服务器返回一个 200 的成功消息否则 MDSBES 服务器会认为发送确认消息失败

可能重次几次后再放弃这样异步消息接收程序有可能重复收到同一条确认消息返回成功

消息的代码如下

OutputStream output = clientSocketgetOutputStream()

String response = HTTP10 200 OK

outputwrite(responsegetBytes())

outputflush()

outputclose()

推送细节分析

了解基本的 MDS 推送知识是不足于编写一个可靠的数据推送程序的要保证数据推送

的可靠性必须了解整个推送过程的每一个细节本章节先从整体上描述了整个数据推送的

调用时序然后从不同的关键点详细讲解数据推送的细节

MDS 推送的整体时序

如上图所示数据推送主要是应用服务器MDS 服务器网络服务手机端几个组件

之间的交互在网络服务这一块我们可以假定这一层是稳定的不做考虑对于网络无法提

供服务等情况我们可以通过手持设备端关闭无线电或者是将手持设备放入无线屏蔽区等方

式模拟所以我们要考虑是应用服务器MDSBES 服务器手持设备这三部分

数据推送由应用服务器发生向 MDSBES 服务器发出 HTTP Post 请求第一个需要考虑

的是该 HTTP Post 请求是否能到达 MDSBES 服务器这一点在ldquo推送请求是否到达 MDSBES

服务器rdquo小节中有详细讨论

MDSBES 服务器在接收到 HTTP Post 请求后会对请求的格式进行判断如果格式错误

如指定目标不存在等则 MDSBES 服务器会返回 400 错误所以第二点要考虑是的如何保

证所有到达 MDSBES 服务器的请求都是合法的有关这一点在ldquo推送请求检查rdquo小节中有

详细描述

MDSBES 服务器发现推送数据格式正确以后还需要判断服务器上的队列是否有足够的

空间保存该数据如果没有发现空闲队列则丢弃该数据并向应用服务器返回 503 错误因此

第三点要考虑的是什么情况会导致服务器推送队列占满如何尽量避免这种情况有关这一

点在ldquoMDSBES 中的推送队列rdquo中有详细描述

MDSBES 服务器将数据推送到客户端以后由客户端程序将数据提取交由相应的应用

逻辑进行处理然后客户端应用在提取数据的时候并不一定能及时地将所有推送到的数据

获取所以第四点要考虑的是手持设备端是否也存在队列如何保证客户端程序将所有数据

都提取出来有关这一点在ldquo手持设备端对消息的处理rdquo小节中有详细描述

手持设备在接收推送数据后会通知 MDSBES 服务器MDSBES 服务器则向应用服务器

发送异步的确认消息通过返回数值 200 或者是 400 通知应用服务器推送数据是推送成功还

是推送失败异步确认消息对于应用开发者有特别的意义通过它可以确定数据是否到达客

户端从而决定是否再次推送数据然后很多开发商都发现异步确认消息的返回的ldquo200rdquo

和ldquo400rdquo并不能准备地反映数据的接收情况所以如何使用异步确认消息在不同的场

景下异步消息会包含什么不同值对于应用开发者而言非常重要有关这一点在小节ldquo异步

确认消息rdquo有详细描述

推送请求是否到达 MDSBES 服务器

有关推送请求是否到达 MDSBES 服务器 很多开发人员都会忽略认为推送请求是否

到达 MDSBES 服务器是由代码是否编写正确决定的一定会在代码测试阶段体现也来也

必须在代码编写阶段解决这个问题然而在生产环境确实有一些因素会导致应用服务器发送

的推送请求无法到达 MDSBES 服务器从而导致推送数据丢失的问题

推送请求无法到达 MDSBES 服务器的一个可能的直接原因是指定的服务器名不正确或

者是指定的端口号不正确原因可能是 BES 管理员修改了 BES 服务器的服务器名IP 地址或

者是推送监听端口

这种问题比较容易发现一方面是因为所有的 BlackBerry 用户此时都无法接收到推送数

据另一方面也因为应用服务器端会报网络连接的异常以 java 代码为例当指定的服务

器名或者是端口号不正确的情况下应用服务器端的推送程序会报以下异常

javanetConnectException Connection refused

或者是报域名无法解析等网络连接级别的异常

发现这种情况只需要检查代码中指定的服务器名和端口号是否和生产环境相同就可以

解决问题

推送请求无法到达 MDSBES 服务器的一个潜在可能性是推送请求的并发数量太大导致

MDSBES 服务器无法及时接收所有请求

这种问题比较难于发现因为部分用户仍然可以收到推送数据只有部分用户的部分数

据丢失了同时这种问题在功能性测试场景下不容易发生只有在大压力测试或者是存在

大量用户的生产环境中才容易出现

产生这种问题的根源是 MDSBES 服务器的并发接收限制MDS 服务器其实是运行在

Tomcat 服务器上一个服务Tomcat 服务器作为应用容器处理了低层的网络请求而 Tomcat

服务器在底层有并发网络请求的限制当网络连接达到服务器限制时新的推送连接会处理

等待状态如果在网络超时前仍无法建立连接则会出现网络超时异常

javanetConnectException Connection timed out connect

根据测试的结果不同版本的 BES 服务器在低层并发网络请求的数量上有差异以 200

个并发限制为例就是说同一时间点只能有 200 个连接可以向 MDSBES 服务器发送推送请

求第 201 个请求以后有所有请求都将进入等待状态如果在 TCPIP 超时限制到达之前

MDSBES 服务器仍无法释放前 200 个请求则等待状态的请求会因为网络超时而中断

当应用服务器采用多线程机制处理推送请求的情况下有可能出现以上情况如果因为业

务原因在短时间内出现大量等待推送的数据应用服务器则有可能启动多于 200 个的线程发

送推送请求如果需要推送的数据太大或者是推送线程需要动态对数据进行计算就有可

能导致线程所启动的连接在短时间内没有关闭当多于 200 个线程处于这种状态的时候

MDSBES 服务器就无法继续接收推送请求了这种问题的一个现象就是应用服务器端报网络

超时异常或者是连接被拒绝的异常

总的而言要保证数据推送的成功第一步需要保证推送数据到达 MDSBES 服务器

发现推送数据没有到达 MDSBES 服务器的情况时应该对应用服务器的日志进行检查进一

步确定问题的原因

推送请求检查

在 MDSBES 服务器接收到推送请求后MDSBES 服务器会对请求进行检查只有符合

推送格式的请求才被 MDSBES 服务器所接收当 MDSBES 服务器接收推送时会返回值为

200 的 HTTP 答复告知应用服务器已经接收该请求当 MDSBES 服务器拒绝推送请求时会

根据拒绝的原因返回相应的错误码可能返回的错误码包括 400403503 等下面对不同的

情况进行描述

第一种请求被拒绝的原因是推送请求的格式不对包括 URL 格式不正确和 HTTP 头不正

确两种情况两种情况 MDSBES 服务器都会返回 400 消息表示拒绝URL 格式不正确包括

URL 中不包含ldquoDESTINATIONrdquo参数或者是不包含ldquoPORTrdquo参数等HTTP 头不正确包括

PUSH ID 不唯一等因为推送请求中的 HTTP 头属性都有缺省值所以使用错误的 HTTP 头属

性并不会引发错误MDSBES 服务器只是忽略该参数如 HTTP 头ldquoX-RIM-PUSH-IDrdquo被误写

为ldquoX-RIMPUSHIDrdquoMDSBES 服务器并不会拒绝该请求只是使用ldquoX-RIM-PUSH-IDrdquo的缺

省值就是服务器随机生成一个唯一的 ID 给推送消息

对于 HTTP 协议而言服务器返回 400 代表服务器一般性错误所以返回消息会以异常

形式被捕获以 java 代码为类程序会捕获到以下网络异常

ldquojavaioIOException Server returned HTTP response code 400 for URL

httprdquo

第二种推送请求被拒绝的情况是推送请求指定的收件人不正确就是指应用服务器发送

推送请求时使用了正确的格式不过 DESTINATION 参数指定的收件人并不存在或者是该收件

人并不是 BlackBerry 用户此时 MDSBES 服务器会返回 403 错误读者需要注意 BES 服务

器 37 之前的版本对于这种情况会视作格式错误返回 400 错误

同样以 java 代码为例收件人不正确的情况下程序会捕获到以下网络异常

ldquojavaioIOException Server returned HTTP response code 403 for URL

httprdquo

第三种请求被拒绝的情况是 MDSBES 服务器上的推送队列已满无法再接收更多的推

送请求此时会返回 503 错误表示服务器暂时没有足够的资源处理该请求这种情况下 java

应用程序会捕获到以下异常

ldquojavaioIOException Server returned HTTP response code 503 for URL

httprdquo

这种情况与推送请求是否正确无关取决于 MDSBES 服务器运行情况关于 MDSBES

服务器的推送队列在下一节中我们才进行详细的讨论

如果数据推送请求通过了 MDSBES 服务器的消息检查则 MDSBES 服务器会将该数据

放入推送队列等待发送同时给应用服务器返回成功消息 200

成功消息是以 HTTP 的 ResponseCode 形式返回的开发人员可以通过 HTTP 连接的相应

方法获取在 java 代码中获取 ResponseCode 的函数是ldquo getResponseCoderdquo只有确认

MDSBES 服务器返回的 ResponseCode 是 200才能确定推送数据已经由 MDSBES 接收对

于其它的异常情况都需要由程序进行判断确定如何调整请求继续重试推送过程

有一点非常重要的是 MDSBES 接收该推送数据并不意味着该数据就一定能到达手持设

备端还有很多因素会导致手持设备端的应用接收不到该数据

MDSBES 中的推送队列

MDSBES 服务器有一个队列用于缓存推送请求因为手持设备并不是一定在线

MDSBES 服务器需要先将推送请求保留在服务端在合适的情况下将数据推送到手持设备端

后才从队列中将该请求移除

MDSBES 服务器上的推送队列是 MDS 推送的关键组件它的行为不仅仅影响应用服务

器端是否能够发送新的请求也影响到手持设备端是否能够接收到数据所以对于 MDS 推

送的开发人员必须了解 MDSBES 服务器上的推送队列

MDSBES 服务器上的推送队列有一个队列深度缺省为 1000也就是说 MDSBES 服务

器在同一时间只能缓存 1000 个推送请求该队列的深度可以由管理员在管理端修改

同时 MDSBES 服务器上的推送队列并不是永久保存推送数据它有一个过期时间缺

省为 10 分钟也就是说一个推送请求在 MDSBES 服务器上最多能够被保留 10 分钟同样

这个过期时间可以由管理员在管理端修改

推送队列的深度修改界面如下

推送队列的过期时间的修改界面如下(以毫秒为单位)

对于推送队列的进一步讨论是有关推送数据进入队列后的状态如何变化下面是推送数

据在队列中的状态变化图说明了推送数据在不同情况下的状态变化下图的各个状态中

淡蓝色的状态为起始状态深蓝色的状态为中间状态红色的状态为终止状态处于中间状

态的数据最终必然会转为终止状态的其中一种

当 MDSBES 服务器接收到推送数据后如果之前目标用户没有 5 个处于暂时失败的数

据包服务器会将数据放入等待发送的队列中推送的数据处于ldquo尝试发送rdquo状态处于ldquo尝

试发送rdquo会在很短的时间内占用 1000 个队列位置中的一个不管是成功还是失败它所占

用的队列位置很快被释放

然后 MDSBES 服务器尝试将数据推送到手持设备端如果此时对应的手持设备处于开

机状态网络一切正常则 MDSBES 服务器将数据发送给手持设备推送数据变成ldquo成功rdquo

状态MDSBES 服务器会向应用服务器发送异步的确认消息同时将数据从队列中移除注

意此时 MDSBES 服务器向应用服务器返回的确认消息并不一定是表示成功 200具体是

返回代表成功的 200 还是返回代表失败的 400 还取决于其它因素有关这一点我们在后面的

章节中详细讨论

如果 MDSBES 服务器尝试将数据推送到手持设备的时候手持设备处于关机状态则该

数据进入ldquo暂时失败rdquo状态处于ldquo暂时失败rdquo状态的数据不占用等待队列的队列位置某

一个目标用户最多只能有 5 条数据处于ldquo暂时失败rdquo状态目前没有明确的数据或者是文档

说明一个 MDSBES 服务器总共可以保留多少条ldquo暂时失败rdquo状态的数据

如果某一用户已有 5 条数据处于ldquo暂时失败rdquo状态MDSBES 服务器接收到该用户新

的推送请求时会直接将该数据转入ldquo等待rdquo状态每一条进入ldquo等待rdquo状态的推送数据会占

用推送队列中的一个位置当队列中的 1000 个位置都被占满的时候 MDSBES 服务器就停止

接收新的数据推送无论新的数据是推送给哪个用户

处于ldquo暂时失败rdquo和ldquo等待rdquo状态的数据会在服务器上保留 10 分钟(如上所述这一

时间可以由管理修改)10 分钟后处于这两种状态的数据将被丢弃进入ldquo过期rdquo状态同

时 MDSBES 服务器会向应用服务器发送异步消息这种情况下发送的异步确认消息一定是

代表失败的 400如果在 10 分钟内手持设备重新进入网络手持设备会发送消息到 MDSBES

服务器告知该手持设备已经上线MDSBES 服务器会将处理ldquo暂时失败rdquo和ldquo等待rdquo状态的

数据转成ldquo尝试发送状态rdquo重新发送该数据需要注意的是在 10 分钟的等待过程中

MDSBES 服务器如果没有接收到手持设备重新连接网络的消息则 MDSBES 服务器不会尝

试重新发送ldquo暂时失败rdquo和ldquo等待rdquo状态的数据

根据以上的分析可以对推出以下结果

1 当用户的手持设备在线时对该用户的数据推送会被马上处理该数据只在很短的时间

占用 1000 个等待队列中的一个位置

2 当用户的手持设备不在线时假设该服务器只有一个用户则服务器在 10 分钟内最多

可以接收该用户的 1005 条数据其中 5 条处于ldquo暂时失败rdquo状态1000 条在等待队列

3 如果用户的手持设备在数据发送给 MDSBES 服务器 10 分钟内重新接入网络MDSBES

服务器可以将该数据推送到手持设备端

4 如果 MDSBES 服务器的等待队列未满推送给新的用户的前 5 条数据不占用等待队列

5 如果 MDSBES 服务器的等待队列已满不管是由哪个用户的数据占满的MDSBES 服

务器都会停止接收所有用户的新的数据推送

总结而言在生产环境中保证 MDSBES 服务器正常工作的关键是要保证 MDSBES 服务

器的等待队列不被占满

手持设备端对推送数据的处理

MDSBES 服务器成功接收应用服务器推送的数据后会将数据推送到手持设备端在手持

设备端需要由客户端应用程序将推送的数据提取出来一个直观的猜测是手持设备端同样需

要一个队列保存数据否则客户端应用程序在提取前一条数据时新到达的数据就会直接丢

失需要验证的是这个队列有多深队列行为是否被系统隐藏经过测试证实手持设备端确

实也存在队列队列深度为 10而且可以反映在应用层其中最值得开发人员注意的是这

个队列是一个可溢出队列关于手持设备端推送数据队列的示意图如下

如图所示当客户端提取程序正在读取数据ldquoRrdquo如图中标有 R 的椭圆形所示如果

有更多的数据被推送到手持设备端所推送的数据会被放入设备的队列中一共有 10 条数

据可以被保留在设备队列中如椭圆形 1 到 10 所示此时如果有更多的数据被推送进来

设备队列已满则这些新的数据会溢出如椭圆形 111213 所示同时对于服务器端

而言数据 111213 已经被推送过一次服务器端不会再次尝试推送这些数据在这种

情况下数据 1 到 10 可以继续被客户端提取程序所提取而数据 111213 则丢失了

对于尚未推送到手持设备端的数据如数据 1415假设因为网络中断没有被推送到

手持设备端则仍保留在服务器端当网络恢复后数据 1415 仍可以被正常处理

对于确认消息(图中橙色圆形所示)的处理则更为复杂在异步确认消息一节中再作讨

从这一节的讨论得出的结果是数据到达手持设备端未必就能够被客户端提取程序提取

客户端提取程序有职责尽快提取数据防止推送数据的溢出溢出的数据在客户端无法再被

处理而且服务器端也不会尝试继续推送这些数据

异步确认消息(应用依赖和非应用依赖)

手持设备端在接收到推送数据后会通知 MDSBES 服务器这时 MDSBES 服务器会根据

最初的推送请求向指定的 URL 发送一个确认消息告知应用服务器某一条推送数据的最终

结果这就是异步确认消息异步确认消息以 http 协议发送给应用服务器消息 200 代表

数据推送成功400 代表数据推送失败

异步确认消息是应用服务器判断推送是否成功的有效工具但是很多开发人员因为对

异步确认消息的机制不了解错误地使用了异步确认消息从而导致数据的丢失所以我们

需要详细了解异步确认消息的工作机制

要讨论异步确认消息的工作机制必然涉及到应用依赖与非应用依赖应用依赖的推送

和非应用依赖的推送对应的异步确认消息工作机制是不同的

下面先讨论非应用依赖的场景这也是很多开发商使用的场景

如果推送是非应用依赖的数据是以到达手持设备为成功标志的只要数据到达了手持

设备MDSBES 服务器就认为该数据推送成功会给应用服务器发送异步确认消息 200

如果数据推送时手持设备不在线则 10 分钟内 MDSBES 服务器会在服务器上保留该数

据如果 10 分钟内手持设备重新接入网络则 MDSBES 服务器会尝试发送该数据一旦数

据到达手持设备MDSBES 服务器都会给应用服务器发送异步确认消息 200

如果 10 分钟内手设备没有接入网络则该推送数据过期MDSBES 服务器会丢弃该数

据同时给应用服务器发送异步确认消息 400

可以看出非应用依赖的推送方式并不能保证客户端应用程序接收到数据在非应用依

赖的推送方式下 MDSBES 返回 200 确认消息也不能认为客户端已接收到数据如果数据送

达手持设备的时候客户端应用程序没有启动所推送的数据会在手持设备端丢失而

MDSBES 服务器认为数据已成功送达或者是客户端应用程序已启动但是没有及时读取推

送数据在手持设备端溢出的数据也会丢失MDSBES 服务器同样认为数据已成功送达

所以在非应用依赖的情况下如果接收到 400 确认消息可以确定客户端没有收到数据

如果接收到 200 确认消息只能认为客户端可能收到了数据

从以上的讨论看非应用依赖的推送方式并不适合用于可靠性要求较高的应用对于可

靠性要求较高的应用需要知道客户端应用程序是否接收到了数据应用依赖的推送方式就

是为此而设计的如果推送是应用依赖的数据是以客户端应用程序确认为成功标志的只

要在应用程序确认后MDSBES 服务器才认为数据推送成功这时才给应用服务器发送异步

确认消息 200

有关客户端应用程序如何确认收到数据在ldquo客户端接收关键代码rdquo一节中有描述简

单而言就是客户端应用程序通过调用 MDSPushInputStream 实例的 accept 函数确认数据收

到如果客户端应用程序在接收到数据后没有调用 accept 函数MDSBES 服务器会认为数

据推送失败给应用服务器发送异步确认消息 400另外一点曾经强调过的是accept 函数

调用后只有 InputStream 和 StreamConnection 都关闭了确认消息才被发送给 MDSBES 服

务器在极端的情况下这一点会影响到异步确认消息的结果

下面具体分析各种情景

如果手持设备在线但是客户端应用程序没有启动推送数据到达手持设备端后没有程

序侦听推送端口当然也没有程序会调用 accept 函数此时 MDSBES 服务器会认为数据推

送失败向应用服务器发送异步确认消息 400

如果手持设备在线客户端应用程序在读取推送数据后没有调用 accept 函数直接关闭

连接此时 MDSBES 服务器同样会认为数据推送失败向应用服务器发送异步确认消息 400

在这种情景下即使客户端接收到了数据但应用服务器仍收到失败消息因为作为成功标

志的 accept 函数并没有调用

如果手持设备在线客户端应用程序在读取推送数据后调用了 accept 函数并关闭连

接此时 MDSBES 服务器会认为数据推送成功向应用服务器发送异步确认消息 200

对于手持设备不在线的情况结果与非应用依赖相似10 分钟内如果手持设备不接入

网络的话推送数据会在 MDSBES 服务器上过期MDSBES 服务器会向应用服务器发送异

步确认消息 400

如果 10 分钟内手持设备重新接入网络MDSBES 服务器会尝试发送数据至手持设备端

还是以 accept 函数的调用为推送成功的标志如果数据重新发送成功但是客户端没有调

用 accept 函数则 MDSBES 服务器会向应用服务器发送异步确认消息 200

对于推送到手持设备端但是溢出的数据因为没有程序能够读取该数据并针对该数据调

用 accept 函数MDSBES 服务器会一直等待到 10 分钟后向应用服务器发送异步确认消息

400认为数据过期

一个特殊的情况是客户端应用程序在接收到数据后调用了 accept但是没有关闭

InputStream 和 StreamConnection此时 MDSBES 服务器会认识客户端还在处理数据会继

续等待如果服务器等待时候超过 10 分钟MDSBES 服务器会认为数据推送失败向应用

服务器发送异步确认消息 400如果在 10 分钟内客户端主动关闭 InputStream 和

StreamConnection或者是网络异常中断MDSBES 服务器会认为数据推送成功向应用服

务器发送异步确认消息 200

下面再讨论一个异常情况下的特例

结合以上描述的各种因素在极端的情况下会出现一些特殊的情况如上图所示假设

MDSBES 服务器在推送了数据 R数据 1 到数据 13 后出现网络中断同时客户端应用程序

正在缓慢地读取数据 R此时数据 1 到数据 10 会被保留在手持设备队列中客户端应用程

序在读取数据 R 后可以继续读取数据 1 到数据 10如果客户端应用程序在读取了数据后都

调用了 accept 函数则会产生对应用确认消息如图中的确认消息 R确认消息 1 到确认消

息 10但是因为网络的中断这些确认消息都无法到达 MDSBES 服务器如果网络在 10

钟内没有恢复MDSBES 服务器上所有等待确认消息的记录都会认识是过期的MDSBES

会向应用服务器发送异步确认消息 400此后即使网络恢复确认消息 R确认消息 1 到确

认消息 10 都不再有效

在这种特殊情况下即使客户端应用程序接收到数据并调用了 accept 函数MDSBES

服务器同样认为数据没有推送成功

根据以上的讨论应用依赖的推送同样不能保证客户端应用一定能收到数据但是在

应用依赖的推送方式下 MDSBES 返回 200 确认消息可以确定客户端已接收到数据因为 200

确认消息是由客户端应用程序调用 accept 函数触发的客户端应用程序可以在确认数据收

到后才调用 accept 函数通知应用服务器该数据已成功接收甚至可以在接收到数据后根

据数据内容决定不调用 accept 函数从而通过应用服务器该数据没有被接收

所以在应用依赖的情况下如果接收到 200 确认消息可以确定客户端已经接收到数据

如果接收到 400 确认消息只能认为客户端可能没有收到数据

因此应用依赖的推送方式更适合可靠性要求高的应用如果应用服务器接收到 200

确认消息就可以认为推送成功继续处理其它事务如果应用服务器接收到 400 确认消息则

需要组织数据重发直到接收到 200 确认消息为止这样就可以保证客户端一定能够接收到

需要推送的数据

如何保证数据推送的可靠性

通过以上几个章节的讨论我们知道虽然 MDSBES 服务器提供了很多机制保证数据

推送的可靠性但是都无法完全保证数据被客户端应用程序所接收

对于数据推送的可靠性保证一方面开发人员需要了解各个可能导致故障的因素在应

用编写的时候尽量减少故障出现的可能性对故障点进行规避另一方面开发人员也需要实

现额外的机制进行数据重发在故障点无法规避的情况下通过数据重发消除故障点的影响

应用依赖和非应用依赖的比较和选择

在实现推送应用的时候首要考虑的是有关应用依赖与非应用依赖的选择

应用依赖的推送形式在可靠性方面更优于非应用依赖的推送形式但是应用依赖的推送

形式对 MDSBES 应用服务器的潜在压力较大客户端应用程序的实现也较为复杂

非应用依赖的推送形式优势在于实现简单对 MDSBES 应用服务器形成的压力相对较

非应用依赖的推送形式从某种程度上讲有点类似于广播协议着重点是从服务器向客户

端发送大量数据并不看重客户端的接收情况从数据的应用类型来分非应用依赖的推送

更适合于天气信息最新报价等数据这些数据在客户端应用关闭时被自然丢弃短时间内

的多条数据有丢失影响也不大必竞用户不会关注 11 点零六分的天气和 11 点零七分的天气

有何不同

应用依赖的推送形式则类似于点对点协议不仅从服务器端向客户端发送数据同时也

关注客户端的接收情况应用依赖的推送更适合于 OAERP 这种应用这种应用需要保证

新的文档或者是记录一定能到达客户端应用丢失一条待办信息可能就会导致整个审批流程

的停滞

再从两种推送方式的本质差别看应用依赖并没有额外的机制消除故障点它的优势在

于它的异步确认消息更可信所以在选择了应用依赖的推送方式后进一步还是需要实现

额外的数据重发机制根据 MDSBES 所发送的异步确认消息实施数据重发

应用依赖的推送再加上结合异步确认消息的数据重发机制是保证推送数据可靠性的

最好方法

MDS 接收线程限制的处理

不管使用什么方式的推送应用开发时都需要考虑到 MDS 接收线程的限制不过以

现在的服务器处理能力正常情况下的 HTTP 推送请求可以在几毫秒或者是几十毫内处理完

成少量的 HTTP 推送不足于构成网络阻塞

需要考虑的是应用服务器端的推送程序在推送过程中不要在建立连接后才动态组织数

据这样会浪费 http 连接资源好的建议是预先组织好数据在建立 HTTP 连接后快速写完

数据尽快关闭连接

此外对于应用服务器的推送线程也要加于控制避免在短时间内无限制地启动新的线

程进行连接否则也容易造成连接过多而阻塞的情况

从网络层面来看调整应用服务器与 MDSBES 服务器之间的网络超时设置也是避免这

种问题的一种方法在允许的范围内调大网络超时时间可以让应用服务器的推送线程等待更

长的时间从而让 MDSBES 有足够的时间释放原有的连接

最后无论使用了什么方法调优都需要对应用服务器的推送程序进行异常处理当发

现网络超时异常的时候可以组织数据重发当然数据重发的间隔时间也要考虑不要因为

重发数据再次造成网络阻塞

MDS 推送队列限制的处理

相对而言MDS 推送队列限制造成的推送失败比 MDS 接收队列造成的推送失败更常见

已经发现的一个实际案例是周未的时候用户大量关机导致大量推送数据处于推送队列中

填满了推送队列从而导致 MDSBES 服务器不再响应新的推送请求一旦过期时间到达

有新的队列位置空闲又有大量新的推送数据重新占满推送队列

对于 MDS 推送队列的限制直观的做法是对队列参数进行调整如之前讨论的管理

员可以对队列的深度和过期时间进行调整

然而出于对过期时间的不正确理解管理员倾向于将过期时间调大当过期时间被调

大后推送给关机用户的数据在服务器占用队列的时间就变长导致 MDS 推送队列更容易

被占满

所以如果对过期时间进行调整的话方向是将过期时间调小过期时间越小等待数

据占用队列的时间就越短MDS 推送队列被占满的可能性就越小但是过期时间也不能

过小过期时间过小可能会导致过多的过期数据使应用服务器需要处理更多的数据重发

比如将过期时间调成 1 分钟用户走过一个人行遂道都可能导致数据发送失败

对于队列深度的调整方向是调大队列深度如从缺省的 1000 调整到 2000此时

MDSBES 服务器在同一时间可以保留更多的等待数据减少了队列占满的机会不过调大队

列深度会让 MDSBES 服务器保留更多的数据从而占用更多的服务器内存对服务器产生

更大的压力所以调用队列深度需要根据服务器的运行能力进行调用

从应用服务器设计角度应用 MDS 队列限制的方法是尽量减少等待数据的发送如发

送第一条数据的时候发现数据过期对于该用户的其它数据可以调整发送时间在更长的时

间间隔后才尝试发送

对于 MDS 队列限制应用服务器端的推送应用需要对 MDSBES 服务器返回的 503 错误

进行处理对于出现 503 错的数据组织重发同样重发的间隔也需要考虑不要因为数据

重发占用更多的推送队列也可以根据目标用户进行判断对于可能关机的用户暂时不重发

数据

手持设备端接收队列的处理

手持设备端接收队列并未从文档中明确列出是根据测试结果分析得出的手持设备端

目前也没有发现设置可以调整该队列所以客户端程序设计的时候不应该依赖手持设备端队

开发人员需要意识到的是手持设备端的接收队列是可溢出队列一旦推送数据从接收队

列出溢出客户端程序就无法提取该数据了所以在客户端应用开始过程中要注意加快推送

数据的提取过程在提取过程中不要对数据进行复杂的计算提取完成后尽快读取下一条数

据对于接收数据的处理和计算可以交由另一个线程完成

因为溢出数据对客户端应用而言不可见所以数据溢出时在客户端应用中也不会有异

常开发人员需要通过服务器返回的过期消息才能知道该数据推送失败

对于这一点客户端应用除了尽量避免数据溢出外无法在客户端通过其它机制进行补

应用依赖的客户端

对于应用依赖的客户端应用关键点是决定何时调用 accept 函数同时保证 InputStream

和 StreamConnection 被关闭

如之前讨论的使用应用依赖的推送关键是希望使用 MDSBES 发送给应用服务器的异

步确认消息从而对数据是否到达进行判断所以应用依赖的客户端应用需要保证正确调用

accept 函数

另外如果 InputStream 和 StreamConnection 没有关闭的话accept 函数所发出的确认

消息无法被 MDSBES 服务器收到开发人员在代码中应该主动关闭 InputStream 和

StreamConnection同时在程序发生异常时也需要在 catch 代码段对 InputStream 和

StreamConnection 进行关闭处理

异步确认消息处理

异步确认消息用于判断数据是否被成功推送至客户端应用要使用异步确认消息首先

要保证成功地无遗漏地接收所有从 MDSBES 服务器发送过来的异步确认消息

异步确认消息是一个 HTTP 消息消息比较短接收过程的并发压力不大下面是一个

异步确认消息的样例

Received notificationGET HTTP11

User-Agent RIM MDSCS

Accept

X-RIM-Push-ID pushID2081773768

X-RIM-Push-Destination mmtestbjsearbcn

X-RIM-Push-Status 400

Host localhost7778

其中最关键是的获取属性 X-RIM-Push-Status根据该属性的值判断数据推送是成功

(200)还是失败(400)当然属性 X-RIM-Push-ID 也非常重要用于确定该确认消息是针

对哪条推送数据的

如果应用服务器端通过确认消息发现数据推送失败则需要根据属性 X-RIM-Push-ID 重

新组织数据进行重发

Page 7: BES 服务器推送机制分析 - images.csdn.netimages.csdn.net/upimgs/lee/BBPDF/BESfwtsjzfx.pdf · 前言 数据推送是BlackBerry 应用平台的一大优势,在BlackBerry 应用平台上部署的应用可以

OutputStream out = conngetOutputStream()

outwrite(datagetBytes())

outclose()

InputStream ins = conngetInputStream()

int contentLength = conngetContentLength()

int responseCode = conngetResponseCode()

Systemoutprintln(Response Code is + responseCode)

if (contentLength gt 0)

byte[] someArray = new byte[contentLength]

DataInputStream dins = new DataInputStream(ins)

dinsreadFully(someArray)

Systemoutprintln(new String(someArray))

else

Systemoutprintln(Content legth of response is 0)

conndisconnect()

catch (IOException e)

Systemerrprintln(Exception + e)

在以上代码中先通过 URL 类建立一个 HTTP 连接定义URL 类的构造函数中第一个参

数为协议类型这里使用ldquohttprdquo第二个参数为服务器名第三个参数为请求的端口号

第二个参数和第三个参数对应了数据推送请求中的 BES_HOST 和 BES_PORT 属性URL 类的

构造函数中第四个参数为相对 URL推送属性 DESTINATION 和 PORT 都放在相对 URL 中代

码如下

URL _pushURL = new URL(http testingBESabccom 8080

pushDESTINATION=user1abccomampPORT=55100ampREQUESTURI=)

建立了 URL 类的实例后可以通过该实例的 openConnection 函数打开连接返回的是

一个 HttpURLConnection 实例

HttpURLConnection conn = (HttpURLConnection) _pushURLopenConnection()

打开 http 连接后可以通过连接实例的 setRequestProperty 函数设置 http 头推送请求中

的 http 头属性在这里设置如

connsetRequestProperty(X-RIM-PUSH-ID pushId)

其中一个关键的 http 头属性是 X-RIM-Push-Reliability-Mode设置该属性为ldquoTRANSPORTrdquo

表示该推送是非应用依赖的推送设置该属性为ldquoAPPLICATIONrdquo表示该推送是应用依赖的

推送两者的具体区别在后面的章节会有详细的讨论以下代码设置该属性为

ldquoAPPLICATIONrdquo

connsetRequestProperty(X-RIM-Push-Reliability-Mode

APPLICATION)

特别要注意如果设置推送类型为应用依赖对于服务器的设置和手持设备有特定的要

求一方面是服务器端必须是 40 以上同时必须设置允许对所使用的端口进行应用依赖的

推送一方面是要手持设备的 ROM 版本也是 40 以上同时服务器必须知道手持设备能否

支持应用依赖的推送对于服务器的端口设置设置界面如下可以由 BES 管理员进行设置

如果是使用开发环境的 MDS-CS 模拟器模拟应用依赖推送的话需要修改开发环境的

MDSconfig目录中的 rimpublicproperty文件去除 pushapplicationreliableports一行的注释

同时加上需要推送的端口如ldquopushapplicationreliableports=10055100rdquo

对于服务器端是否知道手持设备端的版本需要通过手持设备端的操作完成只要手持

设备端的浏览器通过 BES 访问过网络服务器就会记录该手持设备是否支持应用依赖的推

送如果只是测试的话在测试前通过手持设备端的浏览器打开任何一个网站就可以了如

果是生产环境则需要考虑客户端应用在第一次启动的时候主动访问一次网络以保证客户

端都可以正常接收应用依赖的推送

如果以上设置没有完成的话对 MDSBES 服务器发送应用依赖的推送时会被拒绝服

务器端会出现ldquoThe specified delivery method is not possiblerdquo的错误应用服务器的推送程

序则会捕获以下异常

ldquojavaioIOException Server returned HTTP response code 400 for

URL httpapprdquo

设置属性后就需要获取 http连接的 OutputStream将需要推送的数据写入 OutputStream

中写完后关闭连接

需要注意的是 MDSBES 服务器是否接收该推送请求是通过 http 返回码实现的所以推

送应用中需要获取到 http 连接的返回码通过返回码判断推送是否成功代码如下

int responseCode = conngetResponseCode()

客户端接收关键代码

客户端数据接收程序运行在 BlackBerry 手持设备上必须通过 java 语言实现需要调用

BlackBerry 设备端的 API

客户端接收程序的主体就是启动一个线程开启 httpltportgt连接(其中 port 是客户

端需要侦听的端口号)然后从该连接中获取 InputStream从 InputStream 中获取服务器所

推送的数据

下面是侦听线程的 run 方法为保持程序的完整代码中使用到的类属性并没有修改

从该函数中看到的本函数没有定义的变量都是该类的类属性有关完整代码请参考

BlackBerry 开发环境所提供的标准样例

public void run()

StreamConnection stream = null

InputStream input = null

MDSPushInputStream pushInputStream=null

while (_stop)

try

synchronized(this)

_notify =

(StreamConnectionNotifier)Connectoropen(http55100)

while (_stop)

stream = _notifyacceptAndOpen()

try

input = streamopenInputStream()

pushInputStream= new

MDSPushInputStream((HttpServerConnection)stream input)

DataBuffer db = new DataBuffer()

byte[] data = new byte[256]

int chunk = 0

while ( -1 = (chunk = inputread(data)) )

dbwrite(data 0 chunk)

pushInputStreamaccept()

inputclose()

streamclose()

data = dbgetArray()

catch (IOException e1)

Systemerrprintln(e1toString())

if ( input = null )

try

inputclose()

catch (IOException e2)

if ( stream = null )

try

streamclose()

catch (IOException e2)

_notifyclose()

_notify = null

catch (IOException ioe)

if ( _notify = null )

try

_notifyclose()

_notify = null

catch ( IOException e )

以上代码中建立端口侦听的代码如下

_notify = (StreamConnectionNotifier)Connectoropen(http55100)

其中 URLldquohttp55100rdquo是用于侦听推送的特定格式55100 为需要侦听的端口两

个线程不能同时打开同一个端口的侦听所以在这里使用了 synchronized 对线程并发进行了

控制

建立侦听后可以通过函数 acceptAndOpen 开始侦听当线程调用该方法开始侦听的时候

线程会阻塞在这里直到有推送消息到达代码如下

stream = _notifyacceptAndOpen()

侦听之后的代码为获取连接的 InputStream从 InputStream中获取服务器端推送的数据

读取完数据后关闭 InputStream 和 StreamConnection然后重新调用 acceptAdnOpen 继续侦

听端口准备接收新的推送数据

开发人员如果希望使用应用依赖的推送在客户端必须将 InputStream 转换成

MDSPushInputStream同时在接收完数据后调用 MDSPushInputStream 的 accept 函数造知服

务器该数据已收到这也是应用依赖和非应用依赖在代码实现层面的最主要区别代码如下

pushInputStream= new

MDSPushInputStream((HttpServerConnection)stream input)

helliphellip

pushInputStreamaccept()

从良好的编程习惯来讲在连接使用后需要主动关闭所使用的连接和资源如

InputStreamStreamConnectionStreamConnectionNotifier 等所以在样例代码中也可以看

到该样例在异常处理阶段都对这些资源进行了关闭操作在关闭这些连接和资源的时候对

StreamConnectionNotifier 需要特别注意有可能在 StreamConnectionNotifier 中会保留有未

读取的推送数据需要调用 acceptAdnOpen 函数将保留的数据读取出来直到 acceptAdnOpen

处于阻塞状态

另外如果使用了应用依赖的推送客户端程序调用 MDSPushInputStream 的 accept 函

数后确认消息不会马上发出而是在对应用 InputStream 和 StreamConnection 关闭后才发出

所以对于应用依赖的推送保证 InputStream 和 StreamConnection 的关闭非常之重要

异步确认消息接收代码

异步消息的接收代码运行在服务器端在推送请求的 X-RIM-Push-NotifyURL 属性所指定

的服务器上侦听指定端口和应用服务器端的数据推送代码一样异步确认消息的接收程序

可以通过任何支持 HTTP 的语言编写主要的工作是侦听指定端口从指定端口获取 MDSBES

发送回来的确认消息根据 HTTP 协议获取相关属性并返回相关信息

下面以 java 代码为例对关键代码进行说明这段代码一个 java Application 的一部分是

侦听线程的 run 函数

public void run()

try

Systemoutprintln(Waiting for notification on port + 7778

+ )

while (true)

ServerSocket serverSocket = new ServerSocket(7778)

serverSocketsetSoTimeout(120000)

try

Socket clientSocket = serverSocketaccept()

InputStream input = clientSocketgetInputStream()

StringBuffer str = new StringBuffer()

int byteRead = inputread()

while ((byteRead = -1) ampamp (inputavailable() gt 0))

strappend((char) byteRead)

byteRead = inputread()

Systemoutprintln(strtoString())

OutputStream output = clientSocketgetOutputStream()

String response = HTTP10 200 OK

outputwrite(responsegetBytes())

outputflush()

outputclose()

inputclose()

clientSocketclose()

catch (SocketTimeoutException ste)

Systemoutprintln(Notification connection timeout

Restarting)

serverSocketclose()

catch (Exception exception)

exceptionprintStackTrace()

异步确认消息的接收代码比较简单基本上就是打开一个 Socket 侦听指定的端口本

例中是 7778然后不断地从该端口读取数据打开 Socket 侦听的代码如下

ServerSocket serverSocket = new ServerSocket(7778)

在异步消息接收过程中需要注意的是接收完 MDSBES 服务器的确认消息后应该给

MDSBES 服务器返回一个 200 的成功消息否则 MDSBES 服务器会认为发送确认消息失败

可能重次几次后再放弃这样异步消息接收程序有可能重复收到同一条确认消息返回成功

消息的代码如下

OutputStream output = clientSocketgetOutputStream()

String response = HTTP10 200 OK

outputwrite(responsegetBytes())

outputflush()

outputclose()

推送细节分析

了解基本的 MDS 推送知识是不足于编写一个可靠的数据推送程序的要保证数据推送

的可靠性必须了解整个推送过程的每一个细节本章节先从整体上描述了整个数据推送的

调用时序然后从不同的关键点详细讲解数据推送的细节

MDS 推送的整体时序

如上图所示数据推送主要是应用服务器MDS 服务器网络服务手机端几个组件

之间的交互在网络服务这一块我们可以假定这一层是稳定的不做考虑对于网络无法提

供服务等情况我们可以通过手持设备端关闭无线电或者是将手持设备放入无线屏蔽区等方

式模拟所以我们要考虑是应用服务器MDSBES 服务器手持设备这三部分

数据推送由应用服务器发生向 MDSBES 服务器发出 HTTP Post 请求第一个需要考虑

的是该 HTTP Post 请求是否能到达 MDSBES 服务器这一点在ldquo推送请求是否到达 MDSBES

服务器rdquo小节中有详细讨论

MDSBES 服务器在接收到 HTTP Post 请求后会对请求的格式进行判断如果格式错误

如指定目标不存在等则 MDSBES 服务器会返回 400 错误所以第二点要考虑是的如何保

证所有到达 MDSBES 服务器的请求都是合法的有关这一点在ldquo推送请求检查rdquo小节中有

详细描述

MDSBES 服务器发现推送数据格式正确以后还需要判断服务器上的队列是否有足够的

空间保存该数据如果没有发现空闲队列则丢弃该数据并向应用服务器返回 503 错误因此

第三点要考虑的是什么情况会导致服务器推送队列占满如何尽量避免这种情况有关这一

点在ldquoMDSBES 中的推送队列rdquo中有详细描述

MDSBES 服务器将数据推送到客户端以后由客户端程序将数据提取交由相应的应用

逻辑进行处理然后客户端应用在提取数据的时候并不一定能及时地将所有推送到的数据

获取所以第四点要考虑的是手持设备端是否也存在队列如何保证客户端程序将所有数据

都提取出来有关这一点在ldquo手持设备端对消息的处理rdquo小节中有详细描述

手持设备在接收推送数据后会通知 MDSBES 服务器MDSBES 服务器则向应用服务器

发送异步的确认消息通过返回数值 200 或者是 400 通知应用服务器推送数据是推送成功还

是推送失败异步确认消息对于应用开发者有特别的意义通过它可以确定数据是否到达客

户端从而决定是否再次推送数据然后很多开发商都发现异步确认消息的返回的ldquo200rdquo

和ldquo400rdquo并不能准备地反映数据的接收情况所以如何使用异步确认消息在不同的场

景下异步消息会包含什么不同值对于应用开发者而言非常重要有关这一点在小节ldquo异步

确认消息rdquo有详细描述

推送请求是否到达 MDSBES 服务器

有关推送请求是否到达 MDSBES 服务器 很多开发人员都会忽略认为推送请求是否

到达 MDSBES 服务器是由代码是否编写正确决定的一定会在代码测试阶段体现也来也

必须在代码编写阶段解决这个问题然而在生产环境确实有一些因素会导致应用服务器发送

的推送请求无法到达 MDSBES 服务器从而导致推送数据丢失的问题

推送请求无法到达 MDSBES 服务器的一个可能的直接原因是指定的服务器名不正确或

者是指定的端口号不正确原因可能是 BES 管理员修改了 BES 服务器的服务器名IP 地址或

者是推送监听端口

这种问题比较容易发现一方面是因为所有的 BlackBerry 用户此时都无法接收到推送数

据另一方面也因为应用服务器端会报网络连接的异常以 java 代码为例当指定的服务

器名或者是端口号不正确的情况下应用服务器端的推送程序会报以下异常

javanetConnectException Connection refused

或者是报域名无法解析等网络连接级别的异常

发现这种情况只需要检查代码中指定的服务器名和端口号是否和生产环境相同就可以

解决问题

推送请求无法到达 MDSBES 服务器的一个潜在可能性是推送请求的并发数量太大导致

MDSBES 服务器无法及时接收所有请求

这种问题比较难于发现因为部分用户仍然可以收到推送数据只有部分用户的部分数

据丢失了同时这种问题在功能性测试场景下不容易发生只有在大压力测试或者是存在

大量用户的生产环境中才容易出现

产生这种问题的根源是 MDSBES 服务器的并发接收限制MDS 服务器其实是运行在

Tomcat 服务器上一个服务Tomcat 服务器作为应用容器处理了低层的网络请求而 Tomcat

服务器在底层有并发网络请求的限制当网络连接达到服务器限制时新的推送连接会处理

等待状态如果在网络超时前仍无法建立连接则会出现网络超时异常

javanetConnectException Connection timed out connect

根据测试的结果不同版本的 BES 服务器在低层并发网络请求的数量上有差异以 200

个并发限制为例就是说同一时间点只能有 200 个连接可以向 MDSBES 服务器发送推送请

求第 201 个请求以后有所有请求都将进入等待状态如果在 TCPIP 超时限制到达之前

MDSBES 服务器仍无法释放前 200 个请求则等待状态的请求会因为网络超时而中断

当应用服务器采用多线程机制处理推送请求的情况下有可能出现以上情况如果因为业

务原因在短时间内出现大量等待推送的数据应用服务器则有可能启动多于 200 个的线程发

送推送请求如果需要推送的数据太大或者是推送线程需要动态对数据进行计算就有可

能导致线程所启动的连接在短时间内没有关闭当多于 200 个线程处于这种状态的时候

MDSBES 服务器就无法继续接收推送请求了这种问题的一个现象就是应用服务器端报网络

超时异常或者是连接被拒绝的异常

总的而言要保证数据推送的成功第一步需要保证推送数据到达 MDSBES 服务器

发现推送数据没有到达 MDSBES 服务器的情况时应该对应用服务器的日志进行检查进一

步确定问题的原因

推送请求检查

在 MDSBES 服务器接收到推送请求后MDSBES 服务器会对请求进行检查只有符合

推送格式的请求才被 MDSBES 服务器所接收当 MDSBES 服务器接收推送时会返回值为

200 的 HTTP 答复告知应用服务器已经接收该请求当 MDSBES 服务器拒绝推送请求时会

根据拒绝的原因返回相应的错误码可能返回的错误码包括 400403503 等下面对不同的

情况进行描述

第一种请求被拒绝的原因是推送请求的格式不对包括 URL 格式不正确和 HTTP 头不正

确两种情况两种情况 MDSBES 服务器都会返回 400 消息表示拒绝URL 格式不正确包括

URL 中不包含ldquoDESTINATIONrdquo参数或者是不包含ldquoPORTrdquo参数等HTTP 头不正确包括

PUSH ID 不唯一等因为推送请求中的 HTTP 头属性都有缺省值所以使用错误的 HTTP 头属

性并不会引发错误MDSBES 服务器只是忽略该参数如 HTTP 头ldquoX-RIM-PUSH-IDrdquo被误写

为ldquoX-RIMPUSHIDrdquoMDSBES 服务器并不会拒绝该请求只是使用ldquoX-RIM-PUSH-IDrdquo的缺

省值就是服务器随机生成一个唯一的 ID 给推送消息

对于 HTTP 协议而言服务器返回 400 代表服务器一般性错误所以返回消息会以异常

形式被捕获以 java 代码为类程序会捕获到以下网络异常

ldquojavaioIOException Server returned HTTP response code 400 for URL

httprdquo

第二种推送请求被拒绝的情况是推送请求指定的收件人不正确就是指应用服务器发送

推送请求时使用了正确的格式不过 DESTINATION 参数指定的收件人并不存在或者是该收件

人并不是 BlackBerry 用户此时 MDSBES 服务器会返回 403 错误读者需要注意 BES 服务

器 37 之前的版本对于这种情况会视作格式错误返回 400 错误

同样以 java 代码为例收件人不正确的情况下程序会捕获到以下网络异常

ldquojavaioIOException Server returned HTTP response code 403 for URL

httprdquo

第三种请求被拒绝的情况是 MDSBES 服务器上的推送队列已满无法再接收更多的推

送请求此时会返回 503 错误表示服务器暂时没有足够的资源处理该请求这种情况下 java

应用程序会捕获到以下异常

ldquojavaioIOException Server returned HTTP response code 503 for URL

httprdquo

这种情况与推送请求是否正确无关取决于 MDSBES 服务器运行情况关于 MDSBES

服务器的推送队列在下一节中我们才进行详细的讨论

如果数据推送请求通过了 MDSBES 服务器的消息检查则 MDSBES 服务器会将该数据

放入推送队列等待发送同时给应用服务器返回成功消息 200

成功消息是以 HTTP 的 ResponseCode 形式返回的开发人员可以通过 HTTP 连接的相应

方法获取在 java 代码中获取 ResponseCode 的函数是ldquo getResponseCoderdquo只有确认

MDSBES 服务器返回的 ResponseCode 是 200才能确定推送数据已经由 MDSBES 接收对

于其它的异常情况都需要由程序进行判断确定如何调整请求继续重试推送过程

有一点非常重要的是 MDSBES 接收该推送数据并不意味着该数据就一定能到达手持设

备端还有很多因素会导致手持设备端的应用接收不到该数据

MDSBES 中的推送队列

MDSBES 服务器有一个队列用于缓存推送请求因为手持设备并不是一定在线

MDSBES 服务器需要先将推送请求保留在服务端在合适的情况下将数据推送到手持设备端

后才从队列中将该请求移除

MDSBES 服务器上的推送队列是 MDS 推送的关键组件它的行为不仅仅影响应用服务

器端是否能够发送新的请求也影响到手持设备端是否能够接收到数据所以对于 MDS 推

送的开发人员必须了解 MDSBES 服务器上的推送队列

MDSBES 服务器上的推送队列有一个队列深度缺省为 1000也就是说 MDSBES 服务

器在同一时间只能缓存 1000 个推送请求该队列的深度可以由管理员在管理端修改

同时 MDSBES 服务器上的推送队列并不是永久保存推送数据它有一个过期时间缺

省为 10 分钟也就是说一个推送请求在 MDSBES 服务器上最多能够被保留 10 分钟同样

这个过期时间可以由管理员在管理端修改

推送队列的深度修改界面如下

推送队列的过期时间的修改界面如下(以毫秒为单位)

对于推送队列的进一步讨论是有关推送数据进入队列后的状态如何变化下面是推送数

据在队列中的状态变化图说明了推送数据在不同情况下的状态变化下图的各个状态中

淡蓝色的状态为起始状态深蓝色的状态为中间状态红色的状态为终止状态处于中间状

态的数据最终必然会转为终止状态的其中一种

当 MDSBES 服务器接收到推送数据后如果之前目标用户没有 5 个处于暂时失败的数

据包服务器会将数据放入等待发送的队列中推送的数据处于ldquo尝试发送rdquo状态处于ldquo尝

试发送rdquo会在很短的时间内占用 1000 个队列位置中的一个不管是成功还是失败它所占

用的队列位置很快被释放

然后 MDSBES 服务器尝试将数据推送到手持设备端如果此时对应的手持设备处于开

机状态网络一切正常则 MDSBES 服务器将数据发送给手持设备推送数据变成ldquo成功rdquo

状态MDSBES 服务器会向应用服务器发送异步的确认消息同时将数据从队列中移除注

意此时 MDSBES 服务器向应用服务器返回的确认消息并不一定是表示成功 200具体是

返回代表成功的 200 还是返回代表失败的 400 还取决于其它因素有关这一点我们在后面的

章节中详细讨论

如果 MDSBES 服务器尝试将数据推送到手持设备的时候手持设备处于关机状态则该

数据进入ldquo暂时失败rdquo状态处于ldquo暂时失败rdquo状态的数据不占用等待队列的队列位置某

一个目标用户最多只能有 5 条数据处于ldquo暂时失败rdquo状态目前没有明确的数据或者是文档

说明一个 MDSBES 服务器总共可以保留多少条ldquo暂时失败rdquo状态的数据

如果某一用户已有 5 条数据处于ldquo暂时失败rdquo状态MDSBES 服务器接收到该用户新

的推送请求时会直接将该数据转入ldquo等待rdquo状态每一条进入ldquo等待rdquo状态的推送数据会占

用推送队列中的一个位置当队列中的 1000 个位置都被占满的时候 MDSBES 服务器就停止

接收新的数据推送无论新的数据是推送给哪个用户

处于ldquo暂时失败rdquo和ldquo等待rdquo状态的数据会在服务器上保留 10 分钟(如上所述这一

时间可以由管理修改)10 分钟后处于这两种状态的数据将被丢弃进入ldquo过期rdquo状态同

时 MDSBES 服务器会向应用服务器发送异步消息这种情况下发送的异步确认消息一定是

代表失败的 400如果在 10 分钟内手持设备重新进入网络手持设备会发送消息到 MDSBES

服务器告知该手持设备已经上线MDSBES 服务器会将处理ldquo暂时失败rdquo和ldquo等待rdquo状态的

数据转成ldquo尝试发送状态rdquo重新发送该数据需要注意的是在 10 分钟的等待过程中

MDSBES 服务器如果没有接收到手持设备重新连接网络的消息则 MDSBES 服务器不会尝

试重新发送ldquo暂时失败rdquo和ldquo等待rdquo状态的数据

根据以上的分析可以对推出以下结果

1 当用户的手持设备在线时对该用户的数据推送会被马上处理该数据只在很短的时间

占用 1000 个等待队列中的一个位置

2 当用户的手持设备不在线时假设该服务器只有一个用户则服务器在 10 分钟内最多

可以接收该用户的 1005 条数据其中 5 条处于ldquo暂时失败rdquo状态1000 条在等待队列

3 如果用户的手持设备在数据发送给 MDSBES 服务器 10 分钟内重新接入网络MDSBES

服务器可以将该数据推送到手持设备端

4 如果 MDSBES 服务器的等待队列未满推送给新的用户的前 5 条数据不占用等待队列

5 如果 MDSBES 服务器的等待队列已满不管是由哪个用户的数据占满的MDSBES 服

务器都会停止接收所有用户的新的数据推送

总结而言在生产环境中保证 MDSBES 服务器正常工作的关键是要保证 MDSBES 服务

器的等待队列不被占满

手持设备端对推送数据的处理

MDSBES 服务器成功接收应用服务器推送的数据后会将数据推送到手持设备端在手持

设备端需要由客户端应用程序将推送的数据提取出来一个直观的猜测是手持设备端同样需

要一个队列保存数据否则客户端应用程序在提取前一条数据时新到达的数据就会直接丢

失需要验证的是这个队列有多深队列行为是否被系统隐藏经过测试证实手持设备端确

实也存在队列队列深度为 10而且可以反映在应用层其中最值得开发人员注意的是这

个队列是一个可溢出队列关于手持设备端推送数据队列的示意图如下

如图所示当客户端提取程序正在读取数据ldquoRrdquo如图中标有 R 的椭圆形所示如果

有更多的数据被推送到手持设备端所推送的数据会被放入设备的队列中一共有 10 条数

据可以被保留在设备队列中如椭圆形 1 到 10 所示此时如果有更多的数据被推送进来

设备队列已满则这些新的数据会溢出如椭圆形 111213 所示同时对于服务器端

而言数据 111213 已经被推送过一次服务器端不会再次尝试推送这些数据在这种

情况下数据 1 到 10 可以继续被客户端提取程序所提取而数据 111213 则丢失了

对于尚未推送到手持设备端的数据如数据 1415假设因为网络中断没有被推送到

手持设备端则仍保留在服务器端当网络恢复后数据 1415 仍可以被正常处理

对于确认消息(图中橙色圆形所示)的处理则更为复杂在异步确认消息一节中再作讨

从这一节的讨论得出的结果是数据到达手持设备端未必就能够被客户端提取程序提取

客户端提取程序有职责尽快提取数据防止推送数据的溢出溢出的数据在客户端无法再被

处理而且服务器端也不会尝试继续推送这些数据

异步确认消息(应用依赖和非应用依赖)

手持设备端在接收到推送数据后会通知 MDSBES 服务器这时 MDSBES 服务器会根据

最初的推送请求向指定的 URL 发送一个确认消息告知应用服务器某一条推送数据的最终

结果这就是异步确认消息异步确认消息以 http 协议发送给应用服务器消息 200 代表

数据推送成功400 代表数据推送失败

异步确认消息是应用服务器判断推送是否成功的有效工具但是很多开发人员因为对

异步确认消息的机制不了解错误地使用了异步确认消息从而导致数据的丢失所以我们

需要详细了解异步确认消息的工作机制

要讨论异步确认消息的工作机制必然涉及到应用依赖与非应用依赖应用依赖的推送

和非应用依赖的推送对应的异步确认消息工作机制是不同的

下面先讨论非应用依赖的场景这也是很多开发商使用的场景

如果推送是非应用依赖的数据是以到达手持设备为成功标志的只要数据到达了手持

设备MDSBES 服务器就认为该数据推送成功会给应用服务器发送异步确认消息 200

如果数据推送时手持设备不在线则 10 分钟内 MDSBES 服务器会在服务器上保留该数

据如果 10 分钟内手持设备重新接入网络则 MDSBES 服务器会尝试发送该数据一旦数

据到达手持设备MDSBES 服务器都会给应用服务器发送异步确认消息 200

如果 10 分钟内手设备没有接入网络则该推送数据过期MDSBES 服务器会丢弃该数

据同时给应用服务器发送异步确认消息 400

可以看出非应用依赖的推送方式并不能保证客户端应用程序接收到数据在非应用依

赖的推送方式下 MDSBES 返回 200 确认消息也不能认为客户端已接收到数据如果数据送

达手持设备的时候客户端应用程序没有启动所推送的数据会在手持设备端丢失而

MDSBES 服务器认为数据已成功送达或者是客户端应用程序已启动但是没有及时读取推

送数据在手持设备端溢出的数据也会丢失MDSBES 服务器同样认为数据已成功送达

所以在非应用依赖的情况下如果接收到 400 确认消息可以确定客户端没有收到数据

如果接收到 200 确认消息只能认为客户端可能收到了数据

从以上的讨论看非应用依赖的推送方式并不适合用于可靠性要求较高的应用对于可

靠性要求较高的应用需要知道客户端应用程序是否接收到了数据应用依赖的推送方式就

是为此而设计的如果推送是应用依赖的数据是以客户端应用程序确认为成功标志的只

要在应用程序确认后MDSBES 服务器才认为数据推送成功这时才给应用服务器发送异步

确认消息 200

有关客户端应用程序如何确认收到数据在ldquo客户端接收关键代码rdquo一节中有描述简

单而言就是客户端应用程序通过调用 MDSPushInputStream 实例的 accept 函数确认数据收

到如果客户端应用程序在接收到数据后没有调用 accept 函数MDSBES 服务器会认为数

据推送失败给应用服务器发送异步确认消息 400另外一点曾经强调过的是accept 函数

调用后只有 InputStream 和 StreamConnection 都关闭了确认消息才被发送给 MDSBES 服

务器在极端的情况下这一点会影响到异步确认消息的结果

下面具体分析各种情景

如果手持设备在线但是客户端应用程序没有启动推送数据到达手持设备端后没有程

序侦听推送端口当然也没有程序会调用 accept 函数此时 MDSBES 服务器会认为数据推

送失败向应用服务器发送异步确认消息 400

如果手持设备在线客户端应用程序在读取推送数据后没有调用 accept 函数直接关闭

连接此时 MDSBES 服务器同样会认为数据推送失败向应用服务器发送异步确认消息 400

在这种情景下即使客户端接收到了数据但应用服务器仍收到失败消息因为作为成功标

志的 accept 函数并没有调用

如果手持设备在线客户端应用程序在读取推送数据后调用了 accept 函数并关闭连

接此时 MDSBES 服务器会认为数据推送成功向应用服务器发送异步确认消息 200

对于手持设备不在线的情况结果与非应用依赖相似10 分钟内如果手持设备不接入

网络的话推送数据会在 MDSBES 服务器上过期MDSBES 服务器会向应用服务器发送异

步确认消息 400

如果 10 分钟内手持设备重新接入网络MDSBES 服务器会尝试发送数据至手持设备端

还是以 accept 函数的调用为推送成功的标志如果数据重新发送成功但是客户端没有调

用 accept 函数则 MDSBES 服务器会向应用服务器发送异步确认消息 200

对于推送到手持设备端但是溢出的数据因为没有程序能够读取该数据并针对该数据调

用 accept 函数MDSBES 服务器会一直等待到 10 分钟后向应用服务器发送异步确认消息

400认为数据过期

一个特殊的情况是客户端应用程序在接收到数据后调用了 accept但是没有关闭

InputStream 和 StreamConnection此时 MDSBES 服务器会认识客户端还在处理数据会继

续等待如果服务器等待时候超过 10 分钟MDSBES 服务器会认为数据推送失败向应用

服务器发送异步确认消息 400如果在 10 分钟内客户端主动关闭 InputStream 和

StreamConnection或者是网络异常中断MDSBES 服务器会认为数据推送成功向应用服

务器发送异步确认消息 200

下面再讨论一个异常情况下的特例

结合以上描述的各种因素在极端的情况下会出现一些特殊的情况如上图所示假设

MDSBES 服务器在推送了数据 R数据 1 到数据 13 后出现网络中断同时客户端应用程序

正在缓慢地读取数据 R此时数据 1 到数据 10 会被保留在手持设备队列中客户端应用程

序在读取数据 R 后可以继续读取数据 1 到数据 10如果客户端应用程序在读取了数据后都

调用了 accept 函数则会产生对应用确认消息如图中的确认消息 R确认消息 1 到确认消

息 10但是因为网络的中断这些确认消息都无法到达 MDSBES 服务器如果网络在 10

钟内没有恢复MDSBES 服务器上所有等待确认消息的记录都会认识是过期的MDSBES

会向应用服务器发送异步确认消息 400此后即使网络恢复确认消息 R确认消息 1 到确

认消息 10 都不再有效

在这种特殊情况下即使客户端应用程序接收到数据并调用了 accept 函数MDSBES

服务器同样认为数据没有推送成功

根据以上的讨论应用依赖的推送同样不能保证客户端应用一定能收到数据但是在

应用依赖的推送方式下 MDSBES 返回 200 确认消息可以确定客户端已接收到数据因为 200

确认消息是由客户端应用程序调用 accept 函数触发的客户端应用程序可以在确认数据收

到后才调用 accept 函数通知应用服务器该数据已成功接收甚至可以在接收到数据后根

据数据内容决定不调用 accept 函数从而通过应用服务器该数据没有被接收

所以在应用依赖的情况下如果接收到 200 确认消息可以确定客户端已经接收到数据

如果接收到 400 确认消息只能认为客户端可能没有收到数据

因此应用依赖的推送方式更适合可靠性要求高的应用如果应用服务器接收到 200

确认消息就可以认为推送成功继续处理其它事务如果应用服务器接收到 400 确认消息则

需要组织数据重发直到接收到 200 确认消息为止这样就可以保证客户端一定能够接收到

需要推送的数据

如何保证数据推送的可靠性

通过以上几个章节的讨论我们知道虽然 MDSBES 服务器提供了很多机制保证数据

推送的可靠性但是都无法完全保证数据被客户端应用程序所接收

对于数据推送的可靠性保证一方面开发人员需要了解各个可能导致故障的因素在应

用编写的时候尽量减少故障出现的可能性对故障点进行规避另一方面开发人员也需要实

现额外的机制进行数据重发在故障点无法规避的情况下通过数据重发消除故障点的影响

应用依赖和非应用依赖的比较和选择

在实现推送应用的时候首要考虑的是有关应用依赖与非应用依赖的选择

应用依赖的推送形式在可靠性方面更优于非应用依赖的推送形式但是应用依赖的推送

形式对 MDSBES 应用服务器的潜在压力较大客户端应用程序的实现也较为复杂

非应用依赖的推送形式优势在于实现简单对 MDSBES 应用服务器形成的压力相对较

非应用依赖的推送形式从某种程度上讲有点类似于广播协议着重点是从服务器向客户

端发送大量数据并不看重客户端的接收情况从数据的应用类型来分非应用依赖的推送

更适合于天气信息最新报价等数据这些数据在客户端应用关闭时被自然丢弃短时间内

的多条数据有丢失影响也不大必竞用户不会关注 11 点零六分的天气和 11 点零七分的天气

有何不同

应用依赖的推送形式则类似于点对点协议不仅从服务器端向客户端发送数据同时也

关注客户端的接收情况应用依赖的推送更适合于 OAERP 这种应用这种应用需要保证

新的文档或者是记录一定能到达客户端应用丢失一条待办信息可能就会导致整个审批流程

的停滞

再从两种推送方式的本质差别看应用依赖并没有额外的机制消除故障点它的优势在

于它的异步确认消息更可信所以在选择了应用依赖的推送方式后进一步还是需要实现

额外的数据重发机制根据 MDSBES 所发送的异步确认消息实施数据重发

应用依赖的推送再加上结合异步确认消息的数据重发机制是保证推送数据可靠性的

最好方法

MDS 接收线程限制的处理

不管使用什么方式的推送应用开发时都需要考虑到 MDS 接收线程的限制不过以

现在的服务器处理能力正常情况下的 HTTP 推送请求可以在几毫秒或者是几十毫内处理完

成少量的 HTTP 推送不足于构成网络阻塞

需要考虑的是应用服务器端的推送程序在推送过程中不要在建立连接后才动态组织数

据这样会浪费 http 连接资源好的建议是预先组织好数据在建立 HTTP 连接后快速写完

数据尽快关闭连接

此外对于应用服务器的推送线程也要加于控制避免在短时间内无限制地启动新的线

程进行连接否则也容易造成连接过多而阻塞的情况

从网络层面来看调整应用服务器与 MDSBES 服务器之间的网络超时设置也是避免这

种问题的一种方法在允许的范围内调大网络超时时间可以让应用服务器的推送线程等待更

长的时间从而让 MDSBES 有足够的时间释放原有的连接

最后无论使用了什么方法调优都需要对应用服务器的推送程序进行异常处理当发

现网络超时异常的时候可以组织数据重发当然数据重发的间隔时间也要考虑不要因为

重发数据再次造成网络阻塞

MDS 推送队列限制的处理

相对而言MDS 推送队列限制造成的推送失败比 MDS 接收队列造成的推送失败更常见

已经发现的一个实际案例是周未的时候用户大量关机导致大量推送数据处于推送队列中

填满了推送队列从而导致 MDSBES 服务器不再响应新的推送请求一旦过期时间到达

有新的队列位置空闲又有大量新的推送数据重新占满推送队列

对于 MDS 推送队列的限制直观的做法是对队列参数进行调整如之前讨论的管理

员可以对队列的深度和过期时间进行调整

然而出于对过期时间的不正确理解管理员倾向于将过期时间调大当过期时间被调

大后推送给关机用户的数据在服务器占用队列的时间就变长导致 MDS 推送队列更容易

被占满

所以如果对过期时间进行调整的话方向是将过期时间调小过期时间越小等待数

据占用队列的时间就越短MDS 推送队列被占满的可能性就越小但是过期时间也不能

过小过期时间过小可能会导致过多的过期数据使应用服务器需要处理更多的数据重发

比如将过期时间调成 1 分钟用户走过一个人行遂道都可能导致数据发送失败

对于队列深度的调整方向是调大队列深度如从缺省的 1000 调整到 2000此时

MDSBES 服务器在同一时间可以保留更多的等待数据减少了队列占满的机会不过调大队

列深度会让 MDSBES 服务器保留更多的数据从而占用更多的服务器内存对服务器产生

更大的压力所以调用队列深度需要根据服务器的运行能力进行调用

从应用服务器设计角度应用 MDS 队列限制的方法是尽量减少等待数据的发送如发

送第一条数据的时候发现数据过期对于该用户的其它数据可以调整发送时间在更长的时

间间隔后才尝试发送

对于 MDS 队列限制应用服务器端的推送应用需要对 MDSBES 服务器返回的 503 错误

进行处理对于出现 503 错的数据组织重发同样重发的间隔也需要考虑不要因为数据

重发占用更多的推送队列也可以根据目标用户进行判断对于可能关机的用户暂时不重发

数据

手持设备端接收队列的处理

手持设备端接收队列并未从文档中明确列出是根据测试结果分析得出的手持设备端

目前也没有发现设置可以调整该队列所以客户端程序设计的时候不应该依赖手持设备端队

开发人员需要意识到的是手持设备端的接收队列是可溢出队列一旦推送数据从接收队

列出溢出客户端程序就无法提取该数据了所以在客户端应用开始过程中要注意加快推送

数据的提取过程在提取过程中不要对数据进行复杂的计算提取完成后尽快读取下一条数

据对于接收数据的处理和计算可以交由另一个线程完成

因为溢出数据对客户端应用而言不可见所以数据溢出时在客户端应用中也不会有异

常开发人员需要通过服务器返回的过期消息才能知道该数据推送失败

对于这一点客户端应用除了尽量避免数据溢出外无法在客户端通过其它机制进行补

应用依赖的客户端

对于应用依赖的客户端应用关键点是决定何时调用 accept 函数同时保证 InputStream

和 StreamConnection 被关闭

如之前讨论的使用应用依赖的推送关键是希望使用 MDSBES 发送给应用服务器的异

步确认消息从而对数据是否到达进行判断所以应用依赖的客户端应用需要保证正确调用

accept 函数

另外如果 InputStream 和 StreamConnection 没有关闭的话accept 函数所发出的确认

消息无法被 MDSBES 服务器收到开发人员在代码中应该主动关闭 InputStream 和

StreamConnection同时在程序发生异常时也需要在 catch 代码段对 InputStream 和

StreamConnection 进行关闭处理

异步确认消息处理

异步确认消息用于判断数据是否被成功推送至客户端应用要使用异步确认消息首先

要保证成功地无遗漏地接收所有从 MDSBES 服务器发送过来的异步确认消息

异步确认消息是一个 HTTP 消息消息比较短接收过程的并发压力不大下面是一个

异步确认消息的样例

Received notificationGET HTTP11

User-Agent RIM MDSCS

Accept

X-RIM-Push-ID pushID2081773768

X-RIM-Push-Destination mmtestbjsearbcn

X-RIM-Push-Status 400

Host localhost7778

其中最关键是的获取属性 X-RIM-Push-Status根据该属性的值判断数据推送是成功

(200)还是失败(400)当然属性 X-RIM-Push-ID 也非常重要用于确定该确认消息是针

对哪条推送数据的

如果应用服务器端通过确认消息发现数据推送失败则需要根据属性 X-RIM-Push-ID 重

新组织数据进行重发

Page 8: BES 服务器推送机制分析 - images.csdn.netimages.csdn.net/upimgs/lee/BBPDF/BESfwtsjzfx.pdf · 前言 数据推送是BlackBerry 应用平台的一大优势,在BlackBerry 应用平台上部署的应用可以

APPLICATION)

特别要注意如果设置推送类型为应用依赖对于服务器的设置和手持设备有特定的要

求一方面是服务器端必须是 40 以上同时必须设置允许对所使用的端口进行应用依赖的

推送一方面是要手持设备的 ROM 版本也是 40 以上同时服务器必须知道手持设备能否

支持应用依赖的推送对于服务器的端口设置设置界面如下可以由 BES 管理员进行设置

如果是使用开发环境的 MDS-CS 模拟器模拟应用依赖推送的话需要修改开发环境的

MDSconfig目录中的 rimpublicproperty文件去除 pushapplicationreliableports一行的注释

同时加上需要推送的端口如ldquopushapplicationreliableports=10055100rdquo

对于服务器端是否知道手持设备端的版本需要通过手持设备端的操作完成只要手持

设备端的浏览器通过 BES 访问过网络服务器就会记录该手持设备是否支持应用依赖的推

送如果只是测试的话在测试前通过手持设备端的浏览器打开任何一个网站就可以了如

果是生产环境则需要考虑客户端应用在第一次启动的时候主动访问一次网络以保证客户

端都可以正常接收应用依赖的推送

如果以上设置没有完成的话对 MDSBES 服务器发送应用依赖的推送时会被拒绝服

务器端会出现ldquoThe specified delivery method is not possiblerdquo的错误应用服务器的推送程

序则会捕获以下异常

ldquojavaioIOException Server returned HTTP response code 400 for

URL httpapprdquo

设置属性后就需要获取 http连接的 OutputStream将需要推送的数据写入 OutputStream

中写完后关闭连接

需要注意的是 MDSBES 服务器是否接收该推送请求是通过 http 返回码实现的所以推

送应用中需要获取到 http 连接的返回码通过返回码判断推送是否成功代码如下

int responseCode = conngetResponseCode()

客户端接收关键代码

客户端数据接收程序运行在 BlackBerry 手持设备上必须通过 java 语言实现需要调用

BlackBerry 设备端的 API

客户端接收程序的主体就是启动一个线程开启 httpltportgt连接(其中 port 是客户

端需要侦听的端口号)然后从该连接中获取 InputStream从 InputStream 中获取服务器所

推送的数据

下面是侦听线程的 run 方法为保持程序的完整代码中使用到的类属性并没有修改

从该函数中看到的本函数没有定义的变量都是该类的类属性有关完整代码请参考

BlackBerry 开发环境所提供的标准样例

public void run()

StreamConnection stream = null

InputStream input = null

MDSPushInputStream pushInputStream=null

while (_stop)

try

synchronized(this)

_notify =

(StreamConnectionNotifier)Connectoropen(http55100)

while (_stop)

stream = _notifyacceptAndOpen()

try

input = streamopenInputStream()

pushInputStream= new

MDSPushInputStream((HttpServerConnection)stream input)

DataBuffer db = new DataBuffer()

byte[] data = new byte[256]

int chunk = 0

while ( -1 = (chunk = inputread(data)) )

dbwrite(data 0 chunk)

pushInputStreamaccept()

inputclose()

streamclose()

data = dbgetArray()

catch (IOException e1)

Systemerrprintln(e1toString())

if ( input = null )

try

inputclose()

catch (IOException e2)

if ( stream = null )

try

streamclose()

catch (IOException e2)

_notifyclose()

_notify = null

catch (IOException ioe)

if ( _notify = null )

try

_notifyclose()

_notify = null

catch ( IOException e )

以上代码中建立端口侦听的代码如下

_notify = (StreamConnectionNotifier)Connectoropen(http55100)

其中 URLldquohttp55100rdquo是用于侦听推送的特定格式55100 为需要侦听的端口两

个线程不能同时打开同一个端口的侦听所以在这里使用了 synchronized 对线程并发进行了

控制

建立侦听后可以通过函数 acceptAndOpen 开始侦听当线程调用该方法开始侦听的时候

线程会阻塞在这里直到有推送消息到达代码如下

stream = _notifyacceptAndOpen()

侦听之后的代码为获取连接的 InputStream从 InputStream中获取服务器端推送的数据

读取完数据后关闭 InputStream 和 StreamConnection然后重新调用 acceptAdnOpen 继续侦

听端口准备接收新的推送数据

开发人员如果希望使用应用依赖的推送在客户端必须将 InputStream 转换成

MDSPushInputStream同时在接收完数据后调用 MDSPushInputStream 的 accept 函数造知服

务器该数据已收到这也是应用依赖和非应用依赖在代码实现层面的最主要区别代码如下

pushInputStream= new

MDSPushInputStream((HttpServerConnection)stream input)

helliphellip

pushInputStreamaccept()

从良好的编程习惯来讲在连接使用后需要主动关闭所使用的连接和资源如

InputStreamStreamConnectionStreamConnectionNotifier 等所以在样例代码中也可以看

到该样例在异常处理阶段都对这些资源进行了关闭操作在关闭这些连接和资源的时候对

StreamConnectionNotifier 需要特别注意有可能在 StreamConnectionNotifier 中会保留有未

读取的推送数据需要调用 acceptAdnOpen 函数将保留的数据读取出来直到 acceptAdnOpen

处于阻塞状态

另外如果使用了应用依赖的推送客户端程序调用 MDSPushInputStream 的 accept 函

数后确认消息不会马上发出而是在对应用 InputStream 和 StreamConnection 关闭后才发出

所以对于应用依赖的推送保证 InputStream 和 StreamConnection 的关闭非常之重要

异步确认消息接收代码

异步消息的接收代码运行在服务器端在推送请求的 X-RIM-Push-NotifyURL 属性所指定

的服务器上侦听指定端口和应用服务器端的数据推送代码一样异步确认消息的接收程序

可以通过任何支持 HTTP 的语言编写主要的工作是侦听指定端口从指定端口获取 MDSBES

发送回来的确认消息根据 HTTP 协议获取相关属性并返回相关信息

下面以 java 代码为例对关键代码进行说明这段代码一个 java Application 的一部分是

侦听线程的 run 函数

public void run()

try

Systemoutprintln(Waiting for notification on port + 7778

+ )

while (true)

ServerSocket serverSocket = new ServerSocket(7778)

serverSocketsetSoTimeout(120000)

try

Socket clientSocket = serverSocketaccept()

InputStream input = clientSocketgetInputStream()

StringBuffer str = new StringBuffer()

int byteRead = inputread()

while ((byteRead = -1) ampamp (inputavailable() gt 0))

strappend((char) byteRead)

byteRead = inputread()

Systemoutprintln(strtoString())

OutputStream output = clientSocketgetOutputStream()

String response = HTTP10 200 OK

outputwrite(responsegetBytes())

outputflush()

outputclose()

inputclose()

clientSocketclose()

catch (SocketTimeoutException ste)

Systemoutprintln(Notification connection timeout

Restarting)

serverSocketclose()

catch (Exception exception)

exceptionprintStackTrace()

异步确认消息的接收代码比较简单基本上就是打开一个 Socket 侦听指定的端口本

例中是 7778然后不断地从该端口读取数据打开 Socket 侦听的代码如下

ServerSocket serverSocket = new ServerSocket(7778)

在异步消息接收过程中需要注意的是接收完 MDSBES 服务器的确认消息后应该给

MDSBES 服务器返回一个 200 的成功消息否则 MDSBES 服务器会认为发送确认消息失败

可能重次几次后再放弃这样异步消息接收程序有可能重复收到同一条确认消息返回成功

消息的代码如下

OutputStream output = clientSocketgetOutputStream()

String response = HTTP10 200 OK

outputwrite(responsegetBytes())

outputflush()

outputclose()

推送细节分析

了解基本的 MDS 推送知识是不足于编写一个可靠的数据推送程序的要保证数据推送

的可靠性必须了解整个推送过程的每一个细节本章节先从整体上描述了整个数据推送的

调用时序然后从不同的关键点详细讲解数据推送的细节

MDS 推送的整体时序

如上图所示数据推送主要是应用服务器MDS 服务器网络服务手机端几个组件

之间的交互在网络服务这一块我们可以假定这一层是稳定的不做考虑对于网络无法提

供服务等情况我们可以通过手持设备端关闭无线电或者是将手持设备放入无线屏蔽区等方

式模拟所以我们要考虑是应用服务器MDSBES 服务器手持设备这三部分

数据推送由应用服务器发生向 MDSBES 服务器发出 HTTP Post 请求第一个需要考虑

的是该 HTTP Post 请求是否能到达 MDSBES 服务器这一点在ldquo推送请求是否到达 MDSBES

服务器rdquo小节中有详细讨论

MDSBES 服务器在接收到 HTTP Post 请求后会对请求的格式进行判断如果格式错误

如指定目标不存在等则 MDSBES 服务器会返回 400 错误所以第二点要考虑是的如何保

证所有到达 MDSBES 服务器的请求都是合法的有关这一点在ldquo推送请求检查rdquo小节中有

详细描述

MDSBES 服务器发现推送数据格式正确以后还需要判断服务器上的队列是否有足够的

空间保存该数据如果没有发现空闲队列则丢弃该数据并向应用服务器返回 503 错误因此

第三点要考虑的是什么情况会导致服务器推送队列占满如何尽量避免这种情况有关这一

点在ldquoMDSBES 中的推送队列rdquo中有详细描述

MDSBES 服务器将数据推送到客户端以后由客户端程序将数据提取交由相应的应用

逻辑进行处理然后客户端应用在提取数据的时候并不一定能及时地将所有推送到的数据

获取所以第四点要考虑的是手持设备端是否也存在队列如何保证客户端程序将所有数据

都提取出来有关这一点在ldquo手持设备端对消息的处理rdquo小节中有详细描述

手持设备在接收推送数据后会通知 MDSBES 服务器MDSBES 服务器则向应用服务器

发送异步的确认消息通过返回数值 200 或者是 400 通知应用服务器推送数据是推送成功还

是推送失败异步确认消息对于应用开发者有特别的意义通过它可以确定数据是否到达客

户端从而决定是否再次推送数据然后很多开发商都发现异步确认消息的返回的ldquo200rdquo

和ldquo400rdquo并不能准备地反映数据的接收情况所以如何使用异步确认消息在不同的场

景下异步消息会包含什么不同值对于应用开发者而言非常重要有关这一点在小节ldquo异步

确认消息rdquo有详细描述

推送请求是否到达 MDSBES 服务器

有关推送请求是否到达 MDSBES 服务器 很多开发人员都会忽略认为推送请求是否

到达 MDSBES 服务器是由代码是否编写正确决定的一定会在代码测试阶段体现也来也

必须在代码编写阶段解决这个问题然而在生产环境确实有一些因素会导致应用服务器发送

的推送请求无法到达 MDSBES 服务器从而导致推送数据丢失的问题

推送请求无法到达 MDSBES 服务器的一个可能的直接原因是指定的服务器名不正确或

者是指定的端口号不正确原因可能是 BES 管理员修改了 BES 服务器的服务器名IP 地址或

者是推送监听端口

这种问题比较容易发现一方面是因为所有的 BlackBerry 用户此时都无法接收到推送数

据另一方面也因为应用服务器端会报网络连接的异常以 java 代码为例当指定的服务

器名或者是端口号不正确的情况下应用服务器端的推送程序会报以下异常

javanetConnectException Connection refused

或者是报域名无法解析等网络连接级别的异常

发现这种情况只需要检查代码中指定的服务器名和端口号是否和生产环境相同就可以

解决问题

推送请求无法到达 MDSBES 服务器的一个潜在可能性是推送请求的并发数量太大导致

MDSBES 服务器无法及时接收所有请求

这种问题比较难于发现因为部分用户仍然可以收到推送数据只有部分用户的部分数

据丢失了同时这种问题在功能性测试场景下不容易发生只有在大压力测试或者是存在

大量用户的生产环境中才容易出现

产生这种问题的根源是 MDSBES 服务器的并发接收限制MDS 服务器其实是运行在

Tomcat 服务器上一个服务Tomcat 服务器作为应用容器处理了低层的网络请求而 Tomcat

服务器在底层有并发网络请求的限制当网络连接达到服务器限制时新的推送连接会处理

等待状态如果在网络超时前仍无法建立连接则会出现网络超时异常

javanetConnectException Connection timed out connect

根据测试的结果不同版本的 BES 服务器在低层并发网络请求的数量上有差异以 200

个并发限制为例就是说同一时间点只能有 200 个连接可以向 MDSBES 服务器发送推送请

求第 201 个请求以后有所有请求都将进入等待状态如果在 TCPIP 超时限制到达之前

MDSBES 服务器仍无法释放前 200 个请求则等待状态的请求会因为网络超时而中断

当应用服务器采用多线程机制处理推送请求的情况下有可能出现以上情况如果因为业

务原因在短时间内出现大量等待推送的数据应用服务器则有可能启动多于 200 个的线程发

送推送请求如果需要推送的数据太大或者是推送线程需要动态对数据进行计算就有可

能导致线程所启动的连接在短时间内没有关闭当多于 200 个线程处于这种状态的时候

MDSBES 服务器就无法继续接收推送请求了这种问题的一个现象就是应用服务器端报网络

超时异常或者是连接被拒绝的异常

总的而言要保证数据推送的成功第一步需要保证推送数据到达 MDSBES 服务器

发现推送数据没有到达 MDSBES 服务器的情况时应该对应用服务器的日志进行检查进一

步确定问题的原因

推送请求检查

在 MDSBES 服务器接收到推送请求后MDSBES 服务器会对请求进行检查只有符合

推送格式的请求才被 MDSBES 服务器所接收当 MDSBES 服务器接收推送时会返回值为

200 的 HTTP 答复告知应用服务器已经接收该请求当 MDSBES 服务器拒绝推送请求时会

根据拒绝的原因返回相应的错误码可能返回的错误码包括 400403503 等下面对不同的

情况进行描述

第一种请求被拒绝的原因是推送请求的格式不对包括 URL 格式不正确和 HTTP 头不正

确两种情况两种情况 MDSBES 服务器都会返回 400 消息表示拒绝URL 格式不正确包括

URL 中不包含ldquoDESTINATIONrdquo参数或者是不包含ldquoPORTrdquo参数等HTTP 头不正确包括

PUSH ID 不唯一等因为推送请求中的 HTTP 头属性都有缺省值所以使用错误的 HTTP 头属

性并不会引发错误MDSBES 服务器只是忽略该参数如 HTTP 头ldquoX-RIM-PUSH-IDrdquo被误写

为ldquoX-RIMPUSHIDrdquoMDSBES 服务器并不会拒绝该请求只是使用ldquoX-RIM-PUSH-IDrdquo的缺

省值就是服务器随机生成一个唯一的 ID 给推送消息

对于 HTTP 协议而言服务器返回 400 代表服务器一般性错误所以返回消息会以异常

形式被捕获以 java 代码为类程序会捕获到以下网络异常

ldquojavaioIOException Server returned HTTP response code 400 for URL

httprdquo

第二种推送请求被拒绝的情况是推送请求指定的收件人不正确就是指应用服务器发送

推送请求时使用了正确的格式不过 DESTINATION 参数指定的收件人并不存在或者是该收件

人并不是 BlackBerry 用户此时 MDSBES 服务器会返回 403 错误读者需要注意 BES 服务

器 37 之前的版本对于这种情况会视作格式错误返回 400 错误

同样以 java 代码为例收件人不正确的情况下程序会捕获到以下网络异常

ldquojavaioIOException Server returned HTTP response code 403 for URL

httprdquo

第三种请求被拒绝的情况是 MDSBES 服务器上的推送队列已满无法再接收更多的推

送请求此时会返回 503 错误表示服务器暂时没有足够的资源处理该请求这种情况下 java

应用程序会捕获到以下异常

ldquojavaioIOException Server returned HTTP response code 503 for URL

httprdquo

这种情况与推送请求是否正确无关取决于 MDSBES 服务器运行情况关于 MDSBES

服务器的推送队列在下一节中我们才进行详细的讨论

如果数据推送请求通过了 MDSBES 服务器的消息检查则 MDSBES 服务器会将该数据

放入推送队列等待发送同时给应用服务器返回成功消息 200

成功消息是以 HTTP 的 ResponseCode 形式返回的开发人员可以通过 HTTP 连接的相应

方法获取在 java 代码中获取 ResponseCode 的函数是ldquo getResponseCoderdquo只有确认

MDSBES 服务器返回的 ResponseCode 是 200才能确定推送数据已经由 MDSBES 接收对

于其它的异常情况都需要由程序进行判断确定如何调整请求继续重试推送过程

有一点非常重要的是 MDSBES 接收该推送数据并不意味着该数据就一定能到达手持设

备端还有很多因素会导致手持设备端的应用接收不到该数据

MDSBES 中的推送队列

MDSBES 服务器有一个队列用于缓存推送请求因为手持设备并不是一定在线

MDSBES 服务器需要先将推送请求保留在服务端在合适的情况下将数据推送到手持设备端

后才从队列中将该请求移除

MDSBES 服务器上的推送队列是 MDS 推送的关键组件它的行为不仅仅影响应用服务

器端是否能够发送新的请求也影响到手持设备端是否能够接收到数据所以对于 MDS 推

送的开发人员必须了解 MDSBES 服务器上的推送队列

MDSBES 服务器上的推送队列有一个队列深度缺省为 1000也就是说 MDSBES 服务

器在同一时间只能缓存 1000 个推送请求该队列的深度可以由管理员在管理端修改

同时 MDSBES 服务器上的推送队列并不是永久保存推送数据它有一个过期时间缺

省为 10 分钟也就是说一个推送请求在 MDSBES 服务器上最多能够被保留 10 分钟同样

这个过期时间可以由管理员在管理端修改

推送队列的深度修改界面如下

推送队列的过期时间的修改界面如下(以毫秒为单位)

对于推送队列的进一步讨论是有关推送数据进入队列后的状态如何变化下面是推送数

据在队列中的状态变化图说明了推送数据在不同情况下的状态变化下图的各个状态中

淡蓝色的状态为起始状态深蓝色的状态为中间状态红色的状态为终止状态处于中间状

态的数据最终必然会转为终止状态的其中一种

当 MDSBES 服务器接收到推送数据后如果之前目标用户没有 5 个处于暂时失败的数

据包服务器会将数据放入等待发送的队列中推送的数据处于ldquo尝试发送rdquo状态处于ldquo尝

试发送rdquo会在很短的时间内占用 1000 个队列位置中的一个不管是成功还是失败它所占

用的队列位置很快被释放

然后 MDSBES 服务器尝试将数据推送到手持设备端如果此时对应的手持设备处于开

机状态网络一切正常则 MDSBES 服务器将数据发送给手持设备推送数据变成ldquo成功rdquo

状态MDSBES 服务器会向应用服务器发送异步的确认消息同时将数据从队列中移除注

意此时 MDSBES 服务器向应用服务器返回的确认消息并不一定是表示成功 200具体是

返回代表成功的 200 还是返回代表失败的 400 还取决于其它因素有关这一点我们在后面的

章节中详细讨论

如果 MDSBES 服务器尝试将数据推送到手持设备的时候手持设备处于关机状态则该

数据进入ldquo暂时失败rdquo状态处于ldquo暂时失败rdquo状态的数据不占用等待队列的队列位置某

一个目标用户最多只能有 5 条数据处于ldquo暂时失败rdquo状态目前没有明确的数据或者是文档

说明一个 MDSBES 服务器总共可以保留多少条ldquo暂时失败rdquo状态的数据

如果某一用户已有 5 条数据处于ldquo暂时失败rdquo状态MDSBES 服务器接收到该用户新

的推送请求时会直接将该数据转入ldquo等待rdquo状态每一条进入ldquo等待rdquo状态的推送数据会占

用推送队列中的一个位置当队列中的 1000 个位置都被占满的时候 MDSBES 服务器就停止

接收新的数据推送无论新的数据是推送给哪个用户

处于ldquo暂时失败rdquo和ldquo等待rdquo状态的数据会在服务器上保留 10 分钟(如上所述这一

时间可以由管理修改)10 分钟后处于这两种状态的数据将被丢弃进入ldquo过期rdquo状态同

时 MDSBES 服务器会向应用服务器发送异步消息这种情况下发送的异步确认消息一定是

代表失败的 400如果在 10 分钟内手持设备重新进入网络手持设备会发送消息到 MDSBES

服务器告知该手持设备已经上线MDSBES 服务器会将处理ldquo暂时失败rdquo和ldquo等待rdquo状态的

数据转成ldquo尝试发送状态rdquo重新发送该数据需要注意的是在 10 分钟的等待过程中

MDSBES 服务器如果没有接收到手持设备重新连接网络的消息则 MDSBES 服务器不会尝

试重新发送ldquo暂时失败rdquo和ldquo等待rdquo状态的数据

根据以上的分析可以对推出以下结果

1 当用户的手持设备在线时对该用户的数据推送会被马上处理该数据只在很短的时间

占用 1000 个等待队列中的一个位置

2 当用户的手持设备不在线时假设该服务器只有一个用户则服务器在 10 分钟内最多

可以接收该用户的 1005 条数据其中 5 条处于ldquo暂时失败rdquo状态1000 条在等待队列

3 如果用户的手持设备在数据发送给 MDSBES 服务器 10 分钟内重新接入网络MDSBES

服务器可以将该数据推送到手持设备端

4 如果 MDSBES 服务器的等待队列未满推送给新的用户的前 5 条数据不占用等待队列

5 如果 MDSBES 服务器的等待队列已满不管是由哪个用户的数据占满的MDSBES 服

务器都会停止接收所有用户的新的数据推送

总结而言在生产环境中保证 MDSBES 服务器正常工作的关键是要保证 MDSBES 服务

器的等待队列不被占满

手持设备端对推送数据的处理

MDSBES 服务器成功接收应用服务器推送的数据后会将数据推送到手持设备端在手持

设备端需要由客户端应用程序将推送的数据提取出来一个直观的猜测是手持设备端同样需

要一个队列保存数据否则客户端应用程序在提取前一条数据时新到达的数据就会直接丢

失需要验证的是这个队列有多深队列行为是否被系统隐藏经过测试证实手持设备端确

实也存在队列队列深度为 10而且可以反映在应用层其中最值得开发人员注意的是这

个队列是一个可溢出队列关于手持设备端推送数据队列的示意图如下

如图所示当客户端提取程序正在读取数据ldquoRrdquo如图中标有 R 的椭圆形所示如果

有更多的数据被推送到手持设备端所推送的数据会被放入设备的队列中一共有 10 条数

据可以被保留在设备队列中如椭圆形 1 到 10 所示此时如果有更多的数据被推送进来

设备队列已满则这些新的数据会溢出如椭圆形 111213 所示同时对于服务器端

而言数据 111213 已经被推送过一次服务器端不会再次尝试推送这些数据在这种

情况下数据 1 到 10 可以继续被客户端提取程序所提取而数据 111213 则丢失了

对于尚未推送到手持设备端的数据如数据 1415假设因为网络中断没有被推送到

手持设备端则仍保留在服务器端当网络恢复后数据 1415 仍可以被正常处理

对于确认消息(图中橙色圆形所示)的处理则更为复杂在异步确认消息一节中再作讨

从这一节的讨论得出的结果是数据到达手持设备端未必就能够被客户端提取程序提取

客户端提取程序有职责尽快提取数据防止推送数据的溢出溢出的数据在客户端无法再被

处理而且服务器端也不会尝试继续推送这些数据

异步确认消息(应用依赖和非应用依赖)

手持设备端在接收到推送数据后会通知 MDSBES 服务器这时 MDSBES 服务器会根据

最初的推送请求向指定的 URL 发送一个确认消息告知应用服务器某一条推送数据的最终

结果这就是异步确认消息异步确认消息以 http 协议发送给应用服务器消息 200 代表

数据推送成功400 代表数据推送失败

异步确认消息是应用服务器判断推送是否成功的有效工具但是很多开发人员因为对

异步确认消息的机制不了解错误地使用了异步确认消息从而导致数据的丢失所以我们

需要详细了解异步确认消息的工作机制

要讨论异步确认消息的工作机制必然涉及到应用依赖与非应用依赖应用依赖的推送

和非应用依赖的推送对应的异步确认消息工作机制是不同的

下面先讨论非应用依赖的场景这也是很多开发商使用的场景

如果推送是非应用依赖的数据是以到达手持设备为成功标志的只要数据到达了手持

设备MDSBES 服务器就认为该数据推送成功会给应用服务器发送异步确认消息 200

如果数据推送时手持设备不在线则 10 分钟内 MDSBES 服务器会在服务器上保留该数

据如果 10 分钟内手持设备重新接入网络则 MDSBES 服务器会尝试发送该数据一旦数

据到达手持设备MDSBES 服务器都会给应用服务器发送异步确认消息 200

如果 10 分钟内手设备没有接入网络则该推送数据过期MDSBES 服务器会丢弃该数

据同时给应用服务器发送异步确认消息 400

可以看出非应用依赖的推送方式并不能保证客户端应用程序接收到数据在非应用依

赖的推送方式下 MDSBES 返回 200 确认消息也不能认为客户端已接收到数据如果数据送

达手持设备的时候客户端应用程序没有启动所推送的数据会在手持设备端丢失而

MDSBES 服务器认为数据已成功送达或者是客户端应用程序已启动但是没有及时读取推

送数据在手持设备端溢出的数据也会丢失MDSBES 服务器同样认为数据已成功送达

所以在非应用依赖的情况下如果接收到 400 确认消息可以确定客户端没有收到数据

如果接收到 200 确认消息只能认为客户端可能收到了数据

从以上的讨论看非应用依赖的推送方式并不适合用于可靠性要求较高的应用对于可

靠性要求较高的应用需要知道客户端应用程序是否接收到了数据应用依赖的推送方式就

是为此而设计的如果推送是应用依赖的数据是以客户端应用程序确认为成功标志的只

要在应用程序确认后MDSBES 服务器才认为数据推送成功这时才给应用服务器发送异步

确认消息 200

有关客户端应用程序如何确认收到数据在ldquo客户端接收关键代码rdquo一节中有描述简

单而言就是客户端应用程序通过调用 MDSPushInputStream 实例的 accept 函数确认数据收

到如果客户端应用程序在接收到数据后没有调用 accept 函数MDSBES 服务器会认为数

据推送失败给应用服务器发送异步确认消息 400另外一点曾经强调过的是accept 函数

调用后只有 InputStream 和 StreamConnection 都关闭了确认消息才被发送给 MDSBES 服

务器在极端的情况下这一点会影响到异步确认消息的结果

下面具体分析各种情景

如果手持设备在线但是客户端应用程序没有启动推送数据到达手持设备端后没有程

序侦听推送端口当然也没有程序会调用 accept 函数此时 MDSBES 服务器会认为数据推

送失败向应用服务器发送异步确认消息 400

如果手持设备在线客户端应用程序在读取推送数据后没有调用 accept 函数直接关闭

连接此时 MDSBES 服务器同样会认为数据推送失败向应用服务器发送异步确认消息 400

在这种情景下即使客户端接收到了数据但应用服务器仍收到失败消息因为作为成功标

志的 accept 函数并没有调用

如果手持设备在线客户端应用程序在读取推送数据后调用了 accept 函数并关闭连

接此时 MDSBES 服务器会认为数据推送成功向应用服务器发送异步确认消息 200

对于手持设备不在线的情况结果与非应用依赖相似10 分钟内如果手持设备不接入

网络的话推送数据会在 MDSBES 服务器上过期MDSBES 服务器会向应用服务器发送异

步确认消息 400

如果 10 分钟内手持设备重新接入网络MDSBES 服务器会尝试发送数据至手持设备端

还是以 accept 函数的调用为推送成功的标志如果数据重新发送成功但是客户端没有调

用 accept 函数则 MDSBES 服务器会向应用服务器发送异步确认消息 200

对于推送到手持设备端但是溢出的数据因为没有程序能够读取该数据并针对该数据调

用 accept 函数MDSBES 服务器会一直等待到 10 分钟后向应用服务器发送异步确认消息

400认为数据过期

一个特殊的情况是客户端应用程序在接收到数据后调用了 accept但是没有关闭

InputStream 和 StreamConnection此时 MDSBES 服务器会认识客户端还在处理数据会继

续等待如果服务器等待时候超过 10 分钟MDSBES 服务器会认为数据推送失败向应用

服务器发送异步确认消息 400如果在 10 分钟内客户端主动关闭 InputStream 和

StreamConnection或者是网络异常中断MDSBES 服务器会认为数据推送成功向应用服

务器发送异步确认消息 200

下面再讨论一个异常情况下的特例

结合以上描述的各种因素在极端的情况下会出现一些特殊的情况如上图所示假设

MDSBES 服务器在推送了数据 R数据 1 到数据 13 后出现网络中断同时客户端应用程序

正在缓慢地读取数据 R此时数据 1 到数据 10 会被保留在手持设备队列中客户端应用程

序在读取数据 R 后可以继续读取数据 1 到数据 10如果客户端应用程序在读取了数据后都

调用了 accept 函数则会产生对应用确认消息如图中的确认消息 R确认消息 1 到确认消

息 10但是因为网络的中断这些确认消息都无法到达 MDSBES 服务器如果网络在 10

钟内没有恢复MDSBES 服务器上所有等待确认消息的记录都会认识是过期的MDSBES

会向应用服务器发送异步确认消息 400此后即使网络恢复确认消息 R确认消息 1 到确

认消息 10 都不再有效

在这种特殊情况下即使客户端应用程序接收到数据并调用了 accept 函数MDSBES

服务器同样认为数据没有推送成功

根据以上的讨论应用依赖的推送同样不能保证客户端应用一定能收到数据但是在

应用依赖的推送方式下 MDSBES 返回 200 确认消息可以确定客户端已接收到数据因为 200

确认消息是由客户端应用程序调用 accept 函数触发的客户端应用程序可以在确认数据收

到后才调用 accept 函数通知应用服务器该数据已成功接收甚至可以在接收到数据后根

据数据内容决定不调用 accept 函数从而通过应用服务器该数据没有被接收

所以在应用依赖的情况下如果接收到 200 确认消息可以确定客户端已经接收到数据

如果接收到 400 确认消息只能认为客户端可能没有收到数据

因此应用依赖的推送方式更适合可靠性要求高的应用如果应用服务器接收到 200

确认消息就可以认为推送成功继续处理其它事务如果应用服务器接收到 400 确认消息则

需要组织数据重发直到接收到 200 确认消息为止这样就可以保证客户端一定能够接收到

需要推送的数据

如何保证数据推送的可靠性

通过以上几个章节的讨论我们知道虽然 MDSBES 服务器提供了很多机制保证数据

推送的可靠性但是都无法完全保证数据被客户端应用程序所接收

对于数据推送的可靠性保证一方面开发人员需要了解各个可能导致故障的因素在应

用编写的时候尽量减少故障出现的可能性对故障点进行规避另一方面开发人员也需要实

现额外的机制进行数据重发在故障点无法规避的情况下通过数据重发消除故障点的影响

应用依赖和非应用依赖的比较和选择

在实现推送应用的时候首要考虑的是有关应用依赖与非应用依赖的选择

应用依赖的推送形式在可靠性方面更优于非应用依赖的推送形式但是应用依赖的推送

形式对 MDSBES 应用服务器的潜在压力较大客户端应用程序的实现也较为复杂

非应用依赖的推送形式优势在于实现简单对 MDSBES 应用服务器形成的压力相对较

非应用依赖的推送形式从某种程度上讲有点类似于广播协议着重点是从服务器向客户

端发送大量数据并不看重客户端的接收情况从数据的应用类型来分非应用依赖的推送

更适合于天气信息最新报价等数据这些数据在客户端应用关闭时被自然丢弃短时间内

的多条数据有丢失影响也不大必竞用户不会关注 11 点零六分的天气和 11 点零七分的天气

有何不同

应用依赖的推送形式则类似于点对点协议不仅从服务器端向客户端发送数据同时也

关注客户端的接收情况应用依赖的推送更适合于 OAERP 这种应用这种应用需要保证

新的文档或者是记录一定能到达客户端应用丢失一条待办信息可能就会导致整个审批流程

的停滞

再从两种推送方式的本质差别看应用依赖并没有额外的机制消除故障点它的优势在

于它的异步确认消息更可信所以在选择了应用依赖的推送方式后进一步还是需要实现

额外的数据重发机制根据 MDSBES 所发送的异步确认消息实施数据重发

应用依赖的推送再加上结合异步确认消息的数据重发机制是保证推送数据可靠性的

最好方法

MDS 接收线程限制的处理

不管使用什么方式的推送应用开发时都需要考虑到 MDS 接收线程的限制不过以

现在的服务器处理能力正常情况下的 HTTP 推送请求可以在几毫秒或者是几十毫内处理完

成少量的 HTTP 推送不足于构成网络阻塞

需要考虑的是应用服务器端的推送程序在推送过程中不要在建立连接后才动态组织数

据这样会浪费 http 连接资源好的建议是预先组织好数据在建立 HTTP 连接后快速写完

数据尽快关闭连接

此外对于应用服务器的推送线程也要加于控制避免在短时间内无限制地启动新的线

程进行连接否则也容易造成连接过多而阻塞的情况

从网络层面来看调整应用服务器与 MDSBES 服务器之间的网络超时设置也是避免这

种问题的一种方法在允许的范围内调大网络超时时间可以让应用服务器的推送线程等待更

长的时间从而让 MDSBES 有足够的时间释放原有的连接

最后无论使用了什么方法调优都需要对应用服务器的推送程序进行异常处理当发

现网络超时异常的时候可以组织数据重发当然数据重发的间隔时间也要考虑不要因为

重发数据再次造成网络阻塞

MDS 推送队列限制的处理

相对而言MDS 推送队列限制造成的推送失败比 MDS 接收队列造成的推送失败更常见

已经发现的一个实际案例是周未的时候用户大量关机导致大量推送数据处于推送队列中

填满了推送队列从而导致 MDSBES 服务器不再响应新的推送请求一旦过期时间到达

有新的队列位置空闲又有大量新的推送数据重新占满推送队列

对于 MDS 推送队列的限制直观的做法是对队列参数进行调整如之前讨论的管理

员可以对队列的深度和过期时间进行调整

然而出于对过期时间的不正确理解管理员倾向于将过期时间调大当过期时间被调

大后推送给关机用户的数据在服务器占用队列的时间就变长导致 MDS 推送队列更容易

被占满

所以如果对过期时间进行调整的话方向是将过期时间调小过期时间越小等待数

据占用队列的时间就越短MDS 推送队列被占满的可能性就越小但是过期时间也不能

过小过期时间过小可能会导致过多的过期数据使应用服务器需要处理更多的数据重发

比如将过期时间调成 1 分钟用户走过一个人行遂道都可能导致数据发送失败

对于队列深度的调整方向是调大队列深度如从缺省的 1000 调整到 2000此时

MDSBES 服务器在同一时间可以保留更多的等待数据减少了队列占满的机会不过调大队

列深度会让 MDSBES 服务器保留更多的数据从而占用更多的服务器内存对服务器产生

更大的压力所以调用队列深度需要根据服务器的运行能力进行调用

从应用服务器设计角度应用 MDS 队列限制的方法是尽量减少等待数据的发送如发

送第一条数据的时候发现数据过期对于该用户的其它数据可以调整发送时间在更长的时

间间隔后才尝试发送

对于 MDS 队列限制应用服务器端的推送应用需要对 MDSBES 服务器返回的 503 错误

进行处理对于出现 503 错的数据组织重发同样重发的间隔也需要考虑不要因为数据

重发占用更多的推送队列也可以根据目标用户进行判断对于可能关机的用户暂时不重发

数据

手持设备端接收队列的处理

手持设备端接收队列并未从文档中明确列出是根据测试结果分析得出的手持设备端

目前也没有发现设置可以调整该队列所以客户端程序设计的时候不应该依赖手持设备端队

开发人员需要意识到的是手持设备端的接收队列是可溢出队列一旦推送数据从接收队

列出溢出客户端程序就无法提取该数据了所以在客户端应用开始过程中要注意加快推送

数据的提取过程在提取过程中不要对数据进行复杂的计算提取完成后尽快读取下一条数

据对于接收数据的处理和计算可以交由另一个线程完成

因为溢出数据对客户端应用而言不可见所以数据溢出时在客户端应用中也不会有异

常开发人员需要通过服务器返回的过期消息才能知道该数据推送失败

对于这一点客户端应用除了尽量避免数据溢出外无法在客户端通过其它机制进行补

应用依赖的客户端

对于应用依赖的客户端应用关键点是决定何时调用 accept 函数同时保证 InputStream

和 StreamConnection 被关闭

如之前讨论的使用应用依赖的推送关键是希望使用 MDSBES 发送给应用服务器的异

步确认消息从而对数据是否到达进行判断所以应用依赖的客户端应用需要保证正确调用

accept 函数

另外如果 InputStream 和 StreamConnection 没有关闭的话accept 函数所发出的确认

消息无法被 MDSBES 服务器收到开发人员在代码中应该主动关闭 InputStream 和

StreamConnection同时在程序发生异常时也需要在 catch 代码段对 InputStream 和

StreamConnection 进行关闭处理

异步确认消息处理

异步确认消息用于判断数据是否被成功推送至客户端应用要使用异步确认消息首先

要保证成功地无遗漏地接收所有从 MDSBES 服务器发送过来的异步确认消息

异步确认消息是一个 HTTP 消息消息比较短接收过程的并发压力不大下面是一个

异步确认消息的样例

Received notificationGET HTTP11

User-Agent RIM MDSCS

Accept

X-RIM-Push-ID pushID2081773768

X-RIM-Push-Destination mmtestbjsearbcn

X-RIM-Push-Status 400

Host localhost7778

其中最关键是的获取属性 X-RIM-Push-Status根据该属性的值判断数据推送是成功

(200)还是失败(400)当然属性 X-RIM-Push-ID 也非常重要用于确定该确认消息是针

对哪条推送数据的

如果应用服务器端通过确认消息发现数据推送失败则需要根据属性 X-RIM-Push-ID 重

新组织数据进行重发

Page 9: BES 服务器推送机制分析 - images.csdn.netimages.csdn.net/upimgs/lee/BBPDF/BESfwtsjzfx.pdf · 前言 数据推送是BlackBerry 应用平台的一大优势,在BlackBerry 应用平台上部署的应用可以

推送的数据

下面是侦听线程的 run 方法为保持程序的完整代码中使用到的类属性并没有修改

从该函数中看到的本函数没有定义的变量都是该类的类属性有关完整代码请参考

BlackBerry 开发环境所提供的标准样例

public void run()

StreamConnection stream = null

InputStream input = null

MDSPushInputStream pushInputStream=null

while (_stop)

try

synchronized(this)

_notify =

(StreamConnectionNotifier)Connectoropen(http55100)

while (_stop)

stream = _notifyacceptAndOpen()

try

input = streamopenInputStream()

pushInputStream= new

MDSPushInputStream((HttpServerConnection)stream input)

DataBuffer db = new DataBuffer()

byte[] data = new byte[256]

int chunk = 0

while ( -1 = (chunk = inputread(data)) )

dbwrite(data 0 chunk)

pushInputStreamaccept()

inputclose()

streamclose()

data = dbgetArray()

catch (IOException e1)

Systemerrprintln(e1toString())

if ( input = null )

try

inputclose()

catch (IOException e2)

if ( stream = null )

try

streamclose()

catch (IOException e2)

_notifyclose()

_notify = null

catch (IOException ioe)

if ( _notify = null )

try

_notifyclose()

_notify = null

catch ( IOException e )

以上代码中建立端口侦听的代码如下

_notify = (StreamConnectionNotifier)Connectoropen(http55100)

其中 URLldquohttp55100rdquo是用于侦听推送的特定格式55100 为需要侦听的端口两

个线程不能同时打开同一个端口的侦听所以在这里使用了 synchronized 对线程并发进行了

控制

建立侦听后可以通过函数 acceptAndOpen 开始侦听当线程调用该方法开始侦听的时候

线程会阻塞在这里直到有推送消息到达代码如下

stream = _notifyacceptAndOpen()

侦听之后的代码为获取连接的 InputStream从 InputStream中获取服务器端推送的数据

读取完数据后关闭 InputStream 和 StreamConnection然后重新调用 acceptAdnOpen 继续侦

听端口准备接收新的推送数据

开发人员如果希望使用应用依赖的推送在客户端必须将 InputStream 转换成

MDSPushInputStream同时在接收完数据后调用 MDSPushInputStream 的 accept 函数造知服

务器该数据已收到这也是应用依赖和非应用依赖在代码实现层面的最主要区别代码如下

pushInputStream= new

MDSPushInputStream((HttpServerConnection)stream input)

helliphellip

pushInputStreamaccept()

从良好的编程习惯来讲在连接使用后需要主动关闭所使用的连接和资源如

InputStreamStreamConnectionStreamConnectionNotifier 等所以在样例代码中也可以看

到该样例在异常处理阶段都对这些资源进行了关闭操作在关闭这些连接和资源的时候对

StreamConnectionNotifier 需要特别注意有可能在 StreamConnectionNotifier 中会保留有未

读取的推送数据需要调用 acceptAdnOpen 函数将保留的数据读取出来直到 acceptAdnOpen

处于阻塞状态

另外如果使用了应用依赖的推送客户端程序调用 MDSPushInputStream 的 accept 函

数后确认消息不会马上发出而是在对应用 InputStream 和 StreamConnection 关闭后才发出

所以对于应用依赖的推送保证 InputStream 和 StreamConnection 的关闭非常之重要

异步确认消息接收代码

异步消息的接收代码运行在服务器端在推送请求的 X-RIM-Push-NotifyURL 属性所指定

的服务器上侦听指定端口和应用服务器端的数据推送代码一样异步确认消息的接收程序

可以通过任何支持 HTTP 的语言编写主要的工作是侦听指定端口从指定端口获取 MDSBES

发送回来的确认消息根据 HTTP 协议获取相关属性并返回相关信息

下面以 java 代码为例对关键代码进行说明这段代码一个 java Application 的一部分是

侦听线程的 run 函数

public void run()

try

Systemoutprintln(Waiting for notification on port + 7778

+ )

while (true)

ServerSocket serverSocket = new ServerSocket(7778)

serverSocketsetSoTimeout(120000)

try

Socket clientSocket = serverSocketaccept()

InputStream input = clientSocketgetInputStream()

StringBuffer str = new StringBuffer()

int byteRead = inputread()

while ((byteRead = -1) ampamp (inputavailable() gt 0))

strappend((char) byteRead)

byteRead = inputread()

Systemoutprintln(strtoString())

OutputStream output = clientSocketgetOutputStream()

String response = HTTP10 200 OK

outputwrite(responsegetBytes())

outputflush()

outputclose()

inputclose()

clientSocketclose()

catch (SocketTimeoutException ste)

Systemoutprintln(Notification connection timeout

Restarting)

serverSocketclose()

catch (Exception exception)

exceptionprintStackTrace()

异步确认消息的接收代码比较简单基本上就是打开一个 Socket 侦听指定的端口本

例中是 7778然后不断地从该端口读取数据打开 Socket 侦听的代码如下

ServerSocket serverSocket = new ServerSocket(7778)

在异步消息接收过程中需要注意的是接收完 MDSBES 服务器的确认消息后应该给

MDSBES 服务器返回一个 200 的成功消息否则 MDSBES 服务器会认为发送确认消息失败

可能重次几次后再放弃这样异步消息接收程序有可能重复收到同一条确认消息返回成功

消息的代码如下

OutputStream output = clientSocketgetOutputStream()

String response = HTTP10 200 OK

outputwrite(responsegetBytes())

outputflush()

outputclose()

推送细节分析

了解基本的 MDS 推送知识是不足于编写一个可靠的数据推送程序的要保证数据推送

的可靠性必须了解整个推送过程的每一个细节本章节先从整体上描述了整个数据推送的

调用时序然后从不同的关键点详细讲解数据推送的细节

MDS 推送的整体时序

如上图所示数据推送主要是应用服务器MDS 服务器网络服务手机端几个组件

之间的交互在网络服务这一块我们可以假定这一层是稳定的不做考虑对于网络无法提

供服务等情况我们可以通过手持设备端关闭无线电或者是将手持设备放入无线屏蔽区等方

式模拟所以我们要考虑是应用服务器MDSBES 服务器手持设备这三部分

数据推送由应用服务器发生向 MDSBES 服务器发出 HTTP Post 请求第一个需要考虑

的是该 HTTP Post 请求是否能到达 MDSBES 服务器这一点在ldquo推送请求是否到达 MDSBES

服务器rdquo小节中有详细讨论

MDSBES 服务器在接收到 HTTP Post 请求后会对请求的格式进行判断如果格式错误

如指定目标不存在等则 MDSBES 服务器会返回 400 错误所以第二点要考虑是的如何保

证所有到达 MDSBES 服务器的请求都是合法的有关这一点在ldquo推送请求检查rdquo小节中有

详细描述

MDSBES 服务器发现推送数据格式正确以后还需要判断服务器上的队列是否有足够的

空间保存该数据如果没有发现空闲队列则丢弃该数据并向应用服务器返回 503 错误因此

第三点要考虑的是什么情况会导致服务器推送队列占满如何尽量避免这种情况有关这一

点在ldquoMDSBES 中的推送队列rdquo中有详细描述

MDSBES 服务器将数据推送到客户端以后由客户端程序将数据提取交由相应的应用

逻辑进行处理然后客户端应用在提取数据的时候并不一定能及时地将所有推送到的数据

获取所以第四点要考虑的是手持设备端是否也存在队列如何保证客户端程序将所有数据

都提取出来有关这一点在ldquo手持设备端对消息的处理rdquo小节中有详细描述

手持设备在接收推送数据后会通知 MDSBES 服务器MDSBES 服务器则向应用服务器

发送异步的确认消息通过返回数值 200 或者是 400 通知应用服务器推送数据是推送成功还

是推送失败异步确认消息对于应用开发者有特别的意义通过它可以确定数据是否到达客

户端从而决定是否再次推送数据然后很多开发商都发现异步确认消息的返回的ldquo200rdquo

和ldquo400rdquo并不能准备地反映数据的接收情况所以如何使用异步确认消息在不同的场

景下异步消息会包含什么不同值对于应用开发者而言非常重要有关这一点在小节ldquo异步

确认消息rdquo有详细描述

推送请求是否到达 MDSBES 服务器

有关推送请求是否到达 MDSBES 服务器 很多开发人员都会忽略认为推送请求是否

到达 MDSBES 服务器是由代码是否编写正确决定的一定会在代码测试阶段体现也来也

必须在代码编写阶段解决这个问题然而在生产环境确实有一些因素会导致应用服务器发送

的推送请求无法到达 MDSBES 服务器从而导致推送数据丢失的问题

推送请求无法到达 MDSBES 服务器的一个可能的直接原因是指定的服务器名不正确或

者是指定的端口号不正确原因可能是 BES 管理员修改了 BES 服务器的服务器名IP 地址或

者是推送监听端口

这种问题比较容易发现一方面是因为所有的 BlackBerry 用户此时都无法接收到推送数

据另一方面也因为应用服务器端会报网络连接的异常以 java 代码为例当指定的服务

器名或者是端口号不正确的情况下应用服务器端的推送程序会报以下异常

javanetConnectException Connection refused

或者是报域名无法解析等网络连接级别的异常

发现这种情况只需要检查代码中指定的服务器名和端口号是否和生产环境相同就可以

解决问题

推送请求无法到达 MDSBES 服务器的一个潜在可能性是推送请求的并发数量太大导致

MDSBES 服务器无法及时接收所有请求

这种问题比较难于发现因为部分用户仍然可以收到推送数据只有部分用户的部分数

据丢失了同时这种问题在功能性测试场景下不容易发生只有在大压力测试或者是存在

大量用户的生产环境中才容易出现

产生这种问题的根源是 MDSBES 服务器的并发接收限制MDS 服务器其实是运行在

Tomcat 服务器上一个服务Tomcat 服务器作为应用容器处理了低层的网络请求而 Tomcat

服务器在底层有并发网络请求的限制当网络连接达到服务器限制时新的推送连接会处理

等待状态如果在网络超时前仍无法建立连接则会出现网络超时异常

javanetConnectException Connection timed out connect

根据测试的结果不同版本的 BES 服务器在低层并发网络请求的数量上有差异以 200

个并发限制为例就是说同一时间点只能有 200 个连接可以向 MDSBES 服务器发送推送请

求第 201 个请求以后有所有请求都将进入等待状态如果在 TCPIP 超时限制到达之前

MDSBES 服务器仍无法释放前 200 个请求则等待状态的请求会因为网络超时而中断

当应用服务器采用多线程机制处理推送请求的情况下有可能出现以上情况如果因为业

务原因在短时间内出现大量等待推送的数据应用服务器则有可能启动多于 200 个的线程发

送推送请求如果需要推送的数据太大或者是推送线程需要动态对数据进行计算就有可

能导致线程所启动的连接在短时间内没有关闭当多于 200 个线程处于这种状态的时候

MDSBES 服务器就无法继续接收推送请求了这种问题的一个现象就是应用服务器端报网络

超时异常或者是连接被拒绝的异常

总的而言要保证数据推送的成功第一步需要保证推送数据到达 MDSBES 服务器

发现推送数据没有到达 MDSBES 服务器的情况时应该对应用服务器的日志进行检查进一

步确定问题的原因

推送请求检查

在 MDSBES 服务器接收到推送请求后MDSBES 服务器会对请求进行检查只有符合

推送格式的请求才被 MDSBES 服务器所接收当 MDSBES 服务器接收推送时会返回值为

200 的 HTTP 答复告知应用服务器已经接收该请求当 MDSBES 服务器拒绝推送请求时会

根据拒绝的原因返回相应的错误码可能返回的错误码包括 400403503 等下面对不同的

情况进行描述

第一种请求被拒绝的原因是推送请求的格式不对包括 URL 格式不正确和 HTTP 头不正

确两种情况两种情况 MDSBES 服务器都会返回 400 消息表示拒绝URL 格式不正确包括

URL 中不包含ldquoDESTINATIONrdquo参数或者是不包含ldquoPORTrdquo参数等HTTP 头不正确包括

PUSH ID 不唯一等因为推送请求中的 HTTP 头属性都有缺省值所以使用错误的 HTTP 头属

性并不会引发错误MDSBES 服务器只是忽略该参数如 HTTP 头ldquoX-RIM-PUSH-IDrdquo被误写

为ldquoX-RIMPUSHIDrdquoMDSBES 服务器并不会拒绝该请求只是使用ldquoX-RIM-PUSH-IDrdquo的缺

省值就是服务器随机生成一个唯一的 ID 给推送消息

对于 HTTP 协议而言服务器返回 400 代表服务器一般性错误所以返回消息会以异常

形式被捕获以 java 代码为类程序会捕获到以下网络异常

ldquojavaioIOException Server returned HTTP response code 400 for URL

httprdquo

第二种推送请求被拒绝的情况是推送请求指定的收件人不正确就是指应用服务器发送

推送请求时使用了正确的格式不过 DESTINATION 参数指定的收件人并不存在或者是该收件

人并不是 BlackBerry 用户此时 MDSBES 服务器会返回 403 错误读者需要注意 BES 服务

器 37 之前的版本对于这种情况会视作格式错误返回 400 错误

同样以 java 代码为例收件人不正确的情况下程序会捕获到以下网络异常

ldquojavaioIOException Server returned HTTP response code 403 for URL

httprdquo

第三种请求被拒绝的情况是 MDSBES 服务器上的推送队列已满无法再接收更多的推

送请求此时会返回 503 错误表示服务器暂时没有足够的资源处理该请求这种情况下 java

应用程序会捕获到以下异常

ldquojavaioIOException Server returned HTTP response code 503 for URL

httprdquo

这种情况与推送请求是否正确无关取决于 MDSBES 服务器运行情况关于 MDSBES

服务器的推送队列在下一节中我们才进行详细的讨论

如果数据推送请求通过了 MDSBES 服务器的消息检查则 MDSBES 服务器会将该数据

放入推送队列等待发送同时给应用服务器返回成功消息 200

成功消息是以 HTTP 的 ResponseCode 形式返回的开发人员可以通过 HTTP 连接的相应

方法获取在 java 代码中获取 ResponseCode 的函数是ldquo getResponseCoderdquo只有确认

MDSBES 服务器返回的 ResponseCode 是 200才能确定推送数据已经由 MDSBES 接收对

于其它的异常情况都需要由程序进行判断确定如何调整请求继续重试推送过程

有一点非常重要的是 MDSBES 接收该推送数据并不意味着该数据就一定能到达手持设

备端还有很多因素会导致手持设备端的应用接收不到该数据

MDSBES 中的推送队列

MDSBES 服务器有一个队列用于缓存推送请求因为手持设备并不是一定在线

MDSBES 服务器需要先将推送请求保留在服务端在合适的情况下将数据推送到手持设备端

后才从队列中将该请求移除

MDSBES 服务器上的推送队列是 MDS 推送的关键组件它的行为不仅仅影响应用服务

器端是否能够发送新的请求也影响到手持设备端是否能够接收到数据所以对于 MDS 推

送的开发人员必须了解 MDSBES 服务器上的推送队列

MDSBES 服务器上的推送队列有一个队列深度缺省为 1000也就是说 MDSBES 服务

器在同一时间只能缓存 1000 个推送请求该队列的深度可以由管理员在管理端修改

同时 MDSBES 服务器上的推送队列并不是永久保存推送数据它有一个过期时间缺

省为 10 分钟也就是说一个推送请求在 MDSBES 服务器上最多能够被保留 10 分钟同样

这个过期时间可以由管理员在管理端修改

推送队列的深度修改界面如下

推送队列的过期时间的修改界面如下(以毫秒为单位)

对于推送队列的进一步讨论是有关推送数据进入队列后的状态如何变化下面是推送数

据在队列中的状态变化图说明了推送数据在不同情况下的状态变化下图的各个状态中

淡蓝色的状态为起始状态深蓝色的状态为中间状态红色的状态为终止状态处于中间状

态的数据最终必然会转为终止状态的其中一种

当 MDSBES 服务器接收到推送数据后如果之前目标用户没有 5 个处于暂时失败的数

据包服务器会将数据放入等待发送的队列中推送的数据处于ldquo尝试发送rdquo状态处于ldquo尝

试发送rdquo会在很短的时间内占用 1000 个队列位置中的一个不管是成功还是失败它所占

用的队列位置很快被释放

然后 MDSBES 服务器尝试将数据推送到手持设备端如果此时对应的手持设备处于开

机状态网络一切正常则 MDSBES 服务器将数据发送给手持设备推送数据变成ldquo成功rdquo

状态MDSBES 服务器会向应用服务器发送异步的确认消息同时将数据从队列中移除注

意此时 MDSBES 服务器向应用服务器返回的确认消息并不一定是表示成功 200具体是

返回代表成功的 200 还是返回代表失败的 400 还取决于其它因素有关这一点我们在后面的

章节中详细讨论

如果 MDSBES 服务器尝试将数据推送到手持设备的时候手持设备处于关机状态则该

数据进入ldquo暂时失败rdquo状态处于ldquo暂时失败rdquo状态的数据不占用等待队列的队列位置某

一个目标用户最多只能有 5 条数据处于ldquo暂时失败rdquo状态目前没有明确的数据或者是文档

说明一个 MDSBES 服务器总共可以保留多少条ldquo暂时失败rdquo状态的数据

如果某一用户已有 5 条数据处于ldquo暂时失败rdquo状态MDSBES 服务器接收到该用户新

的推送请求时会直接将该数据转入ldquo等待rdquo状态每一条进入ldquo等待rdquo状态的推送数据会占

用推送队列中的一个位置当队列中的 1000 个位置都被占满的时候 MDSBES 服务器就停止

接收新的数据推送无论新的数据是推送给哪个用户

处于ldquo暂时失败rdquo和ldquo等待rdquo状态的数据会在服务器上保留 10 分钟(如上所述这一

时间可以由管理修改)10 分钟后处于这两种状态的数据将被丢弃进入ldquo过期rdquo状态同

时 MDSBES 服务器会向应用服务器发送异步消息这种情况下发送的异步确认消息一定是

代表失败的 400如果在 10 分钟内手持设备重新进入网络手持设备会发送消息到 MDSBES

服务器告知该手持设备已经上线MDSBES 服务器会将处理ldquo暂时失败rdquo和ldquo等待rdquo状态的

数据转成ldquo尝试发送状态rdquo重新发送该数据需要注意的是在 10 分钟的等待过程中

MDSBES 服务器如果没有接收到手持设备重新连接网络的消息则 MDSBES 服务器不会尝

试重新发送ldquo暂时失败rdquo和ldquo等待rdquo状态的数据

根据以上的分析可以对推出以下结果

1 当用户的手持设备在线时对该用户的数据推送会被马上处理该数据只在很短的时间

占用 1000 个等待队列中的一个位置

2 当用户的手持设备不在线时假设该服务器只有一个用户则服务器在 10 分钟内最多

可以接收该用户的 1005 条数据其中 5 条处于ldquo暂时失败rdquo状态1000 条在等待队列

3 如果用户的手持设备在数据发送给 MDSBES 服务器 10 分钟内重新接入网络MDSBES

服务器可以将该数据推送到手持设备端

4 如果 MDSBES 服务器的等待队列未满推送给新的用户的前 5 条数据不占用等待队列

5 如果 MDSBES 服务器的等待队列已满不管是由哪个用户的数据占满的MDSBES 服

务器都会停止接收所有用户的新的数据推送

总结而言在生产环境中保证 MDSBES 服务器正常工作的关键是要保证 MDSBES 服务

器的等待队列不被占满

手持设备端对推送数据的处理

MDSBES 服务器成功接收应用服务器推送的数据后会将数据推送到手持设备端在手持

设备端需要由客户端应用程序将推送的数据提取出来一个直观的猜测是手持设备端同样需

要一个队列保存数据否则客户端应用程序在提取前一条数据时新到达的数据就会直接丢

失需要验证的是这个队列有多深队列行为是否被系统隐藏经过测试证实手持设备端确

实也存在队列队列深度为 10而且可以反映在应用层其中最值得开发人员注意的是这

个队列是一个可溢出队列关于手持设备端推送数据队列的示意图如下

如图所示当客户端提取程序正在读取数据ldquoRrdquo如图中标有 R 的椭圆形所示如果

有更多的数据被推送到手持设备端所推送的数据会被放入设备的队列中一共有 10 条数

据可以被保留在设备队列中如椭圆形 1 到 10 所示此时如果有更多的数据被推送进来

设备队列已满则这些新的数据会溢出如椭圆形 111213 所示同时对于服务器端

而言数据 111213 已经被推送过一次服务器端不会再次尝试推送这些数据在这种

情况下数据 1 到 10 可以继续被客户端提取程序所提取而数据 111213 则丢失了

对于尚未推送到手持设备端的数据如数据 1415假设因为网络中断没有被推送到

手持设备端则仍保留在服务器端当网络恢复后数据 1415 仍可以被正常处理

对于确认消息(图中橙色圆形所示)的处理则更为复杂在异步确认消息一节中再作讨

从这一节的讨论得出的结果是数据到达手持设备端未必就能够被客户端提取程序提取

客户端提取程序有职责尽快提取数据防止推送数据的溢出溢出的数据在客户端无法再被

处理而且服务器端也不会尝试继续推送这些数据

异步确认消息(应用依赖和非应用依赖)

手持设备端在接收到推送数据后会通知 MDSBES 服务器这时 MDSBES 服务器会根据

最初的推送请求向指定的 URL 发送一个确认消息告知应用服务器某一条推送数据的最终

结果这就是异步确认消息异步确认消息以 http 协议发送给应用服务器消息 200 代表

数据推送成功400 代表数据推送失败

异步确认消息是应用服务器判断推送是否成功的有效工具但是很多开发人员因为对

异步确认消息的机制不了解错误地使用了异步确认消息从而导致数据的丢失所以我们

需要详细了解异步确认消息的工作机制

要讨论异步确认消息的工作机制必然涉及到应用依赖与非应用依赖应用依赖的推送

和非应用依赖的推送对应的异步确认消息工作机制是不同的

下面先讨论非应用依赖的场景这也是很多开发商使用的场景

如果推送是非应用依赖的数据是以到达手持设备为成功标志的只要数据到达了手持

设备MDSBES 服务器就认为该数据推送成功会给应用服务器发送异步确认消息 200

如果数据推送时手持设备不在线则 10 分钟内 MDSBES 服务器会在服务器上保留该数

据如果 10 分钟内手持设备重新接入网络则 MDSBES 服务器会尝试发送该数据一旦数

据到达手持设备MDSBES 服务器都会给应用服务器发送异步确认消息 200

如果 10 分钟内手设备没有接入网络则该推送数据过期MDSBES 服务器会丢弃该数

据同时给应用服务器发送异步确认消息 400

可以看出非应用依赖的推送方式并不能保证客户端应用程序接收到数据在非应用依

赖的推送方式下 MDSBES 返回 200 确认消息也不能认为客户端已接收到数据如果数据送

达手持设备的时候客户端应用程序没有启动所推送的数据会在手持设备端丢失而

MDSBES 服务器认为数据已成功送达或者是客户端应用程序已启动但是没有及时读取推

送数据在手持设备端溢出的数据也会丢失MDSBES 服务器同样认为数据已成功送达

所以在非应用依赖的情况下如果接收到 400 确认消息可以确定客户端没有收到数据

如果接收到 200 确认消息只能认为客户端可能收到了数据

从以上的讨论看非应用依赖的推送方式并不适合用于可靠性要求较高的应用对于可

靠性要求较高的应用需要知道客户端应用程序是否接收到了数据应用依赖的推送方式就

是为此而设计的如果推送是应用依赖的数据是以客户端应用程序确认为成功标志的只

要在应用程序确认后MDSBES 服务器才认为数据推送成功这时才给应用服务器发送异步

确认消息 200

有关客户端应用程序如何确认收到数据在ldquo客户端接收关键代码rdquo一节中有描述简

单而言就是客户端应用程序通过调用 MDSPushInputStream 实例的 accept 函数确认数据收

到如果客户端应用程序在接收到数据后没有调用 accept 函数MDSBES 服务器会认为数

据推送失败给应用服务器发送异步确认消息 400另外一点曾经强调过的是accept 函数

调用后只有 InputStream 和 StreamConnection 都关闭了确认消息才被发送给 MDSBES 服

务器在极端的情况下这一点会影响到异步确认消息的结果

下面具体分析各种情景

如果手持设备在线但是客户端应用程序没有启动推送数据到达手持设备端后没有程

序侦听推送端口当然也没有程序会调用 accept 函数此时 MDSBES 服务器会认为数据推

送失败向应用服务器发送异步确认消息 400

如果手持设备在线客户端应用程序在读取推送数据后没有调用 accept 函数直接关闭

连接此时 MDSBES 服务器同样会认为数据推送失败向应用服务器发送异步确认消息 400

在这种情景下即使客户端接收到了数据但应用服务器仍收到失败消息因为作为成功标

志的 accept 函数并没有调用

如果手持设备在线客户端应用程序在读取推送数据后调用了 accept 函数并关闭连

接此时 MDSBES 服务器会认为数据推送成功向应用服务器发送异步确认消息 200

对于手持设备不在线的情况结果与非应用依赖相似10 分钟内如果手持设备不接入

网络的话推送数据会在 MDSBES 服务器上过期MDSBES 服务器会向应用服务器发送异

步确认消息 400

如果 10 分钟内手持设备重新接入网络MDSBES 服务器会尝试发送数据至手持设备端

还是以 accept 函数的调用为推送成功的标志如果数据重新发送成功但是客户端没有调

用 accept 函数则 MDSBES 服务器会向应用服务器发送异步确认消息 200

对于推送到手持设备端但是溢出的数据因为没有程序能够读取该数据并针对该数据调

用 accept 函数MDSBES 服务器会一直等待到 10 分钟后向应用服务器发送异步确认消息

400认为数据过期

一个特殊的情况是客户端应用程序在接收到数据后调用了 accept但是没有关闭

InputStream 和 StreamConnection此时 MDSBES 服务器会认识客户端还在处理数据会继

续等待如果服务器等待时候超过 10 分钟MDSBES 服务器会认为数据推送失败向应用

服务器发送异步确认消息 400如果在 10 分钟内客户端主动关闭 InputStream 和

StreamConnection或者是网络异常中断MDSBES 服务器会认为数据推送成功向应用服

务器发送异步确认消息 200

下面再讨论一个异常情况下的特例

结合以上描述的各种因素在极端的情况下会出现一些特殊的情况如上图所示假设

MDSBES 服务器在推送了数据 R数据 1 到数据 13 后出现网络中断同时客户端应用程序

正在缓慢地读取数据 R此时数据 1 到数据 10 会被保留在手持设备队列中客户端应用程

序在读取数据 R 后可以继续读取数据 1 到数据 10如果客户端应用程序在读取了数据后都

调用了 accept 函数则会产生对应用确认消息如图中的确认消息 R确认消息 1 到确认消

息 10但是因为网络的中断这些确认消息都无法到达 MDSBES 服务器如果网络在 10

钟内没有恢复MDSBES 服务器上所有等待确认消息的记录都会认识是过期的MDSBES

会向应用服务器发送异步确认消息 400此后即使网络恢复确认消息 R确认消息 1 到确

认消息 10 都不再有效

在这种特殊情况下即使客户端应用程序接收到数据并调用了 accept 函数MDSBES

服务器同样认为数据没有推送成功

根据以上的讨论应用依赖的推送同样不能保证客户端应用一定能收到数据但是在

应用依赖的推送方式下 MDSBES 返回 200 确认消息可以确定客户端已接收到数据因为 200

确认消息是由客户端应用程序调用 accept 函数触发的客户端应用程序可以在确认数据收

到后才调用 accept 函数通知应用服务器该数据已成功接收甚至可以在接收到数据后根

据数据内容决定不调用 accept 函数从而通过应用服务器该数据没有被接收

所以在应用依赖的情况下如果接收到 200 确认消息可以确定客户端已经接收到数据

如果接收到 400 确认消息只能认为客户端可能没有收到数据

因此应用依赖的推送方式更适合可靠性要求高的应用如果应用服务器接收到 200

确认消息就可以认为推送成功继续处理其它事务如果应用服务器接收到 400 确认消息则

需要组织数据重发直到接收到 200 确认消息为止这样就可以保证客户端一定能够接收到

需要推送的数据

如何保证数据推送的可靠性

通过以上几个章节的讨论我们知道虽然 MDSBES 服务器提供了很多机制保证数据

推送的可靠性但是都无法完全保证数据被客户端应用程序所接收

对于数据推送的可靠性保证一方面开发人员需要了解各个可能导致故障的因素在应

用编写的时候尽量减少故障出现的可能性对故障点进行规避另一方面开发人员也需要实

现额外的机制进行数据重发在故障点无法规避的情况下通过数据重发消除故障点的影响

应用依赖和非应用依赖的比较和选择

在实现推送应用的时候首要考虑的是有关应用依赖与非应用依赖的选择

应用依赖的推送形式在可靠性方面更优于非应用依赖的推送形式但是应用依赖的推送

形式对 MDSBES 应用服务器的潜在压力较大客户端应用程序的实现也较为复杂

非应用依赖的推送形式优势在于实现简单对 MDSBES 应用服务器形成的压力相对较

非应用依赖的推送形式从某种程度上讲有点类似于广播协议着重点是从服务器向客户

端发送大量数据并不看重客户端的接收情况从数据的应用类型来分非应用依赖的推送

更适合于天气信息最新报价等数据这些数据在客户端应用关闭时被自然丢弃短时间内

的多条数据有丢失影响也不大必竞用户不会关注 11 点零六分的天气和 11 点零七分的天气

有何不同

应用依赖的推送形式则类似于点对点协议不仅从服务器端向客户端发送数据同时也

关注客户端的接收情况应用依赖的推送更适合于 OAERP 这种应用这种应用需要保证

新的文档或者是记录一定能到达客户端应用丢失一条待办信息可能就会导致整个审批流程

的停滞

再从两种推送方式的本质差别看应用依赖并没有额外的机制消除故障点它的优势在

于它的异步确认消息更可信所以在选择了应用依赖的推送方式后进一步还是需要实现

额外的数据重发机制根据 MDSBES 所发送的异步确认消息实施数据重发

应用依赖的推送再加上结合异步确认消息的数据重发机制是保证推送数据可靠性的

最好方法

MDS 接收线程限制的处理

不管使用什么方式的推送应用开发时都需要考虑到 MDS 接收线程的限制不过以

现在的服务器处理能力正常情况下的 HTTP 推送请求可以在几毫秒或者是几十毫内处理完

成少量的 HTTP 推送不足于构成网络阻塞

需要考虑的是应用服务器端的推送程序在推送过程中不要在建立连接后才动态组织数

据这样会浪费 http 连接资源好的建议是预先组织好数据在建立 HTTP 连接后快速写完

数据尽快关闭连接

此外对于应用服务器的推送线程也要加于控制避免在短时间内无限制地启动新的线

程进行连接否则也容易造成连接过多而阻塞的情况

从网络层面来看调整应用服务器与 MDSBES 服务器之间的网络超时设置也是避免这

种问题的一种方法在允许的范围内调大网络超时时间可以让应用服务器的推送线程等待更

长的时间从而让 MDSBES 有足够的时间释放原有的连接

最后无论使用了什么方法调优都需要对应用服务器的推送程序进行异常处理当发

现网络超时异常的时候可以组织数据重发当然数据重发的间隔时间也要考虑不要因为

重发数据再次造成网络阻塞

MDS 推送队列限制的处理

相对而言MDS 推送队列限制造成的推送失败比 MDS 接收队列造成的推送失败更常见

已经发现的一个实际案例是周未的时候用户大量关机导致大量推送数据处于推送队列中

填满了推送队列从而导致 MDSBES 服务器不再响应新的推送请求一旦过期时间到达

有新的队列位置空闲又有大量新的推送数据重新占满推送队列

对于 MDS 推送队列的限制直观的做法是对队列参数进行调整如之前讨论的管理

员可以对队列的深度和过期时间进行调整

然而出于对过期时间的不正确理解管理员倾向于将过期时间调大当过期时间被调

大后推送给关机用户的数据在服务器占用队列的时间就变长导致 MDS 推送队列更容易

被占满

所以如果对过期时间进行调整的话方向是将过期时间调小过期时间越小等待数

据占用队列的时间就越短MDS 推送队列被占满的可能性就越小但是过期时间也不能

过小过期时间过小可能会导致过多的过期数据使应用服务器需要处理更多的数据重发

比如将过期时间调成 1 分钟用户走过一个人行遂道都可能导致数据发送失败

对于队列深度的调整方向是调大队列深度如从缺省的 1000 调整到 2000此时

MDSBES 服务器在同一时间可以保留更多的等待数据减少了队列占满的机会不过调大队

列深度会让 MDSBES 服务器保留更多的数据从而占用更多的服务器内存对服务器产生

更大的压力所以调用队列深度需要根据服务器的运行能力进行调用

从应用服务器设计角度应用 MDS 队列限制的方法是尽量减少等待数据的发送如发

送第一条数据的时候发现数据过期对于该用户的其它数据可以调整发送时间在更长的时

间间隔后才尝试发送

对于 MDS 队列限制应用服务器端的推送应用需要对 MDSBES 服务器返回的 503 错误

进行处理对于出现 503 错的数据组织重发同样重发的间隔也需要考虑不要因为数据

重发占用更多的推送队列也可以根据目标用户进行判断对于可能关机的用户暂时不重发

数据

手持设备端接收队列的处理

手持设备端接收队列并未从文档中明确列出是根据测试结果分析得出的手持设备端

目前也没有发现设置可以调整该队列所以客户端程序设计的时候不应该依赖手持设备端队

开发人员需要意识到的是手持设备端的接收队列是可溢出队列一旦推送数据从接收队

列出溢出客户端程序就无法提取该数据了所以在客户端应用开始过程中要注意加快推送

数据的提取过程在提取过程中不要对数据进行复杂的计算提取完成后尽快读取下一条数

据对于接收数据的处理和计算可以交由另一个线程完成

因为溢出数据对客户端应用而言不可见所以数据溢出时在客户端应用中也不会有异

常开发人员需要通过服务器返回的过期消息才能知道该数据推送失败

对于这一点客户端应用除了尽量避免数据溢出外无法在客户端通过其它机制进行补

应用依赖的客户端

对于应用依赖的客户端应用关键点是决定何时调用 accept 函数同时保证 InputStream

和 StreamConnection 被关闭

如之前讨论的使用应用依赖的推送关键是希望使用 MDSBES 发送给应用服务器的异

步确认消息从而对数据是否到达进行判断所以应用依赖的客户端应用需要保证正确调用

accept 函数

另外如果 InputStream 和 StreamConnection 没有关闭的话accept 函数所发出的确认

消息无法被 MDSBES 服务器收到开发人员在代码中应该主动关闭 InputStream 和

StreamConnection同时在程序发生异常时也需要在 catch 代码段对 InputStream 和

StreamConnection 进行关闭处理

异步确认消息处理

异步确认消息用于判断数据是否被成功推送至客户端应用要使用异步确认消息首先

要保证成功地无遗漏地接收所有从 MDSBES 服务器发送过来的异步确认消息

异步确认消息是一个 HTTP 消息消息比较短接收过程的并发压力不大下面是一个

异步确认消息的样例

Received notificationGET HTTP11

User-Agent RIM MDSCS

Accept

X-RIM-Push-ID pushID2081773768

X-RIM-Push-Destination mmtestbjsearbcn

X-RIM-Push-Status 400

Host localhost7778

其中最关键是的获取属性 X-RIM-Push-Status根据该属性的值判断数据推送是成功

(200)还是失败(400)当然属性 X-RIM-Push-ID 也非常重要用于确定该确认消息是针

对哪条推送数据的

如果应用服务器端通过确认消息发现数据推送失败则需要根据属性 X-RIM-Push-ID 重

新组织数据进行重发

Page 10: BES 服务器推送机制分析 - images.csdn.netimages.csdn.net/upimgs/lee/BBPDF/BESfwtsjzfx.pdf · 前言 数据推送是BlackBerry 应用平台的一大优势,在BlackBerry 应用平台上部署的应用可以

catch (IOException e1)

Systemerrprintln(e1toString())

if ( input = null )

try

inputclose()

catch (IOException e2)

if ( stream = null )

try

streamclose()

catch (IOException e2)

_notifyclose()

_notify = null

catch (IOException ioe)

if ( _notify = null )

try

_notifyclose()

_notify = null

catch ( IOException e )

以上代码中建立端口侦听的代码如下

_notify = (StreamConnectionNotifier)Connectoropen(http55100)

其中 URLldquohttp55100rdquo是用于侦听推送的特定格式55100 为需要侦听的端口两

个线程不能同时打开同一个端口的侦听所以在这里使用了 synchronized 对线程并发进行了

控制

建立侦听后可以通过函数 acceptAndOpen 开始侦听当线程调用该方法开始侦听的时候

线程会阻塞在这里直到有推送消息到达代码如下

stream = _notifyacceptAndOpen()

侦听之后的代码为获取连接的 InputStream从 InputStream中获取服务器端推送的数据

读取完数据后关闭 InputStream 和 StreamConnection然后重新调用 acceptAdnOpen 继续侦

听端口准备接收新的推送数据

开发人员如果希望使用应用依赖的推送在客户端必须将 InputStream 转换成

MDSPushInputStream同时在接收完数据后调用 MDSPushInputStream 的 accept 函数造知服

务器该数据已收到这也是应用依赖和非应用依赖在代码实现层面的最主要区别代码如下

pushInputStream= new

MDSPushInputStream((HttpServerConnection)stream input)

helliphellip

pushInputStreamaccept()

从良好的编程习惯来讲在连接使用后需要主动关闭所使用的连接和资源如

InputStreamStreamConnectionStreamConnectionNotifier 等所以在样例代码中也可以看

到该样例在异常处理阶段都对这些资源进行了关闭操作在关闭这些连接和资源的时候对

StreamConnectionNotifier 需要特别注意有可能在 StreamConnectionNotifier 中会保留有未

读取的推送数据需要调用 acceptAdnOpen 函数将保留的数据读取出来直到 acceptAdnOpen

处于阻塞状态

另外如果使用了应用依赖的推送客户端程序调用 MDSPushInputStream 的 accept 函

数后确认消息不会马上发出而是在对应用 InputStream 和 StreamConnection 关闭后才发出

所以对于应用依赖的推送保证 InputStream 和 StreamConnection 的关闭非常之重要

异步确认消息接收代码

异步消息的接收代码运行在服务器端在推送请求的 X-RIM-Push-NotifyURL 属性所指定

的服务器上侦听指定端口和应用服务器端的数据推送代码一样异步确认消息的接收程序

可以通过任何支持 HTTP 的语言编写主要的工作是侦听指定端口从指定端口获取 MDSBES

发送回来的确认消息根据 HTTP 协议获取相关属性并返回相关信息

下面以 java 代码为例对关键代码进行说明这段代码一个 java Application 的一部分是

侦听线程的 run 函数

public void run()

try

Systemoutprintln(Waiting for notification on port + 7778

+ )

while (true)

ServerSocket serverSocket = new ServerSocket(7778)

serverSocketsetSoTimeout(120000)

try

Socket clientSocket = serverSocketaccept()

InputStream input = clientSocketgetInputStream()

StringBuffer str = new StringBuffer()

int byteRead = inputread()

while ((byteRead = -1) ampamp (inputavailable() gt 0))

strappend((char) byteRead)

byteRead = inputread()

Systemoutprintln(strtoString())

OutputStream output = clientSocketgetOutputStream()

String response = HTTP10 200 OK

outputwrite(responsegetBytes())

outputflush()

outputclose()

inputclose()

clientSocketclose()

catch (SocketTimeoutException ste)

Systemoutprintln(Notification connection timeout

Restarting)

serverSocketclose()

catch (Exception exception)

exceptionprintStackTrace()

异步确认消息的接收代码比较简单基本上就是打开一个 Socket 侦听指定的端口本

例中是 7778然后不断地从该端口读取数据打开 Socket 侦听的代码如下

ServerSocket serverSocket = new ServerSocket(7778)

在异步消息接收过程中需要注意的是接收完 MDSBES 服务器的确认消息后应该给

MDSBES 服务器返回一个 200 的成功消息否则 MDSBES 服务器会认为发送确认消息失败

可能重次几次后再放弃这样异步消息接收程序有可能重复收到同一条确认消息返回成功

消息的代码如下

OutputStream output = clientSocketgetOutputStream()

String response = HTTP10 200 OK

outputwrite(responsegetBytes())

outputflush()

outputclose()

推送细节分析

了解基本的 MDS 推送知识是不足于编写一个可靠的数据推送程序的要保证数据推送

的可靠性必须了解整个推送过程的每一个细节本章节先从整体上描述了整个数据推送的

调用时序然后从不同的关键点详细讲解数据推送的细节

MDS 推送的整体时序

如上图所示数据推送主要是应用服务器MDS 服务器网络服务手机端几个组件

之间的交互在网络服务这一块我们可以假定这一层是稳定的不做考虑对于网络无法提

供服务等情况我们可以通过手持设备端关闭无线电或者是将手持设备放入无线屏蔽区等方

式模拟所以我们要考虑是应用服务器MDSBES 服务器手持设备这三部分

数据推送由应用服务器发生向 MDSBES 服务器发出 HTTP Post 请求第一个需要考虑

的是该 HTTP Post 请求是否能到达 MDSBES 服务器这一点在ldquo推送请求是否到达 MDSBES

服务器rdquo小节中有详细讨论

MDSBES 服务器在接收到 HTTP Post 请求后会对请求的格式进行判断如果格式错误

如指定目标不存在等则 MDSBES 服务器会返回 400 错误所以第二点要考虑是的如何保

证所有到达 MDSBES 服务器的请求都是合法的有关这一点在ldquo推送请求检查rdquo小节中有

详细描述

MDSBES 服务器发现推送数据格式正确以后还需要判断服务器上的队列是否有足够的

空间保存该数据如果没有发现空闲队列则丢弃该数据并向应用服务器返回 503 错误因此

第三点要考虑的是什么情况会导致服务器推送队列占满如何尽量避免这种情况有关这一

点在ldquoMDSBES 中的推送队列rdquo中有详细描述

MDSBES 服务器将数据推送到客户端以后由客户端程序将数据提取交由相应的应用

逻辑进行处理然后客户端应用在提取数据的时候并不一定能及时地将所有推送到的数据

获取所以第四点要考虑的是手持设备端是否也存在队列如何保证客户端程序将所有数据

都提取出来有关这一点在ldquo手持设备端对消息的处理rdquo小节中有详细描述

手持设备在接收推送数据后会通知 MDSBES 服务器MDSBES 服务器则向应用服务器

发送异步的确认消息通过返回数值 200 或者是 400 通知应用服务器推送数据是推送成功还

是推送失败异步确认消息对于应用开发者有特别的意义通过它可以确定数据是否到达客

户端从而决定是否再次推送数据然后很多开发商都发现异步确认消息的返回的ldquo200rdquo

和ldquo400rdquo并不能准备地反映数据的接收情况所以如何使用异步确认消息在不同的场

景下异步消息会包含什么不同值对于应用开发者而言非常重要有关这一点在小节ldquo异步

确认消息rdquo有详细描述

推送请求是否到达 MDSBES 服务器

有关推送请求是否到达 MDSBES 服务器 很多开发人员都会忽略认为推送请求是否

到达 MDSBES 服务器是由代码是否编写正确决定的一定会在代码测试阶段体现也来也

必须在代码编写阶段解决这个问题然而在生产环境确实有一些因素会导致应用服务器发送

的推送请求无法到达 MDSBES 服务器从而导致推送数据丢失的问题

推送请求无法到达 MDSBES 服务器的一个可能的直接原因是指定的服务器名不正确或

者是指定的端口号不正确原因可能是 BES 管理员修改了 BES 服务器的服务器名IP 地址或

者是推送监听端口

这种问题比较容易发现一方面是因为所有的 BlackBerry 用户此时都无法接收到推送数

据另一方面也因为应用服务器端会报网络连接的异常以 java 代码为例当指定的服务

器名或者是端口号不正确的情况下应用服务器端的推送程序会报以下异常

javanetConnectException Connection refused

或者是报域名无法解析等网络连接级别的异常

发现这种情况只需要检查代码中指定的服务器名和端口号是否和生产环境相同就可以

解决问题

推送请求无法到达 MDSBES 服务器的一个潜在可能性是推送请求的并发数量太大导致

MDSBES 服务器无法及时接收所有请求

这种问题比较难于发现因为部分用户仍然可以收到推送数据只有部分用户的部分数

据丢失了同时这种问题在功能性测试场景下不容易发生只有在大压力测试或者是存在

大量用户的生产环境中才容易出现

产生这种问题的根源是 MDSBES 服务器的并发接收限制MDS 服务器其实是运行在

Tomcat 服务器上一个服务Tomcat 服务器作为应用容器处理了低层的网络请求而 Tomcat

服务器在底层有并发网络请求的限制当网络连接达到服务器限制时新的推送连接会处理

等待状态如果在网络超时前仍无法建立连接则会出现网络超时异常

javanetConnectException Connection timed out connect

根据测试的结果不同版本的 BES 服务器在低层并发网络请求的数量上有差异以 200

个并发限制为例就是说同一时间点只能有 200 个连接可以向 MDSBES 服务器发送推送请

求第 201 个请求以后有所有请求都将进入等待状态如果在 TCPIP 超时限制到达之前

MDSBES 服务器仍无法释放前 200 个请求则等待状态的请求会因为网络超时而中断

当应用服务器采用多线程机制处理推送请求的情况下有可能出现以上情况如果因为业

务原因在短时间内出现大量等待推送的数据应用服务器则有可能启动多于 200 个的线程发

送推送请求如果需要推送的数据太大或者是推送线程需要动态对数据进行计算就有可

能导致线程所启动的连接在短时间内没有关闭当多于 200 个线程处于这种状态的时候

MDSBES 服务器就无法继续接收推送请求了这种问题的一个现象就是应用服务器端报网络

超时异常或者是连接被拒绝的异常

总的而言要保证数据推送的成功第一步需要保证推送数据到达 MDSBES 服务器

发现推送数据没有到达 MDSBES 服务器的情况时应该对应用服务器的日志进行检查进一

步确定问题的原因

推送请求检查

在 MDSBES 服务器接收到推送请求后MDSBES 服务器会对请求进行检查只有符合

推送格式的请求才被 MDSBES 服务器所接收当 MDSBES 服务器接收推送时会返回值为

200 的 HTTP 答复告知应用服务器已经接收该请求当 MDSBES 服务器拒绝推送请求时会

根据拒绝的原因返回相应的错误码可能返回的错误码包括 400403503 等下面对不同的

情况进行描述

第一种请求被拒绝的原因是推送请求的格式不对包括 URL 格式不正确和 HTTP 头不正

确两种情况两种情况 MDSBES 服务器都会返回 400 消息表示拒绝URL 格式不正确包括

URL 中不包含ldquoDESTINATIONrdquo参数或者是不包含ldquoPORTrdquo参数等HTTP 头不正确包括

PUSH ID 不唯一等因为推送请求中的 HTTP 头属性都有缺省值所以使用错误的 HTTP 头属

性并不会引发错误MDSBES 服务器只是忽略该参数如 HTTP 头ldquoX-RIM-PUSH-IDrdquo被误写

为ldquoX-RIMPUSHIDrdquoMDSBES 服务器并不会拒绝该请求只是使用ldquoX-RIM-PUSH-IDrdquo的缺

省值就是服务器随机生成一个唯一的 ID 给推送消息

对于 HTTP 协议而言服务器返回 400 代表服务器一般性错误所以返回消息会以异常

形式被捕获以 java 代码为类程序会捕获到以下网络异常

ldquojavaioIOException Server returned HTTP response code 400 for URL

httprdquo

第二种推送请求被拒绝的情况是推送请求指定的收件人不正确就是指应用服务器发送

推送请求时使用了正确的格式不过 DESTINATION 参数指定的收件人并不存在或者是该收件

人并不是 BlackBerry 用户此时 MDSBES 服务器会返回 403 错误读者需要注意 BES 服务

器 37 之前的版本对于这种情况会视作格式错误返回 400 错误

同样以 java 代码为例收件人不正确的情况下程序会捕获到以下网络异常

ldquojavaioIOException Server returned HTTP response code 403 for URL

httprdquo

第三种请求被拒绝的情况是 MDSBES 服务器上的推送队列已满无法再接收更多的推

送请求此时会返回 503 错误表示服务器暂时没有足够的资源处理该请求这种情况下 java

应用程序会捕获到以下异常

ldquojavaioIOException Server returned HTTP response code 503 for URL

httprdquo

这种情况与推送请求是否正确无关取决于 MDSBES 服务器运行情况关于 MDSBES

服务器的推送队列在下一节中我们才进行详细的讨论

如果数据推送请求通过了 MDSBES 服务器的消息检查则 MDSBES 服务器会将该数据

放入推送队列等待发送同时给应用服务器返回成功消息 200

成功消息是以 HTTP 的 ResponseCode 形式返回的开发人员可以通过 HTTP 连接的相应

方法获取在 java 代码中获取 ResponseCode 的函数是ldquo getResponseCoderdquo只有确认

MDSBES 服务器返回的 ResponseCode 是 200才能确定推送数据已经由 MDSBES 接收对

于其它的异常情况都需要由程序进行判断确定如何调整请求继续重试推送过程

有一点非常重要的是 MDSBES 接收该推送数据并不意味着该数据就一定能到达手持设

备端还有很多因素会导致手持设备端的应用接收不到该数据

MDSBES 中的推送队列

MDSBES 服务器有一个队列用于缓存推送请求因为手持设备并不是一定在线

MDSBES 服务器需要先将推送请求保留在服务端在合适的情况下将数据推送到手持设备端

后才从队列中将该请求移除

MDSBES 服务器上的推送队列是 MDS 推送的关键组件它的行为不仅仅影响应用服务

器端是否能够发送新的请求也影响到手持设备端是否能够接收到数据所以对于 MDS 推

送的开发人员必须了解 MDSBES 服务器上的推送队列

MDSBES 服务器上的推送队列有一个队列深度缺省为 1000也就是说 MDSBES 服务

器在同一时间只能缓存 1000 个推送请求该队列的深度可以由管理员在管理端修改

同时 MDSBES 服务器上的推送队列并不是永久保存推送数据它有一个过期时间缺

省为 10 分钟也就是说一个推送请求在 MDSBES 服务器上最多能够被保留 10 分钟同样

这个过期时间可以由管理员在管理端修改

推送队列的深度修改界面如下

推送队列的过期时间的修改界面如下(以毫秒为单位)

对于推送队列的进一步讨论是有关推送数据进入队列后的状态如何变化下面是推送数

据在队列中的状态变化图说明了推送数据在不同情况下的状态变化下图的各个状态中

淡蓝色的状态为起始状态深蓝色的状态为中间状态红色的状态为终止状态处于中间状

态的数据最终必然会转为终止状态的其中一种

当 MDSBES 服务器接收到推送数据后如果之前目标用户没有 5 个处于暂时失败的数

据包服务器会将数据放入等待发送的队列中推送的数据处于ldquo尝试发送rdquo状态处于ldquo尝

试发送rdquo会在很短的时间内占用 1000 个队列位置中的一个不管是成功还是失败它所占

用的队列位置很快被释放

然后 MDSBES 服务器尝试将数据推送到手持设备端如果此时对应的手持设备处于开

机状态网络一切正常则 MDSBES 服务器将数据发送给手持设备推送数据变成ldquo成功rdquo

状态MDSBES 服务器会向应用服务器发送异步的确认消息同时将数据从队列中移除注

意此时 MDSBES 服务器向应用服务器返回的确认消息并不一定是表示成功 200具体是

返回代表成功的 200 还是返回代表失败的 400 还取决于其它因素有关这一点我们在后面的

章节中详细讨论

如果 MDSBES 服务器尝试将数据推送到手持设备的时候手持设备处于关机状态则该

数据进入ldquo暂时失败rdquo状态处于ldquo暂时失败rdquo状态的数据不占用等待队列的队列位置某

一个目标用户最多只能有 5 条数据处于ldquo暂时失败rdquo状态目前没有明确的数据或者是文档

说明一个 MDSBES 服务器总共可以保留多少条ldquo暂时失败rdquo状态的数据

如果某一用户已有 5 条数据处于ldquo暂时失败rdquo状态MDSBES 服务器接收到该用户新

的推送请求时会直接将该数据转入ldquo等待rdquo状态每一条进入ldquo等待rdquo状态的推送数据会占

用推送队列中的一个位置当队列中的 1000 个位置都被占满的时候 MDSBES 服务器就停止

接收新的数据推送无论新的数据是推送给哪个用户

处于ldquo暂时失败rdquo和ldquo等待rdquo状态的数据会在服务器上保留 10 分钟(如上所述这一

时间可以由管理修改)10 分钟后处于这两种状态的数据将被丢弃进入ldquo过期rdquo状态同

时 MDSBES 服务器会向应用服务器发送异步消息这种情况下发送的异步确认消息一定是

代表失败的 400如果在 10 分钟内手持设备重新进入网络手持设备会发送消息到 MDSBES

服务器告知该手持设备已经上线MDSBES 服务器会将处理ldquo暂时失败rdquo和ldquo等待rdquo状态的

数据转成ldquo尝试发送状态rdquo重新发送该数据需要注意的是在 10 分钟的等待过程中

MDSBES 服务器如果没有接收到手持设备重新连接网络的消息则 MDSBES 服务器不会尝

试重新发送ldquo暂时失败rdquo和ldquo等待rdquo状态的数据

根据以上的分析可以对推出以下结果

1 当用户的手持设备在线时对该用户的数据推送会被马上处理该数据只在很短的时间

占用 1000 个等待队列中的一个位置

2 当用户的手持设备不在线时假设该服务器只有一个用户则服务器在 10 分钟内最多

可以接收该用户的 1005 条数据其中 5 条处于ldquo暂时失败rdquo状态1000 条在等待队列

3 如果用户的手持设备在数据发送给 MDSBES 服务器 10 分钟内重新接入网络MDSBES

服务器可以将该数据推送到手持设备端

4 如果 MDSBES 服务器的等待队列未满推送给新的用户的前 5 条数据不占用等待队列

5 如果 MDSBES 服务器的等待队列已满不管是由哪个用户的数据占满的MDSBES 服

务器都会停止接收所有用户的新的数据推送

总结而言在生产环境中保证 MDSBES 服务器正常工作的关键是要保证 MDSBES 服务

器的等待队列不被占满

手持设备端对推送数据的处理

MDSBES 服务器成功接收应用服务器推送的数据后会将数据推送到手持设备端在手持

设备端需要由客户端应用程序将推送的数据提取出来一个直观的猜测是手持设备端同样需

要一个队列保存数据否则客户端应用程序在提取前一条数据时新到达的数据就会直接丢

失需要验证的是这个队列有多深队列行为是否被系统隐藏经过测试证实手持设备端确

实也存在队列队列深度为 10而且可以反映在应用层其中最值得开发人员注意的是这

个队列是一个可溢出队列关于手持设备端推送数据队列的示意图如下

如图所示当客户端提取程序正在读取数据ldquoRrdquo如图中标有 R 的椭圆形所示如果

有更多的数据被推送到手持设备端所推送的数据会被放入设备的队列中一共有 10 条数

据可以被保留在设备队列中如椭圆形 1 到 10 所示此时如果有更多的数据被推送进来

设备队列已满则这些新的数据会溢出如椭圆形 111213 所示同时对于服务器端

而言数据 111213 已经被推送过一次服务器端不会再次尝试推送这些数据在这种

情况下数据 1 到 10 可以继续被客户端提取程序所提取而数据 111213 则丢失了

对于尚未推送到手持设备端的数据如数据 1415假设因为网络中断没有被推送到

手持设备端则仍保留在服务器端当网络恢复后数据 1415 仍可以被正常处理

对于确认消息(图中橙色圆形所示)的处理则更为复杂在异步确认消息一节中再作讨

从这一节的讨论得出的结果是数据到达手持设备端未必就能够被客户端提取程序提取

客户端提取程序有职责尽快提取数据防止推送数据的溢出溢出的数据在客户端无法再被

处理而且服务器端也不会尝试继续推送这些数据

异步确认消息(应用依赖和非应用依赖)

手持设备端在接收到推送数据后会通知 MDSBES 服务器这时 MDSBES 服务器会根据

最初的推送请求向指定的 URL 发送一个确认消息告知应用服务器某一条推送数据的最终

结果这就是异步确认消息异步确认消息以 http 协议发送给应用服务器消息 200 代表

数据推送成功400 代表数据推送失败

异步确认消息是应用服务器判断推送是否成功的有效工具但是很多开发人员因为对

异步确认消息的机制不了解错误地使用了异步确认消息从而导致数据的丢失所以我们

需要详细了解异步确认消息的工作机制

要讨论异步确认消息的工作机制必然涉及到应用依赖与非应用依赖应用依赖的推送

和非应用依赖的推送对应的异步确认消息工作机制是不同的

下面先讨论非应用依赖的场景这也是很多开发商使用的场景

如果推送是非应用依赖的数据是以到达手持设备为成功标志的只要数据到达了手持

设备MDSBES 服务器就认为该数据推送成功会给应用服务器发送异步确认消息 200

如果数据推送时手持设备不在线则 10 分钟内 MDSBES 服务器会在服务器上保留该数

据如果 10 分钟内手持设备重新接入网络则 MDSBES 服务器会尝试发送该数据一旦数

据到达手持设备MDSBES 服务器都会给应用服务器发送异步确认消息 200

如果 10 分钟内手设备没有接入网络则该推送数据过期MDSBES 服务器会丢弃该数

据同时给应用服务器发送异步确认消息 400

可以看出非应用依赖的推送方式并不能保证客户端应用程序接收到数据在非应用依

赖的推送方式下 MDSBES 返回 200 确认消息也不能认为客户端已接收到数据如果数据送

达手持设备的时候客户端应用程序没有启动所推送的数据会在手持设备端丢失而

MDSBES 服务器认为数据已成功送达或者是客户端应用程序已启动但是没有及时读取推

送数据在手持设备端溢出的数据也会丢失MDSBES 服务器同样认为数据已成功送达

所以在非应用依赖的情况下如果接收到 400 确认消息可以确定客户端没有收到数据

如果接收到 200 确认消息只能认为客户端可能收到了数据

从以上的讨论看非应用依赖的推送方式并不适合用于可靠性要求较高的应用对于可

靠性要求较高的应用需要知道客户端应用程序是否接收到了数据应用依赖的推送方式就

是为此而设计的如果推送是应用依赖的数据是以客户端应用程序确认为成功标志的只

要在应用程序确认后MDSBES 服务器才认为数据推送成功这时才给应用服务器发送异步

确认消息 200

有关客户端应用程序如何确认收到数据在ldquo客户端接收关键代码rdquo一节中有描述简

单而言就是客户端应用程序通过调用 MDSPushInputStream 实例的 accept 函数确认数据收

到如果客户端应用程序在接收到数据后没有调用 accept 函数MDSBES 服务器会认为数

据推送失败给应用服务器发送异步确认消息 400另外一点曾经强调过的是accept 函数

调用后只有 InputStream 和 StreamConnection 都关闭了确认消息才被发送给 MDSBES 服

务器在极端的情况下这一点会影响到异步确认消息的结果

下面具体分析各种情景

如果手持设备在线但是客户端应用程序没有启动推送数据到达手持设备端后没有程

序侦听推送端口当然也没有程序会调用 accept 函数此时 MDSBES 服务器会认为数据推

送失败向应用服务器发送异步确认消息 400

如果手持设备在线客户端应用程序在读取推送数据后没有调用 accept 函数直接关闭

连接此时 MDSBES 服务器同样会认为数据推送失败向应用服务器发送异步确认消息 400

在这种情景下即使客户端接收到了数据但应用服务器仍收到失败消息因为作为成功标

志的 accept 函数并没有调用

如果手持设备在线客户端应用程序在读取推送数据后调用了 accept 函数并关闭连

接此时 MDSBES 服务器会认为数据推送成功向应用服务器发送异步确认消息 200

对于手持设备不在线的情况结果与非应用依赖相似10 分钟内如果手持设备不接入

网络的话推送数据会在 MDSBES 服务器上过期MDSBES 服务器会向应用服务器发送异

步确认消息 400

如果 10 分钟内手持设备重新接入网络MDSBES 服务器会尝试发送数据至手持设备端

还是以 accept 函数的调用为推送成功的标志如果数据重新发送成功但是客户端没有调

用 accept 函数则 MDSBES 服务器会向应用服务器发送异步确认消息 200

对于推送到手持设备端但是溢出的数据因为没有程序能够读取该数据并针对该数据调

用 accept 函数MDSBES 服务器会一直等待到 10 分钟后向应用服务器发送异步确认消息

400认为数据过期

一个特殊的情况是客户端应用程序在接收到数据后调用了 accept但是没有关闭

InputStream 和 StreamConnection此时 MDSBES 服务器会认识客户端还在处理数据会继

续等待如果服务器等待时候超过 10 分钟MDSBES 服务器会认为数据推送失败向应用

服务器发送异步确认消息 400如果在 10 分钟内客户端主动关闭 InputStream 和

StreamConnection或者是网络异常中断MDSBES 服务器会认为数据推送成功向应用服

务器发送异步确认消息 200

下面再讨论一个异常情况下的特例

结合以上描述的各种因素在极端的情况下会出现一些特殊的情况如上图所示假设

MDSBES 服务器在推送了数据 R数据 1 到数据 13 后出现网络中断同时客户端应用程序

正在缓慢地读取数据 R此时数据 1 到数据 10 会被保留在手持设备队列中客户端应用程

序在读取数据 R 后可以继续读取数据 1 到数据 10如果客户端应用程序在读取了数据后都

调用了 accept 函数则会产生对应用确认消息如图中的确认消息 R确认消息 1 到确认消

息 10但是因为网络的中断这些确认消息都无法到达 MDSBES 服务器如果网络在 10

钟内没有恢复MDSBES 服务器上所有等待确认消息的记录都会认识是过期的MDSBES

会向应用服务器发送异步确认消息 400此后即使网络恢复确认消息 R确认消息 1 到确

认消息 10 都不再有效

在这种特殊情况下即使客户端应用程序接收到数据并调用了 accept 函数MDSBES

服务器同样认为数据没有推送成功

根据以上的讨论应用依赖的推送同样不能保证客户端应用一定能收到数据但是在

应用依赖的推送方式下 MDSBES 返回 200 确认消息可以确定客户端已接收到数据因为 200

确认消息是由客户端应用程序调用 accept 函数触发的客户端应用程序可以在确认数据收

到后才调用 accept 函数通知应用服务器该数据已成功接收甚至可以在接收到数据后根

据数据内容决定不调用 accept 函数从而通过应用服务器该数据没有被接收

所以在应用依赖的情况下如果接收到 200 确认消息可以确定客户端已经接收到数据

如果接收到 400 确认消息只能认为客户端可能没有收到数据

因此应用依赖的推送方式更适合可靠性要求高的应用如果应用服务器接收到 200

确认消息就可以认为推送成功继续处理其它事务如果应用服务器接收到 400 确认消息则

需要组织数据重发直到接收到 200 确认消息为止这样就可以保证客户端一定能够接收到

需要推送的数据

如何保证数据推送的可靠性

通过以上几个章节的讨论我们知道虽然 MDSBES 服务器提供了很多机制保证数据

推送的可靠性但是都无法完全保证数据被客户端应用程序所接收

对于数据推送的可靠性保证一方面开发人员需要了解各个可能导致故障的因素在应

用编写的时候尽量减少故障出现的可能性对故障点进行规避另一方面开发人员也需要实

现额外的机制进行数据重发在故障点无法规避的情况下通过数据重发消除故障点的影响

应用依赖和非应用依赖的比较和选择

在实现推送应用的时候首要考虑的是有关应用依赖与非应用依赖的选择

应用依赖的推送形式在可靠性方面更优于非应用依赖的推送形式但是应用依赖的推送

形式对 MDSBES 应用服务器的潜在压力较大客户端应用程序的实现也较为复杂

非应用依赖的推送形式优势在于实现简单对 MDSBES 应用服务器形成的压力相对较

非应用依赖的推送形式从某种程度上讲有点类似于广播协议着重点是从服务器向客户

端发送大量数据并不看重客户端的接收情况从数据的应用类型来分非应用依赖的推送

更适合于天气信息最新报价等数据这些数据在客户端应用关闭时被自然丢弃短时间内

的多条数据有丢失影响也不大必竞用户不会关注 11 点零六分的天气和 11 点零七分的天气

有何不同

应用依赖的推送形式则类似于点对点协议不仅从服务器端向客户端发送数据同时也

关注客户端的接收情况应用依赖的推送更适合于 OAERP 这种应用这种应用需要保证

新的文档或者是记录一定能到达客户端应用丢失一条待办信息可能就会导致整个审批流程

的停滞

再从两种推送方式的本质差别看应用依赖并没有额外的机制消除故障点它的优势在

于它的异步确认消息更可信所以在选择了应用依赖的推送方式后进一步还是需要实现

额外的数据重发机制根据 MDSBES 所发送的异步确认消息实施数据重发

应用依赖的推送再加上结合异步确认消息的数据重发机制是保证推送数据可靠性的

最好方法

MDS 接收线程限制的处理

不管使用什么方式的推送应用开发时都需要考虑到 MDS 接收线程的限制不过以

现在的服务器处理能力正常情况下的 HTTP 推送请求可以在几毫秒或者是几十毫内处理完

成少量的 HTTP 推送不足于构成网络阻塞

需要考虑的是应用服务器端的推送程序在推送过程中不要在建立连接后才动态组织数

据这样会浪费 http 连接资源好的建议是预先组织好数据在建立 HTTP 连接后快速写完

数据尽快关闭连接

此外对于应用服务器的推送线程也要加于控制避免在短时间内无限制地启动新的线

程进行连接否则也容易造成连接过多而阻塞的情况

从网络层面来看调整应用服务器与 MDSBES 服务器之间的网络超时设置也是避免这

种问题的一种方法在允许的范围内调大网络超时时间可以让应用服务器的推送线程等待更

长的时间从而让 MDSBES 有足够的时间释放原有的连接

最后无论使用了什么方法调优都需要对应用服务器的推送程序进行异常处理当发

现网络超时异常的时候可以组织数据重发当然数据重发的间隔时间也要考虑不要因为

重发数据再次造成网络阻塞

MDS 推送队列限制的处理

相对而言MDS 推送队列限制造成的推送失败比 MDS 接收队列造成的推送失败更常见

已经发现的一个实际案例是周未的时候用户大量关机导致大量推送数据处于推送队列中

填满了推送队列从而导致 MDSBES 服务器不再响应新的推送请求一旦过期时间到达

有新的队列位置空闲又有大量新的推送数据重新占满推送队列

对于 MDS 推送队列的限制直观的做法是对队列参数进行调整如之前讨论的管理

员可以对队列的深度和过期时间进行调整

然而出于对过期时间的不正确理解管理员倾向于将过期时间调大当过期时间被调

大后推送给关机用户的数据在服务器占用队列的时间就变长导致 MDS 推送队列更容易

被占满

所以如果对过期时间进行调整的话方向是将过期时间调小过期时间越小等待数

据占用队列的时间就越短MDS 推送队列被占满的可能性就越小但是过期时间也不能

过小过期时间过小可能会导致过多的过期数据使应用服务器需要处理更多的数据重发

比如将过期时间调成 1 分钟用户走过一个人行遂道都可能导致数据发送失败

对于队列深度的调整方向是调大队列深度如从缺省的 1000 调整到 2000此时

MDSBES 服务器在同一时间可以保留更多的等待数据减少了队列占满的机会不过调大队

列深度会让 MDSBES 服务器保留更多的数据从而占用更多的服务器内存对服务器产生

更大的压力所以调用队列深度需要根据服务器的运行能力进行调用

从应用服务器设计角度应用 MDS 队列限制的方法是尽量减少等待数据的发送如发

送第一条数据的时候发现数据过期对于该用户的其它数据可以调整发送时间在更长的时

间间隔后才尝试发送

对于 MDS 队列限制应用服务器端的推送应用需要对 MDSBES 服务器返回的 503 错误

进行处理对于出现 503 错的数据组织重发同样重发的间隔也需要考虑不要因为数据

重发占用更多的推送队列也可以根据目标用户进行判断对于可能关机的用户暂时不重发

数据

手持设备端接收队列的处理

手持设备端接收队列并未从文档中明确列出是根据测试结果分析得出的手持设备端

目前也没有发现设置可以调整该队列所以客户端程序设计的时候不应该依赖手持设备端队

开发人员需要意识到的是手持设备端的接收队列是可溢出队列一旦推送数据从接收队

列出溢出客户端程序就无法提取该数据了所以在客户端应用开始过程中要注意加快推送

数据的提取过程在提取过程中不要对数据进行复杂的计算提取完成后尽快读取下一条数

据对于接收数据的处理和计算可以交由另一个线程完成

因为溢出数据对客户端应用而言不可见所以数据溢出时在客户端应用中也不会有异

常开发人员需要通过服务器返回的过期消息才能知道该数据推送失败

对于这一点客户端应用除了尽量避免数据溢出外无法在客户端通过其它机制进行补

应用依赖的客户端

对于应用依赖的客户端应用关键点是决定何时调用 accept 函数同时保证 InputStream

和 StreamConnection 被关闭

如之前讨论的使用应用依赖的推送关键是希望使用 MDSBES 发送给应用服务器的异

步确认消息从而对数据是否到达进行判断所以应用依赖的客户端应用需要保证正确调用

accept 函数

另外如果 InputStream 和 StreamConnection 没有关闭的话accept 函数所发出的确认

消息无法被 MDSBES 服务器收到开发人员在代码中应该主动关闭 InputStream 和

StreamConnection同时在程序发生异常时也需要在 catch 代码段对 InputStream 和

StreamConnection 进行关闭处理

异步确认消息处理

异步确认消息用于判断数据是否被成功推送至客户端应用要使用异步确认消息首先

要保证成功地无遗漏地接收所有从 MDSBES 服务器发送过来的异步确认消息

异步确认消息是一个 HTTP 消息消息比较短接收过程的并发压力不大下面是一个

异步确认消息的样例

Received notificationGET HTTP11

User-Agent RIM MDSCS

Accept

X-RIM-Push-ID pushID2081773768

X-RIM-Push-Destination mmtestbjsearbcn

X-RIM-Push-Status 400

Host localhost7778

其中最关键是的获取属性 X-RIM-Push-Status根据该属性的值判断数据推送是成功

(200)还是失败(400)当然属性 X-RIM-Push-ID 也非常重要用于确定该确认消息是针

对哪条推送数据的

如果应用服务器端通过确认消息发现数据推送失败则需要根据属性 X-RIM-Push-ID 重

新组织数据进行重发

Page 11: BES 服务器推送机制分析 - images.csdn.netimages.csdn.net/upimgs/lee/BBPDF/BESfwtsjzfx.pdf · 前言 数据推送是BlackBerry 应用平台的一大优势,在BlackBerry 应用平台上部署的应用可以

以上代码中建立端口侦听的代码如下

_notify = (StreamConnectionNotifier)Connectoropen(http55100)

其中 URLldquohttp55100rdquo是用于侦听推送的特定格式55100 为需要侦听的端口两

个线程不能同时打开同一个端口的侦听所以在这里使用了 synchronized 对线程并发进行了

控制

建立侦听后可以通过函数 acceptAndOpen 开始侦听当线程调用该方法开始侦听的时候

线程会阻塞在这里直到有推送消息到达代码如下

stream = _notifyacceptAndOpen()

侦听之后的代码为获取连接的 InputStream从 InputStream中获取服务器端推送的数据

读取完数据后关闭 InputStream 和 StreamConnection然后重新调用 acceptAdnOpen 继续侦

听端口准备接收新的推送数据

开发人员如果希望使用应用依赖的推送在客户端必须将 InputStream 转换成

MDSPushInputStream同时在接收完数据后调用 MDSPushInputStream 的 accept 函数造知服

务器该数据已收到这也是应用依赖和非应用依赖在代码实现层面的最主要区别代码如下

pushInputStream= new

MDSPushInputStream((HttpServerConnection)stream input)

helliphellip

pushInputStreamaccept()

从良好的编程习惯来讲在连接使用后需要主动关闭所使用的连接和资源如

InputStreamStreamConnectionStreamConnectionNotifier 等所以在样例代码中也可以看

到该样例在异常处理阶段都对这些资源进行了关闭操作在关闭这些连接和资源的时候对

StreamConnectionNotifier 需要特别注意有可能在 StreamConnectionNotifier 中会保留有未

读取的推送数据需要调用 acceptAdnOpen 函数将保留的数据读取出来直到 acceptAdnOpen

处于阻塞状态

另外如果使用了应用依赖的推送客户端程序调用 MDSPushInputStream 的 accept 函

数后确认消息不会马上发出而是在对应用 InputStream 和 StreamConnection 关闭后才发出

所以对于应用依赖的推送保证 InputStream 和 StreamConnection 的关闭非常之重要

异步确认消息接收代码

异步消息的接收代码运行在服务器端在推送请求的 X-RIM-Push-NotifyURL 属性所指定

的服务器上侦听指定端口和应用服务器端的数据推送代码一样异步确认消息的接收程序

可以通过任何支持 HTTP 的语言编写主要的工作是侦听指定端口从指定端口获取 MDSBES

发送回来的确认消息根据 HTTP 协议获取相关属性并返回相关信息

下面以 java 代码为例对关键代码进行说明这段代码一个 java Application 的一部分是

侦听线程的 run 函数

public void run()

try

Systemoutprintln(Waiting for notification on port + 7778

+ )

while (true)

ServerSocket serverSocket = new ServerSocket(7778)

serverSocketsetSoTimeout(120000)

try

Socket clientSocket = serverSocketaccept()

InputStream input = clientSocketgetInputStream()

StringBuffer str = new StringBuffer()

int byteRead = inputread()

while ((byteRead = -1) ampamp (inputavailable() gt 0))

strappend((char) byteRead)

byteRead = inputread()

Systemoutprintln(strtoString())

OutputStream output = clientSocketgetOutputStream()

String response = HTTP10 200 OK

outputwrite(responsegetBytes())

outputflush()

outputclose()

inputclose()

clientSocketclose()

catch (SocketTimeoutException ste)

Systemoutprintln(Notification connection timeout

Restarting)

serverSocketclose()

catch (Exception exception)

exceptionprintStackTrace()

异步确认消息的接收代码比较简单基本上就是打开一个 Socket 侦听指定的端口本

例中是 7778然后不断地从该端口读取数据打开 Socket 侦听的代码如下

ServerSocket serverSocket = new ServerSocket(7778)

在异步消息接收过程中需要注意的是接收完 MDSBES 服务器的确认消息后应该给

MDSBES 服务器返回一个 200 的成功消息否则 MDSBES 服务器会认为发送确认消息失败

可能重次几次后再放弃这样异步消息接收程序有可能重复收到同一条确认消息返回成功

消息的代码如下

OutputStream output = clientSocketgetOutputStream()

String response = HTTP10 200 OK

outputwrite(responsegetBytes())

outputflush()

outputclose()

推送细节分析

了解基本的 MDS 推送知识是不足于编写一个可靠的数据推送程序的要保证数据推送

的可靠性必须了解整个推送过程的每一个细节本章节先从整体上描述了整个数据推送的

调用时序然后从不同的关键点详细讲解数据推送的细节

MDS 推送的整体时序

如上图所示数据推送主要是应用服务器MDS 服务器网络服务手机端几个组件

之间的交互在网络服务这一块我们可以假定这一层是稳定的不做考虑对于网络无法提

供服务等情况我们可以通过手持设备端关闭无线电或者是将手持设备放入无线屏蔽区等方

式模拟所以我们要考虑是应用服务器MDSBES 服务器手持设备这三部分

数据推送由应用服务器发生向 MDSBES 服务器发出 HTTP Post 请求第一个需要考虑

的是该 HTTP Post 请求是否能到达 MDSBES 服务器这一点在ldquo推送请求是否到达 MDSBES

服务器rdquo小节中有详细讨论

MDSBES 服务器在接收到 HTTP Post 请求后会对请求的格式进行判断如果格式错误

如指定目标不存在等则 MDSBES 服务器会返回 400 错误所以第二点要考虑是的如何保

证所有到达 MDSBES 服务器的请求都是合法的有关这一点在ldquo推送请求检查rdquo小节中有

详细描述

MDSBES 服务器发现推送数据格式正确以后还需要判断服务器上的队列是否有足够的

空间保存该数据如果没有发现空闲队列则丢弃该数据并向应用服务器返回 503 错误因此

第三点要考虑的是什么情况会导致服务器推送队列占满如何尽量避免这种情况有关这一

点在ldquoMDSBES 中的推送队列rdquo中有详细描述

MDSBES 服务器将数据推送到客户端以后由客户端程序将数据提取交由相应的应用

逻辑进行处理然后客户端应用在提取数据的时候并不一定能及时地将所有推送到的数据

获取所以第四点要考虑的是手持设备端是否也存在队列如何保证客户端程序将所有数据

都提取出来有关这一点在ldquo手持设备端对消息的处理rdquo小节中有详细描述

手持设备在接收推送数据后会通知 MDSBES 服务器MDSBES 服务器则向应用服务器

发送异步的确认消息通过返回数值 200 或者是 400 通知应用服务器推送数据是推送成功还

是推送失败异步确认消息对于应用开发者有特别的意义通过它可以确定数据是否到达客

户端从而决定是否再次推送数据然后很多开发商都发现异步确认消息的返回的ldquo200rdquo

和ldquo400rdquo并不能准备地反映数据的接收情况所以如何使用异步确认消息在不同的场

景下异步消息会包含什么不同值对于应用开发者而言非常重要有关这一点在小节ldquo异步

确认消息rdquo有详细描述

推送请求是否到达 MDSBES 服务器

有关推送请求是否到达 MDSBES 服务器 很多开发人员都会忽略认为推送请求是否

到达 MDSBES 服务器是由代码是否编写正确决定的一定会在代码测试阶段体现也来也

必须在代码编写阶段解决这个问题然而在生产环境确实有一些因素会导致应用服务器发送

的推送请求无法到达 MDSBES 服务器从而导致推送数据丢失的问题

推送请求无法到达 MDSBES 服务器的一个可能的直接原因是指定的服务器名不正确或

者是指定的端口号不正确原因可能是 BES 管理员修改了 BES 服务器的服务器名IP 地址或

者是推送监听端口

这种问题比较容易发现一方面是因为所有的 BlackBerry 用户此时都无法接收到推送数

据另一方面也因为应用服务器端会报网络连接的异常以 java 代码为例当指定的服务

器名或者是端口号不正确的情况下应用服务器端的推送程序会报以下异常

javanetConnectException Connection refused

或者是报域名无法解析等网络连接级别的异常

发现这种情况只需要检查代码中指定的服务器名和端口号是否和生产环境相同就可以

解决问题

推送请求无法到达 MDSBES 服务器的一个潜在可能性是推送请求的并发数量太大导致

MDSBES 服务器无法及时接收所有请求

这种问题比较难于发现因为部分用户仍然可以收到推送数据只有部分用户的部分数

据丢失了同时这种问题在功能性测试场景下不容易发生只有在大压力测试或者是存在

大量用户的生产环境中才容易出现

产生这种问题的根源是 MDSBES 服务器的并发接收限制MDS 服务器其实是运行在

Tomcat 服务器上一个服务Tomcat 服务器作为应用容器处理了低层的网络请求而 Tomcat

服务器在底层有并发网络请求的限制当网络连接达到服务器限制时新的推送连接会处理

等待状态如果在网络超时前仍无法建立连接则会出现网络超时异常

javanetConnectException Connection timed out connect

根据测试的结果不同版本的 BES 服务器在低层并发网络请求的数量上有差异以 200

个并发限制为例就是说同一时间点只能有 200 个连接可以向 MDSBES 服务器发送推送请

求第 201 个请求以后有所有请求都将进入等待状态如果在 TCPIP 超时限制到达之前

MDSBES 服务器仍无法释放前 200 个请求则等待状态的请求会因为网络超时而中断

当应用服务器采用多线程机制处理推送请求的情况下有可能出现以上情况如果因为业

务原因在短时间内出现大量等待推送的数据应用服务器则有可能启动多于 200 个的线程发

送推送请求如果需要推送的数据太大或者是推送线程需要动态对数据进行计算就有可

能导致线程所启动的连接在短时间内没有关闭当多于 200 个线程处于这种状态的时候

MDSBES 服务器就无法继续接收推送请求了这种问题的一个现象就是应用服务器端报网络

超时异常或者是连接被拒绝的异常

总的而言要保证数据推送的成功第一步需要保证推送数据到达 MDSBES 服务器

发现推送数据没有到达 MDSBES 服务器的情况时应该对应用服务器的日志进行检查进一

步确定问题的原因

推送请求检查

在 MDSBES 服务器接收到推送请求后MDSBES 服务器会对请求进行检查只有符合

推送格式的请求才被 MDSBES 服务器所接收当 MDSBES 服务器接收推送时会返回值为

200 的 HTTP 答复告知应用服务器已经接收该请求当 MDSBES 服务器拒绝推送请求时会

根据拒绝的原因返回相应的错误码可能返回的错误码包括 400403503 等下面对不同的

情况进行描述

第一种请求被拒绝的原因是推送请求的格式不对包括 URL 格式不正确和 HTTP 头不正

确两种情况两种情况 MDSBES 服务器都会返回 400 消息表示拒绝URL 格式不正确包括

URL 中不包含ldquoDESTINATIONrdquo参数或者是不包含ldquoPORTrdquo参数等HTTP 头不正确包括

PUSH ID 不唯一等因为推送请求中的 HTTP 头属性都有缺省值所以使用错误的 HTTP 头属

性并不会引发错误MDSBES 服务器只是忽略该参数如 HTTP 头ldquoX-RIM-PUSH-IDrdquo被误写

为ldquoX-RIMPUSHIDrdquoMDSBES 服务器并不会拒绝该请求只是使用ldquoX-RIM-PUSH-IDrdquo的缺

省值就是服务器随机生成一个唯一的 ID 给推送消息

对于 HTTP 协议而言服务器返回 400 代表服务器一般性错误所以返回消息会以异常

形式被捕获以 java 代码为类程序会捕获到以下网络异常

ldquojavaioIOException Server returned HTTP response code 400 for URL

httprdquo

第二种推送请求被拒绝的情况是推送请求指定的收件人不正确就是指应用服务器发送

推送请求时使用了正确的格式不过 DESTINATION 参数指定的收件人并不存在或者是该收件

人并不是 BlackBerry 用户此时 MDSBES 服务器会返回 403 错误读者需要注意 BES 服务

器 37 之前的版本对于这种情况会视作格式错误返回 400 错误

同样以 java 代码为例收件人不正确的情况下程序会捕获到以下网络异常

ldquojavaioIOException Server returned HTTP response code 403 for URL

httprdquo

第三种请求被拒绝的情况是 MDSBES 服务器上的推送队列已满无法再接收更多的推

送请求此时会返回 503 错误表示服务器暂时没有足够的资源处理该请求这种情况下 java

应用程序会捕获到以下异常

ldquojavaioIOException Server returned HTTP response code 503 for URL

httprdquo

这种情况与推送请求是否正确无关取决于 MDSBES 服务器运行情况关于 MDSBES

服务器的推送队列在下一节中我们才进行详细的讨论

如果数据推送请求通过了 MDSBES 服务器的消息检查则 MDSBES 服务器会将该数据

放入推送队列等待发送同时给应用服务器返回成功消息 200

成功消息是以 HTTP 的 ResponseCode 形式返回的开发人员可以通过 HTTP 连接的相应

方法获取在 java 代码中获取 ResponseCode 的函数是ldquo getResponseCoderdquo只有确认

MDSBES 服务器返回的 ResponseCode 是 200才能确定推送数据已经由 MDSBES 接收对

于其它的异常情况都需要由程序进行判断确定如何调整请求继续重试推送过程

有一点非常重要的是 MDSBES 接收该推送数据并不意味着该数据就一定能到达手持设

备端还有很多因素会导致手持设备端的应用接收不到该数据

MDSBES 中的推送队列

MDSBES 服务器有一个队列用于缓存推送请求因为手持设备并不是一定在线

MDSBES 服务器需要先将推送请求保留在服务端在合适的情况下将数据推送到手持设备端

后才从队列中将该请求移除

MDSBES 服务器上的推送队列是 MDS 推送的关键组件它的行为不仅仅影响应用服务

器端是否能够发送新的请求也影响到手持设备端是否能够接收到数据所以对于 MDS 推

送的开发人员必须了解 MDSBES 服务器上的推送队列

MDSBES 服务器上的推送队列有一个队列深度缺省为 1000也就是说 MDSBES 服务

器在同一时间只能缓存 1000 个推送请求该队列的深度可以由管理员在管理端修改

同时 MDSBES 服务器上的推送队列并不是永久保存推送数据它有一个过期时间缺

省为 10 分钟也就是说一个推送请求在 MDSBES 服务器上最多能够被保留 10 分钟同样

这个过期时间可以由管理员在管理端修改

推送队列的深度修改界面如下

推送队列的过期时间的修改界面如下(以毫秒为单位)

对于推送队列的进一步讨论是有关推送数据进入队列后的状态如何变化下面是推送数

据在队列中的状态变化图说明了推送数据在不同情况下的状态变化下图的各个状态中

淡蓝色的状态为起始状态深蓝色的状态为中间状态红色的状态为终止状态处于中间状

态的数据最终必然会转为终止状态的其中一种

当 MDSBES 服务器接收到推送数据后如果之前目标用户没有 5 个处于暂时失败的数

据包服务器会将数据放入等待发送的队列中推送的数据处于ldquo尝试发送rdquo状态处于ldquo尝

试发送rdquo会在很短的时间内占用 1000 个队列位置中的一个不管是成功还是失败它所占

用的队列位置很快被释放

然后 MDSBES 服务器尝试将数据推送到手持设备端如果此时对应的手持设备处于开

机状态网络一切正常则 MDSBES 服务器将数据发送给手持设备推送数据变成ldquo成功rdquo

状态MDSBES 服务器会向应用服务器发送异步的确认消息同时将数据从队列中移除注

意此时 MDSBES 服务器向应用服务器返回的确认消息并不一定是表示成功 200具体是

返回代表成功的 200 还是返回代表失败的 400 还取决于其它因素有关这一点我们在后面的

章节中详细讨论

如果 MDSBES 服务器尝试将数据推送到手持设备的时候手持设备处于关机状态则该

数据进入ldquo暂时失败rdquo状态处于ldquo暂时失败rdquo状态的数据不占用等待队列的队列位置某

一个目标用户最多只能有 5 条数据处于ldquo暂时失败rdquo状态目前没有明确的数据或者是文档

说明一个 MDSBES 服务器总共可以保留多少条ldquo暂时失败rdquo状态的数据

如果某一用户已有 5 条数据处于ldquo暂时失败rdquo状态MDSBES 服务器接收到该用户新

的推送请求时会直接将该数据转入ldquo等待rdquo状态每一条进入ldquo等待rdquo状态的推送数据会占

用推送队列中的一个位置当队列中的 1000 个位置都被占满的时候 MDSBES 服务器就停止

接收新的数据推送无论新的数据是推送给哪个用户

处于ldquo暂时失败rdquo和ldquo等待rdquo状态的数据会在服务器上保留 10 分钟(如上所述这一

时间可以由管理修改)10 分钟后处于这两种状态的数据将被丢弃进入ldquo过期rdquo状态同

时 MDSBES 服务器会向应用服务器发送异步消息这种情况下发送的异步确认消息一定是

代表失败的 400如果在 10 分钟内手持设备重新进入网络手持设备会发送消息到 MDSBES

服务器告知该手持设备已经上线MDSBES 服务器会将处理ldquo暂时失败rdquo和ldquo等待rdquo状态的

数据转成ldquo尝试发送状态rdquo重新发送该数据需要注意的是在 10 分钟的等待过程中

MDSBES 服务器如果没有接收到手持设备重新连接网络的消息则 MDSBES 服务器不会尝

试重新发送ldquo暂时失败rdquo和ldquo等待rdquo状态的数据

根据以上的分析可以对推出以下结果

1 当用户的手持设备在线时对该用户的数据推送会被马上处理该数据只在很短的时间

占用 1000 个等待队列中的一个位置

2 当用户的手持设备不在线时假设该服务器只有一个用户则服务器在 10 分钟内最多

可以接收该用户的 1005 条数据其中 5 条处于ldquo暂时失败rdquo状态1000 条在等待队列

3 如果用户的手持设备在数据发送给 MDSBES 服务器 10 分钟内重新接入网络MDSBES

服务器可以将该数据推送到手持设备端

4 如果 MDSBES 服务器的等待队列未满推送给新的用户的前 5 条数据不占用等待队列

5 如果 MDSBES 服务器的等待队列已满不管是由哪个用户的数据占满的MDSBES 服

务器都会停止接收所有用户的新的数据推送

总结而言在生产环境中保证 MDSBES 服务器正常工作的关键是要保证 MDSBES 服务

器的等待队列不被占满

手持设备端对推送数据的处理

MDSBES 服务器成功接收应用服务器推送的数据后会将数据推送到手持设备端在手持

设备端需要由客户端应用程序将推送的数据提取出来一个直观的猜测是手持设备端同样需

要一个队列保存数据否则客户端应用程序在提取前一条数据时新到达的数据就会直接丢

失需要验证的是这个队列有多深队列行为是否被系统隐藏经过测试证实手持设备端确

实也存在队列队列深度为 10而且可以反映在应用层其中最值得开发人员注意的是这

个队列是一个可溢出队列关于手持设备端推送数据队列的示意图如下

如图所示当客户端提取程序正在读取数据ldquoRrdquo如图中标有 R 的椭圆形所示如果

有更多的数据被推送到手持设备端所推送的数据会被放入设备的队列中一共有 10 条数

据可以被保留在设备队列中如椭圆形 1 到 10 所示此时如果有更多的数据被推送进来

设备队列已满则这些新的数据会溢出如椭圆形 111213 所示同时对于服务器端

而言数据 111213 已经被推送过一次服务器端不会再次尝试推送这些数据在这种

情况下数据 1 到 10 可以继续被客户端提取程序所提取而数据 111213 则丢失了

对于尚未推送到手持设备端的数据如数据 1415假设因为网络中断没有被推送到

手持设备端则仍保留在服务器端当网络恢复后数据 1415 仍可以被正常处理

对于确认消息(图中橙色圆形所示)的处理则更为复杂在异步确认消息一节中再作讨

从这一节的讨论得出的结果是数据到达手持设备端未必就能够被客户端提取程序提取

客户端提取程序有职责尽快提取数据防止推送数据的溢出溢出的数据在客户端无法再被

处理而且服务器端也不会尝试继续推送这些数据

异步确认消息(应用依赖和非应用依赖)

手持设备端在接收到推送数据后会通知 MDSBES 服务器这时 MDSBES 服务器会根据

最初的推送请求向指定的 URL 发送一个确认消息告知应用服务器某一条推送数据的最终

结果这就是异步确认消息异步确认消息以 http 协议发送给应用服务器消息 200 代表

数据推送成功400 代表数据推送失败

异步确认消息是应用服务器判断推送是否成功的有效工具但是很多开发人员因为对

异步确认消息的机制不了解错误地使用了异步确认消息从而导致数据的丢失所以我们

需要详细了解异步确认消息的工作机制

要讨论异步确认消息的工作机制必然涉及到应用依赖与非应用依赖应用依赖的推送

和非应用依赖的推送对应的异步确认消息工作机制是不同的

下面先讨论非应用依赖的场景这也是很多开发商使用的场景

如果推送是非应用依赖的数据是以到达手持设备为成功标志的只要数据到达了手持

设备MDSBES 服务器就认为该数据推送成功会给应用服务器发送异步确认消息 200

如果数据推送时手持设备不在线则 10 分钟内 MDSBES 服务器会在服务器上保留该数

据如果 10 分钟内手持设备重新接入网络则 MDSBES 服务器会尝试发送该数据一旦数

据到达手持设备MDSBES 服务器都会给应用服务器发送异步确认消息 200

如果 10 分钟内手设备没有接入网络则该推送数据过期MDSBES 服务器会丢弃该数

据同时给应用服务器发送异步确认消息 400

可以看出非应用依赖的推送方式并不能保证客户端应用程序接收到数据在非应用依

赖的推送方式下 MDSBES 返回 200 确认消息也不能认为客户端已接收到数据如果数据送

达手持设备的时候客户端应用程序没有启动所推送的数据会在手持设备端丢失而

MDSBES 服务器认为数据已成功送达或者是客户端应用程序已启动但是没有及时读取推

送数据在手持设备端溢出的数据也会丢失MDSBES 服务器同样认为数据已成功送达

所以在非应用依赖的情况下如果接收到 400 确认消息可以确定客户端没有收到数据

如果接收到 200 确认消息只能认为客户端可能收到了数据

从以上的讨论看非应用依赖的推送方式并不适合用于可靠性要求较高的应用对于可

靠性要求较高的应用需要知道客户端应用程序是否接收到了数据应用依赖的推送方式就

是为此而设计的如果推送是应用依赖的数据是以客户端应用程序确认为成功标志的只

要在应用程序确认后MDSBES 服务器才认为数据推送成功这时才给应用服务器发送异步

确认消息 200

有关客户端应用程序如何确认收到数据在ldquo客户端接收关键代码rdquo一节中有描述简

单而言就是客户端应用程序通过调用 MDSPushInputStream 实例的 accept 函数确认数据收

到如果客户端应用程序在接收到数据后没有调用 accept 函数MDSBES 服务器会认为数

据推送失败给应用服务器发送异步确认消息 400另外一点曾经强调过的是accept 函数

调用后只有 InputStream 和 StreamConnection 都关闭了确认消息才被发送给 MDSBES 服

务器在极端的情况下这一点会影响到异步确认消息的结果

下面具体分析各种情景

如果手持设备在线但是客户端应用程序没有启动推送数据到达手持设备端后没有程

序侦听推送端口当然也没有程序会调用 accept 函数此时 MDSBES 服务器会认为数据推

送失败向应用服务器发送异步确认消息 400

如果手持设备在线客户端应用程序在读取推送数据后没有调用 accept 函数直接关闭

连接此时 MDSBES 服务器同样会认为数据推送失败向应用服务器发送异步确认消息 400

在这种情景下即使客户端接收到了数据但应用服务器仍收到失败消息因为作为成功标

志的 accept 函数并没有调用

如果手持设备在线客户端应用程序在读取推送数据后调用了 accept 函数并关闭连

接此时 MDSBES 服务器会认为数据推送成功向应用服务器发送异步确认消息 200

对于手持设备不在线的情况结果与非应用依赖相似10 分钟内如果手持设备不接入

网络的话推送数据会在 MDSBES 服务器上过期MDSBES 服务器会向应用服务器发送异

步确认消息 400

如果 10 分钟内手持设备重新接入网络MDSBES 服务器会尝试发送数据至手持设备端

还是以 accept 函数的调用为推送成功的标志如果数据重新发送成功但是客户端没有调

用 accept 函数则 MDSBES 服务器会向应用服务器发送异步确认消息 200

对于推送到手持设备端但是溢出的数据因为没有程序能够读取该数据并针对该数据调

用 accept 函数MDSBES 服务器会一直等待到 10 分钟后向应用服务器发送异步确认消息

400认为数据过期

一个特殊的情况是客户端应用程序在接收到数据后调用了 accept但是没有关闭

InputStream 和 StreamConnection此时 MDSBES 服务器会认识客户端还在处理数据会继

续等待如果服务器等待时候超过 10 分钟MDSBES 服务器会认为数据推送失败向应用

服务器发送异步确认消息 400如果在 10 分钟内客户端主动关闭 InputStream 和

StreamConnection或者是网络异常中断MDSBES 服务器会认为数据推送成功向应用服

务器发送异步确认消息 200

下面再讨论一个异常情况下的特例

结合以上描述的各种因素在极端的情况下会出现一些特殊的情况如上图所示假设

MDSBES 服务器在推送了数据 R数据 1 到数据 13 后出现网络中断同时客户端应用程序

正在缓慢地读取数据 R此时数据 1 到数据 10 会被保留在手持设备队列中客户端应用程

序在读取数据 R 后可以继续读取数据 1 到数据 10如果客户端应用程序在读取了数据后都

调用了 accept 函数则会产生对应用确认消息如图中的确认消息 R确认消息 1 到确认消

息 10但是因为网络的中断这些确认消息都无法到达 MDSBES 服务器如果网络在 10

钟内没有恢复MDSBES 服务器上所有等待确认消息的记录都会认识是过期的MDSBES

会向应用服务器发送异步确认消息 400此后即使网络恢复确认消息 R确认消息 1 到确

认消息 10 都不再有效

在这种特殊情况下即使客户端应用程序接收到数据并调用了 accept 函数MDSBES

服务器同样认为数据没有推送成功

根据以上的讨论应用依赖的推送同样不能保证客户端应用一定能收到数据但是在

应用依赖的推送方式下 MDSBES 返回 200 确认消息可以确定客户端已接收到数据因为 200

确认消息是由客户端应用程序调用 accept 函数触发的客户端应用程序可以在确认数据收

到后才调用 accept 函数通知应用服务器该数据已成功接收甚至可以在接收到数据后根

据数据内容决定不调用 accept 函数从而通过应用服务器该数据没有被接收

所以在应用依赖的情况下如果接收到 200 确认消息可以确定客户端已经接收到数据

如果接收到 400 确认消息只能认为客户端可能没有收到数据

因此应用依赖的推送方式更适合可靠性要求高的应用如果应用服务器接收到 200

确认消息就可以认为推送成功继续处理其它事务如果应用服务器接收到 400 确认消息则

需要组织数据重发直到接收到 200 确认消息为止这样就可以保证客户端一定能够接收到

需要推送的数据

如何保证数据推送的可靠性

通过以上几个章节的讨论我们知道虽然 MDSBES 服务器提供了很多机制保证数据

推送的可靠性但是都无法完全保证数据被客户端应用程序所接收

对于数据推送的可靠性保证一方面开发人员需要了解各个可能导致故障的因素在应

用编写的时候尽量减少故障出现的可能性对故障点进行规避另一方面开发人员也需要实

现额外的机制进行数据重发在故障点无法规避的情况下通过数据重发消除故障点的影响

应用依赖和非应用依赖的比较和选择

在实现推送应用的时候首要考虑的是有关应用依赖与非应用依赖的选择

应用依赖的推送形式在可靠性方面更优于非应用依赖的推送形式但是应用依赖的推送

形式对 MDSBES 应用服务器的潜在压力较大客户端应用程序的实现也较为复杂

非应用依赖的推送形式优势在于实现简单对 MDSBES 应用服务器形成的压力相对较

非应用依赖的推送形式从某种程度上讲有点类似于广播协议着重点是从服务器向客户

端发送大量数据并不看重客户端的接收情况从数据的应用类型来分非应用依赖的推送

更适合于天气信息最新报价等数据这些数据在客户端应用关闭时被自然丢弃短时间内

的多条数据有丢失影响也不大必竞用户不会关注 11 点零六分的天气和 11 点零七分的天气

有何不同

应用依赖的推送形式则类似于点对点协议不仅从服务器端向客户端发送数据同时也

关注客户端的接收情况应用依赖的推送更适合于 OAERP 这种应用这种应用需要保证

新的文档或者是记录一定能到达客户端应用丢失一条待办信息可能就会导致整个审批流程

的停滞

再从两种推送方式的本质差别看应用依赖并没有额外的机制消除故障点它的优势在

于它的异步确认消息更可信所以在选择了应用依赖的推送方式后进一步还是需要实现

额外的数据重发机制根据 MDSBES 所发送的异步确认消息实施数据重发

应用依赖的推送再加上结合异步确认消息的数据重发机制是保证推送数据可靠性的

最好方法

MDS 接收线程限制的处理

不管使用什么方式的推送应用开发时都需要考虑到 MDS 接收线程的限制不过以

现在的服务器处理能力正常情况下的 HTTP 推送请求可以在几毫秒或者是几十毫内处理完

成少量的 HTTP 推送不足于构成网络阻塞

需要考虑的是应用服务器端的推送程序在推送过程中不要在建立连接后才动态组织数

据这样会浪费 http 连接资源好的建议是预先组织好数据在建立 HTTP 连接后快速写完

数据尽快关闭连接

此外对于应用服务器的推送线程也要加于控制避免在短时间内无限制地启动新的线

程进行连接否则也容易造成连接过多而阻塞的情况

从网络层面来看调整应用服务器与 MDSBES 服务器之间的网络超时设置也是避免这

种问题的一种方法在允许的范围内调大网络超时时间可以让应用服务器的推送线程等待更

长的时间从而让 MDSBES 有足够的时间释放原有的连接

最后无论使用了什么方法调优都需要对应用服务器的推送程序进行异常处理当发

现网络超时异常的时候可以组织数据重发当然数据重发的间隔时间也要考虑不要因为

重发数据再次造成网络阻塞

MDS 推送队列限制的处理

相对而言MDS 推送队列限制造成的推送失败比 MDS 接收队列造成的推送失败更常见

已经发现的一个实际案例是周未的时候用户大量关机导致大量推送数据处于推送队列中

填满了推送队列从而导致 MDSBES 服务器不再响应新的推送请求一旦过期时间到达

有新的队列位置空闲又有大量新的推送数据重新占满推送队列

对于 MDS 推送队列的限制直观的做法是对队列参数进行调整如之前讨论的管理

员可以对队列的深度和过期时间进行调整

然而出于对过期时间的不正确理解管理员倾向于将过期时间调大当过期时间被调

大后推送给关机用户的数据在服务器占用队列的时间就变长导致 MDS 推送队列更容易

被占满

所以如果对过期时间进行调整的话方向是将过期时间调小过期时间越小等待数

据占用队列的时间就越短MDS 推送队列被占满的可能性就越小但是过期时间也不能

过小过期时间过小可能会导致过多的过期数据使应用服务器需要处理更多的数据重发

比如将过期时间调成 1 分钟用户走过一个人行遂道都可能导致数据发送失败

对于队列深度的调整方向是调大队列深度如从缺省的 1000 调整到 2000此时

MDSBES 服务器在同一时间可以保留更多的等待数据减少了队列占满的机会不过调大队

列深度会让 MDSBES 服务器保留更多的数据从而占用更多的服务器内存对服务器产生

更大的压力所以调用队列深度需要根据服务器的运行能力进行调用

从应用服务器设计角度应用 MDS 队列限制的方法是尽量减少等待数据的发送如发

送第一条数据的时候发现数据过期对于该用户的其它数据可以调整发送时间在更长的时

间间隔后才尝试发送

对于 MDS 队列限制应用服务器端的推送应用需要对 MDSBES 服务器返回的 503 错误

进行处理对于出现 503 错的数据组织重发同样重发的间隔也需要考虑不要因为数据

重发占用更多的推送队列也可以根据目标用户进行判断对于可能关机的用户暂时不重发

数据

手持设备端接收队列的处理

手持设备端接收队列并未从文档中明确列出是根据测试结果分析得出的手持设备端

目前也没有发现设置可以调整该队列所以客户端程序设计的时候不应该依赖手持设备端队

开发人员需要意识到的是手持设备端的接收队列是可溢出队列一旦推送数据从接收队

列出溢出客户端程序就无法提取该数据了所以在客户端应用开始过程中要注意加快推送

数据的提取过程在提取过程中不要对数据进行复杂的计算提取完成后尽快读取下一条数

据对于接收数据的处理和计算可以交由另一个线程完成

因为溢出数据对客户端应用而言不可见所以数据溢出时在客户端应用中也不会有异

常开发人员需要通过服务器返回的过期消息才能知道该数据推送失败

对于这一点客户端应用除了尽量避免数据溢出外无法在客户端通过其它机制进行补

应用依赖的客户端

对于应用依赖的客户端应用关键点是决定何时调用 accept 函数同时保证 InputStream

和 StreamConnection 被关闭

如之前讨论的使用应用依赖的推送关键是希望使用 MDSBES 发送给应用服务器的异

步确认消息从而对数据是否到达进行判断所以应用依赖的客户端应用需要保证正确调用

accept 函数

另外如果 InputStream 和 StreamConnection 没有关闭的话accept 函数所发出的确认

消息无法被 MDSBES 服务器收到开发人员在代码中应该主动关闭 InputStream 和

StreamConnection同时在程序发生异常时也需要在 catch 代码段对 InputStream 和

StreamConnection 进行关闭处理

异步确认消息处理

异步确认消息用于判断数据是否被成功推送至客户端应用要使用异步确认消息首先

要保证成功地无遗漏地接收所有从 MDSBES 服务器发送过来的异步确认消息

异步确认消息是一个 HTTP 消息消息比较短接收过程的并发压力不大下面是一个

异步确认消息的样例

Received notificationGET HTTP11

User-Agent RIM MDSCS

Accept

X-RIM-Push-ID pushID2081773768

X-RIM-Push-Destination mmtestbjsearbcn

X-RIM-Push-Status 400

Host localhost7778

其中最关键是的获取属性 X-RIM-Push-Status根据该属性的值判断数据推送是成功

(200)还是失败(400)当然属性 X-RIM-Push-ID 也非常重要用于确定该确认消息是针

对哪条推送数据的

如果应用服务器端通过确认消息发现数据推送失败则需要根据属性 X-RIM-Push-ID 重

新组织数据进行重发

Page 12: BES 服务器推送机制分析 - images.csdn.netimages.csdn.net/upimgs/lee/BBPDF/BESfwtsjzfx.pdf · 前言 数据推送是BlackBerry 应用平台的一大优势,在BlackBerry 应用平台上部署的应用可以

ServerSocket serverSocket = new ServerSocket(7778)

serverSocketsetSoTimeout(120000)

try

Socket clientSocket = serverSocketaccept()

InputStream input = clientSocketgetInputStream()

StringBuffer str = new StringBuffer()

int byteRead = inputread()

while ((byteRead = -1) ampamp (inputavailable() gt 0))

strappend((char) byteRead)

byteRead = inputread()

Systemoutprintln(strtoString())

OutputStream output = clientSocketgetOutputStream()

String response = HTTP10 200 OK

outputwrite(responsegetBytes())

outputflush()

outputclose()

inputclose()

clientSocketclose()

catch (SocketTimeoutException ste)

Systemoutprintln(Notification connection timeout

Restarting)

serverSocketclose()

catch (Exception exception)

exceptionprintStackTrace()

异步确认消息的接收代码比较简单基本上就是打开一个 Socket 侦听指定的端口本

例中是 7778然后不断地从该端口读取数据打开 Socket 侦听的代码如下

ServerSocket serverSocket = new ServerSocket(7778)

在异步消息接收过程中需要注意的是接收完 MDSBES 服务器的确认消息后应该给

MDSBES 服务器返回一个 200 的成功消息否则 MDSBES 服务器会认为发送确认消息失败

可能重次几次后再放弃这样异步消息接收程序有可能重复收到同一条确认消息返回成功

消息的代码如下

OutputStream output = clientSocketgetOutputStream()

String response = HTTP10 200 OK

outputwrite(responsegetBytes())

outputflush()

outputclose()

推送细节分析

了解基本的 MDS 推送知识是不足于编写一个可靠的数据推送程序的要保证数据推送

的可靠性必须了解整个推送过程的每一个细节本章节先从整体上描述了整个数据推送的

调用时序然后从不同的关键点详细讲解数据推送的细节

MDS 推送的整体时序

如上图所示数据推送主要是应用服务器MDS 服务器网络服务手机端几个组件

之间的交互在网络服务这一块我们可以假定这一层是稳定的不做考虑对于网络无法提

供服务等情况我们可以通过手持设备端关闭无线电或者是将手持设备放入无线屏蔽区等方

式模拟所以我们要考虑是应用服务器MDSBES 服务器手持设备这三部分

数据推送由应用服务器发生向 MDSBES 服务器发出 HTTP Post 请求第一个需要考虑

的是该 HTTP Post 请求是否能到达 MDSBES 服务器这一点在ldquo推送请求是否到达 MDSBES

服务器rdquo小节中有详细讨论

MDSBES 服务器在接收到 HTTP Post 请求后会对请求的格式进行判断如果格式错误

如指定目标不存在等则 MDSBES 服务器会返回 400 错误所以第二点要考虑是的如何保

证所有到达 MDSBES 服务器的请求都是合法的有关这一点在ldquo推送请求检查rdquo小节中有

详细描述

MDSBES 服务器发现推送数据格式正确以后还需要判断服务器上的队列是否有足够的

空间保存该数据如果没有发现空闲队列则丢弃该数据并向应用服务器返回 503 错误因此

第三点要考虑的是什么情况会导致服务器推送队列占满如何尽量避免这种情况有关这一

点在ldquoMDSBES 中的推送队列rdquo中有详细描述

MDSBES 服务器将数据推送到客户端以后由客户端程序将数据提取交由相应的应用

逻辑进行处理然后客户端应用在提取数据的时候并不一定能及时地将所有推送到的数据

获取所以第四点要考虑的是手持设备端是否也存在队列如何保证客户端程序将所有数据

都提取出来有关这一点在ldquo手持设备端对消息的处理rdquo小节中有详细描述

手持设备在接收推送数据后会通知 MDSBES 服务器MDSBES 服务器则向应用服务器

发送异步的确认消息通过返回数值 200 或者是 400 通知应用服务器推送数据是推送成功还

是推送失败异步确认消息对于应用开发者有特别的意义通过它可以确定数据是否到达客

户端从而决定是否再次推送数据然后很多开发商都发现异步确认消息的返回的ldquo200rdquo

和ldquo400rdquo并不能准备地反映数据的接收情况所以如何使用异步确认消息在不同的场

景下异步消息会包含什么不同值对于应用开发者而言非常重要有关这一点在小节ldquo异步

确认消息rdquo有详细描述

推送请求是否到达 MDSBES 服务器

有关推送请求是否到达 MDSBES 服务器 很多开发人员都会忽略认为推送请求是否

到达 MDSBES 服务器是由代码是否编写正确决定的一定会在代码测试阶段体现也来也

必须在代码编写阶段解决这个问题然而在生产环境确实有一些因素会导致应用服务器发送

的推送请求无法到达 MDSBES 服务器从而导致推送数据丢失的问题

推送请求无法到达 MDSBES 服务器的一个可能的直接原因是指定的服务器名不正确或

者是指定的端口号不正确原因可能是 BES 管理员修改了 BES 服务器的服务器名IP 地址或

者是推送监听端口

这种问题比较容易发现一方面是因为所有的 BlackBerry 用户此时都无法接收到推送数

据另一方面也因为应用服务器端会报网络连接的异常以 java 代码为例当指定的服务

器名或者是端口号不正确的情况下应用服务器端的推送程序会报以下异常

javanetConnectException Connection refused

或者是报域名无法解析等网络连接级别的异常

发现这种情况只需要检查代码中指定的服务器名和端口号是否和生产环境相同就可以

解决问题

推送请求无法到达 MDSBES 服务器的一个潜在可能性是推送请求的并发数量太大导致

MDSBES 服务器无法及时接收所有请求

这种问题比较难于发现因为部分用户仍然可以收到推送数据只有部分用户的部分数

据丢失了同时这种问题在功能性测试场景下不容易发生只有在大压力测试或者是存在

大量用户的生产环境中才容易出现

产生这种问题的根源是 MDSBES 服务器的并发接收限制MDS 服务器其实是运行在

Tomcat 服务器上一个服务Tomcat 服务器作为应用容器处理了低层的网络请求而 Tomcat

服务器在底层有并发网络请求的限制当网络连接达到服务器限制时新的推送连接会处理

等待状态如果在网络超时前仍无法建立连接则会出现网络超时异常

javanetConnectException Connection timed out connect

根据测试的结果不同版本的 BES 服务器在低层并发网络请求的数量上有差异以 200

个并发限制为例就是说同一时间点只能有 200 个连接可以向 MDSBES 服务器发送推送请

求第 201 个请求以后有所有请求都将进入等待状态如果在 TCPIP 超时限制到达之前

MDSBES 服务器仍无法释放前 200 个请求则等待状态的请求会因为网络超时而中断

当应用服务器采用多线程机制处理推送请求的情况下有可能出现以上情况如果因为业

务原因在短时间内出现大量等待推送的数据应用服务器则有可能启动多于 200 个的线程发

送推送请求如果需要推送的数据太大或者是推送线程需要动态对数据进行计算就有可

能导致线程所启动的连接在短时间内没有关闭当多于 200 个线程处于这种状态的时候

MDSBES 服务器就无法继续接收推送请求了这种问题的一个现象就是应用服务器端报网络

超时异常或者是连接被拒绝的异常

总的而言要保证数据推送的成功第一步需要保证推送数据到达 MDSBES 服务器

发现推送数据没有到达 MDSBES 服务器的情况时应该对应用服务器的日志进行检查进一

步确定问题的原因

推送请求检查

在 MDSBES 服务器接收到推送请求后MDSBES 服务器会对请求进行检查只有符合

推送格式的请求才被 MDSBES 服务器所接收当 MDSBES 服务器接收推送时会返回值为

200 的 HTTP 答复告知应用服务器已经接收该请求当 MDSBES 服务器拒绝推送请求时会

根据拒绝的原因返回相应的错误码可能返回的错误码包括 400403503 等下面对不同的

情况进行描述

第一种请求被拒绝的原因是推送请求的格式不对包括 URL 格式不正确和 HTTP 头不正

确两种情况两种情况 MDSBES 服务器都会返回 400 消息表示拒绝URL 格式不正确包括

URL 中不包含ldquoDESTINATIONrdquo参数或者是不包含ldquoPORTrdquo参数等HTTP 头不正确包括

PUSH ID 不唯一等因为推送请求中的 HTTP 头属性都有缺省值所以使用错误的 HTTP 头属

性并不会引发错误MDSBES 服务器只是忽略该参数如 HTTP 头ldquoX-RIM-PUSH-IDrdquo被误写

为ldquoX-RIMPUSHIDrdquoMDSBES 服务器并不会拒绝该请求只是使用ldquoX-RIM-PUSH-IDrdquo的缺

省值就是服务器随机生成一个唯一的 ID 给推送消息

对于 HTTP 协议而言服务器返回 400 代表服务器一般性错误所以返回消息会以异常

形式被捕获以 java 代码为类程序会捕获到以下网络异常

ldquojavaioIOException Server returned HTTP response code 400 for URL

httprdquo

第二种推送请求被拒绝的情况是推送请求指定的收件人不正确就是指应用服务器发送

推送请求时使用了正确的格式不过 DESTINATION 参数指定的收件人并不存在或者是该收件

人并不是 BlackBerry 用户此时 MDSBES 服务器会返回 403 错误读者需要注意 BES 服务

器 37 之前的版本对于这种情况会视作格式错误返回 400 错误

同样以 java 代码为例收件人不正确的情况下程序会捕获到以下网络异常

ldquojavaioIOException Server returned HTTP response code 403 for URL

httprdquo

第三种请求被拒绝的情况是 MDSBES 服务器上的推送队列已满无法再接收更多的推

送请求此时会返回 503 错误表示服务器暂时没有足够的资源处理该请求这种情况下 java

应用程序会捕获到以下异常

ldquojavaioIOException Server returned HTTP response code 503 for URL

httprdquo

这种情况与推送请求是否正确无关取决于 MDSBES 服务器运行情况关于 MDSBES

服务器的推送队列在下一节中我们才进行详细的讨论

如果数据推送请求通过了 MDSBES 服务器的消息检查则 MDSBES 服务器会将该数据

放入推送队列等待发送同时给应用服务器返回成功消息 200

成功消息是以 HTTP 的 ResponseCode 形式返回的开发人员可以通过 HTTP 连接的相应

方法获取在 java 代码中获取 ResponseCode 的函数是ldquo getResponseCoderdquo只有确认

MDSBES 服务器返回的 ResponseCode 是 200才能确定推送数据已经由 MDSBES 接收对

于其它的异常情况都需要由程序进行判断确定如何调整请求继续重试推送过程

有一点非常重要的是 MDSBES 接收该推送数据并不意味着该数据就一定能到达手持设

备端还有很多因素会导致手持设备端的应用接收不到该数据

MDSBES 中的推送队列

MDSBES 服务器有一个队列用于缓存推送请求因为手持设备并不是一定在线

MDSBES 服务器需要先将推送请求保留在服务端在合适的情况下将数据推送到手持设备端

后才从队列中将该请求移除

MDSBES 服务器上的推送队列是 MDS 推送的关键组件它的行为不仅仅影响应用服务

器端是否能够发送新的请求也影响到手持设备端是否能够接收到数据所以对于 MDS 推

送的开发人员必须了解 MDSBES 服务器上的推送队列

MDSBES 服务器上的推送队列有一个队列深度缺省为 1000也就是说 MDSBES 服务

器在同一时间只能缓存 1000 个推送请求该队列的深度可以由管理员在管理端修改

同时 MDSBES 服务器上的推送队列并不是永久保存推送数据它有一个过期时间缺

省为 10 分钟也就是说一个推送请求在 MDSBES 服务器上最多能够被保留 10 分钟同样

这个过期时间可以由管理员在管理端修改

推送队列的深度修改界面如下

推送队列的过期时间的修改界面如下(以毫秒为单位)

对于推送队列的进一步讨论是有关推送数据进入队列后的状态如何变化下面是推送数

据在队列中的状态变化图说明了推送数据在不同情况下的状态变化下图的各个状态中

淡蓝色的状态为起始状态深蓝色的状态为中间状态红色的状态为终止状态处于中间状

态的数据最终必然会转为终止状态的其中一种

当 MDSBES 服务器接收到推送数据后如果之前目标用户没有 5 个处于暂时失败的数

据包服务器会将数据放入等待发送的队列中推送的数据处于ldquo尝试发送rdquo状态处于ldquo尝

试发送rdquo会在很短的时间内占用 1000 个队列位置中的一个不管是成功还是失败它所占

用的队列位置很快被释放

然后 MDSBES 服务器尝试将数据推送到手持设备端如果此时对应的手持设备处于开

机状态网络一切正常则 MDSBES 服务器将数据发送给手持设备推送数据变成ldquo成功rdquo

状态MDSBES 服务器会向应用服务器发送异步的确认消息同时将数据从队列中移除注

意此时 MDSBES 服务器向应用服务器返回的确认消息并不一定是表示成功 200具体是

返回代表成功的 200 还是返回代表失败的 400 还取决于其它因素有关这一点我们在后面的

章节中详细讨论

如果 MDSBES 服务器尝试将数据推送到手持设备的时候手持设备处于关机状态则该

数据进入ldquo暂时失败rdquo状态处于ldquo暂时失败rdquo状态的数据不占用等待队列的队列位置某

一个目标用户最多只能有 5 条数据处于ldquo暂时失败rdquo状态目前没有明确的数据或者是文档

说明一个 MDSBES 服务器总共可以保留多少条ldquo暂时失败rdquo状态的数据

如果某一用户已有 5 条数据处于ldquo暂时失败rdquo状态MDSBES 服务器接收到该用户新

的推送请求时会直接将该数据转入ldquo等待rdquo状态每一条进入ldquo等待rdquo状态的推送数据会占

用推送队列中的一个位置当队列中的 1000 个位置都被占满的时候 MDSBES 服务器就停止

接收新的数据推送无论新的数据是推送给哪个用户

处于ldquo暂时失败rdquo和ldquo等待rdquo状态的数据会在服务器上保留 10 分钟(如上所述这一

时间可以由管理修改)10 分钟后处于这两种状态的数据将被丢弃进入ldquo过期rdquo状态同

时 MDSBES 服务器会向应用服务器发送异步消息这种情况下发送的异步确认消息一定是

代表失败的 400如果在 10 分钟内手持设备重新进入网络手持设备会发送消息到 MDSBES

服务器告知该手持设备已经上线MDSBES 服务器会将处理ldquo暂时失败rdquo和ldquo等待rdquo状态的

数据转成ldquo尝试发送状态rdquo重新发送该数据需要注意的是在 10 分钟的等待过程中

MDSBES 服务器如果没有接收到手持设备重新连接网络的消息则 MDSBES 服务器不会尝

试重新发送ldquo暂时失败rdquo和ldquo等待rdquo状态的数据

根据以上的分析可以对推出以下结果

1 当用户的手持设备在线时对该用户的数据推送会被马上处理该数据只在很短的时间

占用 1000 个等待队列中的一个位置

2 当用户的手持设备不在线时假设该服务器只有一个用户则服务器在 10 分钟内最多

可以接收该用户的 1005 条数据其中 5 条处于ldquo暂时失败rdquo状态1000 条在等待队列

3 如果用户的手持设备在数据发送给 MDSBES 服务器 10 分钟内重新接入网络MDSBES

服务器可以将该数据推送到手持设备端

4 如果 MDSBES 服务器的等待队列未满推送给新的用户的前 5 条数据不占用等待队列

5 如果 MDSBES 服务器的等待队列已满不管是由哪个用户的数据占满的MDSBES 服

务器都会停止接收所有用户的新的数据推送

总结而言在生产环境中保证 MDSBES 服务器正常工作的关键是要保证 MDSBES 服务

器的等待队列不被占满

手持设备端对推送数据的处理

MDSBES 服务器成功接收应用服务器推送的数据后会将数据推送到手持设备端在手持

设备端需要由客户端应用程序将推送的数据提取出来一个直观的猜测是手持设备端同样需

要一个队列保存数据否则客户端应用程序在提取前一条数据时新到达的数据就会直接丢

失需要验证的是这个队列有多深队列行为是否被系统隐藏经过测试证实手持设备端确

实也存在队列队列深度为 10而且可以反映在应用层其中最值得开发人员注意的是这

个队列是一个可溢出队列关于手持设备端推送数据队列的示意图如下

如图所示当客户端提取程序正在读取数据ldquoRrdquo如图中标有 R 的椭圆形所示如果

有更多的数据被推送到手持设备端所推送的数据会被放入设备的队列中一共有 10 条数

据可以被保留在设备队列中如椭圆形 1 到 10 所示此时如果有更多的数据被推送进来

设备队列已满则这些新的数据会溢出如椭圆形 111213 所示同时对于服务器端

而言数据 111213 已经被推送过一次服务器端不会再次尝试推送这些数据在这种

情况下数据 1 到 10 可以继续被客户端提取程序所提取而数据 111213 则丢失了

对于尚未推送到手持设备端的数据如数据 1415假设因为网络中断没有被推送到

手持设备端则仍保留在服务器端当网络恢复后数据 1415 仍可以被正常处理

对于确认消息(图中橙色圆形所示)的处理则更为复杂在异步确认消息一节中再作讨

从这一节的讨论得出的结果是数据到达手持设备端未必就能够被客户端提取程序提取

客户端提取程序有职责尽快提取数据防止推送数据的溢出溢出的数据在客户端无法再被

处理而且服务器端也不会尝试继续推送这些数据

异步确认消息(应用依赖和非应用依赖)

手持设备端在接收到推送数据后会通知 MDSBES 服务器这时 MDSBES 服务器会根据

最初的推送请求向指定的 URL 发送一个确认消息告知应用服务器某一条推送数据的最终

结果这就是异步确认消息异步确认消息以 http 协议发送给应用服务器消息 200 代表

数据推送成功400 代表数据推送失败

异步确认消息是应用服务器判断推送是否成功的有效工具但是很多开发人员因为对

异步确认消息的机制不了解错误地使用了异步确认消息从而导致数据的丢失所以我们

需要详细了解异步确认消息的工作机制

要讨论异步确认消息的工作机制必然涉及到应用依赖与非应用依赖应用依赖的推送

和非应用依赖的推送对应的异步确认消息工作机制是不同的

下面先讨论非应用依赖的场景这也是很多开发商使用的场景

如果推送是非应用依赖的数据是以到达手持设备为成功标志的只要数据到达了手持

设备MDSBES 服务器就认为该数据推送成功会给应用服务器发送异步确认消息 200

如果数据推送时手持设备不在线则 10 分钟内 MDSBES 服务器会在服务器上保留该数

据如果 10 分钟内手持设备重新接入网络则 MDSBES 服务器会尝试发送该数据一旦数

据到达手持设备MDSBES 服务器都会给应用服务器发送异步确认消息 200

如果 10 分钟内手设备没有接入网络则该推送数据过期MDSBES 服务器会丢弃该数

据同时给应用服务器发送异步确认消息 400

可以看出非应用依赖的推送方式并不能保证客户端应用程序接收到数据在非应用依

赖的推送方式下 MDSBES 返回 200 确认消息也不能认为客户端已接收到数据如果数据送

达手持设备的时候客户端应用程序没有启动所推送的数据会在手持设备端丢失而

MDSBES 服务器认为数据已成功送达或者是客户端应用程序已启动但是没有及时读取推

送数据在手持设备端溢出的数据也会丢失MDSBES 服务器同样认为数据已成功送达

所以在非应用依赖的情况下如果接收到 400 确认消息可以确定客户端没有收到数据

如果接收到 200 确认消息只能认为客户端可能收到了数据

从以上的讨论看非应用依赖的推送方式并不适合用于可靠性要求较高的应用对于可

靠性要求较高的应用需要知道客户端应用程序是否接收到了数据应用依赖的推送方式就

是为此而设计的如果推送是应用依赖的数据是以客户端应用程序确认为成功标志的只

要在应用程序确认后MDSBES 服务器才认为数据推送成功这时才给应用服务器发送异步

确认消息 200

有关客户端应用程序如何确认收到数据在ldquo客户端接收关键代码rdquo一节中有描述简

单而言就是客户端应用程序通过调用 MDSPushInputStream 实例的 accept 函数确认数据收

到如果客户端应用程序在接收到数据后没有调用 accept 函数MDSBES 服务器会认为数

据推送失败给应用服务器发送异步确认消息 400另外一点曾经强调过的是accept 函数

调用后只有 InputStream 和 StreamConnection 都关闭了确认消息才被发送给 MDSBES 服

务器在极端的情况下这一点会影响到异步确认消息的结果

下面具体分析各种情景

如果手持设备在线但是客户端应用程序没有启动推送数据到达手持设备端后没有程

序侦听推送端口当然也没有程序会调用 accept 函数此时 MDSBES 服务器会认为数据推

送失败向应用服务器发送异步确认消息 400

如果手持设备在线客户端应用程序在读取推送数据后没有调用 accept 函数直接关闭

连接此时 MDSBES 服务器同样会认为数据推送失败向应用服务器发送异步确认消息 400

在这种情景下即使客户端接收到了数据但应用服务器仍收到失败消息因为作为成功标

志的 accept 函数并没有调用

如果手持设备在线客户端应用程序在读取推送数据后调用了 accept 函数并关闭连

接此时 MDSBES 服务器会认为数据推送成功向应用服务器发送异步确认消息 200

对于手持设备不在线的情况结果与非应用依赖相似10 分钟内如果手持设备不接入

网络的话推送数据会在 MDSBES 服务器上过期MDSBES 服务器会向应用服务器发送异

步确认消息 400

如果 10 分钟内手持设备重新接入网络MDSBES 服务器会尝试发送数据至手持设备端

还是以 accept 函数的调用为推送成功的标志如果数据重新发送成功但是客户端没有调

用 accept 函数则 MDSBES 服务器会向应用服务器发送异步确认消息 200

对于推送到手持设备端但是溢出的数据因为没有程序能够读取该数据并针对该数据调

用 accept 函数MDSBES 服务器会一直等待到 10 分钟后向应用服务器发送异步确认消息

400认为数据过期

一个特殊的情况是客户端应用程序在接收到数据后调用了 accept但是没有关闭

InputStream 和 StreamConnection此时 MDSBES 服务器会认识客户端还在处理数据会继

续等待如果服务器等待时候超过 10 分钟MDSBES 服务器会认为数据推送失败向应用

服务器发送异步确认消息 400如果在 10 分钟内客户端主动关闭 InputStream 和

StreamConnection或者是网络异常中断MDSBES 服务器会认为数据推送成功向应用服

务器发送异步确认消息 200

下面再讨论一个异常情况下的特例

结合以上描述的各种因素在极端的情况下会出现一些特殊的情况如上图所示假设

MDSBES 服务器在推送了数据 R数据 1 到数据 13 后出现网络中断同时客户端应用程序

正在缓慢地读取数据 R此时数据 1 到数据 10 会被保留在手持设备队列中客户端应用程

序在读取数据 R 后可以继续读取数据 1 到数据 10如果客户端应用程序在读取了数据后都

调用了 accept 函数则会产生对应用确认消息如图中的确认消息 R确认消息 1 到确认消

息 10但是因为网络的中断这些确认消息都无法到达 MDSBES 服务器如果网络在 10

钟内没有恢复MDSBES 服务器上所有等待确认消息的记录都会认识是过期的MDSBES

会向应用服务器发送异步确认消息 400此后即使网络恢复确认消息 R确认消息 1 到确

认消息 10 都不再有效

在这种特殊情况下即使客户端应用程序接收到数据并调用了 accept 函数MDSBES

服务器同样认为数据没有推送成功

根据以上的讨论应用依赖的推送同样不能保证客户端应用一定能收到数据但是在

应用依赖的推送方式下 MDSBES 返回 200 确认消息可以确定客户端已接收到数据因为 200

确认消息是由客户端应用程序调用 accept 函数触发的客户端应用程序可以在确认数据收

到后才调用 accept 函数通知应用服务器该数据已成功接收甚至可以在接收到数据后根

据数据内容决定不调用 accept 函数从而通过应用服务器该数据没有被接收

所以在应用依赖的情况下如果接收到 200 确认消息可以确定客户端已经接收到数据

如果接收到 400 确认消息只能认为客户端可能没有收到数据

因此应用依赖的推送方式更适合可靠性要求高的应用如果应用服务器接收到 200

确认消息就可以认为推送成功继续处理其它事务如果应用服务器接收到 400 确认消息则

需要组织数据重发直到接收到 200 确认消息为止这样就可以保证客户端一定能够接收到

需要推送的数据

如何保证数据推送的可靠性

通过以上几个章节的讨论我们知道虽然 MDSBES 服务器提供了很多机制保证数据

推送的可靠性但是都无法完全保证数据被客户端应用程序所接收

对于数据推送的可靠性保证一方面开发人员需要了解各个可能导致故障的因素在应

用编写的时候尽量减少故障出现的可能性对故障点进行规避另一方面开发人员也需要实

现额外的机制进行数据重发在故障点无法规避的情况下通过数据重发消除故障点的影响

应用依赖和非应用依赖的比较和选择

在实现推送应用的时候首要考虑的是有关应用依赖与非应用依赖的选择

应用依赖的推送形式在可靠性方面更优于非应用依赖的推送形式但是应用依赖的推送

形式对 MDSBES 应用服务器的潜在压力较大客户端应用程序的实现也较为复杂

非应用依赖的推送形式优势在于实现简单对 MDSBES 应用服务器形成的压力相对较

非应用依赖的推送形式从某种程度上讲有点类似于广播协议着重点是从服务器向客户

端发送大量数据并不看重客户端的接收情况从数据的应用类型来分非应用依赖的推送

更适合于天气信息最新报价等数据这些数据在客户端应用关闭时被自然丢弃短时间内

的多条数据有丢失影响也不大必竞用户不会关注 11 点零六分的天气和 11 点零七分的天气

有何不同

应用依赖的推送形式则类似于点对点协议不仅从服务器端向客户端发送数据同时也

关注客户端的接收情况应用依赖的推送更适合于 OAERP 这种应用这种应用需要保证

新的文档或者是记录一定能到达客户端应用丢失一条待办信息可能就会导致整个审批流程

的停滞

再从两种推送方式的本质差别看应用依赖并没有额外的机制消除故障点它的优势在

于它的异步确认消息更可信所以在选择了应用依赖的推送方式后进一步还是需要实现

额外的数据重发机制根据 MDSBES 所发送的异步确认消息实施数据重发

应用依赖的推送再加上结合异步确认消息的数据重发机制是保证推送数据可靠性的

最好方法

MDS 接收线程限制的处理

不管使用什么方式的推送应用开发时都需要考虑到 MDS 接收线程的限制不过以

现在的服务器处理能力正常情况下的 HTTP 推送请求可以在几毫秒或者是几十毫内处理完

成少量的 HTTP 推送不足于构成网络阻塞

需要考虑的是应用服务器端的推送程序在推送过程中不要在建立连接后才动态组织数

据这样会浪费 http 连接资源好的建议是预先组织好数据在建立 HTTP 连接后快速写完

数据尽快关闭连接

此外对于应用服务器的推送线程也要加于控制避免在短时间内无限制地启动新的线

程进行连接否则也容易造成连接过多而阻塞的情况

从网络层面来看调整应用服务器与 MDSBES 服务器之间的网络超时设置也是避免这

种问题的一种方法在允许的范围内调大网络超时时间可以让应用服务器的推送线程等待更

长的时间从而让 MDSBES 有足够的时间释放原有的连接

最后无论使用了什么方法调优都需要对应用服务器的推送程序进行异常处理当发

现网络超时异常的时候可以组织数据重发当然数据重发的间隔时间也要考虑不要因为

重发数据再次造成网络阻塞

MDS 推送队列限制的处理

相对而言MDS 推送队列限制造成的推送失败比 MDS 接收队列造成的推送失败更常见

已经发现的一个实际案例是周未的时候用户大量关机导致大量推送数据处于推送队列中

填满了推送队列从而导致 MDSBES 服务器不再响应新的推送请求一旦过期时间到达

有新的队列位置空闲又有大量新的推送数据重新占满推送队列

对于 MDS 推送队列的限制直观的做法是对队列参数进行调整如之前讨论的管理

员可以对队列的深度和过期时间进行调整

然而出于对过期时间的不正确理解管理员倾向于将过期时间调大当过期时间被调

大后推送给关机用户的数据在服务器占用队列的时间就变长导致 MDS 推送队列更容易

被占满

所以如果对过期时间进行调整的话方向是将过期时间调小过期时间越小等待数

据占用队列的时间就越短MDS 推送队列被占满的可能性就越小但是过期时间也不能

过小过期时间过小可能会导致过多的过期数据使应用服务器需要处理更多的数据重发

比如将过期时间调成 1 分钟用户走过一个人行遂道都可能导致数据发送失败

对于队列深度的调整方向是调大队列深度如从缺省的 1000 调整到 2000此时

MDSBES 服务器在同一时间可以保留更多的等待数据减少了队列占满的机会不过调大队

列深度会让 MDSBES 服务器保留更多的数据从而占用更多的服务器内存对服务器产生

更大的压力所以调用队列深度需要根据服务器的运行能力进行调用

从应用服务器设计角度应用 MDS 队列限制的方法是尽量减少等待数据的发送如发

送第一条数据的时候发现数据过期对于该用户的其它数据可以调整发送时间在更长的时

间间隔后才尝试发送

对于 MDS 队列限制应用服务器端的推送应用需要对 MDSBES 服务器返回的 503 错误

进行处理对于出现 503 错的数据组织重发同样重发的间隔也需要考虑不要因为数据

重发占用更多的推送队列也可以根据目标用户进行判断对于可能关机的用户暂时不重发

数据

手持设备端接收队列的处理

手持设备端接收队列并未从文档中明确列出是根据测试结果分析得出的手持设备端

目前也没有发现设置可以调整该队列所以客户端程序设计的时候不应该依赖手持设备端队

开发人员需要意识到的是手持设备端的接收队列是可溢出队列一旦推送数据从接收队

列出溢出客户端程序就无法提取该数据了所以在客户端应用开始过程中要注意加快推送

数据的提取过程在提取过程中不要对数据进行复杂的计算提取完成后尽快读取下一条数

据对于接收数据的处理和计算可以交由另一个线程完成

因为溢出数据对客户端应用而言不可见所以数据溢出时在客户端应用中也不会有异

常开发人员需要通过服务器返回的过期消息才能知道该数据推送失败

对于这一点客户端应用除了尽量避免数据溢出外无法在客户端通过其它机制进行补

应用依赖的客户端

对于应用依赖的客户端应用关键点是决定何时调用 accept 函数同时保证 InputStream

和 StreamConnection 被关闭

如之前讨论的使用应用依赖的推送关键是希望使用 MDSBES 发送给应用服务器的异

步确认消息从而对数据是否到达进行判断所以应用依赖的客户端应用需要保证正确调用

accept 函数

另外如果 InputStream 和 StreamConnection 没有关闭的话accept 函数所发出的确认

消息无法被 MDSBES 服务器收到开发人员在代码中应该主动关闭 InputStream 和

StreamConnection同时在程序发生异常时也需要在 catch 代码段对 InputStream 和

StreamConnection 进行关闭处理

异步确认消息处理

异步确认消息用于判断数据是否被成功推送至客户端应用要使用异步确认消息首先

要保证成功地无遗漏地接收所有从 MDSBES 服务器发送过来的异步确认消息

异步确认消息是一个 HTTP 消息消息比较短接收过程的并发压力不大下面是一个

异步确认消息的样例

Received notificationGET HTTP11

User-Agent RIM MDSCS

Accept

X-RIM-Push-ID pushID2081773768

X-RIM-Push-Destination mmtestbjsearbcn

X-RIM-Push-Status 400

Host localhost7778

其中最关键是的获取属性 X-RIM-Push-Status根据该属性的值判断数据推送是成功

(200)还是失败(400)当然属性 X-RIM-Push-ID 也非常重要用于确定该确认消息是针

对哪条推送数据的

如果应用服务器端通过确认消息发现数据推送失败则需要根据属性 X-RIM-Push-ID 重

新组织数据进行重发

Page 13: BES 服务器推送机制分析 - images.csdn.netimages.csdn.net/upimgs/lee/BBPDF/BESfwtsjzfx.pdf · 前言 数据推送是BlackBerry 应用平台的一大优势,在BlackBerry 应用平台上部署的应用可以

String response = HTTP10 200 OK

outputwrite(responsegetBytes())

outputflush()

outputclose()

推送细节分析

了解基本的 MDS 推送知识是不足于编写一个可靠的数据推送程序的要保证数据推送

的可靠性必须了解整个推送过程的每一个细节本章节先从整体上描述了整个数据推送的

调用时序然后从不同的关键点详细讲解数据推送的细节

MDS 推送的整体时序

如上图所示数据推送主要是应用服务器MDS 服务器网络服务手机端几个组件

之间的交互在网络服务这一块我们可以假定这一层是稳定的不做考虑对于网络无法提

供服务等情况我们可以通过手持设备端关闭无线电或者是将手持设备放入无线屏蔽区等方

式模拟所以我们要考虑是应用服务器MDSBES 服务器手持设备这三部分

数据推送由应用服务器发生向 MDSBES 服务器发出 HTTP Post 请求第一个需要考虑

的是该 HTTP Post 请求是否能到达 MDSBES 服务器这一点在ldquo推送请求是否到达 MDSBES

服务器rdquo小节中有详细讨论

MDSBES 服务器在接收到 HTTP Post 请求后会对请求的格式进行判断如果格式错误

如指定目标不存在等则 MDSBES 服务器会返回 400 错误所以第二点要考虑是的如何保

证所有到达 MDSBES 服务器的请求都是合法的有关这一点在ldquo推送请求检查rdquo小节中有

详细描述

MDSBES 服务器发现推送数据格式正确以后还需要判断服务器上的队列是否有足够的

空间保存该数据如果没有发现空闲队列则丢弃该数据并向应用服务器返回 503 错误因此

第三点要考虑的是什么情况会导致服务器推送队列占满如何尽量避免这种情况有关这一

点在ldquoMDSBES 中的推送队列rdquo中有详细描述

MDSBES 服务器将数据推送到客户端以后由客户端程序将数据提取交由相应的应用

逻辑进行处理然后客户端应用在提取数据的时候并不一定能及时地将所有推送到的数据

获取所以第四点要考虑的是手持设备端是否也存在队列如何保证客户端程序将所有数据

都提取出来有关这一点在ldquo手持设备端对消息的处理rdquo小节中有详细描述

手持设备在接收推送数据后会通知 MDSBES 服务器MDSBES 服务器则向应用服务器

发送异步的确认消息通过返回数值 200 或者是 400 通知应用服务器推送数据是推送成功还

是推送失败异步确认消息对于应用开发者有特别的意义通过它可以确定数据是否到达客

户端从而决定是否再次推送数据然后很多开发商都发现异步确认消息的返回的ldquo200rdquo

和ldquo400rdquo并不能准备地反映数据的接收情况所以如何使用异步确认消息在不同的场

景下异步消息会包含什么不同值对于应用开发者而言非常重要有关这一点在小节ldquo异步

确认消息rdquo有详细描述

推送请求是否到达 MDSBES 服务器

有关推送请求是否到达 MDSBES 服务器 很多开发人员都会忽略认为推送请求是否

到达 MDSBES 服务器是由代码是否编写正确决定的一定会在代码测试阶段体现也来也

必须在代码编写阶段解决这个问题然而在生产环境确实有一些因素会导致应用服务器发送

的推送请求无法到达 MDSBES 服务器从而导致推送数据丢失的问题

推送请求无法到达 MDSBES 服务器的一个可能的直接原因是指定的服务器名不正确或

者是指定的端口号不正确原因可能是 BES 管理员修改了 BES 服务器的服务器名IP 地址或

者是推送监听端口

这种问题比较容易发现一方面是因为所有的 BlackBerry 用户此时都无法接收到推送数

据另一方面也因为应用服务器端会报网络连接的异常以 java 代码为例当指定的服务

器名或者是端口号不正确的情况下应用服务器端的推送程序会报以下异常

javanetConnectException Connection refused

或者是报域名无法解析等网络连接级别的异常

发现这种情况只需要检查代码中指定的服务器名和端口号是否和生产环境相同就可以

解决问题

推送请求无法到达 MDSBES 服务器的一个潜在可能性是推送请求的并发数量太大导致

MDSBES 服务器无法及时接收所有请求

这种问题比较难于发现因为部分用户仍然可以收到推送数据只有部分用户的部分数

据丢失了同时这种问题在功能性测试场景下不容易发生只有在大压力测试或者是存在

大量用户的生产环境中才容易出现

产生这种问题的根源是 MDSBES 服务器的并发接收限制MDS 服务器其实是运行在

Tomcat 服务器上一个服务Tomcat 服务器作为应用容器处理了低层的网络请求而 Tomcat

服务器在底层有并发网络请求的限制当网络连接达到服务器限制时新的推送连接会处理

等待状态如果在网络超时前仍无法建立连接则会出现网络超时异常

javanetConnectException Connection timed out connect

根据测试的结果不同版本的 BES 服务器在低层并发网络请求的数量上有差异以 200

个并发限制为例就是说同一时间点只能有 200 个连接可以向 MDSBES 服务器发送推送请

求第 201 个请求以后有所有请求都将进入等待状态如果在 TCPIP 超时限制到达之前

MDSBES 服务器仍无法释放前 200 个请求则等待状态的请求会因为网络超时而中断

当应用服务器采用多线程机制处理推送请求的情况下有可能出现以上情况如果因为业

务原因在短时间内出现大量等待推送的数据应用服务器则有可能启动多于 200 个的线程发

送推送请求如果需要推送的数据太大或者是推送线程需要动态对数据进行计算就有可

能导致线程所启动的连接在短时间内没有关闭当多于 200 个线程处于这种状态的时候

MDSBES 服务器就无法继续接收推送请求了这种问题的一个现象就是应用服务器端报网络

超时异常或者是连接被拒绝的异常

总的而言要保证数据推送的成功第一步需要保证推送数据到达 MDSBES 服务器

发现推送数据没有到达 MDSBES 服务器的情况时应该对应用服务器的日志进行检查进一

步确定问题的原因

推送请求检查

在 MDSBES 服务器接收到推送请求后MDSBES 服务器会对请求进行检查只有符合

推送格式的请求才被 MDSBES 服务器所接收当 MDSBES 服务器接收推送时会返回值为

200 的 HTTP 答复告知应用服务器已经接收该请求当 MDSBES 服务器拒绝推送请求时会

根据拒绝的原因返回相应的错误码可能返回的错误码包括 400403503 等下面对不同的

情况进行描述

第一种请求被拒绝的原因是推送请求的格式不对包括 URL 格式不正确和 HTTP 头不正

确两种情况两种情况 MDSBES 服务器都会返回 400 消息表示拒绝URL 格式不正确包括

URL 中不包含ldquoDESTINATIONrdquo参数或者是不包含ldquoPORTrdquo参数等HTTP 头不正确包括

PUSH ID 不唯一等因为推送请求中的 HTTP 头属性都有缺省值所以使用错误的 HTTP 头属

性并不会引发错误MDSBES 服务器只是忽略该参数如 HTTP 头ldquoX-RIM-PUSH-IDrdquo被误写

为ldquoX-RIMPUSHIDrdquoMDSBES 服务器并不会拒绝该请求只是使用ldquoX-RIM-PUSH-IDrdquo的缺

省值就是服务器随机生成一个唯一的 ID 给推送消息

对于 HTTP 协议而言服务器返回 400 代表服务器一般性错误所以返回消息会以异常

形式被捕获以 java 代码为类程序会捕获到以下网络异常

ldquojavaioIOException Server returned HTTP response code 400 for URL

httprdquo

第二种推送请求被拒绝的情况是推送请求指定的收件人不正确就是指应用服务器发送

推送请求时使用了正确的格式不过 DESTINATION 参数指定的收件人并不存在或者是该收件

人并不是 BlackBerry 用户此时 MDSBES 服务器会返回 403 错误读者需要注意 BES 服务

器 37 之前的版本对于这种情况会视作格式错误返回 400 错误

同样以 java 代码为例收件人不正确的情况下程序会捕获到以下网络异常

ldquojavaioIOException Server returned HTTP response code 403 for URL

httprdquo

第三种请求被拒绝的情况是 MDSBES 服务器上的推送队列已满无法再接收更多的推

送请求此时会返回 503 错误表示服务器暂时没有足够的资源处理该请求这种情况下 java

应用程序会捕获到以下异常

ldquojavaioIOException Server returned HTTP response code 503 for URL

httprdquo

这种情况与推送请求是否正确无关取决于 MDSBES 服务器运行情况关于 MDSBES

服务器的推送队列在下一节中我们才进行详细的讨论

如果数据推送请求通过了 MDSBES 服务器的消息检查则 MDSBES 服务器会将该数据

放入推送队列等待发送同时给应用服务器返回成功消息 200

成功消息是以 HTTP 的 ResponseCode 形式返回的开发人员可以通过 HTTP 连接的相应

方法获取在 java 代码中获取 ResponseCode 的函数是ldquo getResponseCoderdquo只有确认

MDSBES 服务器返回的 ResponseCode 是 200才能确定推送数据已经由 MDSBES 接收对

于其它的异常情况都需要由程序进行判断确定如何调整请求继续重试推送过程

有一点非常重要的是 MDSBES 接收该推送数据并不意味着该数据就一定能到达手持设

备端还有很多因素会导致手持设备端的应用接收不到该数据

MDSBES 中的推送队列

MDSBES 服务器有一个队列用于缓存推送请求因为手持设备并不是一定在线

MDSBES 服务器需要先将推送请求保留在服务端在合适的情况下将数据推送到手持设备端

后才从队列中将该请求移除

MDSBES 服务器上的推送队列是 MDS 推送的关键组件它的行为不仅仅影响应用服务

器端是否能够发送新的请求也影响到手持设备端是否能够接收到数据所以对于 MDS 推

送的开发人员必须了解 MDSBES 服务器上的推送队列

MDSBES 服务器上的推送队列有一个队列深度缺省为 1000也就是说 MDSBES 服务

器在同一时间只能缓存 1000 个推送请求该队列的深度可以由管理员在管理端修改

同时 MDSBES 服务器上的推送队列并不是永久保存推送数据它有一个过期时间缺

省为 10 分钟也就是说一个推送请求在 MDSBES 服务器上最多能够被保留 10 分钟同样

这个过期时间可以由管理员在管理端修改

推送队列的深度修改界面如下

推送队列的过期时间的修改界面如下(以毫秒为单位)

对于推送队列的进一步讨论是有关推送数据进入队列后的状态如何变化下面是推送数

据在队列中的状态变化图说明了推送数据在不同情况下的状态变化下图的各个状态中

淡蓝色的状态为起始状态深蓝色的状态为中间状态红色的状态为终止状态处于中间状

态的数据最终必然会转为终止状态的其中一种

当 MDSBES 服务器接收到推送数据后如果之前目标用户没有 5 个处于暂时失败的数

据包服务器会将数据放入等待发送的队列中推送的数据处于ldquo尝试发送rdquo状态处于ldquo尝

试发送rdquo会在很短的时间内占用 1000 个队列位置中的一个不管是成功还是失败它所占

用的队列位置很快被释放

然后 MDSBES 服务器尝试将数据推送到手持设备端如果此时对应的手持设备处于开

机状态网络一切正常则 MDSBES 服务器将数据发送给手持设备推送数据变成ldquo成功rdquo

状态MDSBES 服务器会向应用服务器发送异步的确认消息同时将数据从队列中移除注

意此时 MDSBES 服务器向应用服务器返回的确认消息并不一定是表示成功 200具体是

返回代表成功的 200 还是返回代表失败的 400 还取决于其它因素有关这一点我们在后面的

章节中详细讨论

如果 MDSBES 服务器尝试将数据推送到手持设备的时候手持设备处于关机状态则该

数据进入ldquo暂时失败rdquo状态处于ldquo暂时失败rdquo状态的数据不占用等待队列的队列位置某

一个目标用户最多只能有 5 条数据处于ldquo暂时失败rdquo状态目前没有明确的数据或者是文档

说明一个 MDSBES 服务器总共可以保留多少条ldquo暂时失败rdquo状态的数据

如果某一用户已有 5 条数据处于ldquo暂时失败rdquo状态MDSBES 服务器接收到该用户新

的推送请求时会直接将该数据转入ldquo等待rdquo状态每一条进入ldquo等待rdquo状态的推送数据会占

用推送队列中的一个位置当队列中的 1000 个位置都被占满的时候 MDSBES 服务器就停止

接收新的数据推送无论新的数据是推送给哪个用户

处于ldquo暂时失败rdquo和ldquo等待rdquo状态的数据会在服务器上保留 10 分钟(如上所述这一

时间可以由管理修改)10 分钟后处于这两种状态的数据将被丢弃进入ldquo过期rdquo状态同

时 MDSBES 服务器会向应用服务器发送异步消息这种情况下发送的异步确认消息一定是

代表失败的 400如果在 10 分钟内手持设备重新进入网络手持设备会发送消息到 MDSBES

服务器告知该手持设备已经上线MDSBES 服务器会将处理ldquo暂时失败rdquo和ldquo等待rdquo状态的

数据转成ldquo尝试发送状态rdquo重新发送该数据需要注意的是在 10 分钟的等待过程中

MDSBES 服务器如果没有接收到手持设备重新连接网络的消息则 MDSBES 服务器不会尝

试重新发送ldquo暂时失败rdquo和ldquo等待rdquo状态的数据

根据以上的分析可以对推出以下结果

1 当用户的手持设备在线时对该用户的数据推送会被马上处理该数据只在很短的时间

占用 1000 个等待队列中的一个位置

2 当用户的手持设备不在线时假设该服务器只有一个用户则服务器在 10 分钟内最多

可以接收该用户的 1005 条数据其中 5 条处于ldquo暂时失败rdquo状态1000 条在等待队列

3 如果用户的手持设备在数据发送给 MDSBES 服务器 10 分钟内重新接入网络MDSBES

服务器可以将该数据推送到手持设备端

4 如果 MDSBES 服务器的等待队列未满推送给新的用户的前 5 条数据不占用等待队列

5 如果 MDSBES 服务器的等待队列已满不管是由哪个用户的数据占满的MDSBES 服

务器都会停止接收所有用户的新的数据推送

总结而言在生产环境中保证 MDSBES 服务器正常工作的关键是要保证 MDSBES 服务

器的等待队列不被占满

手持设备端对推送数据的处理

MDSBES 服务器成功接收应用服务器推送的数据后会将数据推送到手持设备端在手持

设备端需要由客户端应用程序将推送的数据提取出来一个直观的猜测是手持设备端同样需

要一个队列保存数据否则客户端应用程序在提取前一条数据时新到达的数据就会直接丢

失需要验证的是这个队列有多深队列行为是否被系统隐藏经过测试证实手持设备端确

实也存在队列队列深度为 10而且可以反映在应用层其中最值得开发人员注意的是这

个队列是一个可溢出队列关于手持设备端推送数据队列的示意图如下

如图所示当客户端提取程序正在读取数据ldquoRrdquo如图中标有 R 的椭圆形所示如果

有更多的数据被推送到手持设备端所推送的数据会被放入设备的队列中一共有 10 条数

据可以被保留在设备队列中如椭圆形 1 到 10 所示此时如果有更多的数据被推送进来

设备队列已满则这些新的数据会溢出如椭圆形 111213 所示同时对于服务器端

而言数据 111213 已经被推送过一次服务器端不会再次尝试推送这些数据在这种

情况下数据 1 到 10 可以继续被客户端提取程序所提取而数据 111213 则丢失了

对于尚未推送到手持设备端的数据如数据 1415假设因为网络中断没有被推送到

手持设备端则仍保留在服务器端当网络恢复后数据 1415 仍可以被正常处理

对于确认消息(图中橙色圆形所示)的处理则更为复杂在异步确认消息一节中再作讨

从这一节的讨论得出的结果是数据到达手持设备端未必就能够被客户端提取程序提取

客户端提取程序有职责尽快提取数据防止推送数据的溢出溢出的数据在客户端无法再被

处理而且服务器端也不会尝试继续推送这些数据

异步确认消息(应用依赖和非应用依赖)

手持设备端在接收到推送数据后会通知 MDSBES 服务器这时 MDSBES 服务器会根据

最初的推送请求向指定的 URL 发送一个确认消息告知应用服务器某一条推送数据的最终

结果这就是异步确认消息异步确认消息以 http 协议发送给应用服务器消息 200 代表

数据推送成功400 代表数据推送失败

异步确认消息是应用服务器判断推送是否成功的有效工具但是很多开发人员因为对

异步确认消息的机制不了解错误地使用了异步确认消息从而导致数据的丢失所以我们

需要详细了解异步确认消息的工作机制

要讨论异步确认消息的工作机制必然涉及到应用依赖与非应用依赖应用依赖的推送

和非应用依赖的推送对应的异步确认消息工作机制是不同的

下面先讨论非应用依赖的场景这也是很多开发商使用的场景

如果推送是非应用依赖的数据是以到达手持设备为成功标志的只要数据到达了手持

设备MDSBES 服务器就认为该数据推送成功会给应用服务器发送异步确认消息 200

如果数据推送时手持设备不在线则 10 分钟内 MDSBES 服务器会在服务器上保留该数

据如果 10 分钟内手持设备重新接入网络则 MDSBES 服务器会尝试发送该数据一旦数

据到达手持设备MDSBES 服务器都会给应用服务器发送异步确认消息 200

如果 10 分钟内手设备没有接入网络则该推送数据过期MDSBES 服务器会丢弃该数

据同时给应用服务器发送异步确认消息 400

可以看出非应用依赖的推送方式并不能保证客户端应用程序接收到数据在非应用依

赖的推送方式下 MDSBES 返回 200 确认消息也不能认为客户端已接收到数据如果数据送

达手持设备的时候客户端应用程序没有启动所推送的数据会在手持设备端丢失而

MDSBES 服务器认为数据已成功送达或者是客户端应用程序已启动但是没有及时读取推

送数据在手持设备端溢出的数据也会丢失MDSBES 服务器同样认为数据已成功送达

所以在非应用依赖的情况下如果接收到 400 确认消息可以确定客户端没有收到数据

如果接收到 200 确认消息只能认为客户端可能收到了数据

从以上的讨论看非应用依赖的推送方式并不适合用于可靠性要求较高的应用对于可

靠性要求较高的应用需要知道客户端应用程序是否接收到了数据应用依赖的推送方式就

是为此而设计的如果推送是应用依赖的数据是以客户端应用程序确认为成功标志的只

要在应用程序确认后MDSBES 服务器才认为数据推送成功这时才给应用服务器发送异步

确认消息 200

有关客户端应用程序如何确认收到数据在ldquo客户端接收关键代码rdquo一节中有描述简

单而言就是客户端应用程序通过调用 MDSPushInputStream 实例的 accept 函数确认数据收

到如果客户端应用程序在接收到数据后没有调用 accept 函数MDSBES 服务器会认为数

据推送失败给应用服务器发送异步确认消息 400另外一点曾经强调过的是accept 函数

调用后只有 InputStream 和 StreamConnection 都关闭了确认消息才被发送给 MDSBES 服

务器在极端的情况下这一点会影响到异步确认消息的结果

下面具体分析各种情景

如果手持设备在线但是客户端应用程序没有启动推送数据到达手持设备端后没有程

序侦听推送端口当然也没有程序会调用 accept 函数此时 MDSBES 服务器会认为数据推

送失败向应用服务器发送异步确认消息 400

如果手持设备在线客户端应用程序在读取推送数据后没有调用 accept 函数直接关闭

连接此时 MDSBES 服务器同样会认为数据推送失败向应用服务器发送异步确认消息 400

在这种情景下即使客户端接收到了数据但应用服务器仍收到失败消息因为作为成功标

志的 accept 函数并没有调用

如果手持设备在线客户端应用程序在读取推送数据后调用了 accept 函数并关闭连

接此时 MDSBES 服务器会认为数据推送成功向应用服务器发送异步确认消息 200

对于手持设备不在线的情况结果与非应用依赖相似10 分钟内如果手持设备不接入

网络的话推送数据会在 MDSBES 服务器上过期MDSBES 服务器会向应用服务器发送异

步确认消息 400

如果 10 分钟内手持设备重新接入网络MDSBES 服务器会尝试发送数据至手持设备端

还是以 accept 函数的调用为推送成功的标志如果数据重新发送成功但是客户端没有调

用 accept 函数则 MDSBES 服务器会向应用服务器发送异步确认消息 200

对于推送到手持设备端但是溢出的数据因为没有程序能够读取该数据并针对该数据调

用 accept 函数MDSBES 服务器会一直等待到 10 分钟后向应用服务器发送异步确认消息

400认为数据过期

一个特殊的情况是客户端应用程序在接收到数据后调用了 accept但是没有关闭

InputStream 和 StreamConnection此时 MDSBES 服务器会认识客户端还在处理数据会继

续等待如果服务器等待时候超过 10 分钟MDSBES 服务器会认为数据推送失败向应用

服务器发送异步确认消息 400如果在 10 分钟内客户端主动关闭 InputStream 和

StreamConnection或者是网络异常中断MDSBES 服务器会认为数据推送成功向应用服

务器发送异步确认消息 200

下面再讨论一个异常情况下的特例

结合以上描述的各种因素在极端的情况下会出现一些特殊的情况如上图所示假设

MDSBES 服务器在推送了数据 R数据 1 到数据 13 后出现网络中断同时客户端应用程序

正在缓慢地读取数据 R此时数据 1 到数据 10 会被保留在手持设备队列中客户端应用程

序在读取数据 R 后可以继续读取数据 1 到数据 10如果客户端应用程序在读取了数据后都

调用了 accept 函数则会产生对应用确认消息如图中的确认消息 R确认消息 1 到确认消

息 10但是因为网络的中断这些确认消息都无法到达 MDSBES 服务器如果网络在 10

钟内没有恢复MDSBES 服务器上所有等待确认消息的记录都会认识是过期的MDSBES

会向应用服务器发送异步确认消息 400此后即使网络恢复确认消息 R确认消息 1 到确

认消息 10 都不再有效

在这种特殊情况下即使客户端应用程序接收到数据并调用了 accept 函数MDSBES

服务器同样认为数据没有推送成功

根据以上的讨论应用依赖的推送同样不能保证客户端应用一定能收到数据但是在

应用依赖的推送方式下 MDSBES 返回 200 确认消息可以确定客户端已接收到数据因为 200

确认消息是由客户端应用程序调用 accept 函数触发的客户端应用程序可以在确认数据收

到后才调用 accept 函数通知应用服务器该数据已成功接收甚至可以在接收到数据后根

据数据内容决定不调用 accept 函数从而通过应用服务器该数据没有被接收

所以在应用依赖的情况下如果接收到 200 确认消息可以确定客户端已经接收到数据

如果接收到 400 确认消息只能认为客户端可能没有收到数据

因此应用依赖的推送方式更适合可靠性要求高的应用如果应用服务器接收到 200

确认消息就可以认为推送成功继续处理其它事务如果应用服务器接收到 400 确认消息则

需要组织数据重发直到接收到 200 确认消息为止这样就可以保证客户端一定能够接收到

需要推送的数据

如何保证数据推送的可靠性

通过以上几个章节的讨论我们知道虽然 MDSBES 服务器提供了很多机制保证数据

推送的可靠性但是都无法完全保证数据被客户端应用程序所接收

对于数据推送的可靠性保证一方面开发人员需要了解各个可能导致故障的因素在应

用编写的时候尽量减少故障出现的可能性对故障点进行规避另一方面开发人员也需要实

现额外的机制进行数据重发在故障点无法规避的情况下通过数据重发消除故障点的影响

应用依赖和非应用依赖的比较和选择

在实现推送应用的时候首要考虑的是有关应用依赖与非应用依赖的选择

应用依赖的推送形式在可靠性方面更优于非应用依赖的推送形式但是应用依赖的推送

形式对 MDSBES 应用服务器的潜在压力较大客户端应用程序的实现也较为复杂

非应用依赖的推送形式优势在于实现简单对 MDSBES 应用服务器形成的压力相对较

非应用依赖的推送形式从某种程度上讲有点类似于广播协议着重点是从服务器向客户

端发送大量数据并不看重客户端的接收情况从数据的应用类型来分非应用依赖的推送

更适合于天气信息最新报价等数据这些数据在客户端应用关闭时被自然丢弃短时间内

的多条数据有丢失影响也不大必竞用户不会关注 11 点零六分的天气和 11 点零七分的天气

有何不同

应用依赖的推送形式则类似于点对点协议不仅从服务器端向客户端发送数据同时也

关注客户端的接收情况应用依赖的推送更适合于 OAERP 这种应用这种应用需要保证

新的文档或者是记录一定能到达客户端应用丢失一条待办信息可能就会导致整个审批流程

的停滞

再从两种推送方式的本质差别看应用依赖并没有额外的机制消除故障点它的优势在

于它的异步确认消息更可信所以在选择了应用依赖的推送方式后进一步还是需要实现

额外的数据重发机制根据 MDSBES 所发送的异步确认消息实施数据重发

应用依赖的推送再加上结合异步确认消息的数据重发机制是保证推送数据可靠性的

最好方法

MDS 接收线程限制的处理

不管使用什么方式的推送应用开发时都需要考虑到 MDS 接收线程的限制不过以

现在的服务器处理能力正常情况下的 HTTP 推送请求可以在几毫秒或者是几十毫内处理完

成少量的 HTTP 推送不足于构成网络阻塞

需要考虑的是应用服务器端的推送程序在推送过程中不要在建立连接后才动态组织数

据这样会浪费 http 连接资源好的建议是预先组织好数据在建立 HTTP 连接后快速写完

数据尽快关闭连接

此外对于应用服务器的推送线程也要加于控制避免在短时间内无限制地启动新的线

程进行连接否则也容易造成连接过多而阻塞的情况

从网络层面来看调整应用服务器与 MDSBES 服务器之间的网络超时设置也是避免这

种问题的一种方法在允许的范围内调大网络超时时间可以让应用服务器的推送线程等待更

长的时间从而让 MDSBES 有足够的时间释放原有的连接

最后无论使用了什么方法调优都需要对应用服务器的推送程序进行异常处理当发

现网络超时异常的时候可以组织数据重发当然数据重发的间隔时间也要考虑不要因为

重发数据再次造成网络阻塞

MDS 推送队列限制的处理

相对而言MDS 推送队列限制造成的推送失败比 MDS 接收队列造成的推送失败更常见

已经发现的一个实际案例是周未的时候用户大量关机导致大量推送数据处于推送队列中

填满了推送队列从而导致 MDSBES 服务器不再响应新的推送请求一旦过期时间到达

有新的队列位置空闲又有大量新的推送数据重新占满推送队列

对于 MDS 推送队列的限制直观的做法是对队列参数进行调整如之前讨论的管理

员可以对队列的深度和过期时间进行调整

然而出于对过期时间的不正确理解管理员倾向于将过期时间调大当过期时间被调

大后推送给关机用户的数据在服务器占用队列的时间就变长导致 MDS 推送队列更容易

被占满

所以如果对过期时间进行调整的话方向是将过期时间调小过期时间越小等待数

据占用队列的时间就越短MDS 推送队列被占满的可能性就越小但是过期时间也不能

过小过期时间过小可能会导致过多的过期数据使应用服务器需要处理更多的数据重发

比如将过期时间调成 1 分钟用户走过一个人行遂道都可能导致数据发送失败

对于队列深度的调整方向是调大队列深度如从缺省的 1000 调整到 2000此时

MDSBES 服务器在同一时间可以保留更多的等待数据减少了队列占满的机会不过调大队

列深度会让 MDSBES 服务器保留更多的数据从而占用更多的服务器内存对服务器产生

更大的压力所以调用队列深度需要根据服务器的运行能力进行调用

从应用服务器设计角度应用 MDS 队列限制的方法是尽量减少等待数据的发送如发

送第一条数据的时候发现数据过期对于该用户的其它数据可以调整发送时间在更长的时

间间隔后才尝试发送

对于 MDS 队列限制应用服务器端的推送应用需要对 MDSBES 服务器返回的 503 错误

进行处理对于出现 503 错的数据组织重发同样重发的间隔也需要考虑不要因为数据

重发占用更多的推送队列也可以根据目标用户进行判断对于可能关机的用户暂时不重发

数据

手持设备端接收队列的处理

手持设备端接收队列并未从文档中明确列出是根据测试结果分析得出的手持设备端

目前也没有发现设置可以调整该队列所以客户端程序设计的时候不应该依赖手持设备端队

开发人员需要意识到的是手持设备端的接收队列是可溢出队列一旦推送数据从接收队

列出溢出客户端程序就无法提取该数据了所以在客户端应用开始过程中要注意加快推送

数据的提取过程在提取过程中不要对数据进行复杂的计算提取完成后尽快读取下一条数

据对于接收数据的处理和计算可以交由另一个线程完成

因为溢出数据对客户端应用而言不可见所以数据溢出时在客户端应用中也不会有异

常开发人员需要通过服务器返回的过期消息才能知道该数据推送失败

对于这一点客户端应用除了尽量避免数据溢出外无法在客户端通过其它机制进行补

应用依赖的客户端

对于应用依赖的客户端应用关键点是决定何时调用 accept 函数同时保证 InputStream

和 StreamConnection 被关闭

如之前讨论的使用应用依赖的推送关键是希望使用 MDSBES 发送给应用服务器的异

步确认消息从而对数据是否到达进行判断所以应用依赖的客户端应用需要保证正确调用

accept 函数

另外如果 InputStream 和 StreamConnection 没有关闭的话accept 函数所发出的确认

消息无法被 MDSBES 服务器收到开发人员在代码中应该主动关闭 InputStream 和

StreamConnection同时在程序发生异常时也需要在 catch 代码段对 InputStream 和

StreamConnection 进行关闭处理

异步确认消息处理

异步确认消息用于判断数据是否被成功推送至客户端应用要使用异步确认消息首先

要保证成功地无遗漏地接收所有从 MDSBES 服务器发送过来的异步确认消息

异步确认消息是一个 HTTP 消息消息比较短接收过程的并发压力不大下面是一个

异步确认消息的样例

Received notificationGET HTTP11

User-Agent RIM MDSCS

Accept

X-RIM-Push-ID pushID2081773768

X-RIM-Push-Destination mmtestbjsearbcn

X-RIM-Push-Status 400

Host localhost7778

其中最关键是的获取属性 X-RIM-Push-Status根据该属性的值判断数据推送是成功

(200)还是失败(400)当然属性 X-RIM-Push-ID 也非常重要用于确定该确认消息是针

对哪条推送数据的

如果应用服务器端通过确认消息发现数据推送失败则需要根据属性 X-RIM-Push-ID 重

新组织数据进行重发

Page 14: BES 服务器推送机制分析 - images.csdn.netimages.csdn.net/upimgs/lee/BBPDF/BESfwtsjzfx.pdf · 前言 数据推送是BlackBerry 应用平台的一大优势,在BlackBerry 应用平台上部署的应用可以

MDSBES 服务器发现推送数据格式正确以后还需要判断服务器上的队列是否有足够的

空间保存该数据如果没有发现空闲队列则丢弃该数据并向应用服务器返回 503 错误因此

第三点要考虑的是什么情况会导致服务器推送队列占满如何尽量避免这种情况有关这一

点在ldquoMDSBES 中的推送队列rdquo中有详细描述

MDSBES 服务器将数据推送到客户端以后由客户端程序将数据提取交由相应的应用

逻辑进行处理然后客户端应用在提取数据的时候并不一定能及时地将所有推送到的数据

获取所以第四点要考虑的是手持设备端是否也存在队列如何保证客户端程序将所有数据

都提取出来有关这一点在ldquo手持设备端对消息的处理rdquo小节中有详细描述

手持设备在接收推送数据后会通知 MDSBES 服务器MDSBES 服务器则向应用服务器

发送异步的确认消息通过返回数值 200 或者是 400 通知应用服务器推送数据是推送成功还

是推送失败异步确认消息对于应用开发者有特别的意义通过它可以确定数据是否到达客

户端从而决定是否再次推送数据然后很多开发商都发现异步确认消息的返回的ldquo200rdquo

和ldquo400rdquo并不能准备地反映数据的接收情况所以如何使用异步确认消息在不同的场

景下异步消息会包含什么不同值对于应用开发者而言非常重要有关这一点在小节ldquo异步

确认消息rdquo有详细描述

推送请求是否到达 MDSBES 服务器

有关推送请求是否到达 MDSBES 服务器 很多开发人员都会忽略认为推送请求是否

到达 MDSBES 服务器是由代码是否编写正确决定的一定会在代码测试阶段体现也来也

必须在代码编写阶段解决这个问题然而在生产环境确实有一些因素会导致应用服务器发送

的推送请求无法到达 MDSBES 服务器从而导致推送数据丢失的问题

推送请求无法到达 MDSBES 服务器的一个可能的直接原因是指定的服务器名不正确或

者是指定的端口号不正确原因可能是 BES 管理员修改了 BES 服务器的服务器名IP 地址或

者是推送监听端口

这种问题比较容易发现一方面是因为所有的 BlackBerry 用户此时都无法接收到推送数

据另一方面也因为应用服务器端会报网络连接的异常以 java 代码为例当指定的服务

器名或者是端口号不正确的情况下应用服务器端的推送程序会报以下异常

javanetConnectException Connection refused

或者是报域名无法解析等网络连接级别的异常

发现这种情况只需要检查代码中指定的服务器名和端口号是否和生产环境相同就可以

解决问题

推送请求无法到达 MDSBES 服务器的一个潜在可能性是推送请求的并发数量太大导致

MDSBES 服务器无法及时接收所有请求

这种问题比较难于发现因为部分用户仍然可以收到推送数据只有部分用户的部分数

据丢失了同时这种问题在功能性测试场景下不容易发生只有在大压力测试或者是存在

大量用户的生产环境中才容易出现

产生这种问题的根源是 MDSBES 服务器的并发接收限制MDS 服务器其实是运行在

Tomcat 服务器上一个服务Tomcat 服务器作为应用容器处理了低层的网络请求而 Tomcat

服务器在底层有并发网络请求的限制当网络连接达到服务器限制时新的推送连接会处理

等待状态如果在网络超时前仍无法建立连接则会出现网络超时异常

javanetConnectException Connection timed out connect

根据测试的结果不同版本的 BES 服务器在低层并发网络请求的数量上有差异以 200

个并发限制为例就是说同一时间点只能有 200 个连接可以向 MDSBES 服务器发送推送请

求第 201 个请求以后有所有请求都将进入等待状态如果在 TCPIP 超时限制到达之前

MDSBES 服务器仍无法释放前 200 个请求则等待状态的请求会因为网络超时而中断

当应用服务器采用多线程机制处理推送请求的情况下有可能出现以上情况如果因为业

务原因在短时间内出现大量等待推送的数据应用服务器则有可能启动多于 200 个的线程发

送推送请求如果需要推送的数据太大或者是推送线程需要动态对数据进行计算就有可

能导致线程所启动的连接在短时间内没有关闭当多于 200 个线程处于这种状态的时候

MDSBES 服务器就无法继续接收推送请求了这种问题的一个现象就是应用服务器端报网络

超时异常或者是连接被拒绝的异常

总的而言要保证数据推送的成功第一步需要保证推送数据到达 MDSBES 服务器

发现推送数据没有到达 MDSBES 服务器的情况时应该对应用服务器的日志进行检查进一

步确定问题的原因

推送请求检查

在 MDSBES 服务器接收到推送请求后MDSBES 服务器会对请求进行检查只有符合

推送格式的请求才被 MDSBES 服务器所接收当 MDSBES 服务器接收推送时会返回值为

200 的 HTTP 答复告知应用服务器已经接收该请求当 MDSBES 服务器拒绝推送请求时会

根据拒绝的原因返回相应的错误码可能返回的错误码包括 400403503 等下面对不同的

情况进行描述

第一种请求被拒绝的原因是推送请求的格式不对包括 URL 格式不正确和 HTTP 头不正

确两种情况两种情况 MDSBES 服务器都会返回 400 消息表示拒绝URL 格式不正确包括

URL 中不包含ldquoDESTINATIONrdquo参数或者是不包含ldquoPORTrdquo参数等HTTP 头不正确包括

PUSH ID 不唯一等因为推送请求中的 HTTP 头属性都有缺省值所以使用错误的 HTTP 头属

性并不会引发错误MDSBES 服务器只是忽略该参数如 HTTP 头ldquoX-RIM-PUSH-IDrdquo被误写

为ldquoX-RIMPUSHIDrdquoMDSBES 服务器并不会拒绝该请求只是使用ldquoX-RIM-PUSH-IDrdquo的缺

省值就是服务器随机生成一个唯一的 ID 给推送消息

对于 HTTP 协议而言服务器返回 400 代表服务器一般性错误所以返回消息会以异常

形式被捕获以 java 代码为类程序会捕获到以下网络异常

ldquojavaioIOException Server returned HTTP response code 400 for URL

httprdquo

第二种推送请求被拒绝的情况是推送请求指定的收件人不正确就是指应用服务器发送

推送请求时使用了正确的格式不过 DESTINATION 参数指定的收件人并不存在或者是该收件

人并不是 BlackBerry 用户此时 MDSBES 服务器会返回 403 错误读者需要注意 BES 服务

器 37 之前的版本对于这种情况会视作格式错误返回 400 错误

同样以 java 代码为例收件人不正确的情况下程序会捕获到以下网络异常

ldquojavaioIOException Server returned HTTP response code 403 for URL

httprdquo

第三种请求被拒绝的情况是 MDSBES 服务器上的推送队列已满无法再接收更多的推

送请求此时会返回 503 错误表示服务器暂时没有足够的资源处理该请求这种情况下 java

应用程序会捕获到以下异常

ldquojavaioIOException Server returned HTTP response code 503 for URL

httprdquo

这种情况与推送请求是否正确无关取决于 MDSBES 服务器运行情况关于 MDSBES

服务器的推送队列在下一节中我们才进行详细的讨论

如果数据推送请求通过了 MDSBES 服务器的消息检查则 MDSBES 服务器会将该数据

放入推送队列等待发送同时给应用服务器返回成功消息 200

成功消息是以 HTTP 的 ResponseCode 形式返回的开发人员可以通过 HTTP 连接的相应

方法获取在 java 代码中获取 ResponseCode 的函数是ldquo getResponseCoderdquo只有确认

MDSBES 服务器返回的 ResponseCode 是 200才能确定推送数据已经由 MDSBES 接收对

于其它的异常情况都需要由程序进行判断确定如何调整请求继续重试推送过程

有一点非常重要的是 MDSBES 接收该推送数据并不意味着该数据就一定能到达手持设

备端还有很多因素会导致手持设备端的应用接收不到该数据

MDSBES 中的推送队列

MDSBES 服务器有一个队列用于缓存推送请求因为手持设备并不是一定在线

MDSBES 服务器需要先将推送请求保留在服务端在合适的情况下将数据推送到手持设备端

后才从队列中将该请求移除

MDSBES 服务器上的推送队列是 MDS 推送的关键组件它的行为不仅仅影响应用服务

器端是否能够发送新的请求也影响到手持设备端是否能够接收到数据所以对于 MDS 推

送的开发人员必须了解 MDSBES 服务器上的推送队列

MDSBES 服务器上的推送队列有一个队列深度缺省为 1000也就是说 MDSBES 服务

器在同一时间只能缓存 1000 个推送请求该队列的深度可以由管理员在管理端修改

同时 MDSBES 服务器上的推送队列并不是永久保存推送数据它有一个过期时间缺

省为 10 分钟也就是说一个推送请求在 MDSBES 服务器上最多能够被保留 10 分钟同样

这个过期时间可以由管理员在管理端修改

推送队列的深度修改界面如下

推送队列的过期时间的修改界面如下(以毫秒为单位)

对于推送队列的进一步讨论是有关推送数据进入队列后的状态如何变化下面是推送数

据在队列中的状态变化图说明了推送数据在不同情况下的状态变化下图的各个状态中

淡蓝色的状态为起始状态深蓝色的状态为中间状态红色的状态为终止状态处于中间状

态的数据最终必然会转为终止状态的其中一种

当 MDSBES 服务器接收到推送数据后如果之前目标用户没有 5 个处于暂时失败的数

据包服务器会将数据放入等待发送的队列中推送的数据处于ldquo尝试发送rdquo状态处于ldquo尝

试发送rdquo会在很短的时间内占用 1000 个队列位置中的一个不管是成功还是失败它所占

用的队列位置很快被释放

然后 MDSBES 服务器尝试将数据推送到手持设备端如果此时对应的手持设备处于开

机状态网络一切正常则 MDSBES 服务器将数据发送给手持设备推送数据变成ldquo成功rdquo

状态MDSBES 服务器会向应用服务器发送异步的确认消息同时将数据从队列中移除注

意此时 MDSBES 服务器向应用服务器返回的确认消息并不一定是表示成功 200具体是

返回代表成功的 200 还是返回代表失败的 400 还取决于其它因素有关这一点我们在后面的

章节中详细讨论

如果 MDSBES 服务器尝试将数据推送到手持设备的时候手持设备处于关机状态则该

数据进入ldquo暂时失败rdquo状态处于ldquo暂时失败rdquo状态的数据不占用等待队列的队列位置某

一个目标用户最多只能有 5 条数据处于ldquo暂时失败rdquo状态目前没有明确的数据或者是文档

说明一个 MDSBES 服务器总共可以保留多少条ldquo暂时失败rdquo状态的数据

如果某一用户已有 5 条数据处于ldquo暂时失败rdquo状态MDSBES 服务器接收到该用户新

的推送请求时会直接将该数据转入ldquo等待rdquo状态每一条进入ldquo等待rdquo状态的推送数据会占

用推送队列中的一个位置当队列中的 1000 个位置都被占满的时候 MDSBES 服务器就停止

接收新的数据推送无论新的数据是推送给哪个用户

处于ldquo暂时失败rdquo和ldquo等待rdquo状态的数据会在服务器上保留 10 分钟(如上所述这一

时间可以由管理修改)10 分钟后处于这两种状态的数据将被丢弃进入ldquo过期rdquo状态同

时 MDSBES 服务器会向应用服务器发送异步消息这种情况下发送的异步确认消息一定是

代表失败的 400如果在 10 分钟内手持设备重新进入网络手持设备会发送消息到 MDSBES

服务器告知该手持设备已经上线MDSBES 服务器会将处理ldquo暂时失败rdquo和ldquo等待rdquo状态的

数据转成ldquo尝试发送状态rdquo重新发送该数据需要注意的是在 10 分钟的等待过程中

MDSBES 服务器如果没有接收到手持设备重新连接网络的消息则 MDSBES 服务器不会尝

试重新发送ldquo暂时失败rdquo和ldquo等待rdquo状态的数据

根据以上的分析可以对推出以下结果

1 当用户的手持设备在线时对该用户的数据推送会被马上处理该数据只在很短的时间

占用 1000 个等待队列中的一个位置

2 当用户的手持设备不在线时假设该服务器只有一个用户则服务器在 10 分钟内最多

可以接收该用户的 1005 条数据其中 5 条处于ldquo暂时失败rdquo状态1000 条在等待队列

3 如果用户的手持设备在数据发送给 MDSBES 服务器 10 分钟内重新接入网络MDSBES

服务器可以将该数据推送到手持设备端

4 如果 MDSBES 服务器的等待队列未满推送给新的用户的前 5 条数据不占用等待队列

5 如果 MDSBES 服务器的等待队列已满不管是由哪个用户的数据占满的MDSBES 服

务器都会停止接收所有用户的新的数据推送

总结而言在生产环境中保证 MDSBES 服务器正常工作的关键是要保证 MDSBES 服务

器的等待队列不被占满

手持设备端对推送数据的处理

MDSBES 服务器成功接收应用服务器推送的数据后会将数据推送到手持设备端在手持

设备端需要由客户端应用程序将推送的数据提取出来一个直观的猜测是手持设备端同样需

要一个队列保存数据否则客户端应用程序在提取前一条数据时新到达的数据就会直接丢

失需要验证的是这个队列有多深队列行为是否被系统隐藏经过测试证实手持设备端确

实也存在队列队列深度为 10而且可以反映在应用层其中最值得开发人员注意的是这

个队列是一个可溢出队列关于手持设备端推送数据队列的示意图如下

如图所示当客户端提取程序正在读取数据ldquoRrdquo如图中标有 R 的椭圆形所示如果

有更多的数据被推送到手持设备端所推送的数据会被放入设备的队列中一共有 10 条数

据可以被保留在设备队列中如椭圆形 1 到 10 所示此时如果有更多的数据被推送进来

设备队列已满则这些新的数据会溢出如椭圆形 111213 所示同时对于服务器端

而言数据 111213 已经被推送过一次服务器端不会再次尝试推送这些数据在这种

情况下数据 1 到 10 可以继续被客户端提取程序所提取而数据 111213 则丢失了

对于尚未推送到手持设备端的数据如数据 1415假设因为网络中断没有被推送到

手持设备端则仍保留在服务器端当网络恢复后数据 1415 仍可以被正常处理

对于确认消息(图中橙色圆形所示)的处理则更为复杂在异步确认消息一节中再作讨

从这一节的讨论得出的结果是数据到达手持设备端未必就能够被客户端提取程序提取

客户端提取程序有职责尽快提取数据防止推送数据的溢出溢出的数据在客户端无法再被

处理而且服务器端也不会尝试继续推送这些数据

异步确认消息(应用依赖和非应用依赖)

手持设备端在接收到推送数据后会通知 MDSBES 服务器这时 MDSBES 服务器会根据

最初的推送请求向指定的 URL 发送一个确认消息告知应用服务器某一条推送数据的最终

结果这就是异步确认消息异步确认消息以 http 协议发送给应用服务器消息 200 代表

数据推送成功400 代表数据推送失败

异步确认消息是应用服务器判断推送是否成功的有效工具但是很多开发人员因为对

异步确认消息的机制不了解错误地使用了异步确认消息从而导致数据的丢失所以我们

需要详细了解异步确认消息的工作机制

要讨论异步确认消息的工作机制必然涉及到应用依赖与非应用依赖应用依赖的推送

和非应用依赖的推送对应的异步确认消息工作机制是不同的

下面先讨论非应用依赖的场景这也是很多开发商使用的场景

如果推送是非应用依赖的数据是以到达手持设备为成功标志的只要数据到达了手持

设备MDSBES 服务器就认为该数据推送成功会给应用服务器发送异步确认消息 200

如果数据推送时手持设备不在线则 10 分钟内 MDSBES 服务器会在服务器上保留该数

据如果 10 分钟内手持设备重新接入网络则 MDSBES 服务器会尝试发送该数据一旦数

据到达手持设备MDSBES 服务器都会给应用服务器发送异步确认消息 200

如果 10 分钟内手设备没有接入网络则该推送数据过期MDSBES 服务器会丢弃该数

据同时给应用服务器发送异步确认消息 400

可以看出非应用依赖的推送方式并不能保证客户端应用程序接收到数据在非应用依

赖的推送方式下 MDSBES 返回 200 确认消息也不能认为客户端已接收到数据如果数据送

达手持设备的时候客户端应用程序没有启动所推送的数据会在手持设备端丢失而

MDSBES 服务器认为数据已成功送达或者是客户端应用程序已启动但是没有及时读取推

送数据在手持设备端溢出的数据也会丢失MDSBES 服务器同样认为数据已成功送达

所以在非应用依赖的情况下如果接收到 400 确认消息可以确定客户端没有收到数据

如果接收到 200 确认消息只能认为客户端可能收到了数据

从以上的讨论看非应用依赖的推送方式并不适合用于可靠性要求较高的应用对于可

靠性要求较高的应用需要知道客户端应用程序是否接收到了数据应用依赖的推送方式就

是为此而设计的如果推送是应用依赖的数据是以客户端应用程序确认为成功标志的只

要在应用程序确认后MDSBES 服务器才认为数据推送成功这时才给应用服务器发送异步

确认消息 200

有关客户端应用程序如何确认收到数据在ldquo客户端接收关键代码rdquo一节中有描述简

单而言就是客户端应用程序通过调用 MDSPushInputStream 实例的 accept 函数确认数据收

到如果客户端应用程序在接收到数据后没有调用 accept 函数MDSBES 服务器会认为数

据推送失败给应用服务器发送异步确认消息 400另外一点曾经强调过的是accept 函数

调用后只有 InputStream 和 StreamConnection 都关闭了确认消息才被发送给 MDSBES 服

务器在极端的情况下这一点会影响到异步确认消息的结果

下面具体分析各种情景

如果手持设备在线但是客户端应用程序没有启动推送数据到达手持设备端后没有程

序侦听推送端口当然也没有程序会调用 accept 函数此时 MDSBES 服务器会认为数据推

送失败向应用服务器发送异步确认消息 400

如果手持设备在线客户端应用程序在读取推送数据后没有调用 accept 函数直接关闭

连接此时 MDSBES 服务器同样会认为数据推送失败向应用服务器发送异步确认消息 400

在这种情景下即使客户端接收到了数据但应用服务器仍收到失败消息因为作为成功标

志的 accept 函数并没有调用

如果手持设备在线客户端应用程序在读取推送数据后调用了 accept 函数并关闭连

接此时 MDSBES 服务器会认为数据推送成功向应用服务器发送异步确认消息 200

对于手持设备不在线的情况结果与非应用依赖相似10 分钟内如果手持设备不接入

网络的话推送数据会在 MDSBES 服务器上过期MDSBES 服务器会向应用服务器发送异

步确认消息 400

如果 10 分钟内手持设备重新接入网络MDSBES 服务器会尝试发送数据至手持设备端

还是以 accept 函数的调用为推送成功的标志如果数据重新发送成功但是客户端没有调

用 accept 函数则 MDSBES 服务器会向应用服务器发送异步确认消息 200

对于推送到手持设备端但是溢出的数据因为没有程序能够读取该数据并针对该数据调

用 accept 函数MDSBES 服务器会一直等待到 10 分钟后向应用服务器发送异步确认消息

400认为数据过期

一个特殊的情况是客户端应用程序在接收到数据后调用了 accept但是没有关闭

InputStream 和 StreamConnection此时 MDSBES 服务器会认识客户端还在处理数据会继

续等待如果服务器等待时候超过 10 分钟MDSBES 服务器会认为数据推送失败向应用

服务器发送异步确认消息 400如果在 10 分钟内客户端主动关闭 InputStream 和

StreamConnection或者是网络异常中断MDSBES 服务器会认为数据推送成功向应用服

务器发送异步确认消息 200

下面再讨论一个异常情况下的特例

结合以上描述的各种因素在极端的情况下会出现一些特殊的情况如上图所示假设

MDSBES 服务器在推送了数据 R数据 1 到数据 13 后出现网络中断同时客户端应用程序

正在缓慢地读取数据 R此时数据 1 到数据 10 会被保留在手持设备队列中客户端应用程

序在读取数据 R 后可以继续读取数据 1 到数据 10如果客户端应用程序在读取了数据后都

调用了 accept 函数则会产生对应用确认消息如图中的确认消息 R确认消息 1 到确认消

息 10但是因为网络的中断这些确认消息都无法到达 MDSBES 服务器如果网络在 10

钟内没有恢复MDSBES 服务器上所有等待确认消息的记录都会认识是过期的MDSBES

会向应用服务器发送异步确认消息 400此后即使网络恢复确认消息 R确认消息 1 到确

认消息 10 都不再有效

在这种特殊情况下即使客户端应用程序接收到数据并调用了 accept 函数MDSBES

服务器同样认为数据没有推送成功

根据以上的讨论应用依赖的推送同样不能保证客户端应用一定能收到数据但是在

应用依赖的推送方式下 MDSBES 返回 200 确认消息可以确定客户端已接收到数据因为 200

确认消息是由客户端应用程序调用 accept 函数触发的客户端应用程序可以在确认数据收

到后才调用 accept 函数通知应用服务器该数据已成功接收甚至可以在接收到数据后根

据数据内容决定不调用 accept 函数从而通过应用服务器该数据没有被接收

所以在应用依赖的情况下如果接收到 200 确认消息可以确定客户端已经接收到数据

如果接收到 400 确认消息只能认为客户端可能没有收到数据

因此应用依赖的推送方式更适合可靠性要求高的应用如果应用服务器接收到 200

确认消息就可以认为推送成功继续处理其它事务如果应用服务器接收到 400 确认消息则

需要组织数据重发直到接收到 200 确认消息为止这样就可以保证客户端一定能够接收到

需要推送的数据

如何保证数据推送的可靠性

通过以上几个章节的讨论我们知道虽然 MDSBES 服务器提供了很多机制保证数据

推送的可靠性但是都无法完全保证数据被客户端应用程序所接收

对于数据推送的可靠性保证一方面开发人员需要了解各个可能导致故障的因素在应

用编写的时候尽量减少故障出现的可能性对故障点进行规避另一方面开发人员也需要实

现额外的机制进行数据重发在故障点无法规避的情况下通过数据重发消除故障点的影响

应用依赖和非应用依赖的比较和选择

在实现推送应用的时候首要考虑的是有关应用依赖与非应用依赖的选择

应用依赖的推送形式在可靠性方面更优于非应用依赖的推送形式但是应用依赖的推送

形式对 MDSBES 应用服务器的潜在压力较大客户端应用程序的实现也较为复杂

非应用依赖的推送形式优势在于实现简单对 MDSBES 应用服务器形成的压力相对较

非应用依赖的推送形式从某种程度上讲有点类似于广播协议着重点是从服务器向客户

端发送大量数据并不看重客户端的接收情况从数据的应用类型来分非应用依赖的推送

更适合于天气信息最新报价等数据这些数据在客户端应用关闭时被自然丢弃短时间内

的多条数据有丢失影响也不大必竞用户不会关注 11 点零六分的天气和 11 点零七分的天气

有何不同

应用依赖的推送形式则类似于点对点协议不仅从服务器端向客户端发送数据同时也

关注客户端的接收情况应用依赖的推送更适合于 OAERP 这种应用这种应用需要保证

新的文档或者是记录一定能到达客户端应用丢失一条待办信息可能就会导致整个审批流程

的停滞

再从两种推送方式的本质差别看应用依赖并没有额外的机制消除故障点它的优势在

于它的异步确认消息更可信所以在选择了应用依赖的推送方式后进一步还是需要实现

额外的数据重发机制根据 MDSBES 所发送的异步确认消息实施数据重发

应用依赖的推送再加上结合异步确认消息的数据重发机制是保证推送数据可靠性的

最好方法

MDS 接收线程限制的处理

不管使用什么方式的推送应用开发时都需要考虑到 MDS 接收线程的限制不过以

现在的服务器处理能力正常情况下的 HTTP 推送请求可以在几毫秒或者是几十毫内处理完

成少量的 HTTP 推送不足于构成网络阻塞

需要考虑的是应用服务器端的推送程序在推送过程中不要在建立连接后才动态组织数

据这样会浪费 http 连接资源好的建议是预先组织好数据在建立 HTTP 连接后快速写完

数据尽快关闭连接

此外对于应用服务器的推送线程也要加于控制避免在短时间内无限制地启动新的线

程进行连接否则也容易造成连接过多而阻塞的情况

从网络层面来看调整应用服务器与 MDSBES 服务器之间的网络超时设置也是避免这

种问题的一种方法在允许的范围内调大网络超时时间可以让应用服务器的推送线程等待更

长的时间从而让 MDSBES 有足够的时间释放原有的连接

最后无论使用了什么方法调优都需要对应用服务器的推送程序进行异常处理当发

现网络超时异常的时候可以组织数据重发当然数据重发的间隔时间也要考虑不要因为

重发数据再次造成网络阻塞

MDS 推送队列限制的处理

相对而言MDS 推送队列限制造成的推送失败比 MDS 接收队列造成的推送失败更常见

已经发现的一个实际案例是周未的时候用户大量关机导致大量推送数据处于推送队列中

填满了推送队列从而导致 MDSBES 服务器不再响应新的推送请求一旦过期时间到达

有新的队列位置空闲又有大量新的推送数据重新占满推送队列

对于 MDS 推送队列的限制直观的做法是对队列参数进行调整如之前讨论的管理

员可以对队列的深度和过期时间进行调整

然而出于对过期时间的不正确理解管理员倾向于将过期时间调大当过期时间被调

大后推送给关机用户的数据在服务器占用队列的时间就变长导致 MDS 推送队列更容易

被占满

所以如果对过期时间进行调整的话方向是将过期时间调小过期时间越小等待数

据占用队列的时间就越短MDS 推送队列被占满的可能性就越小但是过期时间也不能

过小过期时间过小可能会导致过多的过期数据使应用服务器需要处理更多的数据重发

比如将过期时间调成 1 分钟用户走过一个人行遂道都可能导致数据发送失败

对于队列深度的调整方向是调大队列深度如从缺省的 1000 调整到 2000此时

MDSBES 服务器在同一时间可以保留更多的等待数据减少了队列占满的机会不过调大队

列深度会让 MDSBES 服务器保留更多的数据从而占用更多的服务器内存对服务器产生

更大的压力所以调用队列深度需要根据服务器的运行能力进行调用

从应用服务器设计角度应用 MDS 队列限制的方法是尽量减少等待数据的发送如发

送第一条数据的时候发现数据过期对于该用户的其它数据可以调整发送时间在更长的时

间间隔后才尝试发送

对于 MDS 队列限制应用服务器端的推送应用需要对 MDSBES 服务器返回的 503 错误

进行处理对于出现 503 错的数据组织重发同样重发的间隔也需要考虑不要因为数据

重发占用更多的推送队列也可以根据目标用户进行判断对于可能关机的用户暂时不重发

数据

手持设备端接收队列的处理

手持设备端接收队列并未从文档中明确列出是根据测试结果分析得出的手持设备端

目前也没有发现设置可以调整该队列所以客户端程序设计的时候不应该依赖手持设备端队

开发人员需要意识到的是手持设备端的接收队列是可溢出队列一旦推送数据从接收队

列出溢出客户端程序就无法提取该数据了所以在客户端应用开始过程中要注意加快推送

数据的提取过程在提取过程中不要对数据进行复杂的计算提取完成后尽快读取下一条数

据对于接收数据的处理和计算可以交由另一个线程完成

因为溢出数据对客户端应用而言不可见所以数据溢出时在客户端应用中也不会有异

常开发人员需要通过服务器返回的过期消息才能知道该数据推送失败

对于这一点客户端应用除了尽量避免数据溢出外无法在客户端通过其它机制进行补

应用依赖的客户端

对于应用依赖的客户端应用关键点是决定何时调用 accept 函数同时保证 InputStream

和 StreamConnection 被关闭

如之前讨论的使用应用依赖的推送关键是希望使用 MDSBES 发送给应用服务器的异

步确认消息从而对数据是否到达进行判断所以应用依赖的客户端应用需要保证正确调用

accept 函数

另外如果 InputStream 和 StreamConnection 没有关闭的话accept 函数所发出的确认

消息无法被 MDSBES 服务器收到开发人员在代码中应该主动关闭 InputStream 和

StreamConnection同时在程序发生异常时也需要在 catch 代码段对 InputStream 和

StreamConnection 进行关闭处理

异步确认消息处理

异步确认消息用于判断数据是否被成功推送至客户端应用要使用异步确认消息首先

要保证成功地无遗漏地接收所有从 MDSBES 服务器发送过来的异步确认消息

异步确认消息是一个 HTTP 消息消息比较短接收过程的并发压力不大下面是一个

异步确认消息的样例

Received notificationGET HTTP11

User-Agent RIM MDSCS

Accept

X-RIM-Push-ID pushID2081773768

X-RIM-Push-Destination mmtestbjsearbcn

X-RIM-Push-Status 400

Host localhost7778

其中最关键是的获取属性 X-RIM-Push-Status根据该属性的值判断数据推送是成功

(200)还是失败(400)当然属性 X-RIM-Push-ID 也非常重要用于确定该确认消息是针

对哪条推送数据的

如果应用服务器端通过确认消息发现数据推送失败则需要根据属性 X-RIM-Push-ID 重

新组织数据进行重发

Page 15: BES 服务器推送机制分析 - images.csdn.netimages.csdn.net/upimgs/lee/BBPDF/BESfwtsjzfx.pdf · 前言 数据推送是BlackBerry 应用平台的一大优势,在BlackBerry 应用平台上部署的应用可以

javanetConnectException Connection timed out connect

根据测试的结果不同版本的 BES 服务器在低层并发网络请求的数量上有差异以 200

个并发限制为例就是说同一时间点只能有 200 个连接可以向 MDSBES 服务器发送推送请

求第 201 个请求以后有所有请求都将进入等待状态如果在 TCPIP 超时限制到达之前

MDSBES 服务器仍无法释放前 200 个请求则等待状态的请求会因为网络超时而中断

当应用服务器采用多线程机制处理推送请求的情况下有可能出现以上情况如果因为业

务原因在短时间内出现大量等待推送的数据应用服务器则有可能启动多于 200 个的线程发

送推送请求如果需要推送的数据太大或者是推送线程需要动态对数据进行计算就有可

能导致线程所启动的连接在短时间内没有关闭当多于 200 个线程处于这种状态的时候

MDSBES 服务器就无法继续接收推送请求了这种问题的一个现象就是应用服务器端报网络

超时异常或者是连接被拒绝的异常

总的而言要保证数据推送的成功第一步需要保证推送数据到达 MDSBES 服务器

发现推送数据没有到达 MDSBES 服务器的情况时应该对应用服务器的日志进行检查进一

步确定问题的原因

推送请求检查

在 MDSBES 服务器接收到推送请求后MDSBES 服务器会对请求进行检查只有符合

推送格式的请求才被 MDSBES 服务器所接收当 MDSBES 服务器接收推送时会返回值为

200 的 HTTP 答复告知应用服务器已经接收该请求当 MDSBES 服务器拒绝推送请求时会

根据拒绝的原因返回相应的错误码可能返回的错误码包括 400403503 等下面对不同的

情况进行描述

第一种请求被拒绝的原因是推送请求的格式不对包括 URL 格式不正确和 HTTP 头不正

确两种情况两种情况 MDSBES 服务器都会返回 400 消息表示拒绝URL 格式不正确包括

URL 中不包含ldquoDESTINATIONrdquo参数或者是不包含ldquoPORTrdquo参数等HTTP 头不正确包括

PUSH ID 不唯一等因为推送请求中的 HTTP 头属性都有缺省值所以使用错误的 HTTP 头属

性并不会引发错误MDSBES 服务器只是忽略该参数如 HTTP 头ldquoX-RIM-PUSH-IDrdquo被误写

为ldquoX-RIMPUSHIDrdquoMDSBES 服务器并不会拒绝该请求只是使用ldquoX-RIM-PUSH-IDrdquo的缺

省值就是服务器随机生成一个唯一的 ID 给推送消息

对于 HTTP 协议而言服务器返回 400 代表服务器一般性错误所以返回消息会以异常

形式被捕获以 java 代码为类程序会捕获到以下网络异常

ldquojavaioIOException Server returned HTTP response code 400 for URL

httprdquo

第二种推送请求被拒绝的情况是推送请求指定的收件人不正确就是指应用服务器发送

推送请求时使用了正确的格式不过 DESTINATION 参数指定的收件人并不存在或者是该收件

人并不是 BlackBerry 用户此时 MDSBES 服务器会返回 403 错误读者需要注意 BES 服务

器 37 之前的版本对于这种情况会视作格式错误返回 400 错误

同样以 java 代码为例收件人不正确的情况下程序会捕获到以下网络异常

ldquojavaioIOException Server returned HTTP response code 403 for URL

httprdquo

第三种请求被拒绝的情况是 MDSBES 服务器上的推送队列已满无法再接收更多的推

送请求此时会返回 503 错误表示服务器暂时没有足够的资源处理该请求这种情况下 java

应用程序会捕获到以下异常

ldquojavaioIOException Server returned HTTP response code 503 for URL

httprdquo

这种情况与推送请求是否正确无关取决于 MDSBES 服务器运行情况关于 MDSBES

服务器的推送队列在下一节中我们才进行详细的讨论

如果数据推送请求通过了 MDSBES 服务器的消息检查则 MDSBES 服务器会将该数据

放入推送队列等待发送同时给应用服务器返回成功消息 200

成功消息是以 HTTP 的 ResponseCode 形式返回的开发人员可以通过 HTTP 连接的相应

方法获取在 java 代码中获取 ResponseCode 的函数是ldquo getResponseCoderdquo只有确认

MDSBES 服务器返回的 ResponseCode 是 200才能确定推送数据已经由 MDSBES 接收对

于其它的异常情况都需要由程序进行判断确定如何调整请求继续重试推送过程

有一点非常重要的是 MDSBES 接收该推送数据并不意味着该数据就一定能到达手持设

备端还有很多因素会导致手持设备端的应用接收不到该数据

MDSBES 中的推送队列

MDSBES 服务器有一个队列用于缓存推送请求因为手持设备并不是一定在线

MDSBES 服务器需要先将推送请求保留在服务端在合适的情况下将数据推送到手持设备端

后才从队列中将该请求移除

MDSBES 服务器上的推送队列是 MDS 推送的关键组件它的行为不仅仅影响应用服务

器端是否能够发送新的请求也影响到手持设备端是否能够接收到数据所以对于 MDS 推

送的开发人员必须了解 MDSBES 服务器上的推送队列

MDSBES 服务器上的推送队列有一个队列深度缺省为 1000也就是说 MDSBES 服务

器在同一时间只能缓存 1000 个推送请求该队列的深度可以由管理员在管理端修改

同时 MDSBES 服务器上的推送队列并不是永久保存推送数据它有一个过期时间缺

省为 10 分钟也就是说一个推送请求在 MDSBES 服务器上最多能够被保留 10 分钟同样

这个过期时间可以由管理员在管理端修改

推送队列的深度修改界面如下

推送队列的过期时间的修改界面如下(以毫秒为单位)

对于推送队列的进一步讨论是有关推送数据进入队列后的状态如何变化下面是推送数

据在队列中的状态变化图说明了推送数据在不同情况下的状态变化下图的各个状态中

淡蓝色的状态为起始状态深蓝色的状态为中间状态红色的状态为终止状态处于中间状

态的数据最终必然会转为终止状态的其中一种

当 MDSBES 服务器接收到推送数据后如果之前目标用户没有 5 个处于暂时失败的数

据包服务器会将数据放入等待发送的队列中推送的数据处于ldquo尝试发送rdquo状态处于ldquo尝

试发送rdquo会在很短的时间内占用 1000 个队列位置中的一个不管是成功还是失败它所占

用的队列位置很快被释放

然后 MDSBES 服务器尝试将数据推送到手持设备端如果此时对应的手持设备处于开

机状态网络一切正常则 MDSBES 服务器将数据发送给手持设备推送数据变成ldquo成功rdquo

状态MDSBES 服务器会向应用服务器发送异步的确认消息同时将数据从队列中移除注

意此时 MDSBES 服务器向应用服务器返回的确认消息并不一定是表示成功 200具体是

返回代表成功的 200 还是返回代表失败的 400 还取决于其它因素有关这一点我们在后面的

章节中详细讨论

如果 MDSBES 服务器尝试将数据推送到手持设备的时候手持设备处于关机状态则该

数据进入ldquo暂时失败rdquo状态处于ldquo暂时失败rdquo状态的数据不占用等待队列的队列位置某

一个目标用户最多只能有 5 条数据处于ldquo暂时失败rdquo状态目前没有明确的数据或者是文档

说明一个 MDSBES 服务器总共可以保留多少条ldquo暂时失败rdquo状态的数据

如果某一用户已有 5 条数据处于ldquo暂时失败rdquo状态MDSBES 服务器接收到该用户新

的推送请求时会直接将该数据转入ldquo等待rdquo状态每一条进入ldquo等待rdquo状态的推送数据会占

用推送队列中的一个位置当队列中的 1000 个位置都被占满的时候 MDSBES 服务器就停止

接收新的数据推送无论新的数据是推送给哪个用户

处于ldquo暂时失败rdquo和ldquo等待rdquo状态的数据会在服务器上保留 10 分钟(如上所述这一

时间可以由管理修改)10 分钟后处于这两种状态的数据将被丢弃进入ldquo过期rdquo状态同

时 MDSBES 服务器会向应用服务器发送异步消息这种情况下发送的异步确认消息一定是

代表失败的 400如果在 10 分钟内手持设备重新进入网络手持设备会发送消息到 MDSBES

服务器告知该手持设备已经上线MDSBES 服务器会将处理ldquo暂时失败rdquo和ldquo等待rdquo状态的

数据转成ldquo尝试发送状态rdquo重新发送该数据需要注意的是在 10 分钟的等待过程中

MDSBES 服务器如果没有接收到手持设备重新连接网络的消息则 MDSBES 服务器不会尝

试重新发送ldquo暂时失败rdquo和ldquo等待rdquo状态的数据

根据以上的分析可以对推出以下结果

1 当用户的手持设备在线时对该用户的数据推送会被马上处理该数据只在很短的时间

占用 1000 个等待队列中的一个位置

2 当用户的手持设备不在线时假设该服务器只有一个用户则服务器在 10 分钟内最多

可以接收该用户的 1005 条数据其中 5 条处于ldquo暂时失败rdquo状态1000 条在等待队列

3 如果用户的手持设备在数据发送给 MDSBES 服务器 10 分钟内重新接入网络MDSBES

服务器可以将该数据推送到手持设备端

4 如果 MDSBES 服务器的等待队列未满推送给新的用户的前 5 条数据不占用等待队列

5 如果 MDSBES 服务器的等待队列已满不管是由哪个用户的数据占满的MDSBES 服

务器都会停止接收所有用户的新的数据推送

总结而言在生产环境中保证 MDSBES 服务器正常工作的关键是要保证 MDSBES 服务

器的等待队列不被占满

手持设备端对推送数据的处理

MDSBES 服务器成功接收应用服务器推送的数据后会将数据推送到手持设备端在手持

设备端需要由客户端应用程序将推送的数据提取出来一个直观的猜测是手持设备端同样需

要一个队列保存数据否则客户端应用程序在提取前一条数据时新到达的数据就会直接丢

失需要验证的是这个队列有多深队列行为是否被系统隐藏经过测试证实手持设备端确

实也存在队列队列深度为 10而且可以反映在应用层其中最值得开发人员注意的是这

个队列是一个可溢出队列关于手持设备端推送数据队列的示意图如下

如图所示当客户端提取程序正在读取数据ldquoRrdquo如图中标有 R 的椭圆形所示如果

有更多的数据被推送到手持设备端所推送的数据会被放入设备的队列中一共有 10 条数

据可以被保留在设备队列中如椭圆形 1 到 10 所示此时如果有更多的数据被推送进来

设备队列已满则这些新的数据会溢出如椭圆形 111213 所示同时对于服务器端

而言数据 111213 已经被推送过一次服务器端不会再次尝试推送这些数据在这种

情况下数据 1 到 10 可以继续被客户端提取程序所提取而数据 111213 则丢失了

对于尚未推送到手持设备端的数据如数据 1415假设因为网络中断没有被推送到

手持设备端则仍保留在服务器端当网络恢复后数据 1415 仍可以被正常处理

对于确认消息(图中橙色圆形所示)的处理则更为复杂在异步确认消息一节中再作讨

从这一节的讨论得出的结果是数据到达手持设备端未必就能够被客户端提取程序提取

客户端提取程序有职责尽快提取数据防止推送数据的溢出溢出的数据在客户端无法再被

处理而且服务器端也不会尝试继续推送这些数据

异步确认消息(应用依赖和非应用依赖)

手持设备端在接收到推送数据后会通知 MDSBES 服务器这时 MDSBES 服务器会根据

最初的推送请求向指定的 URL 发送一个确认消息告知应用服务器某一条推送数据的最终

结果这就是异步确认消息异步确认消息以 http 协议发送给应用服务器消息 200 代表

数据推送成功400 代表数据推送失败

异步确认消息是应用服务器判断推送是否成功的有效工具但是很多开发人员因为对

异步确认消息的机制不了解错误地使用了异步确认消息从而导致数据的丢失所以我们

需要详细了解异步确认消息的工作机制

要讨论异步确认消息的工作机制必然涉及到应用依赖与非应用依赖应用依赖的推送

和非应用依赖的推送对应的异步确认消息工作机制是不同的

下面先讨论非应用依赖的场景这也是很多开发商使用的场景

如果推送是非应用依赖的数据是以到达手持设备为成功标志的只要数据到达了手持

设备MDSBES 服务器就认为该数据推送成功会给应用服务器发送异步确认消息 200

如果数据推送时手持设备不在线则 10 分钟内 MDSBES 服务器会在服务器上保留该数

据如果 10 分钟内手持设备重新接入网络则 MDSBES 服务器会尝试发送该数据一旦数

据到达手持设备MDSBES 服务器都会给应用服务器发送异步确认消息 200

如果 10 分钟内手设备没有接入网络则该推送数据过期MDSBES 服务器会丢弃该数

据同时给应用服务器发送异步确认消息 400

可以看出非应用依赖的推送方式并不能保证客户端应用程序接收到数据在非应用依

赖的推送方式下 MDSBES 返回 200 确认消息也不能认为客户端已接收到数据如果数据送

达手持设备的时候客户端应用程序没有启动所推送的数据会在手持设备端丢失而

MDSBES 服务器认为数据已成功送达或者是客户端应用程序已启动但是没有及时读取推

送数据在手持设备端溢出的数据也会丢失MDSBES 服务器同样认为数据已成功送达

所以在非应用依赖的情况下如果接收到 400 确认消息可以确定客户端没有收到数据

如果接收到 200 确认消息只能认为客户端可能收到了数据

从以上的讨论看非应用依赖的推送方式并不适合用于可靠性要求较高的应用对于可

靠性要求较高的应用需要知道客户端应用程序是否接收到了数据应用依赖的推送方式就

是为此而设计的如果推送是应用依赖的数据是以客户端应用程序确认为成功标志的只

要在应用程序确认后MDSBES 服务器才认为数据推送成功这时才给应用服务器发送异步

确认消息 200

有关客户端应用程序如何确认收到数据在ldquo客户端接收关键代码rdquo一节中有描述简

单而言就是客户端应用程序通过调用 MDSPushInputStream 实例的 accept 函数确认数据收

到如果客户端应用程序在接收到数据后没有调用 accept 函数MDSBES 服务器会认为数

据推送失败给应用服务器发送异步确认消息 400另外一点曾经强调过的是accept 函数

调用后只有 InputStream 和 StreamConnection 都关闭了确认消息才被发送给 MDSBES 服

务器在极端的情况下这一点会影响到异步确认消息的结果

下面具体分析各种情景

如果手持设备在线但是客户端应用程序没有启动推送数据到达手持设备端后没有程

序侦听推送端口当然也没有程序会调用 accept 函数此时 MDSBES 服务器会认为数据推

送失败向应用服务器发送异步确认消息 400

如果手持设备在线客户端应用程序在读取推送数据后没有调用 accept 函数直接关闭

连接此时 MDSBES 服务器同样会认为数据推送失败向应用服务器发送异步确认消息 400

在这种情景下即使客户端接收到了数据但应用服务器仍收到失败消息因为作为成功标

志的 accept 函数并没有调用

如果手持设备在线客户端应用程序在读取推送数据后调用了 accept 函数并关闭连

接此时 MDSBES 服务器会认为数据推送成功向应用服务器发送异步确认消息 200

对于手持设备不在线的情况结果与非应用依赖相似10 分钟内如果手持设备不接入

网络的话推送数据会在 MDSBES 服务器上过期MDSBES 服务器会向应用服务器发送异

步确认消息 400

如果 10 分钟内手持设备重新接入网络MDSBES 服务器会尝试发送数据至手持设备端

还是以 accept 函数的调用为推送成功的标志如果数据重新发送成功但是客户端没有调

用 accept 函数则 MDSBES 服务器会向应用服务器发送异步确认消息 200

对于推送到手持设备端但是溢出的数据因为没有程序能够读取该数据并针对该数据调

用 accept 函数MDSBES 服务器会一直等待到 10 分钟后向应用服务器发送异步确认消息

400认为数据过期

一个特殊的情况是客户端应用程序在接收到数据后调用了 accept但是没有关闭

InputStream 和 StreamConnection此时 MDSBES 服务器会认识客户端还在处理数据会继

续等待如果服务器等待时候超过 10 分钟MDSBES 服务器会认为数据推送失败向应用

服务器发送异步确认消息 400如果在 10 分钟内客户端主动关闭 InputStream 和

StreamConnection或者是网络异常中断MDSBES 服务器会认为数据推送成功向应用服

务器发送异步确认消息 200

下面再讨论一个异常情况下的特例

结合以上描述的各种因素在极端的情况下会出现一些特殊的情况如上图所示假设

MDSBES 服务器在推送了数据 R数据 1 到数据 13 后出现网络中断同时客户端应用程序

正在缓慢地读取数据 R此时数据 1 到数据 10 会被保留在手持设备队列中客户端应用程

序在读取数据 R 后可以继续读取数据 1 到数据 10如果客户端应用程序在读取了数据后都

调用了 accept 函数则会产生对应用确认消息如图中的确认消息 R确认消息 1 到确认消

息 10但是因为网络的中断这些确认消息都无法到达 MDSBES 服务器如果网络在 10

钟内没有恢复MDSBES 服务器上所有等待确认消息的记录都会认识是过期的MDSBES

会向应用服务器发送异步确认消息 400此后即使网络恢复确认消息 R确认消息 1 到确

认消息 10 都不再有效

在这种特殊情况下即使客户端应用程序接收到数据并调用了 accept 函数MDSBES

服务器同样认为数据没有推送成功

根据以上的讨论应用依赖的推送同样不能保证客户端应用一定能收到数据但是在

应用依赖的推送方式下 MDSBES 返回 200 确认消息可以确定客户端已接收到数据因为 200

确认消息是由客户端应用程序调用 accept 函数触发的客户端应用程序可以在确认数据收

到后才调用 accept 函数通知应用服务器该数据已成功接收甚至可以在接收到数据后根

据数据内容决定不调用 accept 函数从而通过应用服务器该数据没有被接收

所以在应用依赖的情况下如果接收到 200 确认消息可以确定客户端已经接收到数据

如果接收到 400 确认消息只能认为客户端可能没有收到数据

因此应用依赖的推送方式更适合可靠性要求高的应用如果应用服务器接收到 200

确认消息就可以认为推送成功继续处理其它事务如果应用服务器接收到 400 确认消息则

需要组织数据重发直到接收到 200 确认消息为止这样就可以保证客户端一定能够接收到

需要推送的数据

如何保证数据推送的可靠性

通过以上几个章节的讨论我们知道虽然 MDSBES 服务器提供了很多机制保证数据

推送的可靠性但是都无法完全保证数据被客户端应用程序所接收

对于数据推送的可靠性保证一方面开发人员需要了解各个可能导致故障的因素在应

用编写的时候尽量减少故障出现的可能性对故障点进行规避另一方面开发人员也需要实

现额外的机制进行数据重发在故障点无法规避的情况下通过数据重发消除故障点的影响

应用依赖和非应用依赖的比较和选择

在实现推送应用的时候首要考虑的是有关应用依赖与非应用依赖的选择

应用依赖的推送形式在可靠性方面更优于非应用依赖的推送形式但是应用依赖的推送

形式对 MDSBES 应用服务器的潜在压力较大客户端应用程序的实现也较为复杂

非应用依赖的推送形式优势在于实现简单对 MDSBES 应用服务器形成的压力相对较

非应用依赖的推送形式从某种程度上讲有点类似于广播协议着重点是从服务器向客户

端发送大量数据并不看重客户端的接收情况从数据的应用类型来分非应用依赖的推送

更适合于天气信息最新报价等数据这些数据在客户端应用关闭时被自然丢弃短时间内

的多条数据有丢失影响也不大必竞用户不会关注 11 点零六分的天气和 11 点零七分的天气

有何不同

应用依赖的推送形式则类似于点对点协议不仅从服务器端向客户端发送数据同时也

关注客户端的接收情况应用依赖的推送更适合于 OAERP 这种应用这种应用需要保证

新的文档或者是记录一定能到达客户端应用丢失一条待办信息可能就会导致整个审批流程

的停滞

再从两种推送方式的本质差别看应用依赖并没有额外的机制消除故障点它的优势在

于它的异步确认消息更可信所以在选择了应用依赖的推送方式后进一步还是需要实现

额外的数据重发机制根据 MDSBES 所发送的异步确认消息实施数据重发

应用依赖的推送再加上结合异步确认消息的数据重发机制是保证推送数据可靠性的

最好方法

MDS 接收线程限制的处理

不管使用什么方式的推送应用开发时都需要考虑到 MDS 接收线程的限制不过以

现在的服务器处理能力正常情况下的 HTTP 推送请求可以在几毫秒或者是几十毫内处理完

成少量的 HTTP 推送不足于构成网络阻塞

需要考虑的是应用服务器端的推送程序在推送过程中不要在建立连接后才动态组织数

据这样会浪费 http 连接资源好的建议是预先组织好数据在建立 HTTP 连接后快速写完

数据尽快关闭连接

此外对于应用服务器的推送线程也要加于控制避免在短时间内无限制地启动新的线

程进行连接否则也容易造成连接过多而阻塞的情况

从网络层面来看调整应用服务器与 MDSBES 服务器之间的网络超时设置也是避免这

种问题的一种方法在允许的范围内调大网络超时时间可以让应用服务器的推送线程等待更

长的时间从而让 MDSBES 有足够的时间释放原有的连接

最后无论使用了什么方法调优都需要对应用服务器的推送程序进行异常处理当发

现网络超时异常的时候可以组织数据重发当然数据重发的间隔时间也要考虑不要因为

重发数据再次造成网络阻塞

MDS 推送队列限制的处理

相对而言MDS 推送队列限制造成的推送失败比 MDS 接收队列造成的推送失败更常见

已经发现的一个实际案例是周未的时候用户大量关机导致大量推送数据处于推送队列中

填满了推送队列从而导致 MDSBES 服务器不再响应新的推送请求一旦过期时间到达

有新的队列位置空闲又有大量新的推送数据重新占满推送队列

对于 MDS 推送队列的限制直观的做法是对队列参数进行调整如之前讨论的管理

员可以对队列的深度和过期时间进行调整

然而出于对过期时间的不正确理解管理员倾向于将过期时间调大当过期时间被调

大后推送给关机用户的数据在服务器占用队列的时间就变长导致 MDS 推送队列更容易

被占满

所以如果对过期时间进行调整的话方向是将过期时间调小过期时间越小等待数

据占用队列的时间就越短MDS 推送队列被占满的可能性就越小但是过期时间也不能

过小过期时间过小可能会导致过多的过期数据使应用服务器需要处理更多的数据重发

比如将过期时间调成 1 分钟用户走过一个人行遂道都可能导致数据发送失败

对于队列深度的调整方向是调大队列深度如从缺省的 1000 调整到 2000此时

MDSBES 服务器在同一时间可以保留更多的等待数据减少了队列占满的机会不过调大队

列深度会让 MDSBES 服务器保留更多的数据从而占用更多的服务器内存对服务器产生

更大的压力所以调用队列深度需要根据服务器的运行能力进行调用

从应用服务器设计角度应用 MDS 队列限制的方法是尽量减少等待数据的发送如发

送第一条数据的时候发现数据过期对于该用户的其它数据可以调整发送时间在更长的时

间间隔后才尝试发送

对于 MDS 队列限制应用服务器端的推送应用需要对 MDSBES 服务器返回的 503 错误

进行处理对于出现 503 错的数据组织重发同样重发的间隔也需要考虑不要因为数据

重发占用更多的推送队列也可以根据目标用户进行判断对于可能关机的用户暂时不重发

数据

手持设备端接收队列的处理

手持设备端接收队列并未从文档中明确列出是根据测试结果分析得出的手持设备端

目前也没有发现设置可以调整该队列所以客户端程序设计的时候不应该依赖手持设备端队

开发人员需要意识到的是手持设备端的接收队列是可溢出队列一旦推送数据从接收队

列出溢出客户端程序就无法提取该数据了所以在客户端应用开始过程中要注意加快推送

数据的提取过程在提取过程中不要对数据进行复杂的计算提取完成后尽快读取下一条数

据对于接收数据的处理和计算可以交由另一个线程完成

因为溢出数据对客户端应用而言不可见所以数据溢出时在客户端应用中也不会有异

常开发人员需要通过服务器返回的过期消息才能知道该数据推送失败

对于这一点客户端应用除了尽量避免数据溢出外无法在客户端通过其它机制进行补

应用依赖的客户端

对于应用依赖的客户端应用关键点是决定何时调用 accept 函数同时保证 InputStream

和 StreamConnection 被关闭

如之前讨论的使用应用依赖的推送关键是希望使用 MDSBES 发送给应用服务器的异

步确认消息从而对数据是否到达进行判断所以应用依赖的客户端应用需要保证正确调用

accept 函数

另外如果 InputStream 和 StreamConnection 没有关闭的话accept 函数所发出的确认

消息无法被 MDSBES 服务器收到开发人员在代码中应该主动关闭 InputStream 和

StreamConnection同时在程序发生异常时也需要在 catch 代码段对 InputStream 和

StreamConnection 进行关闭处理

异步确认消息处理

异步确认消息用于判断数据是否被成功推送至客户端应用要使用异步确认消息首先

要保证成功地无遗漏地接收所有从 MDSBES 服务器发送过来的异步确认消息

异步确认消息是一个 HTTP 消息消息比较短接收过程的并发压力不大下面是一个

异步确认消息的样例

Received notificationGET HTTP11

User-Agent RIM MDSCS

Accept

X-RIM-Push-ID pushID2081773768

X-RIM-Push-Destination mmtestbjsearbcn

X-RIM-Push-Status 400

Host localhost7778

其中最关键是的获取属性 X-RIM-Push-Status根据该属性的值判断数据推送是成功

(200)还是失败(400)当然属性 X-RIM-Push-ID 也非常重要用于确定该确认消息是针

对哪条推送数据的

如果应用服务器端通过确认消息发现数据推送失败则需要根据属性 X-RIM-Push-ID 重

新组织数据进行重发

Page 16: BES 服务器推送机制分析 - images.csdn.netimages.csdn.net/upimgs/lee/BBPDF/BESfwtsjzfx.pdf · 前言 数据推送是BlackBerry 应用平台的一大优势,在BlackBerry 应用平台上部署的应用可以

第二种推送请求被拒绝的情况是推送请求指定的收件人不正确就是指应用服务器发送

推送请求时使用了正确的格式不过 DESTINATION 参数指定的收件人并不存在或者是该收件

人并不是 BlackBerry 用户此时 MDSBES 服务器会返回 403 错误读者需要注意 BES 服务

器 37 之前的版本对于这种情况会视作格式错误返回 400 错误

同样以 java 代码为例收件人不正确的情况下程序会捕获到以下网络异常

ldquojavaioIOException Server returned HTTP response code 403 for URL

httprdquo

第三种请求被拒绝的情况是 MDSBES 服务器上的推送队列已满无法再接收更多的推

送请求此时会返回 503 错误表示服务器暂时没有足够的资源处理该请求这种情况下 java

应用程序会捕获到以下异常

ldquojavaioIOException Server returned HTTP response code 503 for URL

httprdquo

这种情况与推送请求是否正确无关取决于 MDSBES 服务器运行情况关于 MDSBES

服务器的推送队列在下一节中我们才进行详细的讨论

如果数据推送请求通过了 MDSBES 服务器的消息检查则 MDSBES 服务器会将该数据

放入推送队列等待发送同时给应用服务器返回成功消息 200

成功消息是以 HTTP 的 ResponseCode 形式返回的开发人员可以通过 HTTP 连接的相应

方法获取在 java 代码中获取 ResponseCode 的函数是ldquo getResponseCoderdquo只有确认

MDSBES 服务器返回的 ResponseCode 是 200才能确定推送数据已经由 MDSBES 接收对

于其它的异常情况都需要由程序进行判断确定如何调整请求继续重试推送过程

有一点非常重要的是 MDSBES 接收该推送数据并不意味着该数据就一定能到达手持设

备端还有很多因素会导致手持设备端的应用接收不到该数据

MDSBES 中的推送队列

MDSBES 服务器有一个队列用于缓存推送请求因为手持设备并不是一定在线

MDSBES 服务器需要先将推送请求保留在服务端在合适的情况下将数据推送到手持设备端

后才从队列中将该请求移除

MDSBES 服务器上的推送队列是 MDS 推送的关键组件它的行为不仅仅影响应用服务

器端是否能够发送新的请求也影响到手持设备端是否能够接收到数据所以对于 MDS 推

送的开发人员必须了解 MDSBES 服务器上的推送队列

MDSBES 服务器上的推送队列有一个队列深度缺省为 1000也就是说 MDSBES 服务

器在同一时间只能缓存 1000 个推送请求该队列的深度可以由管理员在管理端修改

同时 MDSBES 服务器上的推送队列并不是永久保存推送数据它有一个过期时间缺

省为 10 分钟也就是说一个推送请求在 MDSBES 服务器上最多能够被保留 10 分钟同样

这个过期时间可以由管理员在管理端修改

推送队列的深度修改界面如下

推送队列的过期时间的修改界面如下(以毫秒为单位)

对于推送队列的进一步讨论是有关推送数据进入队列后的状态如何变化下面是推送数

据在队列中的状态变化图说明了推送数据在不同情况下的状态变化下图的各个状态中

淡蓝色的状态为起始状态深蓝色的状态为中间状态红色的状态为终止状态处于中间状

态的数据最终必然会转为终止状态的其中一种

当 MDSBES 服务器接收到推送数据后如果之前目标用户没有 5 个处于暂时失败的数

据包服务器会将数据放入等待发送的队列中推送的数据处于ldquo尝试发送rdquo状态处于ldquo尝

试发送rdquo会在很短的时间内占用 1000 个队列位置中的一个不管是成功还是失败它所占

用的队列位置很快被释放

然后 MDSBES 服务器尝试将数据推送到手持设备端如果此时对应的手持设备处于开

机状态网络一切正常则 MDSBES 服务器将数据发送给手持设备推送数据变成ldquo成功rdquo

状态MDSBES 服务器会向应用服务器发送异步的确认消息同时将数据从队列中移除注

意此时 MDSBES 服务器向应用服务器返回的确认消息并不一定是表示成功 200具体是

返回代表成功的 200 还是返回代表失败的 400 还取决于其它因素有关这一点我们在后面的

章节中详细讨论

如果 MDSBES 服务器尝试将数据推送到手持设备的时候手持设备处于关机状态则该

数据进入ldquo暂时失败rdquo状态处于ldquo暂时失败rdquo状态的数据不占用等待队列的队列位置某

一个目标用户最多只能有 5 条数据处于ldquo暂时失败rdquo状态目前没有明确的数据或者是文档

说明一个 MDSBES 服务器总共可以保留多少条ldquo暂时失败rdquo状态的数据

如果某一用户已有 5 条数据处于ldquo暂时失败rdquo状态MDSBES 服务器接收到该用户新

的推送请求时会直接将该数据转入ldquo等待rdquo状态每一条进入ldquo等待rdquo状态的推送数据会占

用推送队列中的一个位置当队列中的 1000 个位置都被占满的时候 MDSBES 服务器就停止

接收新的数据推送无论新的数据是推送给哪个用户

处于ldquo暂时失败rdquo和ldquo等待rdquo状态的数据会在服务器上保留 10 分钟(如上所述这一

时间可以由管理修改)10 分钟后处于这两种状态的数据将被丢弃进入ldquo过期rdquo状态同

时 MDSBES 服务器会向应用服务器发送异步消息这种情况下发送的异步确认消息一定是

代表失败的 400如果在 10 分钟内手持设备重新进入网络手持设备会发送消息到 MDSBES

服务器告知该手持设备已经上线MDSBES 服务器会将处理ldquo暂时失败rdquo和ldquo等待rdquo状态的

数据转成ldquo尝试发送状态rdquo重新发送该数据需要注意的是在 10 分钟的等待过程中

MDSBES 服务器如果没有接收到手持设备重新连接网络的消息则 MDSBES 服务器不会尝

试重新发送ldquo暂时失败rdquo和ldquo等待rdquo状态的数据

根据以上的分析可以对推出以下结果

1 当用户的手持设备在线时对该用户的数据推送会被马上处理该数据只在很短的时间

占用 1000 个等待队列中的一个位置

2 当用户的手持设备不在线时假设该服务器只有一个用户则服务器在 10 分钟内最多

可以接收该用户的 1005 条数据其中 5 条处于ldquo暂时失败rdquo状态1000 条在等待队列

3 如果用户的手持设备在数据发送给 MDSBES 服务器 10 分钟内重新接入网络MDSBES

服务器可以将该数据推送到手持设备端

4 如果 MDSBES 服务器的等待队列未满推送给新的用户的前 5 条数据不占用等待队列

5 如果 MDSBES 服务器的等待队列已满不管是由哪个用户的数据占满的MDSBES 服

务器都会停止接收所有用户的新的数据推送

总结而言在生产环境中保证 MDSBES 服务器正常工作的关键是要保证 MDSBES 服务

器的等待队列不被占满

手持设备端对推送数据的处理

MDSBES 服务器成功接收应用服务器推送的数据后会将数据推送到手持设备端在手持

设备端需要由客户端应用程序将推送的数据提取出来一个直观的猜测是手持设备端同样需

要一个队列保存数据否则客户端应用程序在提取前一条数据时新到达的数据就会直接丢

失需要验证的是这个队列有多深队列行为是否被系统隐藏经过测试证实手持设备端确

实也存在队列队列深度为 10而且可以反映在应用层其中最值得开发人员注意的是这

个队列是一个可溢出队列关于手持设备端推送数据队列的示意图如下

如图所示当客户端提取程序正在读取数据ldquoRrdquo如图中标有 R 的椭圆形所示如果

有更多的数据被推送到手持设备端所推送的数据会被放入设备的队列中一共有 10 条数

据可以被保留在设备队列中如椭圆形 1 到 10 所示此时如果有更多的数据被推送进来

设备队列已满则这些新的数据会溢出如椭圆形 111213 所示同时对于服务器端

而言数据 111213 已经被推送过一次服务器端不会再次尝试推送这些数据在这种

情况下数据 1 到 10 可以继续被客户端提取程序所提取而数据 111213 则丢失了

对于尚未推送到手持设备端的数据如数据 1415假设因为网络中断没有被推送到

手持设备端则仍保留在服务器端当网络恢复后数据 1415 仍可以被正常处理

对于确认消息(图中橙色圆形所示)的处理则更为复杂在异步确认消息一节中再作讨

从这一节的讨论得出的结果是数据到达手持设备端未必就能够被客户端提取程序提取

客户端提取程序有职责尽快提取数据防止推送数据的溢出溢出的数据在客户端无法再被

处理而且服务器端也不会尝试继续推送这些数据

异步确认消息(应用依赖和非应用依赖)

手持设备端在接收到推送数据后会通知 MDSBES 服务器这时 MDSBES 服务器会根据

最初的推送请求向指定的 URL 发送一个确认消息告知应用服务器某一条推送数据的最终

结果这就是异步确认消息异步确认消息以 http 协议发送给应用服务器消息 200 代表

数据推送成功400 代表数据推送失败

异步确认消息是应用服务器判断推送是否成功的有效工具但是很多开发人员因为对

异步确认消息的机制不了解错误地使用了异步确认消息从而导致数据的丢失所以我们

需要详细了解异步确认消息的工作机制

要讨论异步确认消息的工作机制必然涉及到应用依赖与非应用依赖应用依赖的推送

和非应用依赖的推送对应的异步确认消息工作机制是不同的

下面先讨论非应用依赖的场景这也是很多开发商使用的场景

如果推送是非应用依赖的数据是以到达手持设备为成功标志的只要数据到达了手持

设备MDSBES 服务器就认为该数据推送成功会给应用服务器发送异步确认消息 200

如果数据推送时手持设备不在线则 10 分钟内 MDSBES 服务器会在服务器上保留该数

据如果 10 分钟内手持设备重新接入网络则 MDSBES 服务器会尝试发送该数据一旦数

据到达手持设备MDSBES 服务器都会给应用服务器发送异步确认消息 200

如果 10 分钟内手设备没有接入网络则该推送数据过期MDSBES 服务器会丢弃该数

据同时给应用服务器发送异步确认消息 400

可以看出非应用依赖的推送方式并不能保证客户端应用程序接收到数据在非应用依

赖的推送方式下 MDSBES 返回 200 确认消息也不能认为客户端已接收到数据如果数据送

达手持设备的时候客户端应用程序没有启动所推送的数据会在手持设备端丢失而

MDSBES 服务器认为数据已成功送达或者是客户端应用程序已启动但是没有及时读取推

送数据在手持设备端溢出的数据也会丢失MDSBES 服务器同样认为数据已成功送达

所以在非应用依赖的情况下如果接收到 400 确认消息可以确定客户端没有收到数据

如果接收到 200 确认消息只能认为客户端可能收到了数据

从以上的讨论看非应用依赖的推送方式并不适合用于可靠性要求较高的应用对于可

靠性要求较高的应用需要知道客户端应用程序是否接收到了数据应用依赖的推送方式就

是为此而设计的如果推送是应用依赖的数据是以客户端应用程序确认为成功标志的只

要在应用程序确认后MDSBES 服务器才认为数据推送成功这时才给应用服务器发送异步

确认消息 200

有关客户端应用程序如何确认收到数据在ldquo客户端接收关键代码rdquo一节中有描述简

单而言就是客户端应用程序通过调用 MDSPushInputStream 实例的 accept 函数确认数据收

到如果客户端应用程序在接收到数据后没有调用 accept 函数MDSBES 服务器会认为数

据推送失败给应用服务器发送异步确认消息 400另外一点曾经强调过的是accept 函数

调用后只有 InputStream 和 StreamConnection 都关闭了确认消息才被发送给 MDSBES 服

务器在极端的情况下这一点会影响到异步确认消息的结果

下面具体分析各种情景

如果手持设备在线但是客户端应用程序没有启动推送数据到达手持设备端后没有程

序侦听推送端口当然也没有程序会调用 accept 函数此时 MDSBES 服务器会认为数据推

送失败向应用服务器发送异步确认消息 400

如果手持设备在线客户端应用程序在读取推送数据后没有调用 accept 函数直接关闭

连接此时 MDSBES 服务器同样会认为数据推送失败向应用服务器发送异步确认消息 400

在这种情景下即使客户端接收到了数据但应用服务器仍收到失败消息因为作为成功标

志的 accept 函数并没有调用

如果手持设备在线客户端应用程序在读取推送数据后调用了 accept 函数并关闭连

接此时 MDSBES 服务器会认为数据推送成功向应用服务器发送异步确认消息 200

对于手持设备不在线的情况结果与非应用依赖相似10 分钟内如果手持设备不接入

网络的话推送数据会在 MDSBES 服务器上过期MDSBES 服务器会向应用服务器发送异

步确认消息 400

如果 10 分钟内手持设备重新接入网络MDSBES 服务器会尝试发送数据至手持设备端

还是以 accept 函数的调用为推送成功的标志如果数据重新发送成功但是客户端没有调

用 accept 函数则 MDSBES 服务器会向应用服务器发送异步确认消息 200

对于推送到手持设备端但是溢出的数据因为没有程序能够读取该数据并针对该数据调

用 accept 函数MDSBES 服务器会一直等待到 10 分钟后向应用服务器发送异步确认消息

400认为数据过期

一个特殊的情况是客户端应用程序在接收到数据后调用了 accept但是没有关闭

InputStream 和 StreamConnection此时 MDSBES 服务器会认识客户端还在处理数据会继

续等待如果服务器等待时候超过 10 分钟MDSBES 服务器会认为数据推送失败向应用

服务器发送异步确认消息 400如果在 10 分钟内客户端主动关闭 InputStream 和

StreamConnection或者是网络异常中断MDSBES 服务器会认为数据推送成功向应用服

务器发送异步确认消息 200

下面再讨论一个异常情况下的特例

结合以上描述的各种因素在极端的情况下会出现一些特殊的情况如上图所示假设

MDSBES 服务器在推送了数据 R数据 1 到数据 13 后出现网络中断同时客户端应用程序

正在缓慢地读取数据 R此时数据 1 到数据 10 会被保留在手持设备队列中客户端应用程

序在读取数据 R 后可以继续读取数据 1 到数据 10如果客户端应用程序在读取了数据后都

调用了 accept 函数则会产生对应用确认消息如图中的确认消息 R确认消息 1 到确认消

息 10但是因为网络的中断这些确认消息都无法到达 MDSBES 服务器如果网络在 10

钟内没有恢复MDSBES 服务器上所有等待确认消息的记录都会认识是过期的MDSBES

会向应用服务器发送异步确认消息 400此后即使网络恢复确认消息 R确认消息 1 到确

认消息 10 都不再有效

在这种特殊情况下即使客户端应用程序接收到数据并调用了 accept 函数MDSBES

服务器同样认为数据没有推送成功

根据以上的讨论应用依赖的推送同样不能保证客户端应用一定能收到数据但是在

应用依赖的推送方式下 MDSBES 返回 200 确认消息可以确定客户端已接收到数据因为 200

确认消息是由客户端应用程序调用 accept 函数触发的客户端应用程序可以在确认数据收

到后才调用 accept 函数通知应用服务器该数据已成功接收甚至可以在接收到数据后根

据数据内容决定不调用 accept 函数从而通过应用服务器该数据没有被接收

所以在应用依赖的情况下如果接收到 200 确认消息可以确定客户端已经接收到数据

如果接收到 400 确认消息只能认为客户端可能没有收到数据

因此应用依赖的推送方式更适合可靠性要求高的应用如果应用服务器接收到 200

确认消息就可以认为推送成功继续处理其它事务如果应用服务器接收到 400 确认消息则

需要组织数据重发直到接收到 200 确认消息为止这样就可以保证客户端一定能够接收到

需要推送的数据

如何保证数据推送的可靠性

通过以上几个章节的讨论我们知道虽然 MDSBES 服务器提供了很多机制保证数据

推送的可靠性但是都无法完全保证数据被客户端应用程序所接收

对于数据推送的可靠性保证一方面开发人员需要了解各个可能导致故障的因素在应

用编写的时候尽量减少故障出现的可能性对故障点进行规避另一方面开发人员也需要实

现额外的机制进行数据重发在故障点无法规避的情况下通过数据重发消除故障点的影响

应用依赖和非应用依赖的比较和选择

在实现推送应用的时候首要考虑的是有关应用依赖与非应用依赖的选择

应用依赖的推送形式在可靠性方面更优于非应用依赖的推送形式但是应用依赖的推送

形式对 MDSBES 应用服务器的潜在压力较大客户端应用程序的实现也较为复杂

非应用依赖的推送形式优势在于实现简单对 MDSBES 应用服务器形成的压力相对较

非应用依赖的推送形式从某种程度上讲有点类似于广播协议着重点是从服务器向客户

端发送大量数据并不看重客户端的接收情况从数据的应用类型来分非应用依赖的推送

更适合于天气信息最新报价等数据这些数据在客户端应用关闭时被自然丢弃短时间内

的多条数据有丢失影响也不大必竞用户不会关注 11 点零六分的天气和 11 点零七分的天气

有何不同

应用依赖的推送形式则类似于点对点协议不仅从服务器端向客户端发送数据同时也

关注客户端的接收情况应用依赖的推送更适合于 OAERP 这种应用这种应用需要保证

新的文档或者是记录一定能到达客户端应用丢失一条待办信息可能就会导致整个审批流程

的停滞

再从两种推送方式的本质差别看应用依赖并没有额外的机制消除故障点它的优势在

于它的异步确认消息更可信所以在选择了应用依赖的推送方式后进一步还是需要实现

额外的数据重发机制根据 MDSBES 所发送的异步确认消息实施数据重发

应用依赖的推送再加上结合异步确认消息的数据重发机制是保证推送数据可靠性的

最好方法

MDS 接收线程限制的处理

不管使用什么方式的推送应用开发时都需要考虑到 MDS 接收线程的限制不过以

现在的服务器处理能力正常情况下的 HTTP 推送请求可以在几毫秒或者是几十毫内处理完

成少量的 HTTP 推送不足于构成网络阻塞

需要考虑的是应用服务器端的推送程序在推送过程中不要在建立连接后才动态组织数

据这样会浪费 http 连接资源好的建议是预先组织好数据在建立 HTTP 连接后快速写完

数据尽快关闭连接

此外对于应用服务器的推送线程也要加于控制避免在短时间内无限制地启动新的线

程进行连接否则也容易造成连接过多而阻塞的情况

从网络层面来看调整应用服务器与 MDSBES 服务器之间的网络超时设置也是避免这

种问题的一种方法在允许的范围内调大网络超时时间可以让应用服务器的推送线程等待更

长的时间从而让 MDSBES 有足够的时间释放原有的连接

最后无论使用了什么方法调优都需要对应用服务器的推送程序进行异常处理当发

现网络超时异常的时候可以组织数据重发当然数据重发的间隔时间也要考虑不要因为

重发数据再次造成网络阻塞

MDS 推送队列限制的处理

相对而言MDS 推送队列限制造成的推送失败比 MDS 接收队列造成的推送失败更常见

已经发现的一个实际案例是周未的时候用户大量关机导致大量推送数据处于推送队列中

填满了推送队列从而导致 MDSBES 服务器不再响应新的推送请求一旦过期时间到达

有新的队列位置空闲又有大量新的推送数据重新占满推送队列

对于 MDS 推送队列的限制直观的做法是对队列参数进行调整如之前讨论的管理

员可以对队列的深度和过期时间进行调整

然而出于对过期时间的不正确理解管理员倾向于将过期时间调大当过期时间被调

大后推送给关机用户的数据在服务器占用队列的时间就变长导致 MDS 推送队列更容易

被占满

所以如果对过期时间进行调整的话方向是将过期时间调小过期时间越小等待数

据占用队列的时间就越短MDS 推送队列被占满的可能性就越小但是过期时间也不能

过小过期时间过小可能会导致过多的过期数据使应用服务器需要处理更多的数据重发

比如将过期时间调成 1 分钟用户走过一个人行遂道都可能导致数据发送失败

对于队列深度的调整方向是调大队列深度如从缺省的 1000 调整到 2000此时

MDSBES 服务器在同一时间可以保留更多的等待数据减少了队列占满的机会不过调大队

列深度会让 MDSBES 服务器保留更多的数据从而占用更多的服务器内存对服务器产生

更大的压力所以调用队列深度需要根据服务器的运行能力进行调用

从应用服务器设计角度应用 MDS 队列限制的方法是尽量减少等待数据的发送如发

送第一条数据的时候发现数据过期对于该用户的其它数据可以调整发送时间在更长的时

间间隔后才尝试发送

对于 MDS 队列限制应用服务器端的推送应用需要对 MDSBES 服务器返回的 503 错误

进行处理对于出现 503 错的数据组织重发同样重发的间隔也需要考虑不要因为数据

重发占用更多的推送队列也可以根据目标用户进行判断对于可能关机的用户暂时不重发

数据

手持设备端接收队列的处理

手持设备端接收队列并未从文档中明确列出是根据测试结果分析得出的手持设备端

目前也没有发现设置可以调整该队列所以客户端程序设计的时候不应该依赖手持设备端队

开发人员需要意识到的是手持设备端的接收队列是可溢出队列一旦推送数据从接收队

列出溢出客户端程序就无法提取该数据了所以在客户端应用开始过程中要注意加快推送

数据的提取过程在提取过程中不要对数据进行复杂的计算提取完成后尽快读取下一条数

据对于接收数据的处理和计算可以交由另一个线程完成

因为溢出数据对客户端应用而言不可见所以数据溢出时在客户端应用中也不会有异

常开发人员需要通过服务器返回的过期消息才能知道该数据推送失败

对于这一点客户端应用除了尽量避免数据溢出外无法在客户端通过其它机制进行补

应用依赖的客户端

对于应用依赖的客户端应用关键点是决定何时调用 accept 函数同时保证 InputStream

和 StreamConnection 被关闭

如之前讨论的使用应用依赖的推送关键是希望使用 MDSBES 发送给应用服务器的异

步确认消息从而对数据是否到达进行判断所以应用依赖的客户端应用需要保证正确调用

accept 函数

另外如果 InputStream 和 StreamConnection 没有关闭的话accept 函数所发出的确认

消息无法被 MDSBES 服务器收到开发人员在代码中应该主动关闭 InputStream 和

StreamConnection同时在程序发生异常时也需要在 catch 代码段对 InputStream 和

StreamConnection 进行关闭处理

异步确认消息处理

异步确认消息用于判断数据是否被成功推送至客户端应用要使用异步确认消息首先

要保证成功地无遗漏地接收所有从 MDSBES 服务器发送过来的异步确认消息

异步确认消息是一个 HTTP 消息消息比较短接收过程的并发压力不大下面是一个

异步确认消息的样例

Received notificationGET HTTP11

User-Agent RIM MDSCS

Accept

X-RIM-Push-ID pushID2081773768

X-RIM-Push-Destination mmtestbjsearbcn

X-RIM-Push-Status 400

Host localhost7778

其中最关键是的获取属性 X-RIM-Push-Status根据该属性的值判断数据推送是成功

(200)还是失败(400)当然属性 X-RIM-Push-ID 也非常重要用于确定该确认消息是针

对哪条推送数据的

如果应用服务器端通过确认消息发现数据推送失败则需要根据属性 X-RIM-Push-ID 重

新组织数据进行重发

Page 17: BES 服务器推送机制分析 - images.csdn.netimages.csdn.net/upimgs/lee/BBPDF/BESfwtsjzfx.pdf · 前言 数据推送是BlackBerry 应用平台的一大优势,在BlackBerry 应用平台上部署的应用可以

如果数据推送请求通过了 MDSBES 服务器的消息检查则 MDSBES 服务器会将该数据

放入推送队列等待发送同时给应用服务器返回成功消息 200

成功消息是以 HTTP 的 ResponseCode 形式返回的开发人员可以通过 HTTP 连接的相应

方法获取在 java 代码中获取 ResponseCode 的函数是ldquo getResponseCoderdquo只有确认

MDSBES 服务器返回的 ResponseCode 是 200才能确定推送数据已经由 MDSBES 接收对

于其它的异常情况都需要由程序进行判断确定如何调整请求继续重试推送过程

有一点非常重要的是 MDSBES 接收该推送数据并不意味着该数据就一定能到达手持设

备端还有很多因素会导致手持设备端的应用接收不到该数据

MDSBES 中的推送队列

MDSBES 服务器有一个队列用于缓存推送请求因为手持设备并不是一定在线

MDSBES 服务器需要先将推送请求保留在服务端在合适的情况下将数据推送到手持设备端

后才从队列中将该请求移除

MDSBES 服务器上的推送队列是 MDS 推送的关键组件它的行为不仅仅影响应用服务

器端是否能够发送新的请求也影响到手持设备端是否能够接收到数据所以对于 MDS 推

送的开发人员必须了解 MDSBES 服务器上的推送队列

MDSBES 服务器上的推送队列有一个队列深度缺省为 1000也就是说 MDSBES 服务

器在同一时间只能缓存 1000 个推送请求该队列的深度可以由管理员在管理端修改

同时 MDSBES 服务器上的推送队列并不是永久保存推送数据它有一个过期时间缺

省为 10 分钟也就是说一个推送请求在 MDSBES 服务器上最多能够被保留 10 分钟同样

这个过期时间可以由管理员在管理端修改

推送队列的深度修改界面如下

推送队列的过期时间的修改界面如下(以毫秒为单位)

对于推送队列的进一步讨论是有关推送数据进入队列后的状态如何变化下面是推送数

据在队列中的状态变化图说明了推送数据在不同情况下的状态变化下图的各个状态中

淡蓝色的状态为起始状态深蓝色的状态为中间状态红色的状态为终止状态处于中间状

态的数据最终必然会转为终止状态的其中一种

当 MDSBES 服务器接收到推送数据后如果之前目标用户没有 5 个处于暂时失败的数

据包服务器会将数据放入等待发送的队列中推送的数据处于ldquo尝试发送rdquo状态处于ldquo尝

试发送rdquo会在很短的时间内占用 1000 个队列位置中的一个不管是成功还是失败它所占

用的队列位置很快被释放

然后 MDSBES 服务器尝试将数据推送到手持设备端如果此时对应的手持设备处于开

机状态网络一切正常则 MDSBES 服务器将数据发送给手持设备推送数据变成ldquo成功rdquo

状态MDSBES 服务器会向应用服务器发送异步的确认消息同时将数据从队列中移除注

意此时 MDSBES 服务器向应用服务器返回的确认消息并不一定是表示成功 200具体是

返回代表成功的 200 还是返回代表失败的 400 还取决于其它因素有关这一点我们在后面的

章节中详细讨论

如果 MDSBES 服务器尝试将数据推送到手持设备的时候手持设备处于关机状态则该

数据进入ldquo暂时失败rdquo状态处于ldquo暂时失败rdquo状态的数据不占用等待队列的队列位置某

一个目标用户最多只能有 5 条数据处于ldquo暂时失败rdquo状态目前没有明确的数据或者是文档

说明一个 MDSBES 服务器总共可以保留多少条ldquo暂时失败rdquo状态的数据

如果某一用户已有 5 条数据处于ldquo暂时失败rdquo状态MDSBES 服务器接收到该用户新

的推送请求时会直接将该数据转入ldquo等待rdquo状态每一条进入ldquo等待rdquo状态的推送数据会占

用推送队列中的一个位置当队列中的 1000 个位置都被占满的时候 MDSBES 服务器就停止

接收新的数据推送无论新的数据是推送给哪个用户

处于ldquo暂时失败rdquo和ldquo等待rdquo状态的数据会在服务器上保留 10 分钟(如上所述这一

时间可以由管理修改)10 分钟后处于这两种状态的数据将被丢弃进入ldquo过期rdquo状态同

时 MDSBES 服务器会向应用服务器发送异步消息这种情况下发送的异步确认消息一定是

代表失败的 400如果在 10 分钟内手持设备重新进入网络手持设备会发送消息到 MDSBES

服务器告知该手持设备已经上线MDSBES 服务器会将处理ldquo暂时失败rdquo和ldquo等待rdquo状态的

数据转成ldquo尝试发送状态rdquo重新发送该数据需要注意的是在 10 分钟的等待过程中

MDSBES 服务器如果没有接收到手持设备重新连接网络的消息则 MDSBES 服务器不会尝

试重新发送ldquo暂时失败rdquo和ldquo等待rdquo状态的数据

根据以上的分析可以对推出以下结果

1 当用户的手持设备在线时对该用户的数据推送会被马上处理该数据只在很短的时间

占用 1000 个等待队列中的一个位置

2 当用户的手持设备不在线时假设该服务器只有一个用户则服务器在 10 分钟内最多

可以接收该用户的 1005 条数据其中 5 条处于ldquo暂时失败rdquo状态1000 条在等待队列

3 如果用户的手持设备在数据发送给 MDSBES 服务器 10 分钟内重新接入网络MDSBES

服务器可以将该数据推送到手持设备端

4 如果 MDSBES 服务器的等待队列未满推送给新的用户的前 5 条数据不占用等待队列

5 如果 MDSBES 服务器的等待队列已满不管是由哪个用户的数据占满的MDSBES 服

务器都会停止接收所有用户的新的数据推送

总结而言在生产环境中保证 MDSBES 服务器正常工作的关键是要保证 MDSBES 服务

器的等待队列不被占满

手持设备端对推送数据的处理

MDSBES 服务器成功接收应用服务器推送的数据后会将数据推送到手持设备端在手持

设备端需要由客户端应用程序将推送的数据提取出来一个直观的猜测是手持设备端同样需

要一个队列保存数据否则客户端应用程序在提取前一条数据时新到达的数据就会直接丢

失需要验证的是这个队列有多深队列行为是否被系统隐藏经过测试证实手持设备端确

实也存在队列队列深度为 10而且可以反映在应用层其中最值得开发人员注意的是这

个队列是一个可溢出队列关于手持设备端推送数据队列的示意图如下

如图所示当客户端提取程序正在读取数据ldquoRrdquo如图中标有 R 的椭圆形所示如果

有更多的数据被推送到手持设备端所推送的数据会被放入设备的队列中一共有 10 条数

据可以被保留在设备队列中如椭圆形 1 到 10 所示此时如果有更多的数据被推送进来

设备队列已满则这些新的数据会溢出如椭圆形 111213 所示同时对于服务器端

而言数据 111213 已经被推送过一次服务器端不会再次尝试推送这些数据在这种

情况下数据 1 到 10 可以继续被客户端提取程序所提取而数据 111213 则丢失了

对于尚未推送到手持设备端的数据如数据 1415假设因为网络中断没有被推送到

手持设备端则仍保留在服务器端当网络恢复后数据 1415 仍可以被正常处理

对于确认消息(图中橙色圆形所示)的处理则更为复杂在异步确认消息一节中再作讨

从这一节的讨论得出的结果是数据到达手持设备端未必就能够被客户端提取程序提取

客户端提取程序有职责尽快提取数据防止推送数据的溢出溢出的数据在客户端无法再被

处理而且服务器端也不会尝试继续推送这些数据

异步确认消息(应用依赖和非应用依赖)

手持设备端在接收到推送数据后会通知 MDSBES 服务器这时 MDSBES 服务器会根据

最初的推送请求向指定的 URL 发送一个确认消息告知应用服务器某一条推送数据的最终

结果这就是异步确认消息异步确认消息以 http 协议发送给应用服务器消息 200 代表

数据推送成功400 代表数据推送失败

异步确认消息是应用服务器判断推送是否成功的有效工具但是很多开发人员因为对

异步确认消息的机制不了解错误地使用了异步确认消息从而导致数据的丢失所以我们

需要详细了解异步确认消息的工作机制

要讨论异步确认消息的工作机制必然涉及到应用依赖与非应用依赖应用依赖的推送

和非应用依赖的推送对应的异步确认消息工作机制是不同的

下面先讨论非应用依赖的场景这也是很多开发商使用的场景

如果推送是非应用依赖的数据是以到达手持设备为成功标志的只要数据到达了手持

设备MDSBES 服务器就认为该数据推送成功会给应用服务器发送异步确认消息 200

如果数据推送时手持设备不在线则 10 分钟内 MDSBES 服务器会在服务器上保留该数

据如果 10 分钟内手持设备重新接入网络则 MDSBES 服务器会尝试发送该数据一旦数

据到达手持设备MDSBES 服务器都会给应用服务器发送异步确认消息 200

如果 10 分钟内手设备没有接入网络则该推送数据过期MDSBES 服务器会丢弃该数

据同时给应用服务器发送异步确认消息 400

可以看出非应用依赖的推送方式并不能保证客户端应用程序接收到数据在非应用依

赖的推送方式下 MDSBES 返回 200 确认消息也不能认为客户端已接收到数据如果数据送

达手持设备的时候客户端应用程序没有启动所推送的数据会在手持设备端丢失而

MDSBES 服务器认为数据已成功送达或者是客户端应用程序已启动但是没有及时读取推

送数据在手持设备端溢出的数据也会丢失MDSBES 服务器同样认为数据已成功送达

所以在非应用依赖的情况下如果接收到 400 确认消息可以确定客户端没有收到数据

如果接收到 200 确认消息只能认为客户端可能收到了数据

从以上的讨论看非应用依赖的推送方式并不适合用于可靠性要求较高的应用对于可

靠性要求较高的应用需要知道客户端应用程序是否接收到了数据应用依赖的推送方式就

是为此而设计的如果推送是应用依赖的数据是以客户端应用程序确认为成功标志的只

要在应用程序确认后MDSBES 服务器才认为数据推送成功这时才给应用服务器发送异步

确认消息 200

有关客户端应用程序如何确认收到数据在ldquo客户端接收关键代码rdquo一节中有描述简

单而言就是客户端应用程序通过调用 MDSPushInputStream 实例的 accept 函数确认数据收

到如果客户端应用程序在接收到数据后没有调用 accept 函数MDSBES 服务器会认为数

据推送失败给应用服务器发送异步确认消息 400另外一点曾经强调过的是accept 函数

调用后只有 InputStream 和 StreamConnection 都关闭了确认消息才被发送给 MDSBES 服

务器在极端的情况下这一点会影响到异步确认消息的结果

下面具体分析各种情景

如果手持设备在线但是客户端应用程序没有启动推送数据到达手持设备端后没有程

序侦听推送端口当然也没有程序会调用 accept 函数此时 MDSBES 服务器会认为数据推

送失败向应用服务器发送异步确认消息 400

如果手持设备在线客户端应用程序在读取推送数据后没有调用 accept 函数直接关闭

连接此时 MDSBES 服务器同样会认为数据推送失败向应用服务器发送异步确认消息 400

在这种情景下即使客户端接收到了数据但应用服务器仍收到失败消息因为作为成功标

志的 accept 函数并没有调用

如果手持设备在线客户端应用程序在读取推送数据后调用了 accept 函数并关闭连

接此时 MDSBES 服务器会认为数据推送成功向应用服务器发送异步确认消息 200

对于手持设备不在线的情况结果与非应用依赖相似10 分钟内如果手持设备不接入

网络的话推送数据会在 MDSBES 服务器上过期MDSBES 服务器会向应用服务器发送异

步确认消息 400

如果 10 分钟内手持设备重新接入网络MDSBES 服务器会尝试发送数据至手持设备端

还是以 accept 函数的调用为推送成功的标志如果数据重新发送成功但是客户端没有调

用 accept 函数则 MDSBES 服务器会向应用服务器发送异步确认消息 200

对于推送到手持设备端但是溢出的数据因为没有程序能够读取该数据并针对该数据调

用 accept 函数MDSBES 服务器会一直等待到 10 分钟后向应用服务器发送异步确认消息

400认为数据过期

一个特殊的情况是客户端应用程序在接收到数据后调用了 accept但是没有关闭

InputStream 和 StreamConnection此时 MDSBES 服务器会认识客户端还在处理数据会继

续等待如果服务器等待时候超过 10 分钟MDSBES 服务器会认为数据推送失败向应用

服务器发送异步确认消息 400如果在 10 分钟内客户端主动关闭 InputStream 和

StreamConnection或者是网络异常中断MDSBES 服务器会认为数据推送成功向应用服

务器发送异步确认消息 200

下面再讨论一个异常情况下的特例

结合以上描述的各种因素在极端的情况下会出现一些特殊的情况如上图所示假设

MDSBES 服务器在推送了数据 R数据 1 到数据 13 后出现网络中断同时客户端应用程序

正在缓慢地读取数据 R此时数据 1 到数据 10 会被保留在手持设备队列中客户端应用程

序在读取数据 R 后可以继续读取数据 1 到数据 10如果客户端应用程序在读取了数据后都

调用了 accept 函数则会产生对应用确认消息如图中的确认消息 R确认消息 1 到确认消

息 10但是因为网络的中断这些确认消息都无法到达 MDSBES 服务器如果网络在 10

钟内没有恢复MDSBES 服务器上所有等待确认消息的记录都会认识是过期的MDSBES

会向应用服务器发送异步确认消息 400此后即使网络恢复确认消息 R确认消息 1 到确

认消息 10 都不再有效

在这种特殊情况下即使客户端应用程序接收到数据并调用了 accept 函数MDSBES

服务器同样认为数据没有推送成功

根据以上的讨论应用依赖的推送同样不能保证客户端应用一定能收到数据但是在

应用依赖的推送方式下 MDSBES 返回 200 确认消息可以确定客户端已接收到数据因为 200

确认消息是由客户端应用程序调用 accept 函数触发的客户端应用程序可以在确认数据收

到后才调用 accept 函数通知应用服务器该数据已成功接收甚至可以在接收到数据后根

据数据内容决定不调用 accept 函数从而通过应用服务器该数据没有被接收

所以在应用依赖的情况下如果接收到 200 确认消息可以确定客户端已经接收到数据

如果接收到 400 确认消息只能认为客户端可能没有收到数据

因此应用依赖的推送方式更适合可靠性要求高的应用如果应用服务器接收到 200

确认消息就可以认为推送成功继续处理其它事务如果应用服务器接收到 400 确认消息则

需要组织数据重发直到接收到 200 确认消息为止这样就可以保证客户端一定能够接收到

需要推送的数据

如何保证数据推送的可靠性

通过以上几个章节的讨论我们知道虽然 MDSBES 服务器提供了很多机制保证数据

推送的可靠性但是都无法完全保证数据被客户端应用程序所接收

对于数据推送的可靠性保证一方面开发人员需要了解各个可能导致故障的因素在应

用编写的时候尽量减少故障出现的可能性对故障点进行规避另一方面开发人员也需要实

现额外的机制进行数据重发在故障点无法规避的情况下通过数据重发消除故障点的影响

应用依赖和非应用依赖的比较和选择

在实现推送应用的时候首要考虑的是有关应用依赖与非应用依赖的选择

应用依赖的推送形式在可靠性方面更优于非应用依赖的推送形式但是应用依赖的推送

形式对 MDSBES 应用服务器的潜在压力较大客户端应用程序的实现也较为复杂

非应用依赖的推送形式优势在于实现简单对 MDSBES 应用服务器形成的压力相对较

非应用依赖的推送形式从某种程度上讲有点类似于广播协议着重点是从服务器向客户

端发送大量数据并不看重客户端的接收情况从数据的应用类型来分非应用依赖的推送

更适合于天气信息最新报价等数据这些数据在客户端应用关闭时被自然丢弃短时间内

的多条数据有丢失影响也不大必竞用户不会关注 11 点零六分的天气和 11 点零七分的天气

有何不同

应用依赖的推送形式则类似于点对点协议不仅从服务器端向客户端发送数据同时也

关注客户端的接收情况应用依赖的推送更适合于 OAERP 这种应用这种应用需要保证

新的文档或者是记录一定能到达客户端应用丢失一条待办信息可能就会导致整个审批流程

的停滞

再从两种推送方式的本质差别看应用依赖并没有额外的机制消除故障点它的优势在

于它的异步确认消息更可信所以在选择了应用依赖的推送方式后进一步还是需要实现

额外的数据重发机制根据 MDSBES 所发送的异步确认消息实施数据重发

应用依赖的推送再加上结合异步确认消息的数据重发机制是保证推送数据可靠性的

最好方法

MDS 接收线程限制的处理

不管使用什么方式的推送应用开发时都需要考虑到 MDS 接收线程的限制不过以

现在的服务器处理能力正常情况下的 HTTP 推送请求可以在几毫秒或者是几十毫内处理完

成少量的 HTTP 推送不足于构成网络阻塞

需要考虑的是应用服务器端的推送程序在推送过程中不要在建立连接后才动态组织数

据这样会浪费 http 连接资源好的建议是预先组织好数据在建立 HTTP 连接后快速写完

数据尽快关闭连接

此外对于应用服务器的推送线程也要加于控制避免在短时间内无限制地启动新的线

程进行连接否则也容易造成连接过多而阻塞的情况

从网络层面来看调整应用服务器与 MDSBES 服务器之间的网络超时设置也是避免这

种问题的一种方法在允许的范围内调大网络超时时间可以让应用服务器的推送线程等待更

长的时间从而让 MDSBES 有足够的时间释放原有的连接

最后无论使用了什么方法调优都需要对应用服务器的推送程序进行异常处理当发

现网络超时异常的时候可以组织数据重发当然数据重发的间隔时间也要考虑不要因为

重发数据再次造成网络阻塞

MDS 推送队列限制的处理

相对而言MDS 推送队列限制造成的推送失败比 MDS 接收队列造成的推送失败更常见

已经发现的一个实际案例是周未的时候用户大量关机导致大量推送数据处于推送队列中

填满了推送队列从而导致 MDSBES 服务器不再响应新的推送请求一旦过期时间到达

有新的队列位置空闲又有大量新的推送数据重新占满推送队列

对于 MDS 推送队列的限制直观的做法是对队列参数进行调整如之前讨论的管理

员可以对队列的深度和过期时间进行调整

然而出于对过期时间的不正确理解管理员倾向于将过期时间调大当过期时间被调

大后推送给关机用户的数据在服务器占用队列的时间就变长导致 MDS 推送队列更容易

被占满

所以如果对过期时间进行调整的话方向是将过期时间调小过期时间越小等待数

据占用队列的时间就越短MDS 推送队列被占满的可能性就越小但是过期时间也不能

过小过期时间过小可能会导致过多的过期数据使应用服务器需要处理更多的数据重发

比如将过期时间调成 1 分钟用户走过一个人行遂道都可能导致数据发送失败

对于队列深度的调整方向是调大队列深度如从缺省的 1000 调整到 2000此时

MDSBES 服务器在同一时间可以保留更多的等待数据减少了队列占满的机会不过调大队

列深度会让 MDSBES 服务器保留更多的数据从而占用更多的服务器内存对服务器产生

更大的压力所以调用队列深度需要根据服务器的运行能力进行调用

从应用服务器设计角度应用 MDS 队列限制的方法是尽量减少等待数据的发送如发

送第一条数据的时候发现数据过期对于该用户的其它数据可以调整发送时间在更长的时

间间隔后才尝试发送

对于 MDS 队列限制应用服务器端的推送应用需要对 MDSBES 服务器返回的 503 错误

进行处理对于出现 503 错的数据组织重发同样重发的间隔也需要考虑不要因为数据

重发占用更多的推送队列也可以根据目标用户进行判断对于可能关机的用户暂时不重发

数据

手持设备端接收队列的处理

手持设备端接收队列并未从文档中明确列出是根据测试结果分析得出的手持设备端

目前也没有发现设置可以调整该队列所以客户端程序设计的时候不应该依赖手持设备端队

开发人员需要意识到的是手持设备端的接收队列是可溢出队列一旦推送数据从接收队

列出溢出客户端程序就无法提取该数据了所以在客户端应用开始过程中要注意加快推送

数据的提取过程在提取过程中不要对数据进行复杂的计算提取完成后尽快读取下一条数

据对于接收数据的处理和计算可以交由另一个线程完成

因为溢出数据对客户端应用而言不可见所以数据溢出时在客户端应用中也不会有异

常开发人员需要通过服务器返回的过期消息才能知道该数据推送失败

对于这一点客户端应用除了尽量避免数据溢出外无法在客户端通过其它机制进行补

应用依赖的客户端

对于应用依赖的客户端应用关键点是决定何时调用 accept 函数同时保证 InputStream

和 StreamConnection 被关闭

如之前讨论的使用应用依赖的推送关键是希望使用 MDSBES 发送给应用服务器的异

步确认消息从而对数据是否到达进行判断所以应用依赖的客户端应用需要保证正确调用

accept 函数

另外如果 InputStream 和 StreamConnection 没有关闭的话accept 函数所发出的确认

消息无法被 MDSBES 服务器收到开发人员在代码中应该主动关闭 InputStream 和

StreamConnection同时在程序发生异常时也需要在 catch 代码段对 InputStream 和

StreamConnection 进行关闭处理

异步确认消息处理

异步确认消息用于判断数据是否被成功推送至客户端应用要使用异步确认消息首先

要保证成功地无遗漏地接收所有从 MDSBES 服务器发送过来的异步确认消息

异步确认消息是一个 HTTP 消息消息比较短接收过程的并发压力不大下面是一个

异步确认消息的样例

Received notificationGET HTTP11

User-Agent RIM MDSCS

Accept

X-RIM-Push-ID pushID2081773768

X-RIM-Push-Destination mmtestbjsearbcn

X-RIM-Push-Status 400

Host localhost7778

其中最关键是的获取属性 X-RIM-Push-Status根据该属性的值判断数据推送是成功

(200)还是失败(400)当然属性 X-RIM-Push-ID 也非常重要用于确定该确认消息是针

对哪条推送数据的

如果应用服务器端通过确认消息发现数据推送失败则需要根据属性 X-RIM-Push-ID 重

新组织数据进行重发

Page 18: BES 服务器推送机制分析 - images.csdn.netimages.csdn.net/upimgs/lee/BBPDF/BESfwtsjzfx.pdf · 前言 数据推送是BlackBerry 应用平台的一大优势,在BlackBerry 应用平台上部署的应用可以

推送队列的过期时间的修改界面如下(以毫秒为单位)

对于推送队列的进一步讨论是有关推送数据进入队列后的状态如何变化下面是推送数

据在队列中的状态变化图说明了推送数据在不同情况下的状态变化下图的各个状态中

淡蓝色的状态为起始状态深蓝色的状态为中间状态红色的状态为终止状态处于中间状

态的数据最终必然会转为终止状态的其中一种

当 MDSBES 服务器接收到推送数据后如果之前目标用户没有 5 个处于暂时失败的数

据包服务器会将数据放入等待发送的队列中推送的数据处于ldquo尝试发送rdquo状态处于ldquo尝

试发送rdquo会在很短的时间内占用 1000 个队列位置中的一个不管是成功还是失败它所占

用的队列位置很快被释放

然后 MDSBES 服务器尝试将数据推送到手持设备端如果此时对应的手持设备处于开

机状态网络一切正常则 MDSBES 服务器将数据发送给手持设备推送数据变成ldquo成功rdquo

状态MDSBES 服务器会向应用服务器发送异步的确认消息同时将数据从队列中移除注

意此时 MDSBES 服务器向应用服务器返回的确认消息并不一定是表示成功 200具体是

返回代表成功的 200 还是返回代表失败的 400 还取决于其它因素有关这一点我们在后面的

章节中详细讨论

如果 MDSBES 服务器尝试将数据推送到手持设备的时候手持设备处于关机状态则该

数据进入ldquo暂时失败rdquo状态处于ldquo暂时失败rdquo状态的数据不占用等待队列的队列位置某

一个目标用户最多只能有 5 条数据处于ldquo暂时失败rdquo状态目前没有明确的数据或者是文档

说明一个 MDSBES 服务器总共可以保留多少条ldquo暂时失败rdquo状态的数据

如果某一用户已有 5 条数据处于ldquo暂时失败rdquo状态MDSBES 服务器接收到该用户新

的推送请求时会直接将该数据转入ldquo等待rdquo状态每一条进入ldquo等待rdquo状态的推送数据会占

用推送队列中的一个位置当队列中的 1000 个位置都被占满的时候 MDSBES 服务器就停止

接收新的数据推送无论新的数据是推送给哪个用户

处于ldquo暂时失败rdquo和ldquo等待rdquo状态的数据会在服务器上保留 10 分钟(如上所述这一

时间可以由管理修改)10 分钟后处于这两种状态的数据将被丢弃进入ldquo过期rdquo状态同

时 MDSBES 服务器会向应用服务器发送异步消息这种情况下发送的异步确认消息一定是

代表失败的 400如果在 10 分钟内手持设备重新进入网络手持设备会发送消息到 MDSBES

服务器告知该手持设备已经上线MDSBES 服务器会将处理ldquo暂时失败rdquo和ldquo等待rdquo状态的

数据转成ldquo尝试发送状态rdquo重新发送该数据需要注意的是在 10 分钟的等待过程中

MDSBES 服务器如果没有接收到手持设备重新连接网络的消息则 MDSBES 服务器不会尝

试重新发送ldquo暂时失败rdquo和ldquo等待rdquo状态的数据

根据以上的分析可以对推出以下结果

1 当用户的手持设备在线时对该用户的数据推送会被马上处理该数据只在很短的时间

占用 1000 个等待队列中的一个位置

2 当用户的手持设备不在线时假设该服务器只有一个用户则服务器在 10 分钟内最多

可以接收该用户的 1005 条数据其中 5 条处于ldquo暂时失败rdquo状态1000 条在等待队列

3 如果用户的手持设备在数据发送给 MDSBES 服务器 10 分钟内重新接入网络MDSBES

服务器可以将该数据推送到手持设备端

4 如果 MDSBES 服务器的等待队列未满推送给新的用户的前 5 条数据不占用等待队列

5 如果 MDSBES 服务器的等待队列已满不管是由哪个用户的数据占满的MDSBES 服

务器都会停止接收所有用户的新的数据推送

总结而言在生产环境中保证 MDSBES 服务器正常工作的关键是要保证 MDSBES 服务

器的等待队列不被占满

手持设备端对推送数据的处理

MDSBES 服务器成功接收应用服务器推送的数据后会将数据推送到手持设备端在手持

设备端需要由客户端应用程序将推送的数据提取出来一个直观的猜测是手持设备端同样需

要一个队列保存数据否则客户端应用程序在提取前一条数据时新到达的数据就会直接丢

失需要验证的是这个队列有多深队列行为是否被系统隐藏经过测试证实手持设备端确

实也存在队列队列深度为 10而且可以反映在应用层其中最值得开发人员注意的是这

个队列是一个可溢出队列关于手持设备端推送数据队列的示意图如下

如图所示当客户端提取程序正在读取数据ldquoRrdquo如图中标有 R 的椭圆形所示如果

有更多的数据被推送到手持设备端所推送的数据会被放入设备的队列中一共有 10 条数

据可以被保留在设备队列中如椭圆形 1 到 10 所示此时如果有更多的数据被推送进来

设备队列已满则这些新的数据会溢出如椭圆形 111213 所示同时对于服务器端

而言数据 111213 已经被推送过一次服务器端不会再次尝试推送这些数据在这种

情况下数据 1 到 10 可以继续被客户端提取程序所提取而数据 111213 则丢失了

对于尚未推送到手持设备端的数据如数据 1415假设因为网络中断没有被推送到

手持设备端则仍保留在服务器端当网络恢复后数据 1415 仍可以被正常处理

对于确认消息(图中橙色圆形所示)的处理则更为复杂在异步确认消息一节中再作讨

从这一节的讨论得出的结果是数据到达手持设备端未必就能够被客户端提取程序提取

客户端提取程序有职责尽快提取数据防止推送数据的溢出溢出的数据在客户端无法再被

处理而且服务器端也不会尝试继续推送这些数据

异步确认消息(应用依赖和非应用依赖)

手持设备端在接收到推送数据后会通知 MDSBES 服务器这时 MDSBES 服务器会根据

最初的推送请求向指定的 URL 发送一个确认消息告知应用服务器某一条推送数据的最终

结果这就是异步确认消息异步确认消息以 http 协议发送给应用服务器消息 200 代表

数据推送成功400 代表数据推送失败

异步确认消息是应用服务器判断推送是否成功的有效工具但是很多开发人员因为对

异步确认消息的机制不了解错误地使用了异步确认消息从而导致数据的丢失所以我们

需要详细了解异步确认消息的工作机制

要讨论异步确认消息的工作机制必然涉及到应用依赖与非应用依赖应用依赖的推送

和非应用依赖的推送对应的异步确认消息工作机制是不同的

下面先讨论非应用依赖的场景这也是很多开发商使用的场景

如果推送是非应用依赖的数据是以到达手持设备为成功标志的只要数据到达了手持

设备MDSBES 服务器就认为该数据推送成功会给应用服务器发送异步确认消息 200

如果数据推送时手持设备不在线则 10 分钟内 MDSBES 服务器会在服务器上保留该数

据如果 10 分钟内手持设备重新接入网络则 MDSBES 服务器会尝试发送该数据一旦数

据到达手持设备MDSBES 服务器都会给应用服务器发送异步确认消息 200

如果 10 分钟内手设备没有接入网络则该推送数据过期MDSBES 服务器会丢弃该数

据同时给应用服务器发送异步确认消息 400

可以看出非应用依赖的推送方式并不能保证客户端应用程序接收到数据在非应用依

赖的推送方式下 MDSBES 返回 200 确认消息也不能认为客户端已接收到数据如果数据送

达手持设备的时候客户端应用程序没有启动所推送的数据会在手持设备端丢失而

MDSBES 服务器认为数据已成功送达或者是客户端应用程序已启动但是没有及时读取推

送数据在手持设备端溢出的数据也会丢失MDSBES 服务器同样认为数据已成功送达

所以在非应用依赖的情况下如果接收到 400 确认消息可以确定客户端没有收到数据

如果接收到 200 确认消息只能认为客户端可能收到了数据

从以上的讨论看非应用依赖的推送方式并不适合用于可靠性要求较高的应用对于可

靠性要求较高的应用需要知道客户端应用程序是否接收到了数据应用依赖的推送方式就

是为此而设计的如果推送是应用依赖的数据是以客户端应用程序确认为成功标志的只

要在应用程序确认后MDSBES 服务器才认为数据推送成功这时才给应用服务器发送异步

确认消息 200

有关客户端应用程序如何确认收到数据在ldquo客户端接收关键代码rdquo一节中有描述简

单而言就是客户端应用程序通过调用 MDSPushInputStream 实例的 accept 函数确认数据收

到如果客户端应用程序在接收到数据后没有调用 accept 函数MDSBES 服务器会认为数

据推送失败给应用服务器发送异步确认消息 400另外一点曾经强调过的是accept 函数

调用后只有 InputStream 和 StreamConnection 都关闭了确认消息才被发送给 MDSBES 服

务器在极端的情况下这一点会影响到异步确认消息的结果

下面具体分析各种情景

如果手持设备在线但是客户端应用程序没有启动推送数据到达手持设备端后没有程

序侦听推送端口当然也没有程序会调用 accept 函数此时 MDSBES 服务器会认为数据推

送失败向应用服务器发送异步确认消息 400

如果手持设备在线客户端应用程序在读取推送数据后没有调用 accept 函数直接关闭

连接此时 MDSBES 服务器同样会认为数据推送失败向应用服务器发送异步确认消息 400

在这种情景下即使客户端接收到了数据但应用服务器仍收到失败消息因为作为成功标

志的 accept 函数并没有调用

如果手持设备在线客户端应用程序在读取推送数据后调用了 accept 函数并关闭连

接此时 MDSBES 服务器会认为数据推送成功向应用服务器发送异步确认消息 200

对于手持设备不在线的情况结果与非应用依赖相似10 分钟内如果手持设备不接入

网络的话推送数据会在 MDSBES 服务器上过期MDSBES 服务器会向应用服务器发送异

步确认消息 400

如果 10 分钟内手持设备重新接入网络MDSBES 服务器会尝试发送数据至手持设备端

还是以 accept 函数的调用为推送成功的标志如果数据重新发送成功但是客户端没有调

用 accept 函数则 MDSBES 服务器会向应用服务器发送异步确认消息 200

对于推送到手持设备端但是溢出的数据因为没有程序能够读取该数据并针对该数据调

用 accept 函数MDSBES 服务器会一直等待到 10 分钟后向应用服务器发送异步确认消息

400认为数据过期

一个特殊的情况是客户端应用程序在接收到数据后调用了 accept但是没有关闭

InputStream 和 StreamConnection此时 MDSBES 服务器会认识客户端还在处理数据会继

续等待如果服务器等待时候超过 10 分钟MDSBES 服务器会认为数据推送失败向应用

服务器发送异步确认消息 400如果在 10 分钟内客户端主动关闭 InputStream 和

StreamConnection或者是网络异常中断MDSBES 服务器会认为数据推送成功向应用服

务器发送异步确认消息 200

下面再讨论一个异常情况下的特例

结合以上描述的各种因素在极端的情况下会出现一些特殊的情况如上图所示假设

MDSBES 服务器在推送了数据 R数据 1 到数据 13 后出现网络中断同时客户端应用程序

正在缓慢地读取数据 R此时数据 1 到数据 10 会被保留在手持设备队列中客户端应用程

序在读取数据 R 后可以继续读取数据 1 到数据 10如果客户端应用程序在读取了数据后都

调用了 accept 函数则会产生对应用确认消息如图中的确认消息 R确认消息 1 到确认消

息 10但是因为网络的中断这些确认消息都无法到达 MDSBES 服务器如果网络在 10

钟内没有恢复MDSBES 服务器上所有等待确认消息的记录都会认识是过期的MDSBES

会向应用服务器发送异步确认消息 400此后即使网络恢复确认消息 R确认消息 1 到确

认消息 10 都不再有效

在这种特殊情况下即使客户端应用程序接收到数据并调用了 accept 函数MDSBES

服务器同样认为数据没有推送成功

根据以上的讨论应用依赖的推送同样不能保证客户端应用一定能收到数据但是在

应用依赖的推送方式下 MDSBES 返回 200 确认消息可以确定客户端已接收到数据因为 200

确认消息是由客户端应用程序调用 accept 函数触发的客户端应用程序可以在确认数据收

到后才调用 accept 函数通知应用服务器该数据已成功接收甚至可以在接收到数据后根

据数据内容决定不调用 accept 函数从而通过应用服务器该数据没有被接收

所以在应用依赖的情况下如果接收到 200 确认消息可以确定客户端已经接收到数据

如果接收到 400 确认消息只能认为客户端可能没有收到数据

因此应用依赖的推送方式更适合可靠性要求高的应用如果应用服务器接收到 200

确认消息就可以认为推送成功继续处理其它事务如果应用服务器接收到 400 确认消息则

需要组织数据重发直到接收到 200 确认消息为止这样就可以保证客户端一定能够接收到

需要推送的数据

如何保证数据推送的可靠性

通过以上几个章节的讨论我们知道虽然 MDSBES 服务器提供了很多机制保证数据

推送的可靠性但是都无法完全保证数据被客户端应用程序所接收

对于数据推送的可靠性保证一方面开发人员需要了解各个可能导致故障的因素在应

用编写的时候尽量减少故障出现的可能性对故障点进行规避另一方面开发人员也需要实

现额外的机制进行数据重发在故障点无法规避的情况下通过数据重发消除故障点的影响

应用依赖和非应用依赖的比较和选择

在实现推送应用的时候首要考虑的是有关应用依赖与非应用依赖的选择

应用依赖的推送形式在可靠性方面更优于非应用依赖的推送形式但是应用依赖的推送

形式对 MDSBES 应用服务器的潜在压力较大客户端应用程序的实现也较为复杂

非应用依赖的推送形式优势在于实现简单对 MDSBES 应用服务器形成的压力相对较

非应用依赖的推送形式从某种程度上讲有点类似于广播协议着重点是从服务器向客户

端发送大量数据并不看重客户端的接收情况从数据的应用类型来分非应用依赖的推送

更适合于天气信息最新报价等数据这些数据在客户端应用关闭时被自然丢弃短时间内

的多条数据有丢失影响也不大必竞用户不会关注 11 点零六分的天气和 11 点零七分的天气

有何不同

应用依赖的推送形式则类似于点对点协议不仅从服务器端向客户端发送数据同时也

关注客户端的接收情况应用依赖的推送更适合于 OAERP 这种应用这种应用需要保证

新的文档或者是记录一定能到达客户端应用丢失一条待办信息可能就会导致整个审批流程

的停滞

再从两种推送方式的本质差别看应用依赖并没有额外的机制消除故障点它的优势在

于它的异步确认消息更可信所以在选择了应用依赖的推送方式后进一步还是需要实现

额外的数据重发机制根据 MDSBES 所发送的异步确认消息实施数据重发

应用依赖的推送再加上结合异步确认消息的数据重发机制是保证推送数据可靠性的

最好方法

MDS 接收线程限制的处理

不管使用什么方式的推送应用开发时都需要考虑到 MDS 接收线程的限制不过以

现在的服务器处理能力正常情况下的 HTTP 推送请求可以在几毫秒或者是几十毫内处理完

成少量的 HTTP 推送不足于构成网络阻塞

需要考虑的是应用服务器端的推送程序在推送过程中不要在建立连接后才动态组织数

据这样会浪费 http 连接资源好的建议是预先组织好数据在建立 HTTP 连接后快速写完

数据尽快关闭连接

此外对于应用服务器的推送线程也要加于控制避免在短时间内无限制地启动新的线

程进行连接否则也容易造成连接过多而阻塞的情况

从网络层面来看调整应用服务器与 MDSBES 服务器之间的网络超时设置也是避免这

种问题的一种方法在允许的范围内调大网络超时时间可以让应用服务器的推送线程等待更

长的时间从而让 MDSBES 有足够的时间释放原有的连接

最后无论使用了什么方法调优都需要对应用服务器的推送程序进行异常处理当发

现网络超时异常的时候可以组织数据重发当然数据重发的间隔时间也要考虑不要因为

重发数据再次造成网络阻塞

MDS 推送队列限制的处理

相对而言MDS 推送队列限制造成的推送失败比 MDS 接收队列造成的推送失败更常见

已经发现的一个实际案例是周未的时候用户大量关机导致大量推送数据处于推送队列中

填满了推送队列从而导致 MDSBES 服务器不再响应新的推送请求一旦过期时间到达

有新的队列位置空闲又有大量新的推送数据重新占满推送队列

对于 MDS 推送队列的限制直观的做法是对队列参数进行调整如之前讨论的管理

员可以对队列的深度和过期时间进行调整

然而出于对过期时间的不正确理解管理员倾向于将过期时间调大当过期时间被调

大后推送给关机用户的数据在服务器占用队列的时间就变长导致 MDS 推送队列更容易

被占满

所以如果对过期时间进行调整的话方向是将过期时间调小过期时间越小等待数

据占用队列的时间就越短MDS 推送队列被占满的可能性就越小但是过期时间也不能

过小过期时间过小可能会导致过多的过期数据使应用服务器需要处理更多的数据重发

比如将过期时间调成 1 分钟用户走过一个人行遂道都可能导致数据发送失败

对于队列深度的调整方向是调大队列深度如从缺省的 1000 调整到 2000此时

MDSBES 服务器在同一时间可以保留更多的等待数据减少了队列占满的机会不过调大队

列深度会让 MDSBES 服务器保留更多的数据从而占用更多的服务器内存对服务器产生

更大的压力所以调用队列深度需要根据服务器的运行能力进行调用

从应用服务器设计角度应用 MDS 队列限制的方法是尽量减少等待数据的发送如发

送第一条数据的时候发现数据过期对于该用户的其它数据可以调整发送时间在更长的时

间间隔后才尝试发送

对于 MDS 队列限制应用服务器端的推送应用需要对 MDSBES 服务器返回的 503 错误

进行处理对于出现 503 错的数据组织重发同样重发的间隔也需要考虑不要因为数据

重发占用更多的推送队列也可以根据目标用户进行判断对于可能关机的用户暂时不重发

数据

手持设备端接收队列的处理

手持设备端接收队列并未从文档中明确列出是根据测试结果分析得出的手持设备端

目前也没有发现设置可以调整该队列所以客户端程序设计的时候不应该依赖手持设备端队

开发人员需要意识到的是手持设备端的接收队列是可溢出队列一旦推送数据从接收队

列出溢出客户端程序就无法提取该数据了所以在客户端应用开始过程中要注意加快推送

数据的提取过程在提取过程中不要对数据进行复杂的计算提取完成后尽快读取下一条数

据对于接收数据的处理和计算可以交由另一个线程完成

因为溢出数据对客户端应用而言不可见所以数据溢出时在客户端应用中也不会有异

常开发人员需要通过服务器返回的过期消息才能知道该数据推送失败

对于这一点客户端应用除了尽量避免数据溢出外无法在客户端通过其它机制进行补

应用依赖的客户端

对于应用依赖的客户端应用关键点是决定何时调用 accept 函数同时保证 InputStream

和 StreamConnection 被关闭

如之前讨论的使用应用依赖的推送关键是希望使用 MDSBES 发送给应用服务器的异

步确认消息从而对数据是否到达进行判断所以应用依赖的客户端应用需要保证正确调用

accept 函数

另外如果 InputStream 和 StreamConnection 没有关闭的话accept 函数所发出的确认

消息无法被 MDSBES 服务器收到开发人员在代码中应该主动关闭 InputStream 和

StreamConnection同时在程序发生异常时也需要在 catch 代码段对 InputStream 和

StreamConnection 进行关闭处理

异步确认消息处理

异步确认消息用于判断数据是否被成功推送至客户端应用要使用异步确认消息首先

要保证成功地无遗漏地接收所有从 MDSBES 服务器发送过来的异步确认消息

异步确认消息是一个 HTTP 消息消息比较短接收过程的并发压力不大下面是一个

异步确认消息的样例

Received notificationGET HTTP11

User-Agent RIM MDSCS

Accept

X-RIM-Push-ID pushID2081773768

X-RIM-Push-Destination mmtestbjsearbcn

X-RIM-Push-Status 400

Host localhost7778

其中最关键是的获取属性 X-RIM-Push-Status根据该属性的值判断数据推送是成功

(200)还是失败(400)当然属性 X-RIM-Push-ID 也非常重要用于确定该确认消息是针

对哪条推送数据的

如果应用服务器端通过确认消息发现数据推送失败则需要根据属性 X-RIM-Push-ID 重

新组织数据进行重发

Page 19: BES 服务器推送机制分析 - images.csdn.netimages.csdn.net/upimgs/lee/BBPDF/BESfwtsjzfx.pdf · 前言 数据推送是BlackBerry 应用平台的一大优势,在BlackBerry 应用平台上部署的应用可以

当 MDSBES 服务器接收到推送数据后如果之前目标用户没有 5 个处于暂时失败的数

据包服务器会将数据放入等待发送的队列中推送的数据处于ldquo尝试发送rdquo状态处于ldquo尝

试发送rdquo会在很短的时间内占用 1000 个队列位置中的一个不管是成功还是失败它所占

用的队列位置很快被释放

然后 MDSBES 服务器尝试将数据推送到手持设备端如果此时对应的手持设备处于开

机状态网络一切正常则 MDSBES 服务器将数据发送给手持设备推送数据变成ldquo成功rdquo

状态MDSBES 服务器会向应用服务器发送异步的确认消息同时将数据从队列中移除注

意此时 MDSBES 服务器向应用服务器返回的确认消息并不一定是表示成功 200具体是

返回代表成功的 200 还是返回代表失败的 400 还取决于其它因素有关这一点我们在后面的

章节中详细讨论

如果 MDSBES 服务器尝试将数据推送到手持设备的时候手持设备处于关机状态则该

数据进入ldquo暂时失败rdquo状态处于ldquo暂时失败rdquo状态的数据不占用等待队列的队列位置某

一个目标用户最多只能有 5 条数据处于ldquo暂时失败rdquo状态目前没有明确的数据或者是文档

说明一个 MDSBES 服务器总共可以保留多少条ldquo暂时失败rdquo状态的数据

如果某一用户已有 5 条数据处于ldquo暂时失败rdquo状态MDSBES 服务器接收到该用户新

的推送请求时会直接将该数据转入ldquo等待rdquo状态每一条进入ldquo等待rdquo状态的推送数据会占

用推送队列中的一个位置当队列中的 1000 个位置都被占满的时候 MDSBES 服务器就停止

接收新的数据推送无论新的数据是推送给哪个用户

处于ldquo暂时失败rdquo和ldquo等待rdquo状态的数据会在服务器上保留 10 分钟(如上所述这一

时间可以由管理修改)10 分钟后处于这两种状态的数据将被丢弃进入ldquo过期rdquo状态同

时 MDSBES 服务器会向应用服务器发送异步消息这种情况下发送的异步确认消息一定是

代表失败的 400如果在 10 分钟内手持设备重新进入网络手持设备会发送消息到 MDSBES

服务器告知该手持设备已经上线MDSBES 服务器会将处理ldquo暂时失败rdquo和ldquo等待rdquo状态的

数据转成ldquo尝试发送状态rdquo重新发送该数据需要注意的是在 10 分钟的等待过程中

MDSBES 服务器如果没有接收到手持设备重新连接网络的消息则 MDSBES 服务器不会尝

试重新发送ldquo暂时失败rdquo和ldquo等待rdquo状态的数据

根据以上的分析可以对推出以下结果

1 当用户的手持设备在线时对该用户的数据推送会被马上处理该数据只在很短的时间

占用 1000 个等待队列中的一个位置

2 当用户的手持设备不在线时假设该服务器只有一个用户则服务器在 10 分钟内最多

可以接收该用户的 1005 条数据其中 5 条处于ldquo暂时失败rdquo状态1000 条在等待队列

3 如果用户的手持设备在数据发送给 MDSBES 服务器 10 分钟内重新接入网络MDSBES

服务器可以将该数据推送到手持设备端

4 如果 MDSBES 服务器的等待队列未满推送给新的用户的前 5 条数据不占用等待队列

5 如果 MDSBES 服务器的等待队列已满不管是由哪个用户的数据占满的MDSBES 服

务器都会停止接收所有用户的新的数据推送

总结而言在生产环境中保证 MDSBES 服务器正常工作的关键是要保证 MDSBES 服务

器的等待队列不被占满

手持设备端对推送数据的处理

MDSBES 服务器成功接收应用服务器推送的数据后会将数据推送到手持设备端在手持

设备端需要由客户端应用程序将推送的数据提取出来一个直观的猜测是手持设备端同样需

要一个队列保存数据否则客户端应用程序在提取前一条数据时新到达的数据就会直接丢

失需要验证的是这个队列有多深队列行为是否被系统隐藏经过测试证实手持设备端确

实也存在队列队列深度为 10而且可以反映在应用层其中最值得开发人员注意的是这

个队列是一个可溢出队列关于手持设备端推送数据队列的示意图如下

如图所示当客户端提取程序正在读取数据ldquoRrdquo如图中标有 R 的椭圆形所示如果

有更多的数据被推送到手持设备端所推送的数据会被放入设备的队列中一共有 10 条数

据可以被保留在设备队列中如椭圆形 1 到 10 所示此时如果有更多的数据被推送进来

设备队列已满则这些新的数据会溢出如椭圆形 111213 所示同时对于服务器端

而言数据 111213 已经被推送过一次服务器端不会再次尝试推送这些数据在这种

情况下数据 1 到 10 可以继续被客户端提取程序所提取而数据 111213 则丢失了

对于尚未推送到手持设备端的数据如数据 1415假设因为网络中断没有被推送到

手持设备端则仍保留在服务器端当网络恢复后数据 1415 仍可以被正常处理

对于确认消息(图中橙色圆形所示)的处理则更为复杂在异步确认消息一节中再作讨

从这一节的讨论得出的结果是数据到达手持设备端未必就能够被客户端提取程序提取

客户端提取程序有职责尽快提取数据防止推送数据的溢出溢出的数据在客户端无法再被

处理而且服务器端也不会尝试继续推送这些数据

异步确认消息(应用依赖和非应用依赖)

手持设备端在接收到推送数据后会通知 MDSBES 服务器这时 MDSBES 服务器会根据

最初的推送请求向指定的 URL 发送一个确认消息告知应用服务器某一条推送数据的最终

结果这就是异步确认消息异步确认消息以 http 协议发送给应用服务器消息 200 代表

数据推送成功400 代表数据推送失败

异步确认消息是应用服务器判断推送是否成功的有效工具但是很多开发人员因为对

异步确认消息的机制不了解错误地使用了异步确认消息从而导致数据的丢失所以我们

需要详细了解异步确认消息的工作机制

要讨论异步确认消息的工作机制必然涉及到应用依赖与非应用依赖应用依赖的推送

和非应用依赖的推送对应的异步确认消息工作机制是不同的

下面先讨论非应用依赖的场景这也是很多开发商使用的场景

如果推送是非应用依赖的数据是以到达手持设备为成功标志的只要数据到达了手持

设备MDSBES 服务器就认为该数据推送成功会给应用服务器发送异步确认消息 200

如果数据推送时手持设备不在线则 10 分钟内 MDSBES 服务器会在服务器上保留该数

据如果 10 分钟内手持设备重新接入网络则 MDSBES 服务器会尝试发送该数据一旦数

据到达手持设备MDSBES 服务器都会给应用服务器发送异步确认消息 200

如果 10 分钟内手设备没有接入网络则该推送数据过期MDSBES 服务器会丢弃该数

据同时给应用服务器发送异步确认消息 400

可以看出非应用依赖的推送方式并不能保证客户端应用程序接收到数据在非应用依

赖的推送方式下 MDSBES 返回 200 确认消息也不能认为客户端已接收到数据如果数据送

达手持设备的时候客户端应用程序没有启动所推送的数据会在手持设备端丢失而

MDSBES 服务器认为数据已成功送达或者是客户端应用程序已启动但是没有及时读取推

送数据在手持设备端溢出的数据也会丢失MDSBES 服务器同样认为数据已成功送达

所以在非应用依赖的情况下如果接收到 400 确认消息可以确定客户端没有收到数据

如果接收到 200 确认消息只能认为客户端可能收到了数据

从以上的讨论看非应用依赖的推送方式并不适合用于可靠性要求较高的应用对于可

靠性要求较高的应用需要知道客户端应用程序是否接收到了数据应用依赖的推送方式就

是为此而设计的如果推送是应用依赖的数据是以客户端应用程序确认为成功标志的只

要在应用程序确认后MDSBES 服务器才认为数据推送成功这时才给应用服务器发送异步

确认消息 200

有关客户端应用程序如何确认收到数据在ldquo客户端接收关键代码rdquo一节中有描述简

单而言就是客户端应用程序通过调用 MDSPushInputStream 实例的 accept 函数确认数据收

到如果客户端应用程序在接收到数据后没有调用 accept 函数MDSBES 服务器会认为数

据推送失败给应用服务器发送异步确认消息 400另外一点曾经强调过的是accept 函数

调用后只有 InputStream 和 StreamConnection 都关闭了确认消息才被发送给 MDSBES 服

务器在极端的情况下这一点会影响到异步确认消息的结果

下面具体分析各种情景

如果手持设备在线但是客户端应用程序没有启动推送数据到达手持设备端后没有程

序侦听推送端口当然也没有程序会调用 accept 函数此时 MDSBES 服务器会认为数据推

送失败向应用服务器发送异步确认消息 400

如果手持设备在线客户端应用程序在读取推送数据后没有调用 accept 函数直接关闭

连接此时 MDSBES 服务器同样会认为数据推送失败向应用服务器发送异步确认消息 400

在这种情景下即使客户端接收到了数据但应用服务器仍收到失败消息因为作为成功标

志的 accept 函数并没有调用

如果手持设备在线客户端应用程序在读取推送数据后调用了 accept 函数并关闭连

接此时 MDSBES 服务器会认为数据推送成功向应用服务器发送异步确认消息 200

对于手持设备不在线的情况结果与非应用依赖相似10 分钟内如果手持设备不接入

网络的话推送数据会在 MDSBES 服务器上过期MDSBES 服务器会向应用服务器发送异

步确认消息 400

如果 10 分钟内手持设备重新接入网络MDSBES 服务器会尝试发送数据至手持设备端

还是以 accept 函数的调用为推送成功的标志如果数据重新发送成功但是客户端没有调

用 accept 函数则 MDSBES 服务器会向应用服务器发送异步确认消息 200

对于推送到手持设备端但是溢出的数据因为没有程序能够读取该数据并针对该数据调

用 accept 函数MDSBES 服务器会一直等待到 10 分钟后向应用服务器发送异步确认消息

400认为数据过期

一个特殊的情况是客户端应用程序在接收到数据后调用了 accept但是没有关闭

InputStream 和 StreamConnection此时 MDSBES 服务器会认识客户端还在处理数据会继

续等待如果服务器等待时候超过 10 分钟MDSBES 服务器会认为数据推送失败向应用

服务器发送异步确认消息 400如果在 10 分钟内客户端主动关闭 InputStream 和

StreamConnection或者是网络异常中断MDSBES 服务器会认为数据推送成功向应用服

务器发送异步确认消息 200

下面再讨论一个异常情况下的特例

结合以上描述的各种因素在极端的情况下会出现一些特殊的情况如上图所示假设

MDSBES 服务器在推送了数据 R数据 1 到数据 13 后出现网络中断同时客户端应用程序

正在缓慢地读取数据 R此时数据 1 到数据 10 会被保留在手持设备队列中客户端应用程

序在读取数据 R 后可以继续读取数据 1 到数据 10如果客户端应用程序在读取了数据后都

调用了 accept 函数则会产生对应用确认消息如图中的确认消息 R确认消息 1 到确认消

息 10但是因为网络的中断这些确认消息都无法到达 MDSBES 服务器如果网络在 10

钟内没有恢复MDSBES 服务器上所有等待确认消息的记录都会认识是过期的MDSBES

会向应用服务器发送异步确认消息 400此后即使网络恢复确认消息 R确认消息 1 到确

认消息 10 都不再有效

在这种特殊情况下即使客户端应用程序接收到数据并调用了 accept 函数MDSBES

服务器同样认为数据没有推送成功

根据以上的讨论应用依赖的推送同样不能保证客户端应用一定能收到数据但是在

应用依赖的推送方式下 MDSBES 返回 200 确认消息可以确定客户端已接收到数据因为 200

确认消息是由客户端应用程序调用 accept 函数触发的客户端应用程序可以在确认数据收

到后才调用 accept 函数通知应用服务器该数据已成功接收甚至可以在接收到数据后根

据数据内容决定不调用 accept 函数从而通过应用服务器该数据没有被接收

所以在应用依赖的情况下如果接收到 200 确认消息可以确定客户端已经接收到数据

如果接收到 400 确认消息只能认为客户端可能没有收到数据

因此应用依赖的推送方式更适合可靠性要求高的应用如果应用服务器接收到 200

确认消息就可以认为推送成功继续处理其它事务如果应用服务器接收到 400 确认消息则

需要组织数据重发直到接收到 200 确认消息为止这样就可以保证客户端一定能够接收到

需要推送的数据

如何保证数据推送的可靠性

通过以上几个章节的讨论我们知道虽然 MDSBES 服务器提供了很多机制保证数据

推送的可靠性但是都无法完全保证数据被客户端应用程序所接收

对于数据推送的可靠性保证一方面开发人员需要了解各个可能导致故障的因素在应

用编写的时候尽量减少故障出现的可能性对故障点进行规避另一方面开发人员也需要实

现额外的机制进行数据重发在故障点无法规避的情况下通过数据重发消除故障点的影响

应用依赖和非应用依赖的比较和选择

在实现推送应用的时候首要考虑的是有关应用依赖与非应用依赖的选择

应用依赖的推送形式在可靠性方面更优于非应用依赖的推送形式但是应用依赖的推送

形式对 MDSBES 应用服务器的潜在压力较大客户端应用程序的实现也较为复杂

非应用依赖的推送形式优势在于实现简单对 MDSBES 应用服务器形成的压力相对较

非应用依赖的推送形式从某种程度上讲有点类似于广播协议着重点是从服务器向客户

端发送大量数据并不看重客户端的接收情况从数据的应用类型来分非应用依赖的推送

更适合于天气信息最新报价等数据这些数据在客户端应用关闭时被自然丢弃短时间内

的多条数据有丢失影响也不大必竞用户不会关注 11 点零六分的天气和 11 点零七分的天气

有何不同

应用依赖的推送形式则类似于点对点协议不仅从服务器端向客户端发送数据同时也

关注客户端的接收情况应用依赖的推送更适合于 OAERP 这种应用这种应用需要保证

新的文档或者是记录一定能到达客户端应用丢失一条待办信息可能就会导致整个审批流程

的停滞

再从两种推送方式的本质差别看应用依赖并没有额外的机制消除故障点它的优势在

于它的异步确认消息更可信所以在选择了应用依赖的推送方式后进一步还是需要实现

额外的数据重发机制根据 MDSBES 所发送的异步确认消息实施数据重发

应用依赖的推送再加上结合异步确认消息的数据重发机制是保证推送数据可靠性的

最好方法

MDS 接收线程限制的处理

不管使用什么方式的推送应用开发时都需要考虑到 MDS 接收线程的限制不过以

现在的服务器处理能力正常情况下的 HTTP 推送请求可以在几毫秒或者是几十毫内处理完

成少量的 HTTP 推送不足于构成网络阻塞

需要考虑的是应用服务器端的推送程序在推送过程中不要在建立连接后才动态组织数

据这样会浪费 http 连接资源好的建议是预先组织好数据在建立 HTTP 连接后快速写完

数据尽快关闭连接

此外对于应用服务器的推送线程也要加于控制避免在短时间内无限制地启动新的线

程进行连接否则也容易造成连接过多而阻塞的情况

从网络层面来看调整应用服务器与 MDSBES 服务器之间的网络超时设置也是避免这

种问题的一种方法在允许的范围内调大网络超时时间可以让应用服务器的推送线程等待更

长的时间从而让 MDSBES 有足够的时间释放原有的连接

最后无论使用了什么方法调优都需要对应用服务器的推送程序进行异常处理当发

现网络超时异常的时候可以组织数据重发当然数据重发的间隔时间也要考虑不要因为

重发数据再次造成网络阻塞

MDS 推送队列限制的处理

相对而言MDS 推送队列限制造成的推送失败比 MDS 接收队列造成的推送失败更常见

已经发现的一个实际案例是周未的时候用户大量关机导致大量推送数据处于推送队列中

填满了推送队列从而导致 MDSBES 服务器不再响应新的推送请求一旦过期时间到达

有新的队列位置空闲又有大量新的推送数据重新占满推送队列

对于 MDS 推送队列的限制直观的做法是对队列参数进行调整如之前讨论的管理

员可以对队列的深度和过期时间进行调整

然而出于对过期时间的不正确理解管理员倾向于将过期时间调大当过期时间被调

大后推送给关机用户的数据在服务器占用队列的时间就变长导致 MDS 推送队列更容易

被占满

所以如果对过期时间进行调整的话方向是将过期时间调小过期时间越小等待数

据占用队列的时间就越短MDS 推送队列被占满的可能性就越小但是过期时间也不能

过小过期时间过小可能会导致过多的过期数据使应用服务器需要处理更多的数据重发

比如将过期时间调成 1 分钟用户走过一个人行遂道都可能导致数据发送失败

对于队列深度的调整方向是调大队列深度如从缺省的 1000 调整到 2000此时

MDSBES 服务器在同一时间可以保留更多的等待数据减少了队列占满的机会不过调大队

列深度会让 MDSBES 服务器保留更多的数据从而占用更多的服务器内存对服务器产生

更大的压力所以调用队列深度需要根据服务器的运行能力进行调用

从应用服务器设计角度应用 MDS 队列限制的方法是尽量减少等待数据的发送如发

送第一条数据的时候发现数据过期对于该用户的其它数据可以调整发送时间在更长的时

间间隔后才尝试发送

对于 MDS 队列限制应用服务器端的推送应用需要对 MDSBES 服务器返回的 503 错误

进行处理对于出现 503 错的数据组织重发同样重发的间隔也需要考虑不要因为数据

重发占用更多的推送队列也可以根据目标用户进行判断对于可能关机的用户暂时不重发

数据

手持设备端接收队列的处理

手持设备端接收队列并未从文档中明确列出是根据测试结果分析得出的手持设备端

目前也没有发现设置可以调整该队列所以客户端程序设计的时候不应该依赖手持设备端队

开发人员需要意识到的是手持设备端的接收队列是可溢出队列一旦推送数据从接收队

列出溢出客户端程序就无法提取该数据了所以在客户端应用开始过程中要注意加快推送

数据的提取过程在提取过程中不要对数据进行复杂的计算提取完成后尽快读取下一条数

据对于接收数据的处理和计算可以交由另一个线程完成

因为溢出数据对客户端应用而言不可见所以数据溢出时在客户端应用中也不会有异

常开发人员需要通过服务器返回的过期消息才能知道该数据推送失败

对于这一点客户端应用除了尽量避免数据溢出外无法在客户端通过其它机制进行补

应用依赖的客户端

对于应用依赖的客户端应用关键点是决定何时调用 accept 函数同时保证 InputStream

和 StreamConnection 被关闭

如之前讨论的使用应用依赖的推送关键是希望使用 MDSBES 发送给应用服务器的异

步确认消息从而对数据是否到达进行判断所以应用依赖的客户端应用需要保证正确调用

accept 函数

另外如果 InputStream 和 StreamConnection 没有关闭的话accept 函数所发出的确认

消息无法被 MDSBES 服务器收到开发人员在代码中应该主动关闭 InputStream 和

StreamConnection同时在程序发生异常时也需要在 catch 代码段对 InputStream 和

StreamConnection 进行关闭处理

异步确认消息处理

异步确认消息用于判断数据是否被成功推送至客户端应用要使用异步确认消息首先

要保证成功地无遗漏地接收所有从 MDSBES 服务器发送过来的异步确认消息

异步确认消息是一个 HTTP 消息消息比较短接收过程的并发压力不大下面是一个

异步确认消息的样例

Received notificationGET HTTP11

User-Agent RIM MDSCS

Accept

X-RIM-Push-ID pushID2081773768

X-RIM-Push-Destination mmtestbjsearbcn

X-RIM-Push-Status 400

Host localhost7778

其中最关键是的获取属性 X-RIM-Push-Status根据该属性的值判断数据推送是成功

(200)还是失败(400)当然属性 X-RIM-Push-ID 也非常重要用于确定该确认消息是针

对哪条推送数据的

如果应用服务器端通过确认消息发现数据推送失败则需要根据属性 X-RIM-Push-ID 重

新组织数据进行重发

Page 20: BES 服务器推送机制分析 - images.csdn.netimages.csdn.net/upimgs/lee/BBPDF/BESfwtsjzfx.pdf · 前言 数据推送是BlackBerry 应用平台的一大优势,在BlackBerry 应用平台上部署的应用可以

可以接收该用户的 1005 条数据其中 5 条处于ldquo暂时失败rdquo状态1000 条在等待队列

3 如果用户的手持设备在数据发送给 MDSBES 服务器 10 分钟内重新接入网络MDSBES

服务器可以将该数据推送到手持设备端

4 如果 MDSBES 服务器的等待队列未满推送给新的用户的前 5 条数据不占用等待队列

5 如果 MDSBES 服务器的等待队列已满不管是由哪个用户的数据占满的MDSBES 服

务器都会停止接收所有用户的新的数据推送

总结而言在生产环境中保证 MDSBES 服务器正常工作的关键是要保证 MDSBES 服务

器的等待队列不被占满

手持设备端对推送数据的处理

MDSBES 服务器成功接收应用服务器推送的数据后会将数据推送到手持设备端在手持

设备端需要由客户端应用程序将推送的数据提取出来一个直观的猜测是手持设备端同样需

要一个队列保存数据否则客户端应用程序在提取前一条数据时新到达的数据就会直接丢

失需要验证的是这个队列有多深队列行为是否被系统隐藏经过测试证实手持设备端确

实也存在队列队列深度为 10而且可以反映在应用层其中最值得开发人员注意的是这

个队列是一个可溢出队列关于手持设备端推送数据队列的示意图如下

如图所示当客户端提取程序正在读取数据ldquoRrdquo如图中标有 R 的椭圆形所示如果

有更多的数据被推送到手持设备端所推送的数据会被放入设备的队列中一共有 10 条数

据可以被保留在设备队列中如椭圆形 1 到 10 所示此时如果有更多的数据被推送进来

设备队列已满则这些新的数据会溢出如椭圆形 111213 所示同时对于服务器端

而言数据 111213 已经被推送过一次服务器端不会再次尝试推送这些数据在这种

情况下数据 1 到 10 可以继续被客户端提取程序所提取而数据 111213 则丢失了

对于尚未推送到手持设备端的数据如数据 1415假设因为网络中断没有被推送到

手持设备端则仍保留在服务器端当网络恢复后数据 1415 仍可以被正常处理

对于确认消息(图中橙色圆形所示)的处理则更为复杂在异步确认消息一节中再作讨

从这一节的讨论得出的结果是数据到达手持设备端未必就能够被客户端提取程序提取

客户端提取程序有职责尽快提取数据防止推送数据的溢出溢出的数据在客户端无法再被

处理而且服务器端也不会尝试继续推送这些数据

异步确认消息(应用依赖和非应用依赖)

手持设备端在接收到推送数据后会通知 MDSBES 服务器这时 MDSBES 服务器会根据

最初的推送请求向指定的 URL 发送一个确认消息告知应用服务器某一条推送数据的最终

结果这就是异步确认消息异步确认消息以 http 协议发送给应用服务器消息 200 代表

数据推送成功400 代表数据推送失败

异步确认消息是应用服务器判断推送是否成功的有效工具但是很多开发人员因为对

异步确认消息的机制不了解错误地使用了异步确认消息从而导致数据的丢失所以我们

需要详细了解异步确认消息的工作机制

要讨论异步确认消息的工作机制必然涉及到应用依赖与非应用依赖应用依赖的推送

和非应用依赖的推送对应的异步确认消息工作机制是不同的

下面先讨论非应用依赖的场景这也是很多开发商使用的场景

如果推送是非应用依赖的数据是以到达手持设备为成功标志的只要数据到达了手持

设备MDSBES 服务器就认为该数据推送成功会给应用服务器发送异步确认消息 200

如果数据推送时手持设备不在线则 10 分钟内 MDSBES 服务器会在服务器上保留该数

据如果 10 分钟内手持设备重新接入网络则 MDSBES 服务器会尝试发送该数据一旦数

据到达手持设备MDSBES 服务器都会给应用服务器发送异步确认消息 200

如果 10 分钟内手设备没有接入网络则该推送数据过期MDSBES 服务器会丢弃该数

据同时给应用服务器发送异步确认消息 400

可以看出非应用依赖的推送方式并不能保证客户端应用程序接收到数据在非应用依

赖的推送方式下 MDSBES 返回 200 确认消息也不能认为客户端已接收到数据如果数据送

达手持设备的时候客户端应用程序没有启动所推送的数据会在手持设备端丢失而

MDSBES 服务器认为数据已成功送达或者是客户端应用程序已启动但是没有及时读取推

送数据在手持设备端溢出的数据也会丢失MDSBES 服务器同样认为数据已成功送达

所以在非应用依赖的情况下如果接收到 400 确认消息可以确定客户端没有收到数据

如果接收到 200 确认消息只能认为客户端可能收到了数据

从以上的讨论看非应用依赖的推送方式并不适合用于可靠性要求较高的应用对于可

靠性要求较高的应用需要知道客户端应用程序是否接收到了数据应用依赖的推送方式就

是为此而设计的如果推送是应用依赖的数据是以客户端应用程序确认为成功标志的只

要在应用程序确认后MDSBES 服务器才认为数据推送成功这时才给应用服务器发送异步

确认消息 200

有关客户端应用程序如何确认收到数据在ldquo客户端接收关键代码rdquo一节中有描述简

单而言就是客户端应用程序通过调用 MDSPushInputStream 实例的 accept 函数确认数据收

到如果客户端应用程序在接收到数据后没有调用 accept 函数MDSBES 服务器会认为数

据推送失败给应用服务器发送异步确认消息 400另外一点曾经强调过的是accept 函数

调用后只有 InputStream 和 StreamConnection 都关闭了确认消息才被发送给 MDSBES 服

务器在极端的情况下这一点会影响到异步确认消息的结果

下面具体分析各种情景

如果手持设备在线但是客户端应用程序没有启动推送数据到达手持设备端后没有程

序侦听推送端口当然也没有程序会调用 accept 函数此时 MDSBES 服务器会认为数据推

送失败向应用服务器发送异步确认消息 400

如果手持设备在线客户端应用程序在读取推送数据后没有调用 accept 函数直接关闭

连接此时 MDSBES 服务器同样会认为数据推送失败向应用服务器发送异步确认消息 400

在这种情景下即使客户端接收到了数据但应用服务器仍收到失败消息因为作为成功标

志的 accept 函数并没有调用

如果手持设备在线客户端应用程序在读取推送数据后调用了 accept 函数并关闭连

接此时 MDSBES 服务器会认为数据推送成功向应用服务器发送异步确认消息 200

对于手持设备不在线的情况结果与非应用依赖相似10 分钟内如果手持设备不接入

网络的话推送数据会在 MDSBES 服务器上过期MDSBES 服务器会向应用服务器发送异

步确认消息 400

如果 10 分钟内手持设备重新接入网络MDSBES 服务器会尝试发送数据至手持设备端

还是以 accept 函数的调用为推送成功的标志如果数据重新发送成功但是客户端没有调

用 accept 函数则 MDSBES 服务器会向应用服务器发送异步确认消息 200

对于推送到手持设备端但是溢出的数据因为没有程序能够读取该数据并针对该数据调

用 accept 函数MDSBES 服务器会一直等待到 10 分钟后向应用服务器发送异步确认消息

400认为数据过期

一个特殊的情况是客户端应用程序在接收到数据后调用了 accept但是没有关闭

InputStream 和 StreamConnection此时 MDSBES 服务器会认识客户端还在处理数据会继

续等待如果服务器等待时候超过 10 分钟MDSBES 服务器会认为数据推送失败向应用

服务器发送异步确认消息 400如果在 10 分钟内客户端主动关闭 InputStream 和

StreamConnection或者是网络异常中断MDSBES 服务器会认为数据推送成功向应用服

务器发送异步确认消息 200

下面再讨论一个异常情况下的特例

结合以上描述的各种因素在极端的情况下会出现一些特殊的情况如上图所示假设

MDSBES 服务器在推送了数据 R数据 1 到数据 13 后出现网络中断同时客户端应用程序

正在缓慢地读取数据 R此时数据 1 到数据 10 会被保留在手持设备队列中客户端应用程

序在读取数据 R 后可以继续读取数据 1 到数据 10如果客户端应用程序在读取了数据后都

调用了 accept 函数则会产生对应用确认消息如图中的确认消息 R确认消息 1 到确认消

息 10但是因为网络的中断这些确认消息都无法到达 MDSBES 服务器如果网络在 10

钟内没有恢复MDSBES 服务器上所有等待确认消息的记录都会认识是过期的MDSBES

会向应用服务器发送异步确认消息 400此后即使网络恢复确认消息 R确认消息 1 到确

认消息 10 都不再有效

在这种特殊情况下即使客户端应用程序接收到数据并调用了 accept 函数MDSBES

服务器同样认为数据没有推送成功

根据以上的讨论应用依赖的推送同样不能保证客户端应用一定能收到数据但是在

应用依赖的推送方式下 MDSBES 返回 200 确认消息可以确定客户端已接收到数据因为 200

确认消息是由客户端应用程序调用 accept 函数触发的客户端应用程序可以在确认数据收

到后才调用 accept 函数通知应用服务器该数据已成功接收甚至可以在接收到数据后根

据数据内容决定不调用 accept 函数从而通过应用服务器该数据没有被接收

所以在应用依赖的情况下如果接收到 200 确认消息可以确定客户端已经接收到数据

如果接收到 400 确认消息只能认为客户端可能没有收到数据

因此应用依赖的推送方式更适合可靠性要求高的应用如果应用服务器接收到 200

确认消息就可以认为推送成功继续处理其它事务如果应用服务器接收到 400 确认消息则

需要组织数据重发直到接收到 200 确认消息为止这样就可以保证客户端一定能够接收到

需要推送的数据

如何保证数据推送的可靠性

通过以上几个章节的讨论我们知道虽然 MDSBES 服务器提供了很多机制保证数据

推送的可靠性但是都无法完全保证数据被客户端应用程序所接收

对于数据推送的可靠性保证一方面开发人员需要了解各个可能导致故障的因素在应

用编写的时候尽量减少故障出现的可能性对故障点进行规避另一方面开发人员也需要实

现额外的机制进行数据重发在故障点无法规避的情况下通过数据重发消除故障点的影响

应用依赖和非应用依赖的比较和选择

在实现推送应用的时候首要考虑的是有关应用依赖与非应用依赖的选择

应用依赖的推送形式在可靠性方面更优于非应用依赖的推送形式但是应用依赖的推送

形式对 MDSBES 应用服务器的潜在压力较大客户端应用程序的实现也较为复杂

非应用依赖的推送形式优势在于实现简单对 MDSBES 应用服务器形成的压力相对较

非应用依赖的推送形式从某种程度上讲有点类似于广播协议着重点是从服务器向客户

端发送大量数据并不看重客户端的接收情况从数据的应用类型来分非应用依赖的推送

更适合于天气信息最新报价等数据这些数据在客户端应用关闭时被自然丢弃短时间内

的多条数据有丢失影响也不大必竞用户不会关注 11 点零六分的天气和 11 点零七分的天气

有何不同

应用依赖的推送形式则类似于点对点协议不仅从服务器端向客户端发送数据同时也

关注客户端的接收情况应用依赖的推送更适合于 OAERP 这种应用这种应用需要保证

新的文档或者是记录一定能到达客户端应用丢失一条待办信息可能就会导致整个审批流程

的停滞

再从两种推送方式的本质差别看应用依赖并没有额外的机制消除故障点它的优势在

于它的异步确认消息更可信所以在选择了应用依赖的推送方式后进一步还是需要实现

额外的数据重发机制根据 MDSBES 所发送的异步确认消息实施数据重发

应用依赖的推送再加上结合异步确认消息的数据重发机制是保证推送数据可靠性的

最好方法

MDS 接收线程限制的处理

不管使用什么方式的推送应用开发时都需要考虑到 MDS 接收线程的限制不过以

现在的服务器处理能力正常情况下的 HTTP 推送请求可以在几毫秒或者是几十毫内处理完

成少量的 HTTP 推送不足于构成网络阻塞

需要考虑的是应用服务器端的推送程序在推送过程中不要在建立连接后才动态组织数

据这样会浪费 http 连接资源好的建议是预先组织好数据在建立 HTTP 连接后快速写完

数据尽快关闭连接

此外对于应用服务器的推送线程也要加于控制避免在短时间内无限制地启动新的线

程进行连接否则也容易造成连接过多而阻塞的情况

从网络层面来看调整应用服务器与 MDSBES 服务器之间的网络超时设置也是避免这

种问题的一种方法在允许的范围内调大网络超时时间可以让应用服务器的推送线程等待更

长的时间从而让 MDSBES 有足够的时间释放原有的连接

最后无论使用了什么方法调优都需要对应用服务器的推送程序进行异常处理当发

现网络超时异常的时候可以组织数据重发当然数据重发的间隔时间也要考虑不要因为

重发数据再次造成网络阻塞

MDS 推送队列限制的处理

相对而言MDS 推送队列限制造成的推送失败比 MDS 接收队列造成的推送失败更常见

已经发现的一个实际案例是周未的时候用户大量关机导致大量推送数据处于推送队列中

填满了推送队列从而导致 MDSBES 服务器不再响应新的推送请求一旦过期时间到达

有新的队列位置空闲又有大量新的推送数据重新占满推送队列

对于 MDS 推送队列的限制直观的做法是对队列参数进行调整如之前讨论的管理

员可以对队列的深度和过期时间进行调整

然而出于对过期时间的不正确理解管理员倾向于将过期时间调大当过期时间被调

大后推送给关机用户的数据在服务器占用队列的时间就变长导致 MDS 推送队列更容易

被占满

所以如果对过期时间进行调整的话方向是将过期时间调小过期时间越小等待数

据占用队列的时间就越短MDS 推送队列被占满的可能性就越小但是过期时间也不能

过小过期时间过小可能会导致过多的过期数据使应用服务器需要处理更多的数据重发

比如将过期时间调成 1 分钟用户走过一个人行遂道都可能导致数据发送失败

对于队列深度的调整方向是调大队列深度如从缺省的 1000 调整到 2000此时

MDSBES 服务器在同一时间可以保留更多的等待数据减少了队列占满的机会不过调大队

列深度会让 MDSBES 服务器保留更多的数据从而占用更多的服务器内存对服务器产生

更大的压力所以调用队列深度需要根据服务器的运行能力进行调用

从应用服务器设计角度应用 MDS 队列限制的方法是尽量减少等待数据的发送如发

送第一条数据的时候发现数据过期对于该用户的其它数据可以调整发送时间在更长的时

间间隔后才尝试发送

对于 MDS 队列限制应用服务器端的推送应用需要对 MDSBES 服务器返回的 503 错误

进行处理对于出现 503 错的数据组织重发同样重发的间隔也需要考虑不要因为数据

重发占用更多的推送队列也可以根据目标用户进行判断对于可能关机的用户暂时不重发

数据

手持设备端接收队列的处理

手持设备端接收队列并未从文档中明确列出是根据测试结果分析得出的手持设备端

目前也没有发现设置可以调整该队列所以客户端程序设计的时候不应该依赖手持设备端队

开发人员需要意识到的是手持设备端的接收队列是可溢出队列一旦推送数据从接收队

列出溢出客户端程序就无法提取该数据了所以在客户端应用开始过程中要注意加快推送

数据的提取过程在提取过程中不要对数据进行复杂的计算提取完成后尽快读取下一条数

据对于接收数据的处理和计算可以交由另一个线程完成

因为溢出数据对客户端应用而言不可见所以数据溢出时在客户端应用中也不会有异

常开发人员需要通过服务器返回的过期消息才能知道该数据推送失败

对于这一点客户端应用除了尽量避免数据溢出外无法在客户端通过其它机制进行补

应用依赖的客户端

对于应用依赖的客户端应用关键点是决定何时调用 accept 函数同时保证 InputStream

和 StreamConnection 被关闭

如之前讨论的使用应用依赖的推送关键是希望使用 MDSBES 发送给应用服务器的异

步确认消息从而对数据是否到达进行判断所以应用依赖的客户端应用需要保证正确调用

accept 函数

另外如果 InputStream 和 StreamConnection 没有关闭的话accept 函数所发出的确认

消息无法被 MDSBES 服务器收到开发人员在代码中应该主动关闭 InputStream 和

StreamConnection同时在程序发生异常时也需要在 catch 代码段对 InputStream 和

StreamConnection 进行关闭处理

异步确认消息处理

异步确认消息用于判断数据是否被成功推送至客户端应用要使用异步确认消息首先

要保证成功地无遗漏地接收所有从 MDSBES 服务器发送过来的异步确认消息

异步确认消息是一个 HTTP 消息消息比较短接收过程的并发压力不大下面是一个

异步确认消息的样例

Received notificationGET HTTP11

User-Agent RIM MDSCS

Accept

X-RIM-Push-ID pushID2081773768

X-RIM-Push-Destination mmtestbjsearbcn

X-RIM-Push-Status 400

Host localhost7778

其中最关键是的获取属性 X-RIM-Push-Status根据该属性的值判断数据推送是成功

(200)还是失败(400)当然属性 X-RIM-Push-ID 也非常重要用于确定该确认消息是针

对哪条推送数据的

如果应用服务器端通过确认消息发现数据推送失败则需要根据属性 X-RIM-Push-ID 重

新组织数据进行重发

Page 21: BES 服务器推送机制分析 - images.csdn.netimages.csdn.net/upimgs/lee/BBPDF/BESfwtsjzfx.pdf · 前言 数据推送是BlackBerry 应用平台的一大优势,在BlackBerry 应用平台上部署的应用可以

手持设备端则仍保留在服务器端当网络恢复后数据 1415 仍可以被正常处理

对于确认消息(图中橙色圆形所示)的处理则更为复杂在异步确认消息一节中再作讨

从这一节的讨论得出的结果是数据到达手持设备端未必就能够被客户端提取程序提取

客户端提取程序有职责尽快提取数据防止推送数据的溢出溢出的数据在客户端无法再被

处理而且服务器端也不会尝试继续推送这些数据

异步确认消息(应用依赖和非应用依赖)

手持设备端在接收到推送数据后会通知 MDSBES 服务器这时 MDSBES 服务器会根据

最初的推送请求向指定的 URL 发送一个确认消息告知应用服务器某一条推送数据的最终

结果这就是异步确认消息异步确认消息以 http 协议发送给应用服务器消息 200 代表

数据推送成功400 代表数据推送失败

异步确认消息是应用服务器判断推送是否成功的有效工具但是很多开发人员因为对

异步确认消息的机制不了解错误地使用了异步确认消息从而导致数据的丢失所以我们

需要详细了解异步确认消息的工作机制

要讨论异步确认消息的工作机制必然涉及到应用依赖与非应用依赖应用依赖的推送

和非应用依赖的推送对应的异步确认消息工作机制是不同的

下面先讨论非应用依赖的场景这也是很多开发商使用的场景

如果推送是非应用依赖的数据是以到达手持设备为成功标志的只要数据到达了手持

设备MDSBES 服务器就认为该数据推送成功会给应用服务器发送异步确认消息 200

如果数据推送时手持设备不在线则 10 分钟内 MDSBES 服务器会在服务器上保留该数

据如果 10 分钟内手持设备重新接入网络则 MDSBES 服务器会尝试发送该数据一旦数

据到达手持设备MDSBES 服务器都会给应用服务器发送异步确认消息 200

如果 10 分钟内手设备没有接入网络则该推送数据过期MDSBES 服务器会丢弃该数

据同时给应用服务器发送异步确认消息 400

可以看出非应用依赖的推送方式并不能保证客户端应用程序接收到数据在非应用依

赖的推送方式下 MDSBES 返回 200 确认消息也不能认为客户端已接收到数据如果数据送

达手持设备的时候客户端应用程序没有启动所推送的数据会在手持设备端丢失而

MDSBES 服务器认为数据已成功送达或者是客户端应用程序已启动但是没有及时读取推

送数据在手持设备端溢出的数据也会丢失MDSBES 服务器同样认为数据已成功送达

所以在非应用依赖的情况下如果接收到 400 确认消息可以确定客户端没有收到数据

如果接收到 200 确认消息只能认为客户端可能收到了数据

从以上的讨论看非应用依赖的推送方式并不适合用于可靠性要求较高的应用对于可

靠性要求较高的应用需要知道客户端应用程序是否接收到了数据应用依赖的推送方式就

是为此而设计的如果推送是应用依赖的数据是以客户端应用程序确认为成功标志的只

要在应用程序确认后MDSBES 服务器才认为数据推送成功这时才给应用服务器发送异步

确认消息 200

有关客户端应用程序如何确认收到数据在ldquo客户端接收关键代码rdquo一节中有描述简

单而言就是客户端应用程序通过调用 MDSPushInputStream 实例的 accept 函数确认数据收

到如果客户端应用程序在接收到数据后没有调用 accept 函数MDSBES 服务器会认为数

据推送失败给应用服务器发送异步确认消息 400另外一点曾经强调过的是accept 函数

调用后只有 InputStream 和 StreamConnection 都关闭了确认消息才被发送给 MDSBES 服

务器在极端的情况下这一点会影响到异步确认消息的结果

下面具体分析各种情景

如果手持设备在线但是客户端应用程序没有启动推送数据到达手持设备端后没有程

序侦听推送端口当然也没有程序会调用 accept 函数此时 MDSBES 服务器会认为数据推

送失败向应用服务器发送异步确认消息 400

如果手持设备在线客户端应用程序在读取推送数据后没有调用 accept 函数直接关闭

连接此时 MDSBES 服务器同样会认为数据推送失败向应用服务器发送异步确认消息 400

在这种情景下即使客户端接收到了数据但应用服务器仍收到失败消息因为作为成功标

志的 accept 函数并没有调用

如果手持设备在线客户端应用程序在读取推送数据后调用了 accept 函数并关闭连

接此时 MDSBES 服务器会认为数据推送成功向应用服务器发送异步确认消息 200

对于手持设备不在线的情况结果与非应用依赖相似10 分钟内如果手持设备不接入

网络的话推送数据会在 MDSBES 服务器上过期MDSBES 服务器会向应用服务器发送异

步确认消息 400

如果 10 分钟内手持设备重新接入网络MDSBES 服务器会尝试发送数据至手持设备端

还是以 accept 函数的调用为推送成功的标志如果数据重新发送成功但是客户端没有调

用 accept 函数则 MDSBES 服务器会向应用服务器发送异步确认消息 200

对于推送到手持设备端但是溢出的数据因为没有程序能够读取该数据并针对该数据调

用 accept 函数MDSBES 服务器会一直等待到 10 分钟后向应用服务器发送异步确认消息

400认为数据过期

一个特殊的情况是客户端应用程序在接收到数据后调用了 accept但是没有关闭

InputStream 和 StreamConnection此时 MDSBES 服务器会认识客户端还在处理数据会继

续等待如果服务器等待时候超过 10 分钟MDSBES 服务器会认为数据推送失败向应用

服务器发送异步确认消息 400如果在 10 分钟内客户端主动关闭 InputStream 和

StreamConnection或者是网络异常中断MDSBES 服务器会认为数据推送成功向应用服

务器发送异步确认消息 200

下面再讨论一个异常情况下的特例

结合以上描述的各种因素在极端的情况下会出现一些特殊的情况如上图所示假设

MDSBES 服务器在推送了数据 R数据 1 到数据 13 后出现网络中断同时客户端应用程序

正在缓慢地读取数据 R此时数据 1 到数据 10 会被保留在手持设备队列中客户端应用程

序在读取数据 R 后可以继续读取数据 1 到数据 10如果客户端应用程序在读取了数据后都

调用了 accept 函数则会产生对应用确认消息如图中的确认消息 R确认消息 1 到确认消

息 10但是因为网络的中断这些确认消息都无法到达 MDSBES 服务器如果网络在 10

钟内没有恢复MDSBES 服务器上所有等待确认消息的记录都会认识是过期的MDSBES

会向应用服务器发送异步确认消息 400此后即使网络恢复确认消息 R确认消息 1 到确

认消息 10 都不再有效

在这种特殊情况下即使客户端应用程序接收到数据并调用了 accept 函数MDSBES

服务器同样认为数据没有推送成功

根据以上的讨论应用依赖的推送同样不能保证客户端应用一定能收到数据但是在

应用依赖的推送方式下 MDSBES 返回 200 确认消息可以确定客户端已接收到数据因为 200

确认消息是由客户端应用程序调用 accept 函数触发的客户端应用程序可以在确认数据收

到后才调用 accept 函数通知应用服务器该数据已成功接收甚至可以在接收到数据后根

据数据内容决定不调用 accept 函数从而通过应用服务器该数据没有被接收

所以在应用依赖的情况下如果接收到 200 确认消息可以确定客户端已经接收到数据

如果接收到 400 确认消息只能认为客户端可能没有收到数据

因此应用依赖的推送方式更适合可靠性要求高的应用如果应用服务器接收到 200

确认消息就可以认为推送成功继续处理其它事务如果应用服务器接收到 400 确认消息则

需要组织数据重发直到接收到 200 确认消息为止这样就可以保证客户端一定能够接收到

需要推送的数据

如何保证数据推送的可靠性

通过以上几个章节的讨论我们知道虽然 MDSBES 服务器提供了很多机制保证数据

推送的可靠性但是都无法完全保证数据被客户端应用程序所接收

对于数据推送的可靠性保证一方面开发人员需要了解各个可能导致故障的因素在应

用编写的时候尽量减少故障出现的可能性对故障点进行规避另一方面开发人员也需要实

现额外的机制进行数据重发在故障点无法规避的情况下通过数据重发消除故障点的影响

应用依赖和非应用依赖的比较和选择

在实现推送应用的时候首要考虑的是有关应用依赖与非应用依赖的选择

应用依赖的推送形式在可靠性方面更优于非应用依赖的推送形式但是应用依赖的推送

形式对 MDSBES 应用服务器的潜在压力较大客户端应用程序的实现也较为复杂

非应用依赖的推送形式优势在于实现简单对 MDSBES 应用服务器形成的压力相对较

非应用依赖的推送形式从某种程度上讲有点类似于广播协议着重点是从服务器向客户

端发送大量数据并不看重客户端的接收情况从数据的应用类型来分非应用依赖的推送

更适合于天气信息最新报价等数据这些数据在客户端应用关闭时被自然丢弃短时间内

的多条数据有丢失影响也不大必竞用户不会关注 11 点零六分的天气和 11 点零七分的天气

有何不同

应用依赖的推送形式则类似于点对点协议不仅从服务器端向客户端发送数据同时也

关注客户端的接收情况应用依赖的推送更适合于 OAERP 这种应用这种应用需要保证

新的文档或者是记录一定能到达客户端应用丢失一条待办信息可能就会导致整个审批流程

的停滞

再从两种推送方式的本质差别看应用依赖并没有额外的机制消除故障点它的优势在

于它的异步确认消息更可信所以在选择了应用依赖的推送方式后进一步还是需要实现

额外的数据重发机制根据 MDSBES 所发送的异步确认消息实施数据重发

应用依赖的推送再加上结合异步确认消息的数据重发机制是保证推送数据可靠性的

最好方法

MDS 接收线程限制的处理

不管使用什么方式的推送应用开发时都需要考虑到 MDS 接收线程的限制不过以

现在的服务器处理能力正常情况下的 HTTP 推送请求可以在几毫秒或者是几十毫内处理完

成少量的 HTTP 推送不足于构成网络阻塞

需要考虑的是应用服务器端的推送程序在推送过程中不要在建立连接后才动态组织数

据这样会浪费 http 连接资源好的建议是预先组织好数据在建立 HTTP 连接后快速写完

数据尽快关闭连接

此外对于应用服务器的推送线程也要加于控制避免在短时间内无限制地启动新的线

程进行连接否则也容易造成连接过多而阻塞的情况

从网络层面来看调整应用服务器与 MDSBES 服务器之间的网络超时设置也是避免这

种问题的一种方法在允许的范围内调大网络超时时间可以让应用服务器的推送线程等待更

长的时间从而让 MDSBES 有足够的时间释放原有的连接

最后无论使用了什么方法调优都需要对应用服务器的推送程序进行异常处理当发

现网络超时异常的时候可以组织数据重发当然数据重发的间隔时间也要考虑不要因为

重发数据再次造成网络阻塞

MDS 推送队列限制的处理

相对而言MDS 推送队列限制造成的推送失败比 MDS 接收队列造成的推送失败更常见

已经发现的一个实际案例是周未的时候用户大量关机导致大量推送数据处于推送队列中

填满了推送队列从而导致 MDSBES 服务器不再响应新的推送请求一旦过期时间到达

有新的队列位置空闲又有大量新的推送数据重新占满推送队列

对于 MDS 推送队列的限制直观的做法是对队列参数进行调整如之前讨论的管理

员可以对队列的深度和过期时间进行调整

然而出于对过期时间的不正确理解管理员倾向于将过期时间调大当过期时间被调

大后推送给关机用户的数据在服务器占用队列的时间就变长导致 MDS 推送队列更容易

被占满

所以如果对过期时间进行调整的话方向是将过期时间调小过期时间越小等待数

据占用队列的时间就越短MDS 推送队列被占满的可能性就越小但是过期时间也不能

过小过期时间过小可能会导致过多的过期数据使应用服务器需要处理更多的数据重发

比如将过期时间调成 1 分钟用户走过一个人行遂道都可能导致数据发送失败

对于队列深度的调整方向是调大队列深度如从缺省的 1000 调整到 2000此时

MDSBES 服务器在同一时间可以保留更多的等待数据减少了队列占满的机会不过调大队

列深度会让 MDSBES 服务器保留更多的数据从而占用更多的服务器内存对服务器产生

更大的压力所以调用队列深度需要根据服务器的运行能力进行调用

从应用服务器设计角度应用 MDS 队列限制的方法是尽量减少等待数据的发送如发

送第一条数据的时候发现数据过期对于该用户的其它数据可以调整发送时间在更长的时

间间隔后才尝试发送

对于 MDS 队列限制应用服务器端的推送应用需要对 MDSBES 服务器返回的 503 错误

进行处理对于出现 503 错的数据组织重发同样重发的间隔也需要考虑不要因为数据

重发占用更多的推送队列也可以根据目标用户进行判断对于可能关机的用户暂时不重发

数据

手持设备端接收队列的处理

手持设备端接收队列并未从文档中明确列出是根据测试结果分析得出的手持设备端

目前也没有发现设置可以调整该队列所以客户端程序设计的时候不应该依赖手持设备端队

开发人员需要意识到的是手持设备端的接收队列是可溢出队列一旦推送数据从接收队

列出溢出客户端程序就无法提取该数据了所以在客户端应用开始过程中要注意加快推送

数据的提取过程在提取过程中不要对数据进行复杂的计算提取完成后尽快读取下一条数

据对于接收数据的处理和计算可以交由另一个线程完成

因为溢出数据对客户端应用而言不可见所以数据溢出时在客户端应用中也不会有异

常开发人员需要通过服务器返回的过期消息才能知道该数据推送失败

对于这一点客户端应用除了尽量避免数据溢出外无法在客户端通过其它机制进行补

应用依赖的客户端

对于应用依赖的客户端应用关键点是决定何时调用 accept 函数同时保证 InputStream

和 StreamConnection 被关闭

如之前讨论的使用应用依赖的推送关键是希望使用 MDSBES 发送给应用服务器的异

步确认消息从而对数据是否到达进行判断所以应用依赖的客户端应用需要保证正确调用

accept 函数

另外如果 InputStream 和 StreamConnection 没有关闭的话accept 函数所发出的确认

消息无法被 MDSBES 服务器收到开发人员在代码中应该主动关闭 InputStream 和

StreamConnection同时在程序发生异常时也需要在 catch 代码段对 InputStream 和

StreamConnection 进行关闭处理

异步确认消息处理

异步确认消息用于判断数据是否被成功推送至客户端应用要使用异步确认消息首先

要保证成功地无遗漏地接收所有从 MDSBES 服务器发送过来的异步确认消息

异步确认消息是一个 HTTP 消息消息比较短接收过程的并发压力不大下面是一个

异步确认消息的样例

Received notificationGET HTTP11

User-Agent RIM MDSCS

Accept

X-RIM-Push-ID pushID2081773768

X-RIM-Push-Destination mmtestbjsearbcn

X-RIM-Push-Status 400

Host localhost7778

其中最关键是的获取属性 X-RIM-Push-Status根据该属性的值判断数据推送是成功

(200)还是失败(400)当然属性 X-RIM-Push-ID 也非常重要用于确定该确认消息是针

对哪条推送数据的

如果应用服务器端通过确认消息发现数据推送失败则需要根据属性 X-RIM-Push-ID 重

新组织数据进行重发

Page 22: BES 服务器推送机制分析 - images.csdn.netimages.csdn.net/upimgs/lee/BBPDF/BESfwtsjzfx.pdf · 前言 数据推送是BlackBerry 应用平台的一大优势,在BlackBerry 应用平台上部署的应用可以

据推送失败给应用服务器发送异步确认消息 400另外一点曾经强调过的是accept 函数

调用后只有 InputStream 和 StreamConnection 都关闭了确认消息才被发送给 MDSBES 服

务器在极端的情况下这一点会影响到异步确认消息的结果

下面具体分析各种情景

如果手持设备在线但是客户端应用程序没有启动推送数据到达手持设备端后没有程

序侦听推送端口当然也没有程序会调用 accept 函数此时 MDSBES 服务器会认为数据推

送失败向应用服务器发送异步确认消息 400

如果手持设备在线客户端应用程序在读取推送数据后没有调用 accept 函数直接关闭

连接此时 MDSBES 服务器同样会认为数据推送失败向应用服务器发送异步确认消息 400

在这种情景下即使客户端接收到了数据但应用服务器仍收到失败消息因为作为成功标

志的 accept 函数并没有调用

如果手持设备在线客户端应用程序在读取推送数据后调用了 accept 函数并关闭连

接此时 MDSBES 服务器会认为数据推送成功向应用服务器发送异步确认消息 200

对于手持设备不在线的情况结果与非应用依赖相似10 分钟内如果手持设备不接入

网络的话推送数据会在 MDSBES 服务器上过期MDSBES 服务器会向应用服务器发送异

步确认消息 400

如果 10 分钟内手持设备重新接入网络MDSBES 服务器会尝试发送数据至手持设备端

还是以 accept 函数的调用为推送成功的标志如果数据重新发送成功但是客户端没有调

用 accept 函数则 MDSBES 服务器会向应用服务器发送异步确认消息 200

对于推送到手持设备端但是溢出的数据因为没有程序能够读取该数据并针对该数据调

用 accept 函数MDSBES 服务器会一直等待到 10 分钟后向应用服务器发送异步确认消息

400认为数据过期

一个特殊的情况是客户端应用程序在接收到数据后调用了 accept但是没有关闭

InputStream 和 StreamConnection此时 MDSBES 服务器会认识客户端还在处理数据会继

续等待如果服务器等待时候超过 10 分钟MDSBES 服务器会认为数据推送失败向应用

服务器发送异步确认消息 400如果在 10 分钟内客户端主动关闭 InputStream 和

StreamConnection或者是网络异常中断MDSBES 服务器会认为数据推送成功向应用服

务器发送异步确认消息 200

下面再讨论一个异常情况下的特例

结合以上描述的各种因素在极端的情况下会出现一些特殊的情况如上图所示假设

MDSBES 服务器在推送了数据 R数据 1 到数据 13 后出现网络中断同时客户端应用程序

正在缓慢地读取数据 R此时数据 1 到数据 10 会被保留在手持设备队列中客户端应用程

序在读取数据 R 后可以继续读取数据 1 到数据 10如果客户端应用程序在读取了数据后都

调用了 accept 函数则会产生对应用确认消息如图中的确认消息 R确认消息 1 到确认消

息 10但是因为网络的中断这些确认消息都无法到达 MDSBES 服务器如果网络在 10

钟内没有恢复MDSBES 服务器上所有等待确认消息的记录都会认识是过期的MDSBES

会向应用服务器发送异步确认消息 400此后即使网络恢复确认消息 R确认消息 1 到确

认消息 10 都不再有效

在这种特殊情况下即使客户端应用程序接收到数据并调用了 accept 函数MDSBES

服务器同样认为数据没有推送成功

根据以上的讨论应用依赖的推送同样不能保证客户端应用一定能收到数据但是在

应用依赖的推送方式下 MDSBES 返回 200 确认消息可以确定客户端已接收到数据因为 200

确认消息是由客户端应用程序调用 accept 函数触发的客户端应用程序可以在确认数据收

到后才调用 accept 函数通知应用服务器该数据已成功接收甚至可以在接收到数据后根

据数据内容决定不调用 accept 函数从而通过应用服务器该数据没有被接收

所以在应用依赖的情况下如果接收到 200 确认消息可以确定客户端已经接收到数据

如果接收到 400 确认消息只能认为客户端可能没有收到数据

因此应用依赖的推送方式更适合可靠性要求高的应用如果应用服务器接收到 200

确认消息就可以认为推送成功继续处理其它事务如果应用服务器接收到 400 确认消息则

需要组织数据重发直到接收到 200 确认消息为止这样就可以保证客户端一定能够接收到

需要推送的数据

如何保证数据推送的可靠性

通过以上几个章节的讨论我们知道虽然 MDSBES 服务器提供了很多机制保证数据

推送的可靠性但是都无法完全保证数据被客户端应用程序所接收

对于数据推送的可靠性保证一方面开发人员需要了解各个可能导致故障的因素在应

用编写的时候尽量减少故障出现的可能性对故障点进行规避另一方面开发人员也需要实

现额外的机制进行数据重发在故障点无法规避的情况下通过数据重发消除故障点的影响

应用依赖和非应用依赖的比较和选择

在实现推送应用的时候首要考虑的是有关应用依赖与非应用依赖的选择

应用依赖的推送形式在可靠性方面更优于非应用依赖的推送形式但是应用依赖的推送

形式对 MDSBES 应用服务器的潜在压力较大客户端应用程序的实现也较为复杂

非应用依赖的推送形式优势在于实现简单对 MDSBES 应用服务器形成的压力相对较

非应用依赖的推送形式从某种程度上讲有点类似于广播协议着重点是从服务器向客户

端发送大量数据并不看重客户端的接收情况从数据的应用类型来分非应用依赖的推送

更适合于天气信息最新报价等数据这些数据在客户端应用关闭时被自然丢弃短时间内

的多条数据有丢失影响也不大必竞用户不会关注 11 点零六分的天气和 11 点零七分的天气

有何不同

应用依赖的推送形式则类似于点对点协议不仅从服务器端向客户端发送数据同时也

关注客户端的接收情况应用依赖的推送更适合于 OAERP 这种应用这种应用需要保证

新的文档或者是记录一定能到达客户端应用丢失一条待办信息可能就会导致整个审批流程

的停滞

再从两种推送方式的本质差别看应用依赖并没有额外的机制消除故障点它的优势在

于它的异步确认消息更可信所以在选择了应用依赖的推送方式后进一步还是需要实现

额外的数据重发机制根据 MDSBES 所发送的异步确认消息实施数据重发

应用依赖的推送再加上结合异步确认消息的数据重发机制是保证推送数据可靠性的

最好方法

MDS 接收线程限制的处理

不管使用什么方式的推送应用开发时都需要考虑到 MDS 接收线程的限制不过以

现在的服务器处理能力正常情况下的 HTTP 推送请求可以在几毫秒或者是几十毫内处理完

成少量的 HTTP 推送不足于构成网络阻塞

需要考虑的是应用服务器端的推送程序在推送过程中不要在建立连接后才动态组织数

据这样会浪费 http 连接资源好的建议是预先组织好数据在建立 HTTP 连接后快速写完

数据尽快关闭连接

此外对于应用服务器的推送线程也要加于控制避免在短时间内无限制地启动新的线

程进行连接否则也容易造成连接过多而阻塞的情况

从网络层面来看调整应用服务器与 MDSBES 服务器之间的网络超时设置也是避免这

种问题的一种方法在允许的范围内调大网络超时时间可以让应用服务器的推送线程等待更

长的时间从而让 MDSBES 有足够的时间释放原有的连接

最后无论使用了什么方法调优都需要对应用服务器的推送程序进行异常处理当发

现网络超时异常的时候可以组织数据重发当然数据重发的间隔时间也要考虑不要因为

重发数据再次造成网络阻塞

MDS 推送队列限制的处理

相对而言MDS 推送队列限制造成的推送失败比 MDS 接收队列造成的推送失败更常见

已经发现的一个实际案例是周未的时候用户大量关机导致大量推送数据处于推送队列中

填满了推送队列从而导致 MDSBES 服务器不再响应新的推送请求一旦过期时间到达

有新的队列位置空闲又有大量新的推送数据重新占满推送队列

对于 MDS 推送队列的限制直观的做法是对队列参数进行调整如之前讨论的管理

员可以对队列的深度和过期时间进行调整

然而出于对过期时间的不正确理解管理员倾向于将过期时间调大当过期时间被调

大后推送给关机用户的数据在服务器占用队列的时间就变长导致 MDS 推送队列更容易

被占满

所以如果对过期时间进行调整的话方向是将过期时间调小过期时间越小等待数

据占用队列的时间就越短MDS 推送队列被占满的可能性就越小但是过期时间也不能

过小过期时间过小可能会导致过多的过期数据使应用服务器需要处理更多的数据重发

比如将过期时间调成 1 分钟用户走过一个人行遂道都可能导致数据发送失败

对于队列深度的调整方向是调大队列深度如从缺省的 1000 调整到 2000此时

MDSBES 服务器在同一时间可以保留更多的等待数据减少了队列占满的机会不过调大队

列深度会让 MDSBES 服务器保留更多的数据从而占用更多的服务器内存对服务器产生

更大的压力所以调用队列深度需要根据服务器的运行能力进行调用

从应用服务器设计角度应用 MDS 队列限制的方法是尽量减少等待数据的发送如发

送第一条数据的时候发现数据过期对于该用户的其它数据可以调整发送时间在更长的时

间间隔后才尝试发送

对于 MDS 队列限制应用服务器端的推送应用需要对 MDSBES 服务器返回的 503 错误

进行处理对于出现 503 错的数据组织重发同样重发的间隔也需要考虑不要因为数据

重发占用更多的推送队列也可以根据目标用户进行判断对于可能关机的用户暂时不重发

数据

手持设备端接收队列的处理

手持设备端接收队列并未从文档中明确列出是根据测试结果分析得出的手持设备端

目前也没有发现设置可以调整该队列所以客户端程序设计的时候不应该依赖手持设备端队

开发人员需要意识到的是手持设备端的接收队列是可溢出队列一旦推送数据从接收队

列出溢出客户端程序就无法提取该数据了所以在客户端应用开始过程中要注意加快推送

数据的提取过程在提取过程中不要对数据进行复杂的计算提取完成后尽快读取下一条数

据对于接收数据的处理和计算可以交由另一个线程完成

因为溢出数据对客户端应用而言不可见所以数据溢出时在客户端应用中也不会有异

常开发人员需要通过服务器返回的过期消息才能知道该数据推送失败

对于这一点客户端应用除了尽量避免数据溢出外无法在客户端通过其它机制进行补

应用依赖的客户端

对于应用依赖的客户端应用关键点是决定何时调用 accept 函数同时保证 InputStream

和 StreamConnection 被关闭

如之前讨论的使用应用依赖的推送关键是希望使用 MDSBES 发送给应用服务器的异

步确认消息从而对数据是否到达进行判断所以应用依赖的客户端应用需要保证正确调用

accept 函数

另外如果 InputStream 和 StreamConnection 没有关闭的话accept 函数所发出的确认

消息无法被 MDSBES 服务器收到开发人员在代码中应该主动关闭 InputStream 和

StreamConnection同时在程序发生异常时也需要在 catch 代码段对 InputStream 和

StreamConnection 进行关闭处理

异步确认消息处理

异步确认消息用于判断数据是否被成功推送至客户端应用要使用异步确认消息首先

要保证成功地无遗漏地接收所有从 MDSBES 服务器发送过来的异步确认消息

异步确认消息是一个 HTTP 消息消息比较短接收过程的并发压力不大下面是一个

异步确认消息的样例

Received notificationGET HTTP11

User-Agent RIM MDSCS

Accept

X-RIM-Push-ID pushID2081773768

X-RIM-Push-Destination mmtestbjsearbcn

X-RIM-Push-Status 400

Host localhost7778

其中最关键是的获取属性 X-RIM-Push-Status根据该属性的值判断数据推送是成功

(200)还是失败(400)当然属性 X-RIM-Push-ID 也非常重要用于确定该确认消息是针

对哪条推送数据的

如果应用服务器端通过确认消息发现数据推送失败则需要根据属性 X-RIM-Push-ID 重

新组织数据进行重发

Page 23: BES 服务器推送机制分析 - images.csdn.netimages.csdn.net/upimgs/lee/BBPDF/BESfwtsjzfx.pdf · 前言 数据推送是BlackBerry 应用平台的一大优势,在BlackBerry 应用平台上部署的应用可以

结合以上描述的各种因素在极端的情况下会出现一些特殊的情况如上图所示假设

MDSBES 服务器在推送了数据 R数据 1 到数据 13 后出现网络中断同时客户端应用程序

正在缓慢地读取数据 R此时数据 1 到数据 10 会被保留在手持设备队列中客户端应用程

序在读取数据 R 后可以继续读取数据 1 到数据 10如果客户端应用程序在读取了数据后都

调用了 accept 函数则会产生对应用确认消息如图中的确认消息 R确认消息 1 到确认消

息 10但是因为网络的中断这些确认消息都无法到达 MDSBES 服务器如果网络在 10

钟内没有恢复MDSBES 服务器上所有等待确认消息的记录都会认识是过期的MDSBES

会向应用服务器发送异步确认消息 400此后即使网络恢复确认消息 R确认消息 1 到确

认消息 10 都不再有效

在这种特殊情况下即使客户端应用程序接收到数据并调用了 accept 函数MDSBES

服务器同样认为数据没有推送成功

根据以上的讨论应用依赖的推送同样不能保证客户端应用一定能收到数据但是在

应用依赖的推送方式下 MDSBES 返回 200 确认消息可以确定客户端已接收到数据因为 200

确认消息是由客户端应用程序调用 accept 函数触发的客户端应用程序可以在确认数据收

到后才调用 accept 函数通知应用服务器该数据已成功接收甚至可以在接收到数据后根

据数据内容决定不调用 accept 函数从而通过应用服务器该数据没有被接收

所以在应用依赖的情况下如果接收到 200 确认消息可以确定客户端已经接收到数据

如果接收到 400 确认消息只能认为客户端可能没有收到数据

因此应用依赖的推送方式更适合可靠性要求高的应用如果应用服务器接收到 200

确认消息就可以认为推送成功继续处理其它事务如果应用服务器接收到 400 确认消息则

需要组织数据重发直到接收到 200 确认消息为止这样就可以保证客户端一定能够接收到

需要推送的数据

如何保证数据推送的可靠性

通过以上几个章节的讨论我们知道虽然 MDSBES 服务器提供了很多机制保证数据

推送的可靠性但是都无法完全保证数据被客户端应用程序所接收

对于数据推送的可靠性保证一方面开发人员需要了解各个可能导致故障的因素在应

用编写的时候尽量减少故障出现的可能性对故障点进行规避另一方面开发人员也需要实

现额外的机制进行数据重发在故障点无法规避的情况下通过数据重发消除故障点的影响

应用依赖和非应用依赖的比较和选择

在实现推送应用的时候首要考虑的是有关应用依赖与非应用依赖的选择

应用依赖的推送形式在可靠性方面更优于非应用依赖的推送形式但是应用依赖的推送

形式对 MDSBES 应用服务器的潜在压力较大客户端应用程序的实现也较为复杂

非应用依赖的推送形式优势在于实现简单对 MDSBES 应用服务器形成的压力相对较

非应用依赖的推送形式从某种程度上讲有点类似于广播协议着重点是从服务器向客户

端发送大量数据并不看重客户端的接收情况从数据的应用类型来分非应用依赖的推送

更适合于天气信息最新报价等数据这些数据在客户端应用关闭时被自然丢弃短时间内

的多条数据有丢失影响也不大必竞用户不会关注 11 点零六分的天气和 11 点零七分的天气

有何不同

应用依赖的推送形式则类似于点对点协议不仅从服务器端向客户端发送数据同时也

关注客户端的接收情况应用依赖的推送更适合于 OAERP 这种应用这种应用需要保证

新的文档或者是记录一定能到达客户端应用丢失一条待办信息可能就会导致整个审批流程

的停滞

再从两种推送方式的本质差别看应用依赖并没有额外的机制消除故障点它的优势在

于它的异步确认消息更可信所以在选择了应用依赖的推送方式后进一步还是需要实现

额外的数据重发机制根据 MDSBES 所发送的异步确认消息实施数据重发

应用依赖的推送再加上结合异步确认消息的数据重发机制是保证推送数据可靠性的

最好方法

MDS 接收线程限制的处理

不管使用什么方式的推送应用开发时都需要考虑到 MDS 接收线程的限制不过以

现在的服务器处理能力正常情况下的 HTTP 推送请求可以在几毫秒或者是几十毫内处理完

成少量的 HTTP 推送不足于构成网络阻塞

需要考虑的是应用服务器端的推送程序在推送过程中不要在建立连接后才动态组织数

据这样会浪费 http 连接资源好的建议是预先组织好数据在建立 HTTP 连接后快速写完

数据尽快关闭连接

此外对于应用服务器的推送线程也要加于控制避免在短时间内无限制地启动新的线

程进行连接否则也容易造成连接过多而阻塞的情况

从网络层面来看调整应用服务器与 MDSBES 服务器之间的网络超时设置也是避免这

种问题的一种方法在允许的范围内调大网络超时时间可以让应用服务器的推送线程等待更

长的时间从而让 MDSBES 有足够的时间释放原有的连接

最后无论使用了什么方法调优都需要对应用服务器的推送程序进行异常处理当发

现网络超时异常的时候可以组织数据重发当然数据重发的间隔时间也要考虑不要因为

重发数据再次造成网络阻塞

MDS 推送队列限制的处理

相对而言MDS 推送队列限制造成的推送失败比 MDS 接收队列造成的推送失败更常见

已经发现的一个实际案例是周未的时候用户大量关机导致大量推送数据处于推送队列中

填满了推送队列从而导致 MDSBES 服务器不再响应新的推送请求一旦过期时间到达

有新的队列位置空闲又有大量新的推送数据重新占满推送队列

对于 MDS 推送队列的限制直观的做法是对队列参数进行调整如之前讨论的管理

员可以对队列的深度和过期时间进行调整

然而出于对过期时间的不正确理解管理员倾向于将过期时间调大当过期时间被调

大后推送给关机用户的数据在服务器占用队列的时间就变长导致 MDS 推送队列更容易

被占满

所以如果对过期时间进行调整的话方向是将过期时间调小过期时间越小等待数

据占用队列的时间就越短MDS 推送队列被占满的可能性就越小但是过期时间也不能

过小过期时间过小可能会导致过多的过期数据使应用服务器需要处理更多的数据重发

比如将过期时间调成 1 分钟用户走过一个人行遂道都可能导致数据发送失败

对于队列深度的调整方向是调大队列深度如从缺省的 1000 调整到 2000此时

MDSBES 服务器在同一时间可以保留更多的等待数据减少了队列占满的机会不过调大队

列深度会让 MDSBES 服务器保留更多的数据从而占用更多的服务器内存对服务器产生

更大的压力所以调用队列深度需要根据服务器的运行能力进行调用

从应用服务器设计角度应用 MDS 队列限制的方法是尽量减少等待数据的发送如发

送第一条数据的时候发现数据过期对于该用户的其它数据可以调整发送时间在更长的时

间间隔后才尝试发送

对于 MDS 队列限制应用服务器端的推送应用需要对 MDSBES 服务器返回的 503 错误

进行处理对于出现 503 错的数据组织重发同样重发的间隔也需要考虑不要因为数据

重发占用更多的推送队列也可以根据目标用户进行判断对于可能关机的用户暂时不重发

数据

手持设备端接收队列的处理

手持设备端接收队列并未从文档中明确列出是根据测试结果分析得出的手持设备端

目前也没有发现设置可以调整该队列所以客户端程序设计的时候不应该依赖手持设备端队

开发人员需要意识到的是手持设备端的接收队列是可溢出队列一旦推送数据从接收队

列出溢出客户端程序就无法提取该数据了所以在客户端应用开始过程中要注意加快推送

数据的提取过程在提取过程中不要对数据进行复杂的计算提取完成后尽快读取下一条数

据对于接收数据的处理和计算可以交由另一个线程完成

因为溢出数据对客户端应用而言不可见所以数据溢出时在客户端应用中也不会有异

常开发人员需要通过服务器返回的过期消息才能知道该数据推送失败

对于这一点客户端应用除了尽量避免数据溢出外无法在客户端通过其它机制进行补

应用依赖的客户端

对于应用依赖的客户端应用关键点是决定何时调用 accept 函数同时保证 InputStream

和 StreamConnection 被关闭

如之前讨论的使用应用依赖的推送关键是希望使用 MDSBES 发送给应用服务器的异

步确认消息从而对数据是否到达进行判断所以应用依赖的客户端应用需要保证正确调用

accept 函数

另外如果 InputStream 和 StreamConnection 没有关闭的话accept 函数所发出的确认

消息无法被 MDSBES 服务器收到开发人员在代码中应该主动关闭 InputStream 和

StreamConnection同时在程序发生异常时也需要在 catch 代码段对 InputStream 和

StreamConnection 进行关闭处理

异步确认消息处理

异步确认消息用于判断数据是否被成功推送至客户端应用要使用异步确认消息首先

要保证成功地无遗漏地接收所有从 MDSBES 服务器发送过来的异步确认消息

异步确认消息是一个 HTTP 消息消息比较短接收过程的并发压力不大下面是一个

异步确认消息的样例

Received notificationGET HTTP11

User-Agent RIM MDSCS

Accept

X-RIM-Push-ID pushID2081773768

X-RIM-Push-Destination mmtestbjsearbcn

X-RIM-Push-Status 400

Host localhost7778

其中最关键是的获取属性 X-RIM-Push-Status根据该属性的值判断数据推送是成功

(200)还是失败(400)当然属性 X-RIM-Push-ID 也非常重要用于确定该确认消息是针

对哪条推送数据的

如果应用服务器端通过确认消息发现数据推送失败则需要根据属性 X-RIM-Push-ID 重

新组织数据进行重发

Page 24: BES 服务器推送机制分析 - images.csdn.netimages.csdn.net/upimgs/lee/BBPDF/BESfwtsjzfx.pdf · 前言 数据推送是BlackBerry 应用平台的一大优势,在BlackBerry 应用平台上部署的应用可以

如何保证数据推送的可靠性

通过以上几个章节的讨论我们知道虽然 MDSBES 服务器提供了很多机制保证数据

推送的可靠性但是都无法完全保证数据被客户端应用程序所接收

对于数据推送的可靠性保证一方面开发人员需要了解各个可能导致故障的因素在应

用编写的时候尽量减少故障出现的可能性对故障点进行规避另一方面开发人员也需要实

现额外的机制进行数据重发在故障点无法规避的情况下通过数据重发消除故障点的影响

应用依赖和非应用依赖的比较和选择

在实现推送应用的时候首要考虑的是有关应用依赖与非应用依赖的选择

应用依赖的推送形式在可靠性方面更优于非应用依赖的推送形式但是应用依赖的推送

形式对 MDSBES 应用服务器的潜在压力较大客户端应用程序的实现也较为复杂

非应用依赖的推送形式优势在于实现简单对 MDSBES 应用服务器形成的压力相对较

非应用依赖的推送形式从某种程度上讲有点类似于广播协议着重点是从服务器向客户

端发送大量数据并不看重客户端的接收情况从数据的应用类型来分非应用依赖的推送

更适合于天气信息最新报价等数据这些数据在客户端应用关闭时被自然丢弃短时间内

的多条数据有丢失影响也不大必竞用户不会关注 11 点零六分的天气和 11 点零七分的天气

有何不同

应用依赖的推送形式则类似于点对点协议不仅从服务器端向客户端发送数据同时也

关注客户端的接收情况应用依赖的推送更适合于 OAERP 这种应用这种应用需要保证

新的文档或者是记录一定能到达客户端应用丢失一条待办信息可能就会导致整个审批流程

的停滞

再从两种推送方式的本质差别看应用依赖并没有额外的机制消除故障点它的优势在

于它的异步确认消息更可信所以在选择了应用依赖的推送方式后进一步还是需要实现

额外的数据重发机制根据 MDSBES 所发送的异步确认消息实施数据重发

应用依赖的推送再加上结合异步确认消息的数据重发机制是保证推送数据可靠性的

最好方法

MDS 接收线程限制的处理

不管使用什么方式的推送应用开发时都需要考虑到 MDS 接收线程的限制不过以

现在的服务器处理能力正常情况下的 HTTP 推送请求可以在几毫秒或者是几十毫内处理完

成少量的 HTTP 推送不足于构成网络阻塞

需要考虑的是应用服务器端的推送程序在推送过程中不要在建立连接后才动态组织数

据这样会浪费 http 连接资源好的建议是预先组织好数据在建立 HTTP 连接后快速写完

数据尽快关闭连接

此外对于应用服务器的推送线程也要加于控制避免在短时间内无限制地启动新的线

程进行连接否则也容易造成连接过多而阻塞的情况

从网络层面来看调整应用服务器与 MDSBES 服务器之间的网络超时设置也是避免这

种问题的一种方法在允许的范围内调大网络超时时间可以让应用服务器的推送线程等待更

长的时间从而让 MDSBES 有足够的时间释放原有的连接

最后无论使用了什么方法调优都需要对应用服务器的推送程序进行异常处理当发

现网络超时异常的时候可以组织数据重发当然数据重发的间隔时间也要考虑不要因为

重发数据再次造成网络阻塞

MDS 推送队列限制的处理

相对而言MDS 推送队列限制造成的推送失败比 MDS 接收队列造成的推送失败更常见

已经发现的一个实际案例是周未的时候用户大量关机导致大量推送数据处于推送队列中

填满了推送队列从而导致 MDSBES 服务器不再响应新的推送请求一旦过期时间到达

有新的队列位置空闲又有大量新的推送数据重新占满推送队列

对于 MDS 推送队列的限制直观的做法是对队列参数进行调整如之前讨论的管理

员可以对队列的深度和过期时间进行调整

然而出于对过期时间的不正确理解管理员倾向于将过期时间调大当过期时间被调

大后推送给关机用户的数据在服务器占用队列的时间就变长导致 MDS 推送队列更容易

被占满

所以如果对过期时间进行调整的话方向是将过期时间调小过期时间越小等待数

据占用队列的时间就越短MDS 推送队列被占满的可能性就越小但是过期时间也不能

过小过期时间过小可能会导致过多的过期数据使应用服务器需要处理更多的数据重发

比如将过期时间调成 1 分钟用户走过一个人行遂道都可能导致数据发送失败

对于队列深度的调整方向是调大队列深度如从缺省的 1000 调整到 2000此时

MDSBES 服务器在同一时间可以保留更多的等待数据减少了队列占满的机会不过调大队

列深度会让 MDSBES 服务器保留更多的数据从而占用更多的服务器内存对服务器产生

更大的压力所以调用队列深度需要根据服务器的运行能力进行调用

从应用服务器设计角度应用 MDS 队列限制的方法是尽量减少等待数据的发送如发

送第一条数据的时候发现数据过期对于该用户的其它数据可以调整发送时间在更长的时

间间隔后才尝试发送

对于 MDS 队列限制应用服务器端的推送应用需要对 MDSBES 服务器返回的 503 错误

进行处理对于出现 503 错的数据组织重发同样重发的间隔也需要考虑不要因为数据

重发占用更多的推送队列也可以根据目标用户进行判断对于可能关机的用户暂时不重发

数据

手持设备端接收队列的处理

手持设备端接收队列并未从文档中明确列出是根据测试结果分析得出的手持设备端

目前也没有发现设置可以调整该队列所以客户端程序设计的时候不应该依赖手持设备端队

开发人员需要意识到的是手持设备端的接收队列是可溢出队列一旦推送数据从接收队

列出溢出客户端程序就无法提取该数据了所以在客户端应用开始过程中要注意加快推送

数据的提取过程在提取过程中不要对数据进行复杂的计算提取完成后尽快读取下一条数

据对于接收数据的处理和计算可以交由另一个线程完成

因为溢出数据对客户端应用而言不可见所以数据溢出时在客户端应用中也不会有异

常开发人员需要通过服务器返回的过期消息才能知道该数据推送失败

对于这一点客户端应用除了尽量避免数据溢出外无法在客户端通过其它机制进行补

应用依赖的客户端

对于应用依赖的客户端应用关键点是决定何时调用 accept 函数同时保证 InputStream

和 StreamConnection 被关闭

如之前讨论的使用应用依赖的推送关键是希望使用 MDSBES 发送给应用服务器的异

步确认消息从而对数据是否到达进行判断所以应用依赖的客户端应用需要保证正确调用

accept 函数

另外如果 InputStream 和 StreamConnection 没有关闭的话accept 函数所发出的确认

消息无法被 MDSBES 服务器收到开发人员在代码中应该主动关闭 InputStream 和

StreamConnection同时在程序发生异常时也需要在 catch 代码段对 InputStream 和

StreamConnection 进行关闭处理

异步确认消息处理

异步确认消息用于判断数据是否被成功推送至客户端应用要使用异步确认消息首先

要保证成功地无遗漏地接收所有从 MDSBES 服务器发送过来的异步确认消息

异步确认消息是一个 HTTP 消息消息比较短接收过程的并发压力不大下面是一个

异步确认消息的样例

Received notificationGET HTTP11

User-Agent RIM MDSCS

Accept

X-RIM-Push-ID pushID2081773768

X-RIM-Push-Destination mmtestbjsearbcn

X-RIM-Push-Status 400

Host localhost7778

其中最关键是的获取属性 X-RIM-Push-Status根据该属性的值判断数据推送是成功

(200)还是失败(400)当然属性 X-RIM-Push-ID 也非常重要用于确定该确认消息是针

对哪条推送数据的

如果应用服务器端通过确认消息发现数据推送失败则需要根据属性 X-RIM-Push-ID 重

新组织数据进行重发

Page 25: BES 服务器推送机制分析 - images.csdn.netimages.csdn.net/upimgs/lee/BBPDF/BESfwtsjzfx.pdf · 前言 数据推送是BlackBerry 应用平台的一大优势,在BlackBerry 应用平台上部署的应用可以

长的时间从而让 MDSBES 有足够的时间释放原有的连接

最后无论使用了什么方法调优都需要对应用服务器的推送程序进行异常处理当发

现网络超时异常的时候可以组织数据重发当然数据重发的间隔时间也要考虑不要因为

重发数据再次造成网络阻塞

MDS 推送队列限制的处理

相对而言MDS 推送队列限制造成的推送失败比 MDS 接收队列造成的推送失败更常见

已经发现的一个实际案例是周未的时候用户大量关机导致大量推送数据处于推送队列中

填满了推送队列从而导致 MDSBES 服务器不再响应新的推送请求一旦过期时间到达

有新的队列位置空闲又有大量新的推送数据重新占满推送队列

对于 MDS 推送队列的限制直观的做法是对队列参数进行调整如之前讨论的管理

员可以对队列的深度和过期时间进行调整

然而出于对过期时间的不正确理解管理员倾向于将过期时间调大当过期时间被调

大后推送给关机用户的数据在服务器占用队列的时间就变长导致 MDS 推送队列更容易

被占满

所以如果对过期时间进行调整的话方向是将过期时间调小过期时间越小等待数

据占用队列的时间就越短MDS 推送队列被占满的可能性就越小但是过期时间也不能

过小过期时间过小可能会导致过多的过期数据使应用服务器需要处理更多的数据重发

比如将过期时间调成 1 分钟用户走过一个人行遂道都可能导致数据发送失败

对于队列深度的调整方向是调大队列深度如从缺省的 1000 调整到 2000此时

MDSBES 服务器在同一时间可以保留更多的等待数据减少了队列占满的机会不过调大队

列深度会让 MDSBES 服务器保留更多的数据从而占用更多的服务器内存对服务器产生

更大的压力所以调用队列深度需要根据服务器的运行能力进行调用

从应用服务器设计角度应用 MDS 队列限制的方法是尽量减少等待数据的发送如发

送第一条数据的时候发现数据过期对于该用户的其它数据可以调整发送时间在更长的时

间间隔后才尝试发送

对于 MDS 队列限制应用服务器端的推送应用需要对 MDSBES 服务器返回的 503 错误

进行处理对于出现 503 错的数据组织重发同样重发的间隔也需要考虑不要因为数据

重发占用更多的推送队列也可以根据目标用户进行判断对于可能关机的用户暂时不重发

数据

手持设备端接收队列的处理

手持设备端接收队列并未从文档中明确列出是根据测试结果分析得出的手持设备端

目前也没有发现设置可以调整该队列所以客户端程序设计的时候不应该依赖手持设备端队

开发人员需要意识到的是手持设备端的接收队列是可溢出队列一旦推送数据从接收队

列出溢出客户端程序就无法提取该数据了所以在客户端应用开始过程中要注意加快推送

数据的提取过程在提取过程中不要对数据进行复杂的计算提取完成后尽快读取下一条数

据对于接收数据的处理和计算可以交由另一个线程完成

因为溢出数据对客户端应用而言不可见所以数据溢出时在客户端应用中也不会有异

常开发人员需要通过服务器返回的过期消息才能知道该数据推送失败

对于这一点客户端应用除了尽量避免数据溢出外无法在客户端通过其它机制进行补

应用依赖的客户端

对于应用依赖的客户端应用关键点是决定何时调用 accept 函数同时保证 InputStream

和 StreamConnection 被关闭

如之前讨论的使用应用依赖的推送关键是希望使用 MDSBES 发送给应用服务器的异

步确认消息从而对数据是否到达进行判断所以应用依赖的客户端应用需要保证正确调用

accept 函数

另外如果 InputStream 和 StreamConnection 没有关闭的话accept 函数所发出的确认

消息无法被 MDSBES 服务器收到开发人员在代码中应该主动关闭 InputStream 和

StreamConnection同时在程序发生异常时也需要在 catch 代码段对 InputStream 和

StreamConnection 进行关闭处理

异步确认消息处理

异步确认消息用于判断数据是否被成功推送至客户端应用要使用异步确认消息首先

要保证成功地无遗漏地接收所有从 MDSBES 服务器发送过来的异步确认消息

异步确认消息是一个 HTTP 消息消息比较短接收过程的并发压力不大下面是一个

异步确认消息的样例

Received notificationGET HTTP11

User-Agent RIM MDSCS

Accept

X-RIM-Push-ID pushID2081773768

X-RIM-Push-Destination mmtestbjsearbcn

X-RIM-Push-Status 400

Host localhost7778

其中最关键是的获取属性 X-RIM-Push-Status根据该属性的值判断数据推送是成功

(200)还是失败(400)当然属性 X-RIM-Push-ID 也非常重要用于确定该确认消息是针

对哪条推送数据的

如果应用服务器端通过确认消息发现数据推送失败则需要根据属性 X-RIM-Push-ID 重

新组织数据进行重发

Page 26: BES 服务器推送机制分析 - images.csdn.netimages.csdn.net/upimgs/lee/BBPDF/BESfwtsjzfx.pdf · 前言 数据推送是BlackBerry 应用平台的一大优势,在BlackBerry 应用平台上部署的应用可以

对于这一点客户端应用除了尽量避免数据溢出外无法在客户端通过其它机制进行补

应用依赖的客户端

对于应用依赖的客户端应用关键点是决定何时调用 accept 函数同时保证 InputStream

和 StreamConnection 被关闭

如之前讨论的使用应用依赖的推送关键是希望使用 MDSBES 发送给应用服务器的异

步确认消息从而对数据是否到达进行判断所以应用依赖的客户端应用需要保证正确调用

accept 函数

另外如果 InputStream 和 StreamConnection 没有关闭的话accept 函数所发出的确认

消息无法被 MDSBES 服务器收到开发人员在代码中应该主动关闭 InputStream 和

StreamConnection同时在程序发生异常时也需要在 catch 代码段对 InputStream 和

StreamConnection 进行关闭处理

异步确认消息处理

异步确认消息用于判断数据是否被成功推送至客户端应用要使用异步确认消息首先

要保证成功地无遗漏地接收所有从 MDSBES 服务器发送过来的异步确认消息

异步确认消息是一个 HTTP 消息消息比较短接收过程的并发压力不大下面是一个

异步确认消息的样例

Received notificationGET HTTP11

User-Agent RIM MDSCS

Accept

X-RIM-Push-ID pushID2081773768

X-RIM-Push-Destination mmtestbjsearbcn

X-RIM-Push-Status 400

Host localhost7778

其中最关键是的获取属性 X-RIM-Push-Status根据该属性的值判断数据推送是成功

(200)还是失败(400)当然属性 X-RIM-Push-ID 也非常重要用于确定该确认消息是针

对哪条推送数据的

如果应用服务器端通过确认消息发现数据推送失败则需要根据属性 X-RIM-Push-ID 重

新组织数据进行重发