记录一次spring boot 集成 websocket的记录

记录一次spring boot 2.x 集成 websocket 以及 SSE的两种实现方式

第一次使用底层 webSocket 失败了 目前还没找到原因

先贴代码:
主要需求:

  1. .webSocket

  2. 通过path传参

  3. 第一步创建webSocketConfig

@Configuration     //这个的有
@EnableWebSocket  //这个的有
public class WebSocketConfig implements WebSocketConfigurer {
    private static final Logger LOG = LoggerFactory.getLogger(WebSocketConfig.class);

    @Autowired
    public EventBusToWebSocketTongwebHandler eventBusToWebSocketTongwebHandler;

    @Override
    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
        LOG.info("我执行了"+ eventBusToWebSocketTongwebHandler.toString());
        System.out.println("eventBusToWebSocketTongwebHandler.toString() = " + eventBusToWebSocketTongwebHandler.toString());
        registry.addHandler(eventBusToWebSocketTongwebHandler, "/api/v1/event/streams.ws/","/api/v1/event/streams.ws/**","/api/v1/event/streams.ws/b136e2f7-d9a3-4179-a5ec-d4caabf761ad")
            .addInterceptors(httpSessionHandshakeInterceptor()).setAllowedOrigins("*");
    }

  1. 创建HandshakeInterceptor,因为我需要从路径中获取参数
package io.syndesis.server.runtime;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor;

import java.util.Map;

public class CustomerHttpSessionHandshakeInterceptor extends HttpSessionHandshakeInterceptor {

    private static final Logger LOG = LoggerFactory.getLogger(CustomerHttpSessionHandshakeInterceptor.class);

    public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response,
                                   WebSocketHandler wsHandler, Map<String, Object> attributes) throws Exception {

        // Get the URI segment corresponding to the auction id during handshake
        String path = request.getURI().getPath();
        String auctionId = path.substring(path.lastIndexOf('/') + 1);

        // This will be added to the websocket session
        if(path.startsWith(EventBusToWebSocketTongwebHandler.DEFAULT_PATH)){
            attributes.put(EventBusToWebSocketTongwebHandler.SUB_ID, auctionId);
        }
        LOG.info("path"+ path + "  auctionId:"+ auctionId);
        //if(path.startsWith(EventBusToWebSocketTongwebHandler.DEFAULT_PATH)){
        //attributes.put("auctionId", auctionId);
        return super.beforeHandshake(request, response, wsHandler, attributes);
    }

    public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response,
                               WebSocketHandler wsHandler, Exception exception) {
        System.out.println("After Handshake");
        LOG.info("After Handshake");
        super.afterHandshake(request, response, wsHandler, exception);
    }
}

  1. 创建处理类

import io.syndesis.common.model.EventMessage;
import io.syndesis.common.util.EventBus;
import io.syndesis.server.endpoint.v1.handler.events.EventReservationsHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;


/**
 * Connects the the EventBus to an Undertow WebSocket Event handler
 * at the "/api/v1/events.ws/{:subscription}" path.
 */

@Component
public class EventBusToWebSocketTongwebHandler  extends TextWebSocketHandler {

    private static final Logger LOG = LoggerFactory.getLogger(EventBusToWebSocketTongwebHandler.class);

    public static final String DEFAULT_PATH = "/api/v1/event/streams.ws";
    public static final String SUB_ID = "subscriptionId";
    protected final SyndesisCorsConfiguration cors;
    protected final EventBus bus;
    protected final EventReservationsHandler eventReservationsHandler;
    protected String path = DEFAULT_PATH;

    public static final HashMap<String, WebSocketSession> userSessionMap;

    static {
        userSessionMap = new HashMap<>();
    }
    /**
     * 连接成功时候,会触发UI上onopen方法
     */
    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        System.out.println("connect to the websocket success......");
        LOG.info("connect to the websocket success......");
        Map<String, Object> attributes = session.getAttributes();
        String subscriptionId = (String) attributes.get(SUB_ID);
        if(subscriptionId!= null) {
            userSessionMap.put(subscriptionId, session);
        }
    }


    //@Autowired
    public EventBusToWebSocketTongwebHandler(SyndesisCorsConfiguration cors, EventBus bus, EventReservationsHandler eventReservationsHandler) {
        this.cors = cors;
        this.bus = bus;
        this.eventReservationsHandler = eventReservationsHandler;
        path = DEFAULT_PATH;
        LOG.info("EventBusToWebSocketTongwebHandler初始化成功");
    }

    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
        String subscriptionId = (String) session.getAttributes().get(SUB_ID);
        EventReservationsHandler.Reservation reservation = eventReservationsHandler.claimReservation(subscriptionId);
        if (reservation == null) {
            LOG.info("Principal is: {}", reservation.getPrincipal());
            System.out.println("我是你爸爸1");
            send(session,"error", "Invalid subscription: not reserved");
            //safeClose(session);
            //return;
        }else {
            LOG.info("Principal is: {}", reservation.getPrincipal());
            System.out.println("我是你爸爸2");
            send(session, "message", "connected");
        }

    }

    @Override
    public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
        if (session.isOpen()) {
            session.close();
        }
        Map<String, Object> attributes = session.getAttributes();
        String subscriptionId = (String) attributes.get(SUB_ID);
        if(subscriptionId != null) {
            userSessionMap.remove(subscriptionId);
        }
    }

    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) throws Exception {
        Map<String, Object> attributes = session.getAttributes();
        String subscriptionId = (String) attributes.get(SUB_ID);
        if(subscriptionId != null) {
            userSessionMap.remove(subscriptionId);
        }
    }

    @Override
    public boolean supportsPartialMessages() {
        return false;
    }

    private void send(WebSocketSession session, String type, String data) throws IOException {
        session.sendMessage(new TextMessage(EventMessage.of(type, data).toJson()));
    }

    private void safeClose(WebSocketSession session) throws IOException {
        Map<String, Object> attributes = session.getAttributes();
        String subscriptionId = (String) attributes.get(SUB_ID);
        session.close();
        if(subscriptionId != null) {
            userSessionMap.remove(subscriptionId);
        }
    }


}


这次失败了就先不赘述了

第二次使用@ServerEndpoint 成功了

注意事项

  1. @ServerEndpoint 注解的类无法使用传统的单例@Resource等,无论是在setter方法或者属性上亦或者在构造方法上
  2. 需要使用ApplicationContextAware 但是实例化顺序一定要在websocket类之后。我一开始就是websocket类在ApplicationContextAware 之前实例化所有失败了了 spring 实例化应该是从上而下的class开始

贴代码
工具类

package io.syndesis.server.runtime;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;

import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;


@Component
public class SpringUtils implements ApplicationContextAware {

    private static final Logger LOG = LoggerFactory.getLogger(EventBusToWebSocketTongwebController.class);
    private static ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        LOG.info("初始化SpringUtils"+ applicationContext);
        if (SpringUtils.applicationContext == null) {
            SpringUtils.applicationContext = applicationContext;
        }
    }

    public static ApplicationContext getContext() {
        return applicationContext != null ? applicationContext : null;
    }


    public static Object getBean(String beanName){
        return applicationContext.getBean(beanName);
    }

    public static <T> T getBean(Class<T> clazz){
        return (T)applicationContext.getBean(clazz);
    }
}

WebSocketConfig

package io.syndesis.server.runtime;



import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;


@Configuration
@EnableWebSocket
public class WebSocketConfig {

    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }
}

@ServerEndpoint

/*
 * Copyright (C) 2016 Red Hat, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package io.syndesis.server.runtime;

import io.syndesis.common.model.EventMessage;
import io.syndesis.common.util.EventBus;
import io.syndesis.server.endpoint.v1.handler.events.EventReservationsHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.DependsOn;
import org.springframework.stereotype.Controller;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;


import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import javax.websocket.OnClose;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArraySet;


/**
 * Connects the the EventBus to an Undertow WebSocket Event handler
 * at the "/api/v1/events.ws/{:subscription}" path.
 */

@Controller
@ServerEndpoint("/api/v1/event/streams.ws/{subscriptionId}")
@DependsOn("springUtils") //为了处理无法实例化SpringUtils中的applictioncontext
public class EventBusToWebSocketTongwebController{

    private static final Logger LOG = LoggerFactory.getLogger(EventBusToWebSocketTongwebController.class);

    public static final String DEFAULT_PATH = "/api/v1/event/streams.ws";
    public static final String SUB_ID = "subscriptionId";

    protected  SyndesisCorsConfiguration cors = SpringUtils.getBean(SyndesisCorsConfiguration.class);

    protected  EventBus bus = SpringUtils.getBean(EventBus.class);

    protected  EventReservationsHandler eventReservationsHandler = SpringUtils.getBean(EventReservationsHandler.class);

    protected String path = DEFAULT_PATH;


    private Session session;
    public static CopyOnWriteArraySet<EventBusToWebSocketTongwebController> webSockets = new CopyOnWriteArraySet<>();
    private static ConcurrentHashMap<String, Session> sessionPool = new ConcurrentHashMap<>();

/*    @Autowired
    public EventBusToWebSocketTongwebController(SyndesisCorsConfiguration cors, EventBus bus, EventReservationsHandler eventReservationsHandler) {
        this.cors = cors;
        this.bus = bus;
        this.eventReservationsHandler = eventReservationsHandler;
        path = DEFAULT_PATH;
        LOG.info("EventBusToWebSocketTongwebController初始化成功");
    }*/
    public EventBusToWebSocketTongwebController() {
        path = DEFAULT_PATH;
        LOG.info("EventBusToWebSocketTongwebController初始化成功");
    }

    @OnOpen
    public void onOpen(Session session, @PathParam(value = "subscriptionId") String subscriptionId) {
        LOG.info("获取到subscriptionId:"+subscriptionId);
        this.session = session;
        webSockets.add(this);
        sessionPool.put(subscriptionId, session);
        EventReservationsHandler.Reservation reservation = null;
        if(null != eventReservationsHandler){
            reservation = eventReservationsHandler.claimReservation(subscriptionId);
        }
        if (reservation == null) {
            LOG.info("Principal is: {}", reservation.getPrincipal());
            LOG.info("我是你爸爸1");
            sendOneMessage(subscriptionId,"error", "Invalid subscription: not reserved");
            safeOnClose(subscriptionId);
            return;
        }else {
            LOG.info("Principal is: {}", reservation.getPrincipal());
            System.out.println("我是你爸爸2");
            sendOneMessage(subscriptionId, "message", "connected");
        }
        //sessionPool.forEach((key, value) -> {});
        LOG.info("【websocket消息】有新的连接,总数为:" + webSockets.size());
    }

    /**
     * 断开连接
     */
    @OnClose
    public void onClose() {
        webSockets.remove(this);
        LOG.info("【websocket消息】连接断开,总数为:" + webSockets.size());
    }
    public void safeOnClose(String subscriptionId) {
        webSockets.remove(this);
        sessionPool.remove(subscriptionId);
    }
    /**
     * 收到客户端消息
     *
     * @param message
     */
    @OnMessage
    public void onMessage(String message) {
        LOG.info("【websocket消息】收到客户端消息:" + message);
        if(null == message || "".equals(message)){
            message = "我是你爸爸";
        }
        sendAllMessage(message);
    }

    /**
     * 广播消息
     */
    public void sendAllMessage(String message) {
        for (EventBusToWebSocketTongwebController webSocket : webSockets) {
            try {
                webSocket.session.getAsyncRemote().sendText(message);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 给指定人发送单点消息
     *
     * @param code
     * @param message
     */
    public void sendOneMessage(String code, String type, String data) {
        Session session = sessionPool.get(code);
        //在发送数据之前先确认 session是否已经打开 使用session.isOpen() 为true 则发送消息
        if (session != null && session.isOpen()) {
            try {
                session.getAsyncRemote().sendText(EventMessage.of(type, data).toJson());
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }


    public SyndesisCorsConfiguration getCors() {
        return cors;
    }


    public void setCors(SyndesisCorsConfiguration cors) {
        this.cors = cors;
    }

    public EventBus getBus() {
        return bus;
    }
    public void setBus(EventBus bus) {
        this.bus = bus;
    }

    public EventReservationsHandler getEventReservationsHandler() {
        return eventReservationsHandler;
    }

    public void setEventReservationsHandler(EventReservationsHandler eventReservationsHandler) {
        this.eventReservationsHandler = eventReservationsHandler;
    }

    


}


原生servlet实现sse

sse教程 参考资料如有侵权请联系删除:Server-Sent Events 教程

nginx对sse支持的配置
这三个配置
proxy_set_header Connection ‘’;
proxy_http_version 1.1;
chunked_transfer_encoding off;

location ~ ^/stream1 {
proxy_pass https://message_upstream;
proxy_buffering off;
proxy_cache off;
proxy_set_header Host $host;

proxy_set_header Connection ‘’;
proxy_http_version 1.1;
chunked_transfer_encoding off;
}

demo
location /api/v1/event/streams {
if ($request_method = ‘OPTIONS’) {
return 204;
}

    proxy_pass         https://backends;
    proxy_cookie_flags ~ secure samesite=none;
    proxy_set_header   Host fxconnect-connector3.apps.all-in-one.ceake.fanxing;
    proxy_ssl_server_name on;
    proxy_ssl_session_reuse off;
    proxy_buffering off;
    proxy_cache off;
    proxy_set_header Connection '';
    proxy_http_version 1.1;
    chunked_transfer_encoding off;
    # proxy_set_header Upgrade $http_upgrade;
    # proxy_set_header Connection "upgrade";

}

原生的写法demo

/*
 * Copyright (C) 2016 Red Hat, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import io.syndesis.common.model.EventMessage;
import io.syndesis.common.util.EventBus;
import io.syndesis.common.util.json.JsonUtils;
import io.syndesis.server.endpoint.v1.handler.events.EventReservationsHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;

import javax.servlet.*;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.Field;
import java.util.Map;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/*
 * Connects the the EventBus to an Undertow Sever Side Event handler
 * at the "/api/v1/events/{:subscription}" path.
 */

//@Component
//@Controller
//@RequestMapping("/api/v1/event/streams")
@WebServlet(name = "myServlet",urlPatterns = "/api/v1/event/streams/*",asyncSupported = true)
public class EventBusToServerSentEventsSseTongweb extends HttpServlet {
    /**
     * messageId的 SseEmitter对象映射集
     */
    private static Map<String, SseEmitter> sseEmitterMap = new ConcurrentHashMap<>();
    private static Map<String, AsyncContext> AsyncContextMap = new ConcurrentHashMap<>();
    private static final Logger LOG = LoggerFactory.getLogger(EventBusToServerSentEventsSseTongweb.class);
    public final static String SSE_MESSAGE_EVENT = "event: message\n";
    public final static String SSE_CHANGE_EVENT = "event: change-event\n";
    public final static String EVENT = "event: ";
    public static final String DEFAULT_PATH = "/api/v1/event/streams";
    protected final SyndesisCorsConfiguration cors;
    protected final EventBus bus;
    protected final EventReservationsHandler eventReservationsHandler;
    protected String path = DEFAULT_PATH;

    @Autowired
    public EventBusToServerSentEventsSseTongweb(SyndesisCorsConfiguration cors, EventBus bus, EventReservationsHandler eventReservationsHandler) {
        this.cors = cors;
        this.bus = bus;
        this.eventReservationsHandler = eventReservationsHandler;
        LOG.info("EventBusToServerSentEventsSseTongweb初始化成功!");
        LOG.info(cors+"bus:"+bus+":"+eventReservationsHandler);
    }

    //private static ThreadPoolExecutor executor = new ThreadPoolExecutor(2, 5, 50000L, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<>(10));
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        response.setContentType("text/event-stream");
        response.setCharacterEncoding("utf-8");
        final AsyncContext asyncContext  =
            request.isAsyncStarted()
                ? request.getAsyncContext()
                : request.startAsync(request, response);
        String uri = request.getRequestURI();
        final String subscriptionId = uri.substring(path.length() + 1);
        LOG.info("subscriptionId:"+subscriptionId);
        PrintWriter pw = response.getWriter();
        EventReservationsHandler.Reservation reservation = eventReservationsHandler.claimReservation(subscriptionId);
        if (reservation == null) {
            //connection.send("Invalid subscription: not reserved", "error", null, null);
            pw.write( SSE_MESSAGE_EVENT + "data: "+ "Invalid subscription: not reserved" +"\n\n");
            pw.flush();
            pw.close();
            //asyncContext.complete();
            return;
        }
        LOG.info("Principal is: {}", reservation.getPrincipal());
        String message = "data:"+ "connected" +"\n\n";
        LOG.info("发送的消息值:{}",message);
        pw.write(message);
        pw.flush();
        asyncContext.addListener(new AsyncListener() {
            @Override
            public void onComplete(AsyncEvent asyncEvent) throws IOException {
                LOG.info("asyncContext-onComplete");
                AsyncContextMap.remove(subscriptionId);
                LOG.info("onCompleteremove:{}",subscriptionId);
                bus.unsubscribe(subscriptionId);
            }

            @Override
            public void onTimeout(AsyncEvent asyncEvent) throws IOException {
                LOG.info("asyncContext-超时了");
                AsyncContextMap.remove(subscriptionId);
                //asyncEvent.getAsyncContext().complete();
                LOG.info("onTimeoutremove:{}",subscriptionId);
                bus.unsubscribe(subscriptionId);
                //asyncContext.getResponse().getWriter().print(name + ":timeout");
                //asyncContext.complete();
            }

            @Override
            public void onError(AsyncEvent asyncEvent) throws IOException {
                LOG.info("asyncContext-onError");
                AsyncContextMap.remove(subscriptionId);
                LOG.info("onErrorremove:{}",subscriptionId);
                bus.unsubscribe(subscriptionId);
            }

            @Override
            public void onStartAsync(AsyncEvent asyncEvent) throws IOException {
                LOG.info("asyncContext-onStartAsync");
                //AsyncContextMap.remove(subscriptionId);
            }

        });
        asyncContext.setTimeout(0L);//设置AsyncContext的超时时间,默认30秒,0或者负值表示不超时
        AsyncContextMap.put(subscriptionId,asyncContext);
        bus.subscribe(subscriptionId, (type, data) -> {
            LOG.info(subscriptionId);
            AsyncContext asyncContextla = AsyncContextMap.get(subscriptionId);
            if(null == asyncContextla ){
                LOG.info("asyncContextla为空");
            }else {
                try {
                    if(null != asyncContextla.getRequest()){
                        PrintWriter pwlamada = asyncContextla.getResponse().getWriter();
                        if (null == pwlamada) {
                            LOG.info("pwlamada为空");
                        } else {
                            if (!pwlamada.checkError()) {
                                LOG.info("Principal is: {}", JsonUtils.toPrettyString(data));
                                String messageInlamada =SSE_CHANGE_EVENT + "data: " + data + "\n\n";
                                pwlamada.write(messageInlamada);
                                pwlamada.flush();
                                //pwlamada.close();
                                LOG.info("发送的消息值:{}", messageInlamada);
                            } else {
                                bus.unsubscribe(subscriptionId);
                                //asyncContext.complete();
                                //AsyncContextMap.remove(subscriptionId);
                                String str = "客户端断开连接";
                                LOG.info("结果:{}", str);
                            }
                        }
                    }else{
                        LOG.info("asyncContextla中的request为空了");
                    }
                } catch (IOException e) {
                    LOG.info("出现异常: {}",e.getMessage());
                }
            }

        });
        //asyncContext.setTimeout(25 * 1000);
        //executor.execute(()->{doSomething(asyncContext);});
        LOG.info("连上了: {}",subscriptionId);
    }
    /*private void doSomething(AsyncContext asyncContext) {
        ServletResponse response = asyncContext.getResponse();
        HttpServletRequest request = (HttpServletRequest)asyncContext.getRequest();
        String requestUri = request.getRequestURI();
        System.out.println("requestUri = " + requestUri);

        try {
            PrintWriter pw=response.getWriter();
            while (true){
                Thread.sleep(1000);

                pw.write("data:"+ requestUri  + "This is a test message\n\n");
                pw.flush();
                //asyncContext.complete();
                if (pw.checkError()) {
                    asyncContext.complete();
                    String str = "客户端断开连接";
                    LOG.info("结果:{}", str);
                    return;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
            return;
        }
    }*/

    //@CrossOrigin
    //@RequestMapping(value = "/{subscriptionId}", produces = MediaType.TEXT_EVENT_STREAM_VALUE, method = RequestMethod.GET)
    /*public SseEmitter sse(@PathVariable(name = "subscriptionId") String subscriptionId){
        LOG.info("EventBusToServerSentEventsSseTongweb接收到请求!"+subscriptionId);
        SseEmitter sseEmitter = SseServer.createConnect(subscriptionId);
        if(null == sseEmitter){
            LOG.info("sseEmitter为空,key:"+subscriptionId);
            return sseEmitter;
        }
        EventReservationsHandler.Reservation reservation = eventReservationsHandler.claimReservation(subscriptionId);
        if (reservation == null) {
            LOG.info("reservation为null");
            SseServer.sendMessage(subscriptionId,EventMessage.of("error","Invalid subscription: not reserved" ).toJson());
            //connection.send("Invalid subscription: not reserved", "error", null, null);
            SseServer.removeMessageId(subscriptionId);
            //connection.shutdown();
            return sseEmitter;
        }
        LOG.info("Principal is: {}", reservation.getPrincipal());
        //connection.send("connected", "message", null, null);
        SseServer.sendMessage(subscriptionId,EventMessage.of( "message","connected").toJson());
        boolean complete =  (boolean)getFieldValueByObject(sseEmitter,"complete","ResponseBodyEmitter");
        //s.setKeepAliveTime(25 * 1000);
        bus.subscribe(subscriptionId, (type, data) -> {
            if (!complete) {
                LOG.info("Principal is: {}", JsonUtils.toPrettyString(data));
                SseServer.sendMessage(subscriptionId,EventMessage.of(type,data).toJson());
            } else {
                bus.unsubscribe(subscriptionId);
            }
        });
        return sseEmitter;
     }


    public static Object getFieldValueByObject(Object object, String targetFieldName,String superclassSimpleName) {

        // 获取该对象的Class
        Class objClass = object.getClass();
        // 初始化返回值
        Object result = null;
        for( ;objClass != Object.class; objClass = objClass.getSuperclass()){
           if(null!=superclassSimpleName && superclassSimpleName.equals(objClass.getSimpleName())){

               Field[] fields = objClass.getDeclaredFields();
               for (Field field : fields) {
                   // 属性名称
                   String currentFieldName = "";
                   // 获取属性上面的注解 import com.fasterxml.jackson.annotation.JsonProperty;
                   *//**
                    * 举例: @JsonProperty("roleIds")
                    * private String roleIds;
                    *//*
                   try {
*//*                       boolean has_JsonProperty = field.isAnnotationPresent(JsonProperty.class);

                if (has_JsonProperty) {
                    currentFieldName = field.getAnnotation(JsonProperty.class).value();
                } else {
                    currentFieldName = field.getName();
                }*//*

                       if (currentFieldName.equals(targetFieldName)) {
                           field.setAccessible(true);
                           result = field.get(object);
                           return result; // 通过反射拿到该属性在此对象中的值(也可能是个对象)
                       }
                   } catch (SecurityException e) {
                       // 安全性异常
                       e.printStackTrace();
                   } catch (IllegalArgumentException e) {
                       // 非法参数
                       e.printStackTrace();
                   } catch (IllegalAccessException e) {
                       // 无访问权限
                       e.printStackTrace();
                   }
               }
           }else {
               continue;
           }
       }
        // 获取所有的属性数组
        return result;
    }*/
}

resteasy的写法

package io.syndesis.server.endpoint.v1.handler.stream;

import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.syndesis.common.model.ChangeEvent;
import io.syndesis.common.model.EventMessage;
import io.syndesis.common.util.EventBus;
import io.syndesis.common.util.json.JsonUtils;
import io.syndesis.server.endpoint.v1.handler.events.EventReservationsHandler;
import io.syndesis.server.runtime.EventBusToServerSentEventsSseTongweb;
import io.syndesis.server.runtime.SyndesisCorsConfiguration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.validation.constraints.NotNull;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.sse.OutboundSseEvent;
import javax.ws.rs.sse.Sse;
import javax.ws.rs.sse.SseBroadcaster;
import javax.ws.rs.sse.SseEventSink;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

@Path("/event/streams")
@Tag(name = "stream")
@Component
public class StreamHandler {
    private Sse sse;
    private  OutboundSseEvent.Builder eventBuilder;

    private SseBroadcaster sseBroadcaster;

    public static  Map<String,String> ms = new ConcurrentHashMap<>();

    private static final Logger LOG = LoggerFactory.getLogger(EventBusToServerSentEventsSseTongweb.class);

    public static final String DEFAULT_PATH = "/api/v1/event/streams";
    protected final SyndesisCorsConfiguration cors;
    protected final EventBus bus;
    protected final EventReservationsHandler eventReservationsHandler;
    protected String path = DEFAULT_PATH;

    @GET
    @Path(value = "/{subscriptionId}")
    @Produces("text/event-stream")
    public void getStockPrices(@NotNull @PathParam("subscriptionId") @Parameter(required = true) final String subscriptionId, @Context SseEventSink sseEventSink, @Context Sse sse) {
        LOG.info("subscriptionId:{}",subscriptionId);
        EventReservationsHandler.Reservation reservation = eventReservationsHandler.claimReservation(subscriptionId);
        LOG.info("reservation: {}",reservation.toString());
        if (reservation == null) {
            OutboundSseEvent sseEvent = this.eventBuilder
                .name("message")
                .id(String.valueOf(subscriptionId))
                .mediaType(MediaType.APPLICATION_JSON_TYPE)
                .data(String.class, "Invalid subscription: not reserved")
                .reconnectDelay(25*1000)
                .comment("event")
                .build();
            sseEventSink.send(sseEvent);
            sseEventSink.close();
            return;
        }
        LOG.info("Principal is: {}", reservation.getPrincipal());
        //connection.send("connected", "message", null, null);
        OutboundSseEvent sseEvent = this.eventBuilder
            .name("message")
            .id(String.valueOf(subscriptionId))
            .mediaType(MediaType.APPLICATION_JSON_TYPE)
            .data(String.class, "connected")
            .reconnectDelay(25*1000)
            .comment("event")
            .build();
        sseEventSink.send(sseEvent);
        StreamHandler.ms.put(sseEventSink.toString(),subscriptionId);
        this.sseBroadcaster.register(sseEventSink);
        //connection.setKeepAliveTime(25 * 1000);

        bus.subscribe(subscriptionId, (type, data) -> {
            if (!sseEventSink.isClosed()) {
                LOG.info("Principal is: {}", JsonUtils.toPrettyString(data));
                OutboundSseEvent sseEvent1 = this.eventBuilder
                    .name(type)
                    .id(String.valueOf(subscriptionId))
                    .mediaType(MediaType.APPLICATION_JSON_TYPE)
                    .data(String.class, data)
                    .reconnectDelay(25*1000)
                    .comment("event")
                    .build();
                sseEventSink.send(sseEvent1);
            } else {
                bus.unsubscribe(subscriptionId);
                ms.remove(subscriptionId);
                sseEventSink.close();

            }
        });
        LOG.info("运行结束了");
    }
    @Autowired
    public StreamHandler(SyndesisCorsConfiguration cors, EventBus bus, EventReservationsHandler eventReservationsHandler) {
        this.cors = cors;
        this.bus = bus;
        this.eventReservationsHandler = eventReservationsHandler;
        LOG.info("StreamHandler初始化成功!");
        LOG.info(cors+"bus:"+bus+":"+eventReservationsHandler);
    }
    @Context
    public void setSse(Sse sse) {
        this.sse = sse;
        this.eventBuilder = sse.newEventBuilder();
        this.sseBroadcaster = sse.newBroadcaster();
        this.sseBroadcaster.onClose(sseEventSink -> {
            LOG.info("我关闭了:{}",sseEventSink.toString());
            String id =  StreamHandler.ms.get(sseEventSink.toString());
            bus.unsubscribe(id);
     /*       OutboundSseEvent sseEvent1 = this.eventBuilder
                .name("message")
                .id(String.valueOf(11))
                .mediaType(MediaType.APPLICATION_JSON_TYPE)
                .data(String.class, EventMessage.of("error","Invalid subscription: not reserved").toJson())
                .reconnectDelay(4000)
                .comment("close的")
                .build();
            sseEventSink.send(sseEvent1);*/
        });
        this.sseBroadcaster.onError((sseEventSink, throwable) -> {
            LOG.info("出错了:{}",throwable.getMessage());
            String id =  StreamHandler.ms.get(sseEventSink.toString());
            bus.unsubscribe(id);
        });
    }

}

springboot 写法(未使用,未测试)




sse 前端写法

let eventSource2 = new EventSource(’
https://10.253.192.62/api/v1/event/streams/bf65f47f-0cde-49e8-9a9f-236fb8ed3ac8’, { withCredentials: false });
eventSource2.onopen = function(event) {
console.log(‘Connection opened’)
}

eventSource2.onmessage = function(event) {
console.log('Received message: ’ + event.data);
}

eventSource2.onerror = function(event) {
console.log('Error occurred: ’ + event.event);
}

  • 19
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值