實(shí)時(shí)獲取 springboot-websocket-session

WebSocketSessionContainer

import org.springframework.stereotype.Component;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.messaging.SubProtocolWebSocketHandler;

import javax.inject.Inject;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Function;
import java.util.stream.Collectors;

@Component
public class WebSocketSessionContainer {
    private final Map<String, Object> webSocketSessionMap;

    private final SubProtocolWebSocketHandler        websocketHandler;
    private final Field                              field_session;
    private final Function<Object, WebSocketSession> transferFn;

    @Inject
    public WebSocketSessionContainer(WebSocketHandler websocketHandler) throws ClassNotFoundException {
        this.websocketHandler = SubProtocolWebSocketHandler.class.cast(websocketHandler);

        Field field_sessions = ReflectionUtils.findField(SubProtocolWebSocketHandler.class, "sessions");
        ReflectionUtils.makeAccessible(field_sessions);
        Object idToSessionHolder = ReflectionUtils.getField(field_sessions, websocketHandler);
        this.webSocketSessionMap = idToSessionHolder != null ? (Map<String, Object>) idToSessionHolder : Map.of();

        String   typeName = ((ParameterizedType) field_sessions.getGenericType()).getActualTypeArguments()[1].getTypeName();
        Class<?> clazz    = ClassUtils.forName(typeName, null);  //WebSocketSessionHolder.class
        this.field_session = ReflectionUtils.findField(clazz, "session");
        ReflectionUtils.makeAccessible(this.field_session);  //type is WebSocketSession

        this.transferFn = (webSocketSessionHolder) -> (WebSocketSession) ReflectionUtils.getField(field_session, webSocketSessionHolder);

    }


    public Map<String, WebSocketSession> findAll() {
        Function<String, Object> fn = k -> webSocketSessionMap.get(k);
        return webSocketSessionMap.keySet().stream()
                .collect(Collectors.toUnmodifiableMap(Function.identity(), fn.andThen(transferFn)));
    }

    public WebSocketSession getOrNull(String id) {
        return Optional.ofNullable(webSocketSessionMap.getOrDefault(id, null))
                .filter(Objects::nonNull)
                .map(transferFn)
                .orElseGet(() -> null);
    }

}

結(jié)合 springboot-actuator 暴露 session

import com.ft.suse.websocket.session.WebSocketSessionContainer;
import lombok.*;
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
import org.springframework.boot.actuate.endpoint.annotation.Selector;
import org.springframework.boot.actuate.endpoint.annotation.WriteOperation;
import org.springframework.http.HttpHeaders;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.WebSocketExtension;
import org.springframework.web.socket.WebSocketSession;

import javax.inject.Inject;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.URI;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * http or jmx
 */
@Endpoint(id = "websocket-session")
@Component
public class WebSocketSessionEndpoint {

    private final WebSocketSessionContainer wsSessionContainer;

    @Inject
    public WebSocketSessionEndpoint(WebSocketSessionContainer wsSessionContainer) {
        this.wsSessionContainer = wsSessionContainer;
    }


    @ReadOperation
    public Map<String, MySessionDescriptor> sessions() {
        return wsSessionContainer.findAll().entrySet().stream()
                .filter(Objects::nonNull)
                .collect(Collectors.toMap(e -> e.getKey(), e -> from(e.getValue())));
    }

    @ReadOperation
    public MySessionDescriptor sessionEntry(@Selector String id) {
        return Optional.ofNullable(wsSessionContainer.getOrNull(id))
                .filter(Objects::nonNull)
                .map(this::from)
                .orElseGet(() -> null);
    }

    @WriteOperation
    public String closeSession(@Selector String id) {
        WebSocketSession wsSession = wsSessionContainer.getOrNull(id);
        if (wsSession != null) {
            if (wsSession.isOpen()) {
                try {
                    wsSession.close();
                    return "[success]";
                } catch (IOException e) {
//                e.printStackTrace();
                    return "[fail]:" + e.getMessage();
                }
            } else {
                return "[fail]:session is already closed";
            }
        }
        return "[fail]:session not existed";
    }

    private MySessionDescriptor from(WebSocketSession session) {
        return MySessionDescriptor.builder()
                .id(session.getId())
                .uri(session.getUri())
                .username(session.getPrincipal().getName())
                .acceptedProtocol(session.getAcceptedProtocol())
                .binaryMessageSizeLimit(session.getBinaryMessageSizeLimit())
                .extensions(session.getExtensions())
                .handshakeHeaders(session.getHandshakeHeaders())
                .localAddress(session.getLocalAddress())
                .remoteAddress(session.getRemoteAddress())
                .open(session.isOpen())
                .textMessageSizeLimit(session.getTextMessageSizeLimit())
                .build();
    }

    @Builder
    @NoArgsConstructor
    @AllArgsConstructor
    @Setter
    @Getter
    public static class MySessionDescriptor {
        private String                   id;
        private URI                      uri;
        private String                   username;
        private boolean                  open;
        private InetSocketAddress        remoteAddress;
        private InetSocketAddress        localAddress;
        private String                   acceptedProtocol;
        private Map<String, Object>      attributes;
        private HttpHeaders              handshakeHeaders;
        private int                      textMessageSizeLimit;
        private int                      binaryMessageSizeLimit;
        private List<WebSocketExtension> extensions;
    }
}
import org.springframework.boot.actuate.endpoint.web.annotation.RestControllerEndpoint;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;

import javax.inject.Inject;
import java.util.Map;

@Component
@RestControllerEndpoint(id = "websocket-session-rest")
public class WebSocketSessionController {
    private final WebSocketSessionEndpoint delegate;

    @Inject
    public WebSocketSessionController(WebSocketSessionEndpoint delegate) {
        this.delegate = delegate;
    }

    @GetMapping("")
    public Map<String, WebSocketSessionEndpoint.MySessionDescriptor> sessions() {
        return delegate.sessions();
    }

    @GetMapping("/{id}")
    public WebSocketSessionEndpoint.MySessionDescriptor sessionEntry(@PathVariable("id") String id) {
        return delegate.sessionEntry(id);
    }

    @GetMapping("/{id}/close")
    public String closeSession(@PathVariable("id") String id) {
        return delegate.closeSession(id);
    }
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末卢鹦,一起剝皮案震驚了整個(gè)濱河市孕豹,隨后出現(xiàn)的幾起案子钩乍,更是在濱河造成了極大的恐慌,老刑警劉巖祠乃,帶你破解...
    沈念sama閱讀 222,252評(píng)論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡绣檬,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,886評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門嫂粟,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)娇未,“玉大人,你說(shuō)我怎么就攤上這事星虹×闾В” “怎么了?”我有些...
    開封第一講書人閱讀 168,814評(píng)論 0 361
  • 文/不壞的土叔 我叫張陵宽涌,是天一觀的道長(zhǎng)平夜。 經(jīng)常有香客問(wèn)我,道長(zhǎng)卸亮,這世上最難降的妖魔是什么褥芒? 我笑而不...
    開封第一講書人閱讀 59,869評(píng)論 1 299
  • 正文 為了忘掉前任,我火速辦了婚禮嫡良,結(jié)果婚禮上锰扶,老公的妹妹穿的比我還像新娘。我一直安慰自己寝受,他們只是感情好坷牛,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,888評(píng)論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著很澄,像睡著了一般京闰。 火紅的嫁衣襯著肌膚如雪颜及。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,475評(píng)論 1 312
  • 那天蹂楣,我揣著相機(jī)與錄音俏站,去河邊找鬼。 笑死痊土,一個(gè)胖子當(dāng)著我的面吹牛肄扎,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播赁酝,決...
    沈念sama閱讀 41,010評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼犯祠,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了酌呆?” 一聲冷哼從身側(cè)響起衡载,我...
    開封第一講書人閱讀 39,924評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎隙袁,沒想到半個(gè)月后痰娱,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,469評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡菩收,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,552評(píng)論 3 342
  • 正文 我和宋清朗相戀三年梨睁,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片坛梁。...
    茶點(diǎn)故事閱讀 40,680評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡而姐,死狀恐怖腊凶,靈堂內(nèi)的尸體忽然破棺而出划咐,到底是詐尸還是另有隱情,我是刑警寧澤钧萍,帶...
    沈念sama閱讀 36,362評(píng)論 5 351
  • 正文 年R本政府宣布褐缠,位于F島的核電站,受9級(jí)特大地震影響风瘦,放射性物質(zhì)發(fā)生泄漏队魏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,037評(píng)論 3 335
  • 文/蒙蒙 一万搔、第九天 我趴在偏房一處隱蔽的房頂上張望胡桨。 院中可真熱鬧,春花似錦瞬雹、人聲如沸昧谊。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,519評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)呢诬。三九已至涌哲,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間尚镰,已是汗流浹背阀圾。 一陣腳步聲響...
    開封第一講書人閱讀 33,621評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留狗唉,地道東北人初烘。 一個(gè)月前我還...
    沈念sama閱讀 49,099評(píng)論 3 378
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像敞曹,于是被迫代替她去往敵國(guó)和親账月。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,691評(píng)論 2 361

推薦閱讀更多精彩內(nèi)容