一个bug肝一周...忍不住提了issue

导航

  • Socket.IO是什么
  • Socket.IO的应用场景
  • 为什么选socket.io-client-java
  • 实战案例
  • 参考

本文首发于智客工坊-《socket.io客户端向webserver发送消息实践》,感谢您的阅读,预计阅读时长2min。

Socket.IO是什么

Socket.IO是一个库,它支持客户端和服务器之间的低延迟双向基于事件的通信。

它构建在WebSocket协议之上,并提供额外的保证,如回退到HTTP长轮询或自动重新连接。



Socket.IO的应用场景

Socket.IO目前应用比较多的场景就是网页的IM实时聊天。

Notes: 在C#中,也有个类库signalr实现简单的网页实时聊天。

Socket.IO server端有以下几种不同编程语言的实现:

Socket.IO client端,大多数主流编程语言的也有实现

为什么选socket.io-client-java

本文主要是针对socket.io-client-java的一次实践。

我们团队已经使用Node.js搭建了webserver,并实现了web客户端和webserver的消息互通(即双方都是基于JavaScript的实现)。

但是,有个特殊业务场景,需要在我们后端业务接口中根据业务状态变更向指定的IM会话投递实时消息。

这种需求的实现方案:

  • 方案1,后端业务接口将消息投递到kafka topic,再由webserver消费指定topic,实现消息的实时推送
  • 方案2,后端直接连接webserver,然后投递消息

综合考虑之后,我们选择了方案2。

因此,技术选型上就只有华山一条路——socket.io-client-java

在实现的过程中,也确实是踩了一些坑,所以记录一下,顺便和大家分享一下。

实战案例

现在我们开始socket.io-client-java
之旅吧!

引入socket.io-client-java

Notice: socket.io客户端和服务端的版本要匹配,否则会连不上或者没有反应。

根据socket.io-client-java官方文档给出的版本匹配表格:

Client version Socket.IO server
0.9.x 1.x
1.x 2.x
2.x 3.x / 4.x

因为我们webserver端的socket.io 版本 "socket.io": "^2.4.1"

所以,客户端我只能选择1.x,这里选择1.0.0。

        <dependency>
            <groupId>io.socket</groupId>
            <artifactId>socket.io-client</artifactId>
            <version>1.0.0</version>
        </dependency>

maven更新之后,即可使用。



代码实现

package com.zhike.blogmanager.Msg;

import io.socket.client.IO;
import io.socket.client.Socket;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j;
import org.apache.poi.ddf.EscherColorRef;
import org.json.JSONException;
import org.json.JSONObject;
import org.springframework.stereotype.Component;

import java.net.URI;
import java.util.Date;

/**
 * Created with IntelliJ IDEA.
 * User: lenovo
 * Date: 2022/6/25
 * Time: 21:25
 * Description: No Description
 */
@Component
@RequiredArgsConstructor
public class PushMessageManager {

    private Socket socket;

    /**
     * 消息推送到webserver
     * */
    public void pushToWebServer() {
        //保证只会实例化一次socket
        if(socket==null)
        {
            connentSocket();
            System.out.println(socket);
        }

        //构造JSONObject对象
        JSONObject data=bulidMsg();
        System.out.println("【客户端推送消息】"+data);
        //event 要和webserver一致才能接受到消息
        socket.emit("2",data);

        if(!socket.connected())
        {
            socket.connect();
        }
    }

    private void connentSocket(){
        try
        {
            //String url ="http://172.xx.xx.xx:3000";
            //String url ="http://172.xx.xx.xx:3001";
            String url = "http://172.xx.xx.xx:3002";//web服务器地址以实际为准
            IO.Options options = new IO.Options();
            options.forceNew = true;
            // 失败重连时间间隔
            options.reconnectionDelay = 1000;
            // 连接超时时间
            options.timeout = 5000;

            socket = IO.socket(URI.create(url), options);
        }catch (Exception ex)
        {
            System.out.println("连接服务器失败,error:"+ex);
        }
    }

    /**
     * 消息体构造
     * 定义须和webserver保持一致,webserver才能解析
     * */
    private  JSONObject bulidMsg()
    {
        JSONObject data = new JSONObject();
        try {
            data.put("type", "1");
            data.put("from", "[FromUSerId]");
            data.put("to", "[ToUserId]");
            data.put("msgContent", "Hello World!");
            data.put("msgTime", new Date());
        } catch (JSONException e) {
            throw new AssertionError(e);
        }
        return  data;
    }
}

代码中有详细注释,不再赘述。

断线重连的坑

这里还有个巨坑,不知道是否是socket.io-client-java的bug。

如果webserver部署了多个应用并被nginx负载,如下:

server {
    listen 3000;
    server_name localhost;
    
    access_log  /data/logs/nginx/webserver/access.log  main;

    location / {
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_set_header Host $host;

      proxy_pass http://webserver-nodes;

      # enable WebSockets
      proxy_http_version 1.1;
      proxy_set_header Upgrade $http_upgrade;
      proxy_set_header Connection "upgrade";
    }
  }

  upstream webserver-nodes {
    # enable sticky session based on IP
    # ip_hash;

    server 172.xx.xx.xx:3001;
    server 172.xx.xx.xx:3002;
  }

当我设置url="http://172.xx.xx.xx:3000"的时候,就会出现socket会在两台webserver之间disconnect,reconnect的情况。

当时同事反馈,使用JavaScript client连接是正常的。

这个研究了两三天了,尝试了很多方式依然没有解决。

所以,最终我们只能指定连接其中一台webserver。

最后

最后给提了一个issue #715



参考

posted @ 2022-06-28 12:52  智客工坊  阅读(1260)  评论(0编辑  收藏  举报