【java】实现sse调用websocket接口,忽略wss证书并控制sse吐字速度


工具类
专栏收录该内容
11 篇文章0 订阅
订阅专栏
maven
        <dependency>
            <groupId>org.java-websocket</groupId>
            <artifactId>Java-WebSocket</artifactId>
            <version>1.5.3</version>
        </dependency>
1
2
3
4
5
AsyncConfig
package com.test.demo.sse;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.CustomizableThreadFactory;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;

/**
 * <p>
 * <code>AsyncConfig</code>
 * </p>
 * Description: 异步配置
 */
@EnableAsync
@Configuration
public class AsyncConfig {

    /**
     * 核心线程数(默认线程数)
     */
    @Value("${sync.corePoolSize:50}")
    private int corePoolSize;
    /**
     * 最大线程数
     */
    @Value("${sync.maxPoolSize:200}")
    private int maxPoolSize;
    /**
     * 缓冲队列数数量
     */
    @Value("${sync.queueCapacity:10000000}")
    private int queueCapacity;

    @Bean
    public Executor executor() {
        ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
        // 核心线程数(默认线程数)
        taskExecutor.setCorePoolSize(corePoolSize);
        // 最大线程数
        taskExecutor.setMaxPoolSize(maxPoolSize);
        // 缓冲队列数,默认Integer.MAX_VALUE.
        taskExecutor.setQueueCapacity(queueCapacity);
        // 线程池名前缀
        taskExecutor.setThreadNamePrefix("async-executor-");
        // 允许线程空闲时间(单位:秒),默认:60
        // taskExecutor.setKeepAliveSeconds(60);
        // 线程池对拒绝任务的处理策略,默认值AbortPolicy
        // taskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());
        // 初始化
        taskExecutor.initialize();

        return taskExecutor;
    }

    @Bean
    public ScheduledExecutorService scheduledExecutorService() {
        return Executors.newScheduledThreadPool(corePoolSize,
                new CustomizableThreadFactory("schedule-executor-"));
    }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
SpringContextUtils
package com.test.demo.sse;

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationEvent;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;

/**
 * Spring ApplicationContext 工具类
 */
@Component
public class SpringContextUtils implements ApplicationContextAware {

    /**
     * 上下文对象实例
     */
    private static ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        SpringContextUtils.applicationContext = applicationContext;
    }

    /**
     * 获取applicationContext
     *
     * @return
     */
    public static ApplicationContext getApplicationContext() {
        return applicationContext;
    }

    /**
     * 获取HttpServletRequest
     */
    public static HttpServletRequest getHttpServletRequest() {
        return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
    }

    public static String getDomain() {
        HttpServletRequest request = getHttpServletRequest();
        StringBuffer url = request.getRequestURL();
        return url.delete(url.length() - request.getRequestURI().length(), url.length()).toString();
    }

    public static String getOrigin() {
        HttpServletRequest request = getHttpServletRequest();
        return request.getHeader("Origin");
    }

    /**
     * 通过name获取 Bean.
     *
     * @param name
     * @return
     */
    public static Object getBean(String name) {
        return getApplicationContext().getBean(name);
    }

    /**
     * 通过class获取Bean.
     *
     * @param clazz
     * @param <T>
     * @return
     */
    public static <T> T getBean(Class<T> clazz) {
        return getApplicationContext().getBean(clazz);
    }

    /**
     * 通过name,以及Clazz返回指定的Bean
     *
     * @param name
     * @param clazz
     * @param <T>
     * @return
     */
    public static <T> T getBean(String name, Class<T> clazz) {
        return getApplicationContext().getBean(name, clazz);
    }

    /**
     * 发布事件
     *
     * @param event
     */
    public static void publishEvent(ApplicationEvent event) {
        if (applicationContext == null) {
            return;
        }
        applicationContext.publishEvent(event);
    }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
MySseEmitter
package com.test.demo.sse;

import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;

import java.nio.charset.StandardCharsets;
import java.util.UUID;
import java.util.concurrent.*;

/**
 * <p>
 * <code>MySseEmitter</code>
 * </p>
 * Description:  解决SseEmitter浏览器下中文乱码问题
 */
@EqualsAndHashCode(callSuper = true)
@Data
@Slf4j
public class MySseEmitter extends SseEmitter {
    /**
     * websocket返回的所有信息,只用于将消息发送到前端
     */
    private StringBuilder totalAnswer = new StringBuilder();
    /**
     * websocket返回的所有信息,用于最终存储的消息内容
     */
    private StringBuilder totalAnswerStorage = new StringBuilder();
    /**
     * 链接是否已主动断开,,true:已主动断开,false:未断开
     */
    private boolean disconnected = false;
    /**
     * 本条消息的唯一id
     */
    private String messageUuid = UUID.randomUUID().toString();
    /**
     * 本次会话的唯一id
     */
    private String conversationUuid = UUID.randomUUID().toString();
    /**
     * 是否匀速返回,true:需要匀速,false:不需要匀速
     */
    private boolean speedControl;
    /**
     * 是否已经开始匀速返回信息,true:已经开始,false:还没有开始
     */
    private boolean startSendMsgWithSpeedControl = false;
    /**
     * 已经发送的消息的长度
     */
    private int sendLength = 0;
    /**
     * 所有消息是否已经全部匀速返回,true:已经全部返回,false:还没有全部返回
     */
    private boolean endSendMsgWithSpeedControl = false;
    /**
     * 匀速吐字间隔时间,单位:毫秒
     */
    private long sleepTime = 20L;
    /**
     * 匀速发送消息时每次返回多少个字符
     */
    private int sendMsgSpeed = 1;
    /**
     * 超时时间,单位:毫秒
     */
    private long timeout;

    /**
     * 当前登录人
     */
    private String userUid;
    /**
     * 当前登录人的问题
     */
    private String userQuestion;


    /**
     * 解决中文乱码
     *
     * @param outputMessage
     */
    @Override
    protected void extendResponse(ServerHttpResponse outputMessage) {
        super.extendResponse(outputMessage);
        HttpHeaders headers = outputMessage.getHeaders();
        headers.setContentType(new MediaType(MediaType.TEXT_EVENT_STREAM, StandardCharsets.UTF_8));
    }

    /**
     * 创建SSE对象
     *
     * @param speedControl 是否开启匀速,true:开启,false:关闭
     * @param timeout      超时时间,单位:毫秒
     * @param userUid      当前登录人
     * @param userQuestion 当前登录人的问题
     */
    public MySseEmitter(boolean speedControl, long timeout, String userUid, String userQuestion) {
        // 设置超时时间,单位:毫秒
        super(timeout);
        this.speedControl = speedControl;
        this.timeout = timeout;
        this.userUid = userUid;
        this.userQuestion = userQuestion;
    }

    /**
     * 自定义发送消息方法
     *
     * @param message    具体消息
     * @param msgStorage 本次发送的消息内容是否需要进行存储,true:需要,false:不需要
     * @return 是否需要关闭链接,true:是,false:否
     */
    public boolean mySend(String message, boolean msgStorage) {
        try {
            if (StringUtils.isNotEmpty(message)) {
                // 处理换行,PC换行\r、\n、、\r\n都行,移动只能\r\n
                message = message.replaceAll("\r", "\n")
                        .replaceAll("\n\n", "\n")
                        .replaceAll("\n\n", "\n")
                        .replaceAll("\n\n", "\n")
                        .replaceAll("\n\n", "\n")
                        .replaceAll("\n\n", "\n")
                        .replaceAll("\n\n", "\n")
                        .replaceAll("\n\n", "\n")
                        .replaceAll("\n\n", "\n")
                        .replaceAll("\n\n", "\n")
                        .replaceAll("\n\n", "\n")
                        .replaceAll("\n\n", "\n")
                        .replaceAll("\n\n", "\n")
                        .replaceAll("\n", "\r\n");

                this.totalAnswer.append(message);
                if (msgStorage) {
                    this.totalAnswerStorage.append(message);
                }
                if (!this.speedControl) {
                    super.send(message);
                } else if (!this.startSendMsgWithSpeedControl && !this.disconnected) {
                    // 异步发送
                    SpringContextUtils.getBean(AsyncService.class).sendMsgWithSpeedControl(this);
                }
            }
        } catch (Exception e) {
            log.error("==>MySseEmitter send error,conversationUuid:{}", this.conversationUuid, e);
            this.disconnected = true;
        }
        return this.disconnected;
    }

    /**
     * 断开连接
     *
     * @param msgStorageType 消息处理类型,0:不存储,1:本地数据库存储
     */
    public void myComplete(String msgStorageType) {
        try {
            if (!this.speedControl || this.disconnected || this.endSendMsgWithSpeedControl) {
                super.complete();
            } else {
                Future<?> future = SpringContextUtils.getBean(ScheduledExecutorService.class).scheduleAtFixedRate(() -> {
                    if (this.endSendMsgWithSpeedControl) {
                        log.info("==>当前消息已全部返回完成,主动断开与端上链接,conversationUuid:{}", conversationUuid);
                        throw new RuntimeException("==>当前消息已全部返回完成,主动断开与端上链接,conversationUuid:" + conversationUuid);
                    }
                }, this.sleepTime, this.sleepTime, TimeUnit.MILLISECONDS);
                try {
                    // 超时时间,单位:毫秒
                    future.get(timeout, TimeUnit.MILLISECONDS);
                } catch (TimeoutException | ExecutionException e) {
                    log.info("==>等待断开链接任务执行结束,conversationUuid:{}", conversationUuid);
                    // 取消任务
                    future.cancel(true);
                } catch (Exception e) {
                    log.error("==>等待断开链接任务执行异常,conversationUuid:{}", conversationUuid, e);
                    // 取消任务
                    future.cancel(true);
                }

                super.complete();
            }
        } catch (Exception ignore) {
        }

        if ("1".equals(msgStorageType)) {
            // 本地数据库存储消息,异步保存数据
            SpringContextUtils.getBean(AsyncService.class).saveMsg(this.messageUuid, this.conversationUuid,
                    this.userUid, this.userQuestion, this.totalAnswerStorage.toString());
        }
    }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
MyWebSocketClient
package com.test.demo.sse;

import lombok.extern.slf4j.Slf4j;
import org.java_websocket.client.WebSocketClient;
import org.java_websocket.drafts.Draft_6455;

import javax.net.ssl.*;
import java.net.Socket;
import java.net.URI;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;

/**
 * <p>
 * <code>MyWebSocketClient</code>
 * </p>
 * Description: 自定义WebSocketClient,忽略wss证书
 */
@Slf4j
public abstract class MyWebSocketClient extends WebSocketClient {

    /**
     * 创建WebSocketClient
     *
     * @param serverUri      websocket 地址
     * @param connectTimeout 连接超时时间,单位:毫秒
     */
    public MyWebSocketClient(URI serverUri, int connectTimeout) {
        // 设置连接超时时间
        super(serverUri, new Draft_6455(), null, connectTimeout);
        // 设置不验证SSL证书的SSLContext
        TrustManager[] trustAllCerts = new TrustManager[]{new X509ExtendedTrustManager() {
            @Override
            public void checkClientTrusted(X509Certificate[] x509Certificates, String s, Socket socket) throws CertificateException {

            }

            @Override
            public void checkServerTrusted(X509Certificate[] x509Certificates, String s, Socket socket) throws CertificateException {

            }

            @Override
            public void checkClientTrusted(X509Certificate[] x509Certificates, String s, SSLEngine sslEngine) throws CertificateException {

            }

            @Override
            public void checkServerTrusted(X509Certificate[] x509Certificates, String s, SSLEngine sslEngine) throws CertificateException {

            }

            @Override
            public X509Certificate[] getAcceptedIssuers() {
                return null;
            }

            @Override
            public void checkClientTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {
            }

            @Override
            public void checkServerTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {
            }
        }};

        try {
            SSLContext ssl = SSLContext.getInstance("SSL");
            ssl.init(null, trustAllCerts, new java.security.SecureRandom());
            SSLSocketFactory socketFactory = ssl.getSocketFactory();
            this.setSocketFactory(socketFactory);
        } catch (Exception e) {
            log.error("==>初始化SSLContext失败", e);
        }
    }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
MyWebSocketClientHelper
package com.test.demo.sse;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.java_websocket.client.WebSocketClient;
import org.java_websocket.handshake.ServerHandshake;

import java.net.URI;

/**
 * <p>
 * <code>MyWebSocketClientHelper</code>
 * </p>
 * Description:
 */
@Slf4j
public class MyWebSocketClientHelper {

    /**
     * WebSocketClient连接并发送消息
     *
     * @param sseEmitter     sse链接
     * @param msgStorageType 消息处理类型,0:不存储,1:本地数据库存储
     */
    public static void connectAndSend(MySseEmitter sseEmitter, String msgStorageType) {
        String commonErrorMsg = "通用错误信息,报错啦";
        String messageUuid = sseEmitter.getMessageUuid();

        WebSocketClient client = null;
        try {
            client = new MyWebSocketClient(new URI("wss://xxxxx"), Integer.parseInt(Long.toString(sseEmitter.getTimeout()))) {
                @Override
                public void onOpen(ServerHandshake serverHandshake) {
                    log.info("==>connect success,messageUuid:{}", messageUuid);
                    try {
                        String requestParam = "xxxxxxx";
                        this.send(requestParam);
                    } catch (Exception e) {
                        log.error("==>sendRequest error,messageUuid:{}", messageUuid, e);
                        throw e;
                    }
                }

                @Override
                public void onMessage(String result) {
                    log.info("==>messageUuid:{},onMessage:{}", messageUuid, result);
                    try {
                        sseEmitter.mySend(result, true);
                    } catch (Exception e) {
                        log.error("==>onMessage error,messageUuid:{}", messageUuid, e);
                    }
                }

                @Override
                public void onClose(int code, String reason, boolean remote) {
                    // 1. code(int类型):表示关闭连接的原因,通常是一个整数。例如,如果连接正常关闭,code的值可能是1000(表示正常关闭);如果连接因为服务器主动关闭而关闭,code的值可能是1006(表示服务器端强制关闭)。
                    // 2. reason(String类型):表示关闭连接的原因,通常是一段文本描述。这个参数是可选的,如果没有提供原因,可以传递一个空字符串或者null。
                    // 3. remote(boolean类型):表示连接是否被清理。如果为true,表示连接正常关闭;如果为false,表示连接异常关闭。
                    log.info("==>onClose,messageUuid:{},code:{},reason:{},remote:{}", messageUuid, code, reason, remote);
                    try {
                        if (!sseEmitter.isDisconnected() && StringUtils.isBlank(sseEmitter.getTotalAnswer().toString())) {
                            sseEmitter.mySend(commonErrorMsg, true);
                        }
                        sseEmitter.myComplete(msgStorageType);
                    } catch (Exception ignored) {
                    }
                }

                @Override
                public void onError(Exception e) {
                    log.error("==>onError,messageUuid:{}", messageUuid, e);
                    try {
                        this.close();
                    } catch (Exception ignored) {
                    }
                }
            };
            client.connect();
        } catch (Exception e) {
            log.error("==>WebSocketClientConnectAndSend error,messageUuid:{}", messageUuid, e);
            try {
                if (client != null) {
                    client.close();
                }
            } catch (Exception ignored) {
            }
        }
    }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
AsyncService
package com.test.demo.sse;

/**
 * <p>
 * <code>AsyncService</code>
 * </p>
 * Description:
 */
public interface AsyncService {

    /**
     * 异步匀速返回消息
     *
     * @param sseEmitter
     */
    void sendMsgWithSpeedControl(MySseEmitter sseEmitter);

    /**
     * 异步保存消息
     *
     * @param messageUuid        消息uid
     * @param conversationUuid   会话uid
     * @param userUid            当前登录人uid
     * @param userQuestion       用户输入的问题
     * @param totalAnswerStorage websocket返回的具体消息内容
     */
    void saveMsg(String messageUuid, String conversationUuid, String userUid, String userQuestion, String totalAnswerStorage);
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
AsyncServiceImpl
package com.test.demo.sse;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.concurrent.*;

/**
 * <p>
 * <code>AsyncServiceImpl</code>
 * </p>
 * Description:
 */
@Slf4j
@Async
@Service
public class AsyncServiceImpl implements AsyncService {

    @Lazy
    @Autowired
    ScheduledExecutorService scheduledExecutorService;

    @Override
    public void sendMsgWithSpeedControl(MySseEmitter sseEmitter) {
        sseEmitter.setStartSendMsgWithSpeedControl(true);
        sseEmitter.setEndSendMsgWithSpeedControl(false);

        final String messageUuid = sseEmitter.getMessageUuid();

        // 使用scheduleAtFixedRate方法安排任务
        Future<?> future = scheduledExecutorService.scheduleAtFixedRate(() -> {
            int sendLength = sseEmitter.getSendLength();
            int sendMsgSpeed = sseEmitter.getSendMsgSpeed();
            // 当前时刻,所有的消息
            String nowAllAnswer = sseEmitter.getTotalAnswer().toString();
            int totalAnswerLength = nowAllAnswer.length();
            if (sendLength >= totalAnswerLength) {
                log.info("==>当前时刻所有消息已全部发送完成,任务执行结束,messageUuid:{}", messageUuid);
                throw new RuntimeException("==>当前时刻所有消息已全部发送完成,任务执行结束,messageUuid:" + messageUuid);
            }

            String message;
            if ((sendLength + sendMsgSpeed) > totalAnswerLength) {
                message = nowAllAnswer.substring(sendLength);
            } else {
                message = nowAllAnswer.substring(sendLength, sendLength + sendMsgSpeed);
            }

            if (message.endsWith("\r") && (sendLength + sendMsgSpeed + 1) <= totalAnswerLength) {
                message = nowAllAnswer.substring(sendLength, sendLength + sendMsgSpeed + 1);
            }

            try {
                sseEmitter.send(message);
            } catch (Exception e) {
                sseEmitter.setDisconnected(true);
                log.info("==>发送消息失败,视为端上主动断开链接,任务执行结束,messageUuid:{}", messageUuid);
                throw new RuntimeException("==>发送消息失败,视为端上主动断开链接,任务执行结束,messageUuid:" + messageUuid);
            }
            sendLength += message.length();
            sseEmitter.setSendLength(sendLength);
        }, sseEmitter.getSleepTime(), sseEmitter.getSleepTime(), TimeUnit.MILLISECONDS);

        // 尝试获取任务结果,如果超过超时时间则抛出TimeoutException异常
        try {
            // 超时时间,单位:毫秒
            future.get(sseEmitter.getTimeout(), TimeUnit.MILLISECONDS);
        } catch (TimeoutException | ExecutionException e) {
            log.info("==>任务执行结束,messageUuid:{}", messageUuid);
            // 取消任务
            future.cancel(true);
        } catch (Exception e) {
            log.error("==>任务执行异常,messageUuid:{}", messageUuid, e);
            // 取消任务
            future.cancel(true);
        }

        sseEmitter.setStartSendMsgWithSpeedControl(false);
        sseEmitter.setEndSendMsgWithSpeedControl(true);
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveMsg(String messageUuid, String conversationUuid, String userUid, String userQuestion, String totalAnswerStorage) {
        log.info("==>保存会话和信息,messageUuid:{},conversationUuid:{},userUid:{},userQuestion:{},totalAnswerStorage:{}",
                messageUuid, conversationUuid, userUid, userQuestion, totalAnswerStorage);
        // 会话不存在的,新建会话并保存


        // 保存消息
    }
}


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
使用方法
package com.test.demo.sse;

import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;

/**
 * <p>
 * <code>SseTestController</code>
 * </p>
 * Description:
 */
@Slf4j
@RestController
@RequestMapping("/sse")
public class SseTestController {

    /**
     * 用户提问提问
     *
     * @param input 输入信息
     * @return
     */
    @PostMapping(value = "/ask")
    public SseEmitter ask(@RequestBody @Valid Input input) {
        MySseEmitter sseEmitter = new MySseEmitter(true, 60000L, input.getUserUid(), input.getUserQuestion);
        try {
            MyWebSocketClientHelper.connectAndSend(sseEmitter, "1");
        } catch (Exception e) {
            log.error("ask error", e);
            sseEmitter.myComplete("1");
        }
        return sseEmitter;
    }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
文章知识点与官方知识档案匹配,可进一步学习相关知识
————————————————

                            版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
                        
原文链接:https://blog.csdn.net/muguazhi/article/details/140345010

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值