All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.redkale.net.http.WebSocketNode Maven / Gradle / Ivy

/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package org.redkale.net.http;

import static org.redkale.net.http.WebSocket.*;
import java.io.*;
import java.net.*;
import java.util.*;
import java.util.concurrent.*;
import java.util.logging.*;
import java.util.stream.Stream;
import javax.annotation.*;
import org.redkale.boot.*;
import org.redkale.convert.*;
import org.redkale.convert.json.JsonConvert;
import org.redkale.service.*;
import org.redkale.source.*;
import org.redkale.util.*;

/**
 *
 * 

* 详情见: https://redkale.org * * @author zhangjx */ public abstract class WebSocketNode { @Comment("存储用户ID的key前缀") public static final String SOURCE_SNCP_USERID_PREFIX = "sncpws_uid:"; @Comment("存储当前SNCP节点列表的key") public static final String SOURCE_SNCP_ADDRS_KEY = "sncpws_addrs"; protected final Logger logger = Logger.getLogger(this.getClass().getSimpleName()); //"SNCP_ADDR" 如果不是分布式(没有SNCP) 值为null @Resource(name = Application.RESNAME_SNCP_ADDR) protected InetSocketAddress localSncpAddress; //为SncpServer的服务address //如果不是分布式(没有SNCP) 值为null @RpcRemote protected WebSocketNode remoteNode; @Resource(name = "$_sendconvert") protected Convert sendConvert; //存放所有用户分布在节点上的队列信息,Set 为 sncpnode 的集合, key: groupid //集合包含 localSncpAddress //如果不是分布式(没有SNCP),sncpNodeAddresses 将不会被用到 @Resource(name = "$") protected CacheSource sncpNodeAddresses; //当前节点的本地WebSocketEngine protected WebSocketEngine localEngine; protected Semaphore semaphore; public void init(AnyValue conf) { if (sncpNodeAddresses != null && "memory".equals(sncpNodeAddresses.getType())) { sncpNodeAddresses.initValueType(InetSocketAddress.class); } if (localEngine != null) { int wsthreads = localEngine.wsthreads; if (wsthreads == 0) wsthreads = Runtime.getRuntime().availableProcessors() * 8; if (wsthreads > 0) this.semaphore = new Semaphore(wsthreads); } } public void destroy(AnyValue conf) { } @Local public final Semaphore getSemaphore() { return semaphore; } @Local public final void postDestroy(AnyValue conf) { if (this.localEngine == null) return; //关掉所有本地本地WebSocket this.localEngine.getLocalWebSockets().forEach(g -> g.close()); if (sncpNodeAddresses != null && localSncpAddress != null) { sncpNodeAddresses.removeSetItem(SOURCE_SNCP_ADDRS_KEY, InetSocketAddress.class, localSncpAddress); } } protected abstract CompletableFuture> getWebSocketAddresses(@RpcTargetAddress InetSocketAddress targetAddress, Serializable userid); protected abstract CompletableFuture sendMessage(@RpcTargetAddress InetSocketAddress targetAddress, Object message, boolean last, Serializable userid); protected abstract CompletableFuture broadcastMessage(@RpcTargetAddress InetSocketAddress targetAddress, WebSocketRange wsrange, Object message, boolean last); protected abstract CompletableFuture sendAction(@RpcTargetAddress InetSocketAddress targetAddress, WebSocketAction action, Serializable userid); protected abstract CompletableFuture broadcastAction(@RpcTargetAddress InetSocketAddress targetAddress, WebSocketAction action); protected abstract CompletableFuture connect(Serializable userid, InetSocketAddress sncpAddr); protected abstract CompletableFuture disconnect(Serializable userid, InetSocketAddress sncpAddr); protected abstract CompletableFuture changeUserid(Serializable fromuserid, Serializable touserid, InetSocketAddress sncpAddr); protected abstract CompletableFuture existsWebSocket(Serializable userid, @RpcTargetAddress InetSocketAddress targetAddress); protected abstract CompletableFuture forceCloseWebSocket(Serializable userid, @RpcTargetAddress InetSocketAddress targetAddress); //-------------------------------------------------------------------------------- final CompletableFuture connect(final Serializable userid) { if (logger.isLoggable(Level.FINEST)) logger.finest(localSncpAddress + " receive websocket connect event (" + userid + " on " + (this.localEngine == null ? null : this.localEngine.getEngineid()) + ")."); return connect(userid, localSncpAddress); } final CompletableFuture disconnect(final Serializable userid) { if (logger.isLoggable(Level.FINEST)) logger.finest(localSncpAddress + " receive websocket disconnect event (" + userid + " on " + (this.localEngine == null ? null : this.localEngine.getEngineid()) + ")."); return disconnect(userid, localSncpAddress); } final CompletableFuture changeUserid(Serializable olduserid, final Serializable newuserid) { if (logger.isLoggable(Level.FINEST)) logger.finest(localSncpAddress + " receive websocket changeUserid event (from " + olduserid + " to " + newuserid + " on " + (this.localEngine == null ? null : this.localEngine.getEngineid()) + ")."); return changeUserid(olduserid, newuserid, localSncpAddress); } //-------------------------------------------------------------------------------- /** * 获取目标地址
* 该方法仅供内部调用 * * @param targetAddress InetSocketAddress * @param userid Serializable * * @return 客户端地址列表 */ protected CompletableFuture> remoteWebSocketAddresses(@RpcTargetAddress InetSocketAddress targetAddress, Serializable userid) { if (remoteNode == null) return CompletableFuture.completedFuture(null); try { return remoteNode.getWebSocketAddresses(targetAddress, userid); } catch (Exception e) { logger.log(Level.WARNING, "remote " + targetAddress + " websocket getOnlineRemoteAddresses error", e); return CompletableFuture.completedFuture(null); } } /** * 获取用户在线的SNCP节点地址列表,不是分布式则返回元素数量为1,且元素值为null的列表
* InetSocketAddress 为 SNCP节点地址 * * @param userid Serializable * * @return 地址列表 */ public CompletableFuture> getRpcNodeAddresses(final Serializable userid) { if (this.sncpNodeAddresses != null) { tryAcquireSemaphore(); CompletableFuture> result = this.sncpNodeAddresses.getCollectionAsync(SOURCE_SNCP_USERID_PREFIX + userid, InetSocketAddress.class); if (semaphore != null) result.whenComplete((r, e) -> releaseSemaphore()); return result; } List rs = new ArrayList<>(); rs.add(this.localSncpAddress); return CompletableFuture.completedFuture(rs); } /** * 获取在线用户的详细连接信息
* Map.key 为 SNCP节点地址, 含值为null的key表示没有分布式 * Map.value 为 用户客户端的IP * * @param userid Serializable * * @return 地址集合 */ public CompletableFuture>> getRpcNodeWebSocketAddresses(final Serializable userid) { CompletableFuture> sncpFuture = getRpcNodeAddresses(userid); return sncpFuture.thenCompose((Collection addrs) -> { if (logger.isLoggable(Level.FINEST)) logger.finest("websocket found userid:" + userid + " on " + addrs); if (addrs == null || addrs.isEmpty()) return CompletableFuture.completedFuture(new HashMap<>()); CompletableFuture>> future = null; for (final InetSocketAddress nodeAddress : addrs) { CompletableFuture>> mapFuture = getWebSocketAddresses(nodeAddress, userid) .thenCompose((List list) -> CompletableFuture.completedFuture(Utility.ofMap(nodeAddress, list))); future = future == null ? mapFuture : future.thenCombine(mapFuture, (a, b) -> Utility.merge(a, b)); } return future == null ? CompletableFuture.completedFuture(new HashMap<>()) : future; }); } /** * 获取在线用户总数 * * * @return boolean */ public CompletableFuture getUserSize() { if (this.localEngine != null && this.sncpNodeAddresses == null) { return CompletableFuture.completedFuture(this.localEngine.getLocalUserSize()); } tryAcquireSemaphore(); CompletableFuture rs = this.sncpNodeAddresses.queryKeysStartsWithAsync(SOURCE_SNCP_USERID_PREFIX).thenApply(v -> v.size()); if (semaphore != null) rs.whenComplete((r, e) -> releaseSemaphore()); return rs; } /** * @deprecated * * 判断指定用户是否WebSocket在线 * * @param userid Serializable * * @return boolean */ private CompletableFuture existsWebSocket2(final Serializable userid) { if (this.localEngine != null && this.sncpNodeAddresses == null) { return CompletableFuture.completedFuture(this.localEngine.existsLocalWebSocket(userid)); } tryAcquireSemaphore(); CompletableFuture rs = this.sncpNodeAddresses.existsAsync(SOURCE_SNCP_USERID_PREFIX + userid); if (semaphore != null) rs.whenComplete((r, e) -> releaseSemaphore()); return rs; } /** * 判断指定用户是否WebSocket在线 * * @param userid Serializable * * @return boolean */ @Local public CompletableFuture existsWebSocket(final Serializable userid) { CompletableFuture localFuture = null; if (this.localEngine != null) localFuture = CompletableFuture.completedFuture(localEngine.existsLocalWebSocket(userid)); if (this.sncpNodeAddresses == null || this.remoteNode == null) { if (logger.isLoggable(Level.FINEST)) logger.finest("websocket remote node is null"); //没有CacheSource就不会有分布式节点 return localFuture; } //远程节点关闭 tryAcquireSemaphore(); CompletableFuture> addrsFuture = sncpNodeAddresses.getCollectionAsync(SOURCE_SNCP_USERID_PREFIX + userid, InetSocketAddress.class); if (semaphore != null) addrsFuture.whenComplete((r, e) -> releaseSemaphore()); CompletableFuture remoteFuture = addrsFuture.thenCompose((Collection addrs) -> { if (logger.isLoggable(Level.FINEST)) logger.finest("websocket found userid:" + userid + " on " + addrs); if (addrs == null || addrs.isEmpty()) return CompletableFuture.completedFuture(false); CompletableFuture future = null; for (InetSocketAddress addr : addrs) { if (addr == null || addr.equals(localSncpAddress)) continue; future = future == null ? remoteNode.existsWebSocket(userid, addr) : future.thenCombine(remoteNode.existsWebSocket(userid, addr), (a, b) -> a | b); } return future == null ? CompletableFuture.completedFuture(false) : future; }); return localFuture == null ? remoteFuture : localFuture.thenCombine(remoteFuture, (a, b) -> a | b); } /** * 强制关闭用户WebSocket * * @param userid Serializable * * @return int */ @Local public CompletableFuture forceCloseWebSocket(final Serializable userid) { CompletableFuture localFuture = null; if (this.localEngine != null) localFuture = CompletableFuture.completedFuture(localEngine.forceCloseLocalWebSocket(userid)); if (this.sncpNodeAddresses == null || this.remoteNode == null) { if (logger.isLoggable(Level.FINEST)) logger.finest("websocket remote node is null"); //没有CacheSource就不会有分布式节点 return localFuture; } //远程节点关闭 tryAcquireSemaphore(); CompletableFuture> addrsFuture = sncpNodeAddresses.getCollectionAsync(SOURCE_SNCP_USERID_PREFIX + userid, InetSocketAddress.class); if (semaphore != null) addrsFuture.whenComplete((r, e) -> releaseSemaphore()); CompletableFuture remoteFuture = addrsFuture.thenCompose((Collection addrs) -> { if (logger.isLoggable(Level.FINEST)) logger.finest("websocket found userid:" + userid + " on " + addrs); if (addrs == null || addrs.isEmpty()) return CompletableFuture.completedFuture(0); CompletableFuture future = null; for (InetSocketAddress addr : addrs) { if (addr == null || addr.equals(localSncpAddress)) continue; future = future == null ? remoteNode.forceCloseWebSocket(userid, addr) : future.thenCombine(remoteNode.forceCloseWebSocket(userid, addr), (a, b) -> a + b); } return future == null ? CompletableFuture.completedFuture(0) : future; }); return localFuture == null ? remoteFuture : localFuture.thenCombine(remoteFuture, (a, b) -> a + b); } //-------------------------------------------------------------------------------- /** * 获取本地的WebSocketEngine,没有则返回null * * * @return WebSocketEngine */ @Local public final WebSocketEngine getLocalWebSocketEngine() { return this.localEngine; } /** * 向指定用户发送消息,先发送本地连接,再发送远程连接
* 如果当前WebSocketNode是远程模式,此方法只发送远程连接 * * @param message 消息内容 * @param userids Stream * * @return 为0表示成功, 其他值表示部分发送异常 */ @Local public final CompletableFuture sendMessage(Object message, final Stream userids) { return sendMessage((Convert) null, message, true, userids); } /** * 向指定用户发送消息,先发送本地连接,再发送远程连接
* 如果当前WebSocketNode是远程模式,此方法只发送远程连接 * * @param message 消息内容 * @param userids Serializable[] * * @return 为0表示成功, 其他值表示部分发送异常 */ @Local public final CompletableFuture sendMessage(Object message, final Serializable... userids) { return sendMessage((Convert) null, message, true, userids); } /** * 向指定用户发送消息,先发送本地连接,再发送远程连接
* 如果当前WebSocketNode是远程模式,此方法只发送远程连接 * * @param convert Convert * @param message 消息内容 * @param userids Stream * * @return 为0表示成功, 其他值表示部分发送异常 */ @Local public final CompletableFuture sendMessage(final Convert convert, Object message, final Stream userids) { return sendMessage(convert, message, true, userids); } /** * 向指定用户发送消息,先发送本地连接,再发送远程连接
* 如果当前WebSocketNode是远程模式,此方法只发送远程连接 * * @param convert Convert * @param message 消息内容 * @param userids Serializable[] * * @return 为0表示成功, 其他值表示部分发送异常 */ @Local public final CompletableFuture sendMessage(final Convert convert, Object message, final Serializable... userids) { return sendMessage(convert, message, true, userids); } /** * 向指定用户发送消息,先发送本地连接,再发送远程连接
* 如果当前WebSocketNode是远程模式,此方法只发送远程连接 * * @param message 消息内容 * @param last 是否最后一条 * @param userids Stream * * @return 为0表示成功, 其他值表示部分发送异常 */ @Local public final CompletableFuture sendMessage(final Object message, final boolean last, final Stream userids) { return sendMessage((Convert) null, message, last, userids); } /** * 向指定用户发送消息,先发送本地连接,再发送远程连接
* 如果当前WebSocketNode是远程模式,此方法只发送远程连接 * * @param message 消息内容 * @param last 是否最后一条 * @param userids Serializable[] * * @return 为0表示成功, 其他值表示部分发送异常 */ @Local public final CompletableFuture sendMessage(final Object message, final boolean last, final Serializable... userids) { return sendMessage((Convert) null, message, last, userids); } /** * 向指定用户发送消息,先发送本地连接,再发送远程连接
* 如果当前WebSocketNode是远程模式,此方法只发送远程连接 * * @param convert Convert * @param message0 消息内容 * @param last 是否最后一条 * @param userids Stream * * @return 为0表示成功, 其他值表示部分发送异常 */ @Local public final CompletableFuture sendMessage(final Convert convert, final Object message0, final boolean last, final Stream userids) { Object[] array = userids.toArray(); Serializable[] ss = new Serializable[array.length]; for (int i = 0; i < array.length; i++) { ss[i] = (Serializable) array[i]; } return sendMessage(convert, message0, last, ss); } /** * 向指定用户发送消息,先发送本地连接,再发送远程连接
* 如果当前WebSocketNode是远程模式,此方法只发送远程连接 * * @param convert Convert * @param message0 消息内容 * @param last 是否最后一条 * @param userids Serializable[] * * @return 为0表示成功, 其他值表示部分发送异常 */ @Local public CompletableFuture sendMessage(final Convert convert, final Object message0, final boolean last, final Serializable... userids) { if (userids == null || userids.length < 1) return CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY); if (message0 instanceof CompletableFuture) return ((CompletableFuture) message0).thenApply(msg -> sendMessage(convert, msg, last, userids)); final Object message = (convert == null || message0 instanceof WebSocketPacket) ? message0 : ((convert instanceof TextConvert) ? new WebSocketPacket(((TextConvert) convert).convertTo(message0), last) : new WebSocketPacket(((BinaryConvert) convert).convertTo(message0), last)); if (this.localEngine != null && this.sncpNodeAddresses == null) { //本地模式且没有分布式 return this.localEngine.sendMessage(message, last, userids); } final Object remoteMessage = formatRemoteMessage(message); CompletableFuture future = null; for (Serializable userid : userids) { future = future == null ? sendOneMessage(remoteMessage, last, userid) : future.thenCombine(sendOneMessage(remoteMessage, last, userid), (a, b) -> a | b); } return future == null ? CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY) : future; } /** * 广播消息, 给所有人发消息 * * @param message 消息内容 * * @return 为0表示成功, 其他值表示部分发送异常 */ @Local public final CompletableFuture broadcastMessage(final Object message) { return broadcastMessage((Convert) null, message, true); } /** * 广播消息, 给所有人发消息 * * @param wsrange 过滤条件 * @param message 消息内容 * * @return 为0表示成功, 其他值表示部分发送异常 */ @Local public final CompletableFuture broadcastMessage(final WebSocketRange wsrange, final Object message) { return broadcastMessage(wsrange, (Convert) null, message, true); } /** * 广播消息, 给所有人发消息 * * @param convert Convert * @param message 消息内容 * * @return 为0表示成功, 其他值表示部分发送异常 */ @Local public final CompletableFuture broadcastMessage(final Convert convert, final Object message) { return broadcastMessage(convert, message, true); } /** * 广播消息, 给所有人发消息 * * @param wsrange 过滤条件 * @param convert Convert * @param message 消息内容 * * @return 为0表示成功, 其他值表示部分发送异常 */ @Local public final CompletableFuture broadcastMessage(final WebSocketRange wsrange, final Convert convert, final Object message) { return broadcastMessage(wsrange, convert, message, true); } /** * 广播消息, 给所有人发消息 * * @param message 消息内容 * @param last 是否最后一条 * * @return 为0表示成功, 其他值表示部分发送异常 */ @Local public final CompletableFuture broadcastMessage(final Object message, final boolean last) { return broadcastMessage((Convert) null, message, last); } /** * 广播消息, 给所有人发消息 * * @param wsrange 过滤条件 * @param message 消息内容 * @param last 是否最后一条 * * @return 为0表示成功, 其他值表示部分发送异常 */ @Local public final CompletableFuture broadcastMessage(final WebSocketRange wsrange, final Object message, final boolean last) { return broadcastMessage(wsrange, (Convert) null, message, last); } /** * 广播消息, 给所有人发消息 * * @param convert Convert * @param message0 消息内容 * @param last 是否最后一条 * * @return 为0表示成功, 其他值表示部分发送异常 */ @Local public final CompletableFuture broadcastMessage(final Convert convert, final Object message0, final boolean last) { return broadcastMessage((WebSocketRange) null, convert, message0, last); } /** * 广播消息, 给所有人发消息 * * @param wsrange 过滤条件 * @param convert Convert * @param message0 消息内容 * @param last 是否最后一条 * * @return 为0表示成功, 其他值表示部分发送异常 */ @Local public CompletableFuture broadcastMessage(final WebSocketRange wsrange, final Convert convert, final Object message0, final boolean last) { if (message0 instanceof CompletableFuture) return ((CompletableFuture) message0).thenApply(msg -> broadcastMessage(wsrange, convert, msg, last)); final Object message = (convert == null || message0 instanceof WebSocketPacket) ? message0 : ((convert instanceof TextConvert) ? new WebSocketPacket(((TextConvert) convert).convertTo(message0), last) : new WebSocketPacket(((BinaryConvert) convert).convertTo(message0), last)); if (this.localEngine != null && this.sncpNodeAddresses == null) { //本地模式且没有分布式 return this.localEngine.broadcastMessage(wsrange, message, last); } final Object remoteMessage = formatRemoteMessage(message); CompletableFuture localFuture = this.localEngine == null ? null : this.localEngine.broadcastMessage(wsrange, message, last); tryAcquireSemaphore(); CompletableFuture> addrsFuture = sncpNodeAddresses.getCollectionAsync(SOURCE_SNCP_ADDRS_KEY, InetSocketAddress.class); if (semaphore != null) addrsFuture.whenComplete((r, e) -> releaseSemaphore()); CompletableFuture remoteFuture = addrsFuture.thenCompose((Collection addrs) -> { if (logger.isLoggable(Level.FINEST)) logger.finest("websocket broadcast message (" + remoteMessage + ") on " + addrs); if (addrs == null || addrs.isEmpty()) return CompletableFuture.completedFuture(0); CompletableFuture future = null; for (InetSocketAddress addr : addrs) { if (addr == null || addr.equals(localSncpAddress)) continue; future = future == null ? remoteNode.broadcastMessage(addr, wsrange, remoteMessage, last) : future.thenCombine(remoteNode.broadcastMessage(addr, wsrange, remoteMessage, last), (a, b) -> a | b); } return future == null ? CompletableFuture.completedFuture(0) : future; }); return localFuture == null ? remoteFuture : localFuture.thenCombine(remoteFuture, (a, b) -> a | b); } protected CompletableFuture sendOneMessage(final Object message, final boolean last, final Serializable userid) { if (message instanceof CompletableFuture) return ((CompletableFuture) message).thenApply(msg -> sendOneMessage(msg, last, userid)); if (logger.isLoggable(Level.FINEST)) { logger.finest("websocket want send message {userid:" + userid + ", content:'" + (message instanceof WebSocketPacket ? ((WebSocketPacket) message).toSimpleString() : JsonConvert.root().convertTo(message)) + "'} from locale node to " + ((this.localEngine != null) ? "locale" : "remote") + " engine"); } CompletableFuture localFuture = null; if (this.localEngine != null) localFuture = localEngine.sendMessage(message, last, userid); if (this.sncpNodeAddresses == null || this.remoteNode == null) { if (logger.isLoggable(Level.FINEST)) logger.finest("websocket remote node is null"); //没有CacheSource就不会有分布式节点 return localFuture == null ? CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY) : localFuture; } //远程节点发送消息 final Object remoteMessage = formatRemoteMessage(message); tryAcquireSemaphore(); CompletableFuture> addrsFuture = sncpNodeAddresses.getCollectionAsync(SOURCE_SNCP_USERID_PREFIX + userid, InetSocketAddress.class); if (semaphore != null) addrsFuture.whenComplete((r, e) -> releaseSemaphore()); CompletableFuture remoteFuture = addrsFuture.thenCompose((Collection addrs) -> { if (addrs == null || addrs.isEmpty()) { if (logger.isLoggable(Level.FINER)) logger.finer("websocket not found userid:" + userid + " on any node "); return CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY); } if (logger.isLoggable(Level.FINEST)) logger.finest("websocket(localaddr=" + localSncpAddress + ") found userid:" + userid + " on " + addrs); CompletableFuture future = null; for (InetSocketAddress addr : addrs) { if (addr == null || addr.equals(localSncpAddress)) continue; future = future == null ? remoteNode.sendMessage(addr, remoteMessage, last, userid) : future.thenCombine(remoteNode.sendMessage(addr, remoteMessage, last, userid), (a, b) -> a | b); } return future == null ? CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY) : future; }); return localFuture == null ? remoteFuture : localFuture.thenCombine(remoteFuture, (a, b) -> a | b); } /** * 广播操作, 给所有人发操作 * * @param action 操作参数 * * @return 为0表示成功, 其他值表示部分发送异常 */ @Local public CompletableFuture broadcastAction(final WebSocketAction action) { if (this.localEngine != null && this.sncpNodeAddresses == null) { //本地模式且没有分布式 return this.localEngine.broadcastAction(action); } CompletableFuture localFuture = this.localEngine == null ? null : this.localEngine.broadcastAction(action); tryAcquireSemaphore(); CompletableFuture> addrsFuture = sncpNodeAddresses.getCollectionAsync(SOURCE_SNCP_ADDRS_KEY, InetSocketAddress.class); if (semaphore != null) addrsFuture.whenComplete((r, e) -> releaseSemaphore()); CompletableFuture remoteFuture = addrsFuture.thenCompose((Collection addrs) -> { if (logger.isLoggable(Level.FINEST)) logger.finest("websocket broadcast action (" + action + ") on " + addrs); if (addrs == null || addrs.isEmpty()) return CompletableFuture.completedFuture(0); CompletableFuture future = null; for (InetSocketAddress addr : addrs) { if (addr == null || addr.equals(localSncpAddress)) continue; future = future == null ? remoteNode.broadcastAction(addr, action) : future.thenCombine(remoteNode.broadcastAction(addr, action), (a, b) -> a | b); } return future == null ? CompletableFuture.completedFuture(0) : future; }); return localFuture == null ? remoteFuture : localFuture.thenCombine(remoteFuture, (a, b) -> a | b); } /** * 向指定用户发送操作,先发送本地连接,再发送远程连接
* 如果当前WebSocketNode是远程模式,此方法只发送远程连接 * * @param action 操作参数 * @param userids Serializable[] * * @return 为0表示成功, 其他值表示部分发送异常 */ @Local public CompletableFuture sendAction(final WebSocketAction action, final Serializable... userids) { if (userids == null || userids.length < 1) return CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY); if (this.localEngine != null && this.sncpNodeAddresses == null) { //本地模式且没有分布式 return this.localEngine.sendAction(action, userids); } CompletableFuture future = null; for (Serializable userid : userids) { future = future == null ? sendOneAction(action, userid) : future.thenCombine(sendOneAction(action, userid), (a, b) -> a | b); } return future == null ? CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY) : future; } protected CompletableFuture sendOneAction(final WebSocketAction action, final Serializable userid) { if (logger.isLoggable(Level.FINEST)) { logger.finest("websocket want send action {userid:" + userid + ", action:" + action + "} from locale node to " + ((this.localEngine != null) ? "locale" : "remote") + " engine"); } CompletableFuture localFuture = null; if (this.localEngine != null) localFuture = localEngine.sendAction(action, userid); if (this.sncpNodeAddresses == null || this.remoteNode == null) { if (logger.isLoggable(Level.FINEST)) logger.finest("websocket remote node is null"); //没有CacheSource就不会有分布式节点 return localFuture == null ? CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY) : localFuture; } //远程节点发送操作 tryAcquireSemaphore(); CompletableFuture> addrsFuture = sncpNodeAddresses.getCollectionAsync(SOURCE_SNCP_USERID_PREFIX + userid, InetSocketAddress.class); if (semaphore != null) addrsFuture.whenComplete((r, e) -> releaseSemaphore()); CompletableFuture remoteFuture = addrsFuture.thenCompose((Collection addrs) -> { if (addrs == null || addrs.isEmpty()) { if (logger.isLoggable(Level.FINER)) logger.finer("websocket not found userid:" + userid + " on any node "); return CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY); } if (logger.isLoggable(Level.FINEST)) logger.finest("websocket(localaddr=" + localSncpAddress + ") found userid:" + userid + " on " + addrs); CompletableFuture future = null; for (InetSocketAddress addr : addrs) { if (addr == null || addr.equals(localSncpAddress)) continue; future = future == null ? remoteNode.sendAction(addr, action, userid) : future.thenCombine(remoteNode.sendAction(addr, action, userid), (a, b) -> a | b); } return future == null ? CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY) : future; }); return localFuture == null ? remoteFuture : localFuture.thenCombine(remoteFuture, (a, b) -> a | b); } protected Object formatRemoteMessage(Object message) { if (message instanceof WebSocketPacket) return message; if (message instanceof byte[]) return message; if (message instanceof CharSequence) return message; if (sendConvert instanceof TextConvert) ((TextConvert) sendConvert).convertTo(message); if (sendConvert instanceof BinaryConvert) ((BinaryConvert) sendConvert).convertTo(message); return JsonConvert.root().convertTo(message); } protected boolean tryAcquireSemaphore() { if (this.semaphore == null) return true; try { return this.semaphore.tryAcquire(6, TimeUnit.SECONDS); } catch (Exception e) { return false; } } protected void releaseSemaphore() { if (this.semaphore != null) this.semaphore.release(); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy