Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
796 views
in Technique[技术] by (71.8m points)

Incompatibile SockJS! Main site uses:.... sockjs报错?

先上图:

1591954825504.jpg

sockjs stomp springboot-websocket

公司的项目,经测试,直连没有问题,但只要放到服务器上,除了第一次连接,用户只要刷新页面,触发重连就开始报错。

nginx的socket已经配置过了,

在前端报错的同时,后端也会报断开连接(不知道是不是心跳包报的错)

错误信息如下:
1591955395584.jpg

因为socket和项目是同端口,所以没有办法远程调试。。。

下面是我的配置信息

nginx:

nginx.conf:

user  nginx;
worker_processes  1;

error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;


events {
    worker_connections  1024;
}


http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;

    sendfile        on;
    #tcp_nopush     on;

    keepalive_timeout  65;

    gzip  on;

    map $http_upgrade $connection_upgrade {
        default upgrade;
        ''      close;
    }

    include /etc/nginx/conf.d/default/default.conf; #先去加载 default.conf 保证直接内网访问ip时 映射到default.conf内
    include /etc/nginx/conf.d/*.conf;
}

xxx.xxxx.xxx.conf:

server {
        listen 80;
        server_name xxx.xxxx.xxx;

        location / {
                #add_header 'Access-Control-Allow-Origin' '*';
                proxy_http_version 1.1;
                proxy_pass http://192.168.1.23:27081;
                proxy_set_header Host $host;
                proxy_set_header Upgrade $http_upgrade;
                proxy_set_header Connection "upgrade";
                proxy_set_header X-Forwarded-For $remote_addr;
                proxy_set_header X-Real-Ip $proxy_add_x_forwarded_for;
                proxy_connect_timeout 60;
                proxy_read_timeout 600;
                proxy_send_timeout 600;
        }
}

java

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
    @Autowired
    TokenStore tokenStore;
    public static final String Topic = "ws-topic";
    public static final String Queue = "ws-queue";
    public static final String HandUrl = "/endpoint";

    private static long HEART_BEAT = 5000;

    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        //注册STOMP的endpoint,使用SockJS
        registry.addEndpoint(HandUrl)
                .setAllowedOrigins("*")
                .addInterceptors(new ConnectionInterceptor())
                .withSockJS()
                .setClientLibraryUrl("https://cdn.jsdelivr.net/sockjs/1.0.0/sockjs.min.js") // 无效
                .setMessageCodec(new FastjsonSockJsMessageCodec());//使用fastjson序列化

    }

    @Override
    public void configureMessageBroker(MessageBrokerRegistry registry) {

        //心跳任务
        ThreadPoolTaskScheduler te = new ThreadPoolTaskScheduler();
        te.setPoolSize(1);
        te.setThreadNamePrefix("wss-heartbeat-thread-");
        te.initialize();

        //注册消息代理地址
        registry.enableSimpleBroker(String.format("/%s/", Topic), String.format("/%s/", Queue))
                .setHeartbeatValue(new long[]{HEART_BEAT, HEART_BEAT})
                .setTaskScheduler(te);
        registry.setApplicationDestinationPrefixes("/ws/ws-bmw"); // 广播消息订阅前缀
        registry.setUserDestinationPrefix("/ws/ws-user"); // 点对点消息订阅前缀
//        registry.setPreservePublishOrder(true); // 保证推送顺序,但会增加性能开销
    }

    // 拦截替换 Principal
    @Override
    public void configureClientInboundChannel(ChannelRegistration registration) {
        registration.interceptors(new ChannelInterceptor() {
            @Override
            public Message<?> preSend(@NotNull Message<?> message, MessageChannel channel) {
                StompHeaderAccessor accessor = MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class);
                if (accessor==null) throw new BadCredentialsException("未授权");
                // 判断是否断开连接
                //1、判断是否首次连接
                if (StompCommand.CONNECT.equals(accessor.getCommand())){
                    //2、拿到token,获取授权信息
                    List<String> authorization = accessor.getNativeHeader("Authorization");
                    if (authorization==null || authorization.size()==0) throw new BadCredentialsException("未授权");

                    String token = authorization.stream().findFirst().orElse(null);
                    if (StringUtils.isEmpty(token)) throw new BadCredentialsException("未授权");

                    OAuth2Authentication oAuth2Authentication = tokenStore.readAuthentication(token);
                    if (oAuth2Authentication==null) throw new BadCredentialsException("未授权");
                    UserDetails userDetails = (UserDetails)oAuth2Authentication.getUserAuthentication().getPrincipal();
                    if (userDetails==null) throw new BadCredentialsException("未授权");

                    SocketUserInfo socketUserInfo = new SocketUserInfo();

                    BeanUtils.copyProperties(userDetails, socketUserInfo);

                    accessor.setUser(socketUserInfo);

                    return message;
                }
                //不是首次连接,已经登陆成功
                return message;
            }

        });
    }


}

Vue:

socket.js:


import SockJS from 'sockjs-client';
import Stomp from 'stompjs';
import { getToken } from "./util";
import config from '@/config/config'

const baseURL = process.env.NODE_ENV === 'development' ? config.baseUrl.dev : config.baseUrl.pro;

class Socket {

    constructor(baseUrl = baseURL){
        this.stompClient = {};
        this.baseUrl = baseUrl//baseUrl;

        this.tryCount = 10; //重连尝试次数, 小于1为不重连
        this.tryDuration = 1000; //重连时间间隔 单位ms

        this.handUrl = "/endpoint"; // 握手连接
    };

    handler(data){}; // 该方法在组件内被重写

    connectionError(err, userId){
        console.error("握手出错", err);
        this.tryOpen(userId);
    }

    connection(userId) {
        // 发起连接
        this.stompClient.connect(this.getHeaders(), () => { this.connectionSuccess(userId); } , err =>{ this.connectionError(err, userId); });
    };

    tryOpen(userId, tempCount = this.tryCount) {
        let tryTask = (tempCount)=>{
            if(tempCount<1) return;
            if(!this.stompClient || this.stompClient=={} || !this.stompClient.connected ) {
                console.warn(`尝试重连: ${this.baseUrl}${this.handUrl}`);
                this.connection(userId);
                setTimeout(()=>{
                    tryTask(tempCount-1);
                }, this.tryDuration);
            }
        }
        tryTask(tempCount);
    };
    

    disconnect() {
        if (this.stompClient) {
            this.stompClient.disconnect();
        }
    };

    init(userId){
        if(!userId) return;
        // 建立连接对象
        let socket = new SockJS(`${this.baseUrl}${this.handUrl}`);
        // 获取STOMP子协议的客户端对象
        this.stompClient = Stomp.over(socket);

        // 日志
        this.stompClient.debug = process.env.NODE_ENV === 'development' ? str=>{
            console.log(str)
        }: str=>{};

        // 心跳包
        this.stompClient.heartbeat.outgoing = 10000; // 非负整数 发送心跳的间隔(PING) 单位ms 0表示不发送 
        this.stompClient.heartbeat.incoming = 10000; // 非负整数 接收心跳的最小时间间隔(PONG) 单位ms 0表示不想接收

        this.stompClient.ws.onclose = () => {
            this.tryOpen(userId);
        }
        this.stompClient.ws.onerror = () => {
            this.tryOpen(userId);
        }

        console.log(this.stompClient.ws)

        this.connection(userId);
        
    };

    // 连接成功
    connectionSuccess(userId){
        //订阅 点对点接口
        this.stompClient.subscribe('/ws/ws-user/ws-queue/getResponse', msg => {
            this.handler(msg); //处理消息
        }, this.getHeaders());

        this.stompClient.send(`/ws/ws-bmw/online`, this.getHeaders(), JSON.stringify({id:userId}))

        // this.stompClient.send("/ws-bmw/ws-user/receive",
        //     headers,
        //     JSON.stringify({a:"123"}),
        // )   //用户加入接口
    }

    getHeaders(){
        return {
            Authorization: getToken()
        }
    }

    destroyed () {
        this.disconnect();
    }
}




export default new Socket(baseURL)

服务器环境

1591956575410.jpg

弄这个东西弄了一周了,请教一个解决办法,谢谢??


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

问题已解决。

具体原因:
因为项目socket连接是采用域名连接,域名解析需要时间,而sockjs连接的超时时间是动态的,具体参考官方文档的超时配置:https://github.com/sockjs/sockjs-client 并不能计算域名解析的耗时,所以前端手动加入了超时配置:

let socket = new SockJS(`${this.baseUrl}${this.handUrl}`, undefined, {
            timeout: 10000 // 超时,单位毫秒
        });

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...