Java for Web学习笔记(九一):消息和集群(6)利用websocket实现订阅和发布(下)
来源:互联网 发布:北京中轴线一日游 知乎 编辑:程序博客网 时间:2024/06/01 10:43
ClusterEventMulticaster:自定义的ApplicationEventMulticaster
设置为root上下文的bean
在root上下文的配置文件中:
@Beanpublic ApplicationEventMulticaster applicationEventMulticaster(){ SimpleApplicationEventMulticaster eventMulticaster = new ClusterEventMulticaster(); //ClusterManager也是在监听事件,如果我们没有再次设置后台运行setTaskExecutor(),那么ClusterManager中的onApplicationEvent()就必须带上@Async的标记,否则这里会花费1分钟的时间,在此期间,由于无法往下继续进行web上下文的初始化(需先完成onApplicationEvent()),即/ping无法正常回应,最终导致失败。安全起见,我们仍可以在ClusterManager的onApplicationEvent()上加上@Async,大不了属于线程中的线程。 eventMulticaster.setTaskExecutor(new SimpleAsyncTaskExecutor()); return eventMulticaster;}
ClusterEventMulticaster代码
public class ClusterEventMulticaster extends SimpleApplicationEventMulticaster{ private static final Logger log = LogManager.getLogger(); private final Set<ClusterMessagingEndpoint> endpoints = new HashSet<>(); //管理和维护各个websocket连接 @Inject ApplicationContext context; //作为在Root上下文中设置为bean,因此这里将获得的是root上下文 //【1】自定义消息发布,如果属于ClusterEvent事件,将通过websocket发布给集群(当然排除掉接收的集群事件) @Override public void multicastEvent(ApplicationEvent event, ResolvableType eventType) { try{ super.multicastEvent(event, eventType); }finally{ try{ if(event instanceof ClusterEvent && !((ClusterEvent)event).isRebroadcasted()) publishClusteredEvent((ClusterEvent)event); }catch(Exception e){ log.error("Failed to broadcast distributable event to cluster.",e); } } } //【2】集群事件收发的相关方法 //【2.1】通过websocket向集群广播事件 private void publishClusteredEvent(ClusterEvent event){ synchronized(endpoints){ for(ClusterMessagingEndpoint endpoint : this.endpoints){ endpoint.send(event); } } } //【2.2】从websocket中接收到事件,需要向本地发布,以便触发响应的ApplicationEventListener。同时设置了消息中的rebroadcasted标记,表明无需向集群广播词消息 protected final void handleReceivedClusteredEvent(ClusterEvent event){ event.setRebroadcasted(); this.multicastEvent(event,null); //相当于在本地发布 } //【3】管理socket连接(ClusterMessagingEndpoint) //【3.1】注册websocket连接 protected void registerEndpoint(ClusterMessagingEndpoint endpoint){ synchronized (endpoints) { this.endpoints.add(endpoint); } } //【3.2】注销websocket连接 protected void deregisterEndpoint(ClusterMessagingEndpoint endpoint){ synchronized(endpoints){ this.endpoints.remove(endpoint); } } //【3.3】程序退出时,关闭所有的websocket连接 @PreDestroy public void shutdown(){ synchronized(this.endpoints){ for(ClusterMessagingEndpoint endpoint : this.endpoints){ endpoint.close(); } } } //【3.4】此处更合适的名字是createNode,根据监听的到websocket的url,创建连接(创建ClusterMessagingEndpoint对象),需要注意的WebSocketEndpoint不属于Spring框架,要手动将其纳入,否则无法在WebSocketEndpoint中支持@Inject。这在之前学些过,再次重温。 protected void registerNode(String endpoint){ log.info("Connecting to cluster node {}.", endpoint); /* A WebSocketContainer is an implementation provided object that provides applications a view * on the container running it. The WebSocketContainer container various configuration parameters * that control default session and buffer properties of the endpoints it contains. It also allows * the developer to deploy websocket client endpoints by initiating a web socket handshake from * the provided endpoint to a supplied URI where the peer endpoint is presumed to reside. * * A WebSocketContainer may be accessed by concurrent threads, so implementations must ensure the * integrity of its mutable attributes in such circumstances. * * URI uri = new URI("ws","localhost:8080",path,null,null); * Session session = ContainerProvider.getWebSocketContainer().connectToServer(this, uri); * */ WebSocketContainer container = ContainerProvider.getWebSocketContainer(); /* ClusterMessagingEndpoint是websocket的cilent+server,websocket不是spring framework。如果要向bean那样可以自动注入等,我们需要使用org.springframework.beans.factory.annotation.Configurable标注对于非Spring管理的bean。*/ ClusterMessagingEndpoint bean = this.context.getAutowireCapableBeanFactory() .createBean(ClusterMessagingEndpoint.class); try { log.info("Connect to server {}", endpoint); container.connectToServer(bean, new URI(endpoint)); } catch (DeploymentException | IOException | URISyntaxException e) { log.error("Failed to connect to cluster node {}.", endpoint, e); } }}
ClusterMessagingEndpoint:websocket的endpoint
//【1】我们关心的是从websocket连接中收到的集群的event,对于websocket的server和client放在一起处理。将事件对象序列化后,直接在websocket的连接中传递,因此codec就是实现序列化和反序列化,由内部静态类Codec来实现@ServerEndpoint(value = "/services/Messaging/{securityCode}", encoders = { ClusterMessagingEndpoint.Codec.class }, decoders = { ClusterMessagingEndpoint.Codec.class }, configurator = SpringConfigurator.class)@ClientEndpoint( encoders = { ClusterMessagingEndpoint.Codec.class }, decoders = { ClusterMessagingEndpoint.Codec.class })public class ClusterMessagingEndpoint { private static final Logger log = LogManager.getLogger(); private Session session; //支持@Inject,对于server,设置configurator为SpringConfigurator.class,每一个新的连接都会创建@ServiceEndpoint的bean。对于client需要作为Spring的bean创建,见之前在ClusterEventMulticaster中的代码。 @Inject ClusterEventMulticaster multicaster; @OnOpen public void open(Session session){ Map<String, String> parameters = session.getPathParameters(); if(parameters.containsKey("securityCode") && !"a83teo83hou9883hha9".equals(parameters.get("securityCode"))){ try { log.error("Received connection with illegal code {}.", parameters.get("securityCode")); session.close(new CloseReason(CloseReason.CloseCodes.VIOLATED_POLICY, "Illegal Code")); } catch (IOException e) { log.warn("Failed to close illegal connection.", e); } }else{ log.info("Successful connection onOpen."); this.session = session; this.multicaster.registerEndpoint(this); //websocket连接成功建立 } } @OnMessage public void receive(ClusterEvent message){ this.multicaster.handleReceivedClusteredEvent(message); //收到集群中的消息 } @OnClose public void close(){ log.info("Cluster node connection closed."); this.multicaster.deregisterEndpoint(this); //对方关闭连接 if(this.session.isOpen()){ try { this.session.close(); } catch (IOException e) { log.warn("Error while closing cluster node connection.", e); } } } public void send(ClusterEvent message){ try { session.getBasicRemote().sendObject(message); //通过websocket连接发布事件(消息) } catch (IOException | EncodeException e) { log.error("Failed to send message to adjacent node.", e); } } //【2】将事件对象序列化后,直接在websocket的连接中传递,因此codec就是实现序列化和反序列化,由内部静态类Codec来实现 public static class Codec implements Encoder.BinaryStream<ClusterEvent>,Decoder.BinaryStream<ClusterEvent> { @Override public void init(EndpointConfig config) { } @Override public void destroy() { } @Override public ClusterEvent decode(InputStream is) throws DecodeException, IOException { try(ObjectInputStream ois = new ObjectInputStream(is);){ try { return (ClusterEvent)ois.readObject(); } catch (ClassNotFoundException e) { throw new DecodeException((String)null, "Failed to decode.", e); } } } @Override public void encode(ClusterEvent object, OutputStream os) throws EncodeException, IOException { try(ObjectOutputStream oos = new ObjectOutputStream(os);){ oos.writeObject(object); } } }}
相关链接: 我的Professional Java for Web Applications相关文章
阅读全文
0 0
- Java for Web学习笔记(九一):消息和集群(6)利用websocket实现订阅和发布(下)
- Java for Web学习笔记(九十):消息和集群(5)利用websocket实现订阅和发布(上)
- Java for Web学习笔记(九五):消息和集群(10)利用RabbitMQ实现订阅和发布
- Java for Web学习笔记(九四):消息和集群(9)RabbitMQ和消息模式(下)
- Java for Web学习笔记(八九):消息和集群(4)定制发布和订购
- Java for Web学习笔记(九二):消息和集群(7)RabbitMQ和消息模式(上)
- Java for Web学习笔记(九三):消息和集群(8)RabbitMQ和消息模式(中)
- Java for Web学习笔记(四七):WebSocket(4)Java Client和二进制消息
- Java for Web学习笔记(八六):消息和集群(1)一般性了解
- Java for Web学习笔记(八八):消息和集群(3)序列化Serializable
- Java for Web学习笔记(八七):消息和集群(2)应用内的publish和subscribe
- Java for Web学习笔记(四八):WebSocket(5)encoder,decoder和configurator
- Redis学习笔记(6)消息的订阅与发布
- Redis学习笔记(十)消息通知(任务队列和发布订阅模式)
- Redis学习笔记(十)消息通知(任务队列和发布订阅模式)
- 消息队列-ActiveMQ学习笔记(三)-发布-订阅消息模式实现
- 消息队列-ActiveMQ学习笔记(三)-发布-订阅消息模式实现
- 利用redis简单实现消息订阅和发布
- C语言单元小结(1)
- shader总结七
- 什么是重定位?为什么需要重定位?(嵌入式下)
- 第一个项目(游戏)
- Spring Boot中Web应用的统一异常处理
- Java for Web学习笔记(九一):消息和集群(6)利用websocket实现订阅和发布(下)
- linux常用指令
- SparkML-note-PCA
- Python常用库大全
- LED灯
- Spring Boot属性配置文件详解
- 2017年11月10日作业
- 1023
- 谈谈