-
Pushlet 2.0.3
源码分析
----
服务器端
1
总体架构
Pushlet
从功能上实现了服务器推技术,整个框架涉及了服务器端以及客户端的
部署。服务
器端采用
servlet
技
术,监听客户端请求。客户端分为两大类,浏览器以及桌面应用程序。
下图描述了系统的
整体框架:
图
1
pushlet
总体架构图
从图中可
以看出服务器端返回响应的出口只有一个,那就是
clientAdapter
,它只是一个接
口,根据不同的客户端类型来产生相应的
adapter
发送响应结果。
各个类的主要职责描述:
Pushl
et
:负责接收所有用户请求,并将请求包装为
event
p>
对象,在根据
session
、
event
、
request
、
response
对象构造一个
command
对象,最后将
command
< br>对象交由
controller
处
理。
Session
:代表一次用
户会话,此类不同于
httpsession
,因为此
session
的实现是使用类
似
url
重写方式,
在服务器分配了
< br>sessionid
之后的每个请求中都加入这个参数以标识会话。
会话在其存活期内有效。
Controller<
/p>
:
是所有命令的执行器,
包括各种控制命
令以及数据推送命令。
不过对于数据推
送的实际执行并不是在<
/p>
controller
中实现,而是委托给
subscriber
只执行。
S
ubscriber
:
这是核心类之一。
它维护了一个阻塞的事件队列,
根据客户端使用的不同模式
(
框架定义的模式有:
stream
,
p
ull/poll
,
stream
使用
了
http
长连接,
pull/pol
l
则是通
过客户端定时刷新实现服务端推送)来发送响应事件。
Dispatcher
:事件分发器
,也是核心类之一。
事件来源可以是客户
(通过
publish
命令发布
事件),也可以是
eventSource
。实现了多播,广播以及单播事件,具体采用哪种
方式根据
事件属性决定。事件接收端即是
subscriber
的事件队列。
clientAdap
ter
:有
3
个具体实现,
browserAdapter
、
XMLAdap
ter
、
serializedAdapter
。分
别用来发送
javascript
、
xml
、序列化数据。使用于不同的客户端。具体使
用哪种
adapter
需要根据用户请求事件的
format
参数决定。
其他公共类:提供了日志、可配置等功能。
图
2
核心类的对应关系
2
原理分析
Pushlet
采用服务器端回调技术以及
HTTP
长连接实现了
服务器推服务的两种模式
,
即
stre
am
,
pull/poll
。
其中对于浏览器客户端还应用了
DHTML
技术
,
通过回调
javascript
函数
在不刷
新页面的前提下实时更新,
普通的桌面应用很容易便可以
实现这种效果。
为了提高系统的可
靠性以及健壮性,通信过程中
开通了两条通道,控制通道和数据通道。控制通道不会阻塞,
能够实时接收客户命令,而
数据通道工作在阻塞模式下,当传输模式为
stream
时,数
据通
道连接不断开,
直至用户发送断开命令或客户端退出或服务
器异常,
为了防止阻塞时间过长
导致客户端无法得知服务器是否
正常工作,
系统设置了阻塞过期时间,
并且在过期之后向客
p>
户端发送心跳消息表明自身仍然存活。而在
pull/poll
p>
模式下,阻塞直至有数据可以发送,
然后断开连接。
浏览器客户端需要不停的发送心跳请求,
目的是为了解决浏览器一直繁忙的
p>
状态。以下是系统的协议服务(
protocol
services
)
Service
Description
join
启动一个会话
leave
结束会话
subscribe
订阅相关主题
unsubscribe
取消订阅相关主题
listen <
/p>
打开数据通道,以
stream
、
pull
或
poll
模式开始数据流传输。在
pull/poll
模式
下,服务器提供所谓的刷新操作,实际上是客户端来重新请求以获取数据
join-listen
通过一个请求完成会话启动,订阅并
监听数据。执行完之后的状态与执行完
listen
类似。
p>
publish
发布数据,然后服务器
将其分发。客户端可以使用它进行多播或单播数据。
heartbeat
表明会话存活
3
具体实现
Pushlet
采用了大量的单例和工厂模式,另外还有适配器模式、命令模式。实现中遵循面向
< br>接口以及抽象类编程,
这些使得系统易于理解,
易于扩展
。
系统的大多数属性都是在配置文
件中指定,
< br>如果有通过系统扩展点编写的扩展类要替代默认实现的话,
只需要修改配置文件<
/p>
指向你自己的类文件即可,
不需改动代码。
接下来就沿着请求—响应的主线来分析系统源码。
请求入口
pushlet
Init
()方法:
30 String webInfPath =
getServletContext().getRealPath(
31 (web
InfPath);//
载入配置文件
,
存放在该类的变量中
32
33
();//
初始化日志类
34
35 // Start
36
(
built=
37
38 //
Start session manager,
负责管理
se
ssion
生命周期
,
这是系统的扩展
点
,
下文详解
.
39 tance().start();
40
41 // Start event
Dispatcher,
负责分发系统或客户事件
42 tance().start();
43
44
45 if
(lProperty(S_ACTIVATE)) {
46
(webInfPath);//
启动事件源管理器
47 } else {
48
(
49 }
初始化完毕之后便可以处理用户请求了
.
它可以处理两种请求
,get
< br>和
post,
处理方式主要是
提
取请求参数,
然后将其封装成
event
事件对象,
再进一步构造
command
对象,
最终的处理
有交给了
con
troller
。
这部分的代码很简单,
因为主要的处理逻辑都委托给了
controller
。
p>
这段代码有几点是值得学习的。
1
)
抽象。
Event
对象封装了属性—值对,内部通过
< br>hashmap
实现,原理上来讲,它可以封
装任何信息
,
为了使这样的一个抽象能够适于作为系统的通用数据抽象形式,
还需要加入一
个必备属性,即
event_type
。请求以及数据均被定义为事件,然后通过内部协议来区分它
们。
通过抽象之后,
系统可以以一致的处理形式应对各种数据。
后面将要分析的
subscriber
维护着一个
事件队列,使用该队列完成所有的交互。这便是使用了这个抽象机制的好处。
2
)
命令模
式
command
。一个命令里包含了请求事件、响应事件以及
response
,
request<
/p>
,
session
对象。
Controller
便是这个命令的执行器,
通过一
个简单的
doCommand
接口隐藏了
内部复杂的处理逻辑,
降低了模块的耦合度。
执行完命令之后
,
要输出的结果就是响应事件
responseEvent
p>
。
Controller
处理代码如下
p>
49 // Update lease time to li
ve
,更新
session
生存期,防
止过期
50 ();
51
52 // Set remote IP address of
client
,设置远程客户端地址
53 ress(oteAddr());
54
55 debug(
56
57 //
Get event type
58 String eventType =
ntType();
59
60 // Determine
action based on event
type
,根据事件类型采取
相应的操作,分别构造响应事件
61
if ((Protocol.E_REFRESH)) {
62 //
Pull/poll mode clients that refresh
63
doRefresh(aCommand);
64 } else if
((Protocol.E_SUBSCRIBE)) {
65 //
Subscribe
66 doSubscribe(aCommand);
67 } else if ((Protocol.E_UNSUBSCRIBE))
{
68 // Unsubscribe
69
doUnsubscribe(aCommand);
70 } else if
((Protocol.E_JOIN)) {
71 // Join
72 doJoin(aCommand);
73 }
else if ((Protocol.E_JOIN_LISTEN)) {
74
// Join and listen (for simple and e.g. REST apps)
75 doJoinListen(aCommand);
76 } else if ((Protocol.E_LEAVE)) {
77 // Leave
78
doLeave(aCommand);
79 } else if
((Protocol.E_HEARTBEAT)) {
80 //
Heartbeat mainly to do away with browser
81 doHeartbeat(aCommand);
82
} else if ((Protocol.E_PUBLISH)) {
83
// Publish event
84
doPublish(aCommand);
85 } else if
((Protocol.E_LISTEN)) {
86 // Listen to
pushed events
87 doListen(aCommand);
88 }
89
90 //
Handle response back to client
91 if
(th(Protocol.E_LISTEN) ||
92
(Protocol.E_REFRESH)) {
//
请求
类型是
listen
或
refresh
,表明是获取数据
93 //
Data channel events
94 // Loops until
refresh or connection closed
95
getSubscriber().fetchEvents(aCommand);
96
97 } else {
98
// Send response for control
commands
,控制命令,直接返回。
99 sendControlResponse(aCommand);
00 }
sendControlResponse
()代码:
31 sponseHeader
s();//
设置响应头,主要是客户端
//
< br>缓存无效
32
33 //
Let clientAdapter determine how to send event
//
通过
clientAdapter
发送响应事件
34
entAdapter().start();
35
36
// Push to client through client adapter
37 entAdapter().push(ponseEvent());
38
39 // One shot response
40 entAdapter().stop();
Subscriber
:
fetchEvents
()部分代码:
。。。。。。。。。。。。。。。。。
。。。。。。。。。。。。。。。。
15 Event[] events = null;
16
17 // Main loop: as long as connected,
get events and push to client
18 long
eventSeqNr = 1;
19 while (isActive()) {
//
这个循环保证了连接不被关闭,即可以以流的
//
方式发送响应
到客户端,真正意义上的服务器推送数据
< br>
-
-
-
-
-
-
-
-
-
上一篇:德语分级考试
下一篇:英语专业八级听力词汇(政经文社各领域)