我无法为Atmosphere资源的每个用户会话建立Atmosphere Broadcaster。我可以从文档中收集到的是如何构建向每个用户广播相同消息的“聊天”应用程序。
是否可以让Atmosphere框架为每个用户会话建立一个频道,或者我是否必须做一些事情并使用内存映射自己处理这些连接?
这是我想要的资源:
/websockets/notifications
我希望用户A和B从不同的浏览器连接到此频道,然后能够独立地传输消息。我应该能够使用他们的会话ID让大气层了解哪个人发送回复。
Atmosphere是否支持此事?
相关POM.xml
<spring-boot-starter-web.version>1.3.3.RELEASE</spring-boot-starter-web.version>
<atmosphere-runtime.version>2.4.4</atmosphere-runtime.version>
<atmosphere-javascript.version>2.3.0</atmosphere-javascript.version>
大气配置
package com.hello;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRegistration;
import org.atmosphere.cache.UUIDBroadcasterCache;
import org.atmosphere.cpr.ApplicationConfig;
import org.atmosphere.cpr.AtmosphereFramework;
import org.atmosphere.cpr.AtmosphereServlet;
import org.atmosphere.cpr.MetaBroadcaster;
import org.springframework.boot.context.embedded.ServletContextInitializer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class AtmosphereConfiguration implements ServletContextInitializer {
@Bean
public AtmosphereServlet atmosphereServlet() {
return new AtmosphereServlet();
}
@Bean
public AtmosphereFramework atmosphereFramework() {
return atmosphereServlet().framework();
}
@Bean
public MetaBroadcaster metaBroadcaster() {
AtmosphereFramework framework = atmosphereFramework();
return framework.metaBroadcaster();
}
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
configureAthmosphere(atmosphereServlet(), servletContext);
}
private void configureAthmosphere(AtmosphereServlet servlet, ServletContext servletContext) {
ServletRegistration.Dynamic atmosphereServlet = servletContext.addServlet("atmosphereServlet", servlet);
atmosphereServlet.setInitParameter(ApplicationConfig.ANNOTATION_PACKAGE, "com.hello");
atmosphereServlet.setInitParameter(ApplicationConfig.BROADCASTER_CACHE, UUIDBroadcasterCache.class.getName());
atmosphereServlet.setInitParameter(ApplicationConfig.BROADCASTER_SHARABLE_THREAD_POOLS, "true");
atmosphereServlet.setInitParameter(ApplicationConfig.BROADCASTER_MESSAGE_PROCESSING_THREADPOOL_MAXSIZE, "10");
atmosphereServlet.setInitParameter(ApplicationConfig.BROADCASTER_ASYNC_WRITE_THREADPOOL_MAXSIZE, "10");
servletContext.addListener(new org.atmosphere.cpr.SessionSupport());
atmosphereServlet.addMapping("/websocket/*");
atmosphereServlet.setLoadOnStartup(0);
atmosphereServlet.setAsyncSupported(true);
}
}
大气资源
package com.hello;
import java.nio.charset.StandardCharsets;
import org.atmosphere.config.service.Get;
import org.atmosphere.config.service.Disconnect;
import org.atmosphere.config.service.ManagedService;
import org.atmosphere.config.service.Ready;
import org.atmosphere.cpr.AtmosphereResource;
import org.atmosphere.cpr.AtmosphereResourceEvent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ManagedService(path = NotificationAtmosphereResource.PATH)
public class NotificationAtmosphereResource {
public static final String PATH = "/websocket/notifications";
private Logger logger = LoggerFactory.getLogger(NotificationAtmosphereResource.class);
@Get
public void init(AtmosphereResource resource){
resource.getResponse().setCharacterEncoding(StandardCharsets.UTF_8.name());
}
@Ready
public void onReady(final AtmosphereResource resource) {
logger.info("Connected {}", resource.uuid());
}
@Disconnect
public void onDisconnect(AtmosphereResourceEvent event) {
logger.info("Client {} disconnected [{}]", event.getResource().uuid(),
(event.isCancelled() ? "cancelled" : "closed"));
}
}
我发出消息的服务
package com.hello;
import org.atmosphere.cpr.MetaBroadcaster;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class NotificationEmitterBean implements NotificationEmitter {
private Logger logger = LoggerFactory.getLogger(NotificationEmitterBean.class);
@Autowired
private MetaBroadcaster metaBroadcaster;
@Autowired
private NotificationService notificationService;
@Autowired
private JsonMapper jsonMapper;
@Override
public void emitNotification(String sessionId, String msg) {
// This will broadcast to all users on /websocket/notifications
// How can I use sessionId to broadcast to the respective browser?
metaBroadcaster.broadcastTo(NotificationAtmosphereResource.PATH,
jsonMapper.toJson(msg));
}
}
}
答案 0 :(得分:0)
我能够让这个工作的唯一方法是创建我自己的基于会话的广播员。我使用Jeanfrancois Arcand编写的ExcludeSessionBroadcaster作为我的基线。
<强> IncludeSessionBroadcaster.java 强>
package com.hello;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.Future;
import org.atmosphere.cpr.AtmosphereConfig;
import org.atmosphere.cpr.AtmosphereResource;
import org.atmosphere.cpr.Broadcaster;
import org.atmosphere.cpr.BroadcasterFuture;
import org.atmosphere.cpr.DefaultBroadcaster;
import org.atmosphere.cpr.Deliver;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* An implementation of {@link DefaultBroadcaster} that include one or more {@link AtmosphereResource}
*
* Based on ExcludeSessionBroadcaster written by Jeanfrancois Arcand
*
* @author Steven Zgaljic
*/
public class IncludeSessionBroadcaster extends DefaultBroadcaster {
private static final Logger logger = LoggerFactory.getLogger(IncludeSessionBroadcaster.class);
public IncludeSessionBroadcaster(){}
public Broadcaster initialize(String id, AtmosphereConfig config) {
return super.initialize(id, config);
}
/**
* the AtmosphereResource r will be include for this broadcast
*
* @param msg
* @param r
* @return
*/
@Override
public Future<Object> broadcast(Object msg, AtmosphereResource r) {
if (destroyed.get()) {
throw new IllegalStateException("This Broadcaster has been destroyed and cannot be used");
}
Set<AtmosphereResource> sub = new HashSet<AtmosphereResource>();
sub.removeAll(resources);
sub.add(r);
start();
Object newMsg = filter(msg);
if (newMsg == null) {
return null;
}
BroadcasterFuture<Object> f = new BroadcasterFuture<Object>(newMsg, sub.size());
dispatchMessages(new Deliver(newMsg, sub, f, msg));
return f;
}
/**
* the AtmosphereResources subset will be include for this broadcast
*
* @param msg
* @param subset
* @return
*/
@Override
public Future<Object> broadcast(Object msg, Set<AtmosphereResource> subset) {
if (destroyed.get()) {
return futureDone(msg);
}
subset.retainAll(resources);
start();
Object newMsg = filter(msg);
if (newMsg == null) {
return futureDone(msg);
}
BroadcasterFuture<Object> f = new BroadcasterFuture<Object>(newMsg, subset.size());
dispatchMessages(new Deliver(newMsg, subset, f, msg));
return f;
}
/**
* session will be include for this broadcast
*
* @param msg
* @param s
* @return
*/
public Future<Object> broadcast(Object msg, String sessionId) {
if (destroyed.get()) {
return futureDone(msg);
}
Set<AtmosphereResource> subset = new HashSet<AtmosphereResource>();
for (AtmosphereResource r : resources) {
if (!r.getAtmosphereResourceEvent().isCancelled() &&
sessionId.equals(r.getRequest().getSession().getId())) {
subset.add(r);
break;
}
}
start();
Object newMsg = filter(msg);
if (newMsg == null) {
return futureDone(msg);
}
BroadcasterFuture<Object> f = new BroadcasterFuture<Object>(newMsg, subset.size());
dispatchMessages(new Deliver(newMsg, subset, f, msg));
return f;
}
}
然后我将这个广播公司分配给了Atmosphere资源。
<强> NotificationAtmosphereResource.java 强>
package com.hello;
import java.nio.charset.StandardCharsets;
import org.atmosphere.config.service.Get;
import org.atmosphere.config.service.Disconnect;
import org.atmosphere.config.service.ManagedService;
import org.atmosphere.config.service.Ready;
import org.atmosphere.cpr.AtmosphereResource;
import org.atmosphere.cpr.AtmosphereResourceEvent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ManagedService(path = NotificationAtmosphereResource.PATH,
broadcaster=IncludeSessionBroadcaster.class)
public class NotificationAtmosphereResource {
public static final String PATH = "/websocket/notifications";
private Logger logger = LoggerFactory.getLogger(NotificationAtmosphereResource.class);
@Get
public void init(AtmosphereResource resource){
resource.getResponse().setCharacterEncoding(StandardCharsets.UTF_8.name());
}
@Ready
public void onReady(final AtmosphereResource resource) {
logger.info("Connected {}", resource.uuid());
}
@Disconnect
public void onDisconnect(AtmosphereResourceEvent event) {
logger.info("Client {} disconnected [{}]", event.getResource().uuid(),
(event.isCancelled() ? "cancelled" : "closed"));
}
}
然后我可以只向我想要的浏览器(sessionId)发送消息。
<强> NotificationEmitterBean.java 强>
package com.hello;
import org.atmosphere.cpr.MetaBroadcaster;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class NotificationEmitterBean implements NotificationEmitter {
private Logger logger = LoggerFactory.getLogger(NotificationEmitterBean.class);
@Autowired
private BroadcasterFactory factory;
@Override
public void emitNotification(String sessionId, String msg) {
((IncludeSessionBroadcaster)factory.lookup(NotificationAtmosphereResource.PATH)).broadcast(msg, sessionId);
}
}
}