二 java端
=========================
基本概念
=========================
Cometd java端的实现建立在流行的jetty http服务器及servlet容器上。尽管它基于jetty,但是
它也可以简便的移植到其他2.5及以上的servlet容器(因为它使用了便携式的jetty continuation api)。
使用了cometd的war包可以部署到除jetty外的其他servlet2.5容器上,但是应用较少,因为它会减弱与servlet的集成。
当部署在servlet3.0容器上时,cometd app将会使用servlet容器提供的异步特性。并具有充分的便携性及高扩展性。
cometd java实现提供了一个客户端包和服务端包。
java cometd 基本概念
comtd技术使用Bayeux协议提供的可伸缩的基于HTTP通讯的消息传递来实现。
一般来说,消息系统是通过一个网络协议通讯的客户端和服务端组成。这种捕获模式成为半对象加协议。
Sessions
Client-session是代表与Bayeux服务器通讯会话的客户端的一半对象。客户端建立session对象后,最初与服务端的SeverSession并无关联。
只有当客户端session与服务端通讯握手,此时创建响应的服务器会话,并创建两个半对象之间的关系。
client-session对象直接指向远程的客户端,但它也同样存在于服务端。Bayeux服务器只知道server session半对象,唯一创建server Session办对象的途径是首先建立它的客户端session通讯者,并通过server通讯握手。
为此,在服务端有一个附加的概念:LocalSession。它集成自ClientSession。LocalSession是一个存在于服务端的client Session,它是服务端本地的对象。
例如:服务端的services与local session关联在一起。在服务端service建立之前,local session进行通讯握手并创server session半对象通讯服务。因此Bayeux服务端可以通过ServerSession以同样的方式处理远程的session和local session。
在cometd中,两个半对象通过交换信息来通讯。
message
java api提供了服务端的消息接口,用来进行只读信息的交互。
用户可以修改消息内容,Message的内置子接口Mutable通过回调函数的参数使用。
在服务端,内置接口ServerMessage允许信息只读的交互,Mutable内置接口被作为回调函数的参数传送。
message被用来在channel里面传送。
channel
channel被定义为消息的主题,消息可以发送给所有订阅该主题并可接收主题信息的人。
在服务端,ServerChannel子接口允许与channel进行交互,例如通过发布消息或者增加监听者。
=================================
cometd java 服务端配置
=================================
BayeuxServer和服务传输协议参数可以在web.xml中作为CometdServlet的初始化参数来配置。
当CometD servlet创建了BayeuxServer实例后,servlet的初始化参数会通过BayeuxServer实例转变成服务传输协议配置。
ComtedServlet必须在web.xml里配置,否则服务器不能使用Bayeux协议。通常将路径配置成/cometd/*,但是可以更改url-pattern路径。
BayeuxServer 配置文件
logLevel 默认0 0=off 1=config 2=info 3=debug
transports 默认空 以逗号分隔的ServerTransport实现类,这些实现类新增至默认的server transport上
allowedTransports 默认空 以逗号分隔的可以允许使用的ServerTransports实现类,如果未特殊说明,默认的通讯将被允许。
Server Transport Configuration
cometd 2中,服务协议可以通过插件化参数的形式配置,它可以使用指定的前缀来配置不同的协议参数。
例如,timeout参数没有任何前缀,因此它对所有的协议都有效,long-polling.jsonp.timeout参数重写了timeout参数,该参数对callback polling协议有用。
timeout 默认:30000ms 服务器将空数据包响应至一个长轮询前等待接收消息的最大时间
ws.timeout 默认:15000ms 类似于timeout参数,仅对websocket协议生效
interval 默认:0ms 指定客户端在一个长轮询结束到下一个长轮询开始之间的等待时间
ws.interval 默认:2500ms 类似于interval,仅对websocket协议生效
maxInterval 默认:10000ms 在客户端被认为失效及离开之前,服务端等待客户端长轮询的最大时间
ws.maxInterval 默认:15000ms 类似于maxInterval,仅对websocket生效
maxLazyTimeout 默认:5000ms 服务器等待lazy消息发布的最大时间
metaConnectDeliverOnly 默认:false 传输协议是否只能通过long poll方式传递信息
jsonDebug 默认:false 是否打开debug
maxSessionsPerBrowser 默认:1 每个浏览器可允许的最大session数量,
allowMultiSessionsNoBrowser 默认:false 当浏览器未被检测到时是否允许多个Sessions
multiSessionInterval 默认:2000
Advanced Configuration
若果你使用的是jetty 7,你可能会想配置CrossOriginFilter
跨站脚本访问
Authorization(身份认证)
Bayeux对象可以被配置在SecurityPolicy对象中,SecurityPolicy对象允许控制Bayeux协议的各个步骤。例如handshake、subscription、publish等
默认情况下,Bayeux对象没有被配置到SecurityPolicy,这意味着任何操作都是经过授权的。
SecurityPolicy 有一个默认的实现对象DefaultSecurityPolicy,它可作为一个基类用于定制安全权限。
SecurityPolicy 的方法有:
boolean canHandshake(BayeuxServer server, ServerSession session, ServerMessage message);
boolean canCreate(BayeuxServer server, ServerSession session, String channelId, ServerMessage message);
boolean canSubscribe(BayeuxServer server, ServerSession session, ServerChannel channel, ServerMessage messsage);
boolean canPublish(BayeuxServer server, ServerSession session, ServerChannel channel, ServerMessage messsage);
DefaultSecurityPolicy 实现了:
1.允许任何handshake
2.允许创建除meta channel之外的任何由客户端发送通讯握手过来的channel。
3.可进行任何由客户端handshake过来的订阅,除meta channel之外
4.允许publish 除meta channel
Authorizers(授权者)
Authorizers 实现了Autorizer,并在服务端启动时添加到channel中。此时channel并未发生任何操作。
Authorizers非常适合用来控制channel在启动时进行身份认证,或者在运行时建立重新建立ID的channel。
Autorizers不支持meta channel,但是可以被添加到一个带有通配符的channel中,并可以影响所有符合的通配channel。
一个Autorizers添加到/**上,会影响所有的channel。对非通配的,会作用到父级,如:/chat/room/10上添加授权者,也会作用于/chat/room/* /chat/room/** /chat/** /**
=================================
mutiple sessions
=================================
HTTP 协议推荐每个域最多有两个connect连接。虽然现在浏览器默认配置没两个域有两个以上的连接,
但是这样做是不安全的,因此任何的iframe,标签或者窗口在同一个浏览器连接到同一主机时需要共享两个连接。
如果两个iframe/tabs/windows启动了一个Bayeux通讯,都将开始一个长轮询连接请求,并且两个连接都被消耗掉,使得其它的Bayeux请求无法被传送,直到其中的长轮询结束。
================================
服务端 service
================================
CometD服务是一个java类,它允许开发者指定当贝叶消息到达贝叶通道时运行的代码块。
消息到达一个已订阅通道的服务器实例,回调方法将被调用执行用户特定的代码。
Cometd 2 服务有两种:继承和注解。
1.继承
Cometd继承服务实现自org.cometd.server.AbstractService。它指定了服务器感兴趣的Bayeux通道,
public class EchoService extends AbstractService //继承 AbstractService
{
public EchoService(BayeuxServer bayeuxServer)
{
super(bayeuxServer, "echo"); //通过BayeuxServer对象和服务通道名称调用父类构造
addService("/echo", "processEcho"); //订阅echo通道, 并指定消息到达通道时的回调函数
}
public void processEcho(ServerSession remote, Map<String, Object> data) //定义回调函数
{
remote.deliver(getServerSession(), "/echo", data, null); //使用ServerSessionAPI将消息返回给特定的客户端
}
}
这是一个简单的echo服务,远程的客户端通过echo通道将数据返回给远程的客户端本身。
BayeuxService需要回调函数至少实现以下方法中的一个:
// 获取远程session对象及消息对象
public void processEcho(ServerSession remote, Message message)
//获取远程session对象,以及消息对象的Map形式
// 附加的消息信息,例如当通道或id丢失的时候
public void processEcho(ServerSession remote, Map<String, Object> data)
//获得远程session对象,channel名称,消息对象,消息id
public void processEcho(ServerSession remote, String channelName, Message message, String messageId)
//获得远程session对象,channel名称,消息对象map形式,消息id
public void processEcho(ServerSession remote, String channelName, Map<String, Object> data, String messageId)
既然chanel名称可以在subscribe()中指定,并且可以应用通配,如:
public class BaseballTeamService extends AbstractService
{
public BaseballTeamService(BayeuxServer bayeux)
{
super(bayeux, "baseballTeam");
addService("/baseball/team/*", "processBaseballTeam");
}
public void processBaseballTeam(ServerSession remote, String channelName, Map<String, Object> data, String messageId)
{
// Upon receiving a message on channel /baseball/team/*, forward to channel /events/baseball/team/*
//接收/baseball/team/*的消息,并发布至/events/baseball/team/*通道
getBayeux().getChannel("/events" + channelName).publish(getServerSession(), data, null);
}
}
在示例代码中,我们通过调用deliver()方法对一个指定的远程客户单发送消息,使用publish方法对订阅了某一通道的所有客户端发送消息。
2.注解
从Cometd2.1.0开始支持注解
通过@Service标注的类作为标注服务,可以使用在客户端和服务端。
-----------------
服务端注解服务
-----------------
服务器端的cometd通常用AbstractServer实现类来实现,这些类的实例通常是一个单例模式,并且在web app配置并在启动的时候创建。
AbstractServer对可用的服务端功能实现、与serverSession访问相关联、使用服务实例注册、消息回调等功能提供较少的方法。
一个服务可能依赖于其他的服务,并且可能需要管理生命周期,例如在适当的时候可能需要调用服务的start()或stop()方法。
在继承AbstractService这种实现方式上,依赖注入和生命周期的管理需要手动在servlet或者监听者里面配置。
服务端通过注解方式实现的服务对Cometd产品特点提供了完美的支持。并且有限的支持依赖注入和生命周期管理通过org.cometd.annotation.ServerAnnotationProcessor类。
@@依赖注入支持
Comted工程提供了有限的依赖注入支持,因为这通常通过其他框架实现,例如spring或guice。
尤其是,它近支持了注入BayeuxServer对象的fields和methods。并且依赖注入仅在未执行的情况下执行一次。
如:
@org.cometd.annotation.Service("echoService")//使用@Service注解server类,并指定server名为“echoService”
public class EchoService
{
@javax.inject.Inject//通过标准的JSR 330注解field
private BayeuxServer bayeux;
}
@Inject注入被遵从JSR330标准的依赖注入容器所支持,如Spring3.x。
@@生命周期管理支持
Cometd 工程通过JSR 250 @PostConstruct and @PreDestroy标准 提供了生命周期管理注解。
这对于那些不适用依赖注入容器来管理生命周期的如spring提供支持。
@@通道配置支持
为了在chanel被实际订阅之前初始化,Comted API提供了BayeuxServer.createIfAbsent方法,它可以通过参数配置给定的channel
此外,在任何订阅或者监听者添加之前配置channel这一步骤非常的有用,例如在channel上配置授权人。
在注解服务内,我们可以使用@Configure注解方法,如:
@Service("echoService")
public class EchoService
{
@Inject
private BayeuxServer bayeux;
@Configure("/echo")
public void configure(ConfigurableServerChannel channel)
{
channel.setLazy(true);
channel.addAuthorizer(GrantAuthorizer.GRANT_PUBLISH);
}
}
@@Session 配置支持
继承AbstractServer的服务有两个方便反而方法访问LocalSession和ServerSession,方法名分别是
getLocalSession()和getServerSession()。
@Service("echoService")
public class EchoService
{
@Inject
private BayeuxServer bayeux;
@org.cometd.annotation.Session
private LocalSession localSession;
@org.cometd.annotation.Session
private ServerSession serverSession;
}
@session注解的域是可选的。我们可以仅仅使用LocalSession field,或者两个,或者都不使用。这取决于你是否需要他们。
Session域或者方法不能被@Inject标注。因为LocalSession和ServerSession是有关联的,并且绑定一个特殊的服务实例。
使用普通的注入机制会导致程序混乱。
@@监听着配置支持
对服务端来说,使用@Listener注解方法表示回调在服务端处理的消息调用的回调过程。
监听者消息通过获得SeverSession半对象的引用来发送消息,并且通过ServerMessage对server加工。
@Service("echoService")
public class EchoService
{
@Inject
private BayeuxServer bayeux;
@Session
private ServerSession serverSession;
@org.cometd.annotation.Listener("/echo")
public void echo(ServerSession remote, ServerMessage.Mutable message)
{
String channel = message.getChannel();
Object data = message.getData();
remote.deliver(serverSession, channel, data, null);
}
}
回调函数可能会返回false指明随后的监听者不应该被执行并且消息不会被publish
@@支持订阅配置
对服务端来说,使用@Subscription标注的方法表示本地端处理消息时的回调函数
本地处理等同于远程客户端处理,不是服务端本地。
语义非常类似于远程客户端的处理,就意义而言,消息在服务端处理完毕后将会被发布,当它到达另一端时发布者将不可用。
因此消息将被载入Message对象中,而非ServerMessage中.
@@注解处理
cometd注解处理类为ServerAnnotationProcessor
BayeuxServer bayeux = ...;
// Create the ServerAnnotationProcessor
ServerAnnotationProcessor processor = new ServerAnnotationProcessor(bayeux);
// Create the service instance
EchoService service = new EchoService();
// Process the annotated service
processor.process(service);
当process方法返回之后,服务器通过调用初始化生命周期方法以及注册监听者和订阅者已经处理完标注的BayeuxServer对象以及session对象