码农知识堂 - 1000bd
  •   Python
  •   PHP
  •   JS/TS
  •   JAVA
  •   C/C++
  •   C#
  •   GO
  •   Kotlin
  •   Swift
  • 一个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端有以下几种不同编程语言的实现:

    • JavaScript (whose documentation can be found here on this website)
      • Installation steps
      • API
      • Source code
    • Java: https://github.com/mrniko/netty-socketio
    • Java: https://github.com/trinopoty/socket.io-server-java
    • Python: https://github.com/miguelgrinberg/python-socketio
    • Golang: https://github.com/googollee/go-socket.io

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

    • JavaScript (which can be run either in the browser, in Node.js or in React Native)
      • Installation steps
      • API
      • Source code
    • Java: https://github.com/socketio/socket.io-client-java
    • C++: https://github.com/socketio/socket.io-client-cpp
    • Swift: https://github.com/socketio/socket.io-client-swift
    • Dart: https://github.com/rikulo/socket.io-client-dart
    • Python: https://github.com/miguelgrinberg/python-socketio
    • .Net: https://github.com/doghappy/socket.io-client-csharp
    • Rust: https://github.com/1c3t3a/rust-socketio
    • Kotlin: https://github.com/icerockdev/moko-socket-io

    为什么选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



    参考

    • https://socket.io/docs/v4/
    • https://huongdanjava.com/websocket-with-socket-io-java.html
    • https://github.com/socketio/socket.io-client-java
    • https://socketio.github.io/socket.io-client-java/emitting_events.html
  • 相关阅读:
    C语言:两个数的交换函数
    解决办法:AndroidStudio升级到android-studio-2021.3.1.16-windows.exe的坑
    【体系结构】计算机体系结构知识点清单
    【RabbitMQ】——死信
    python---求面积练习:提示用户输入圆的半径,根据公式S = πr2求圆的面积
    谈谈行情与就业吧!
    【随想】每日两题Day.1
    从 cursor 、 Claude code 迁移到 codex,30 分钟快速上手 codex 常用技巧
    @RequestBody、 @RequestParam 、 @PathVariable 和 @Vaild 注解
    上周热点回顾(12.18-12.24)
  • 原文地址:https://www.cnblogs.com/lucky_hu/p/16419044.html
  • 最新文章
  • 沪漂五周年了:我越来越迷茫了
    Agentic Skill Routing 实战:别再把所有 Skill 塞进 AI Agent 上下文
    MySQL-Seconds_behind_master的精度误差
    [MAF预定义ChatClient中间件-03]CachingChatClient——利用缓存省钱省时间
    AI的至暗历史:从万众期待到被政府撤资,AI的两次死亡徘徊
    Agent OS :五种驯服不确定性的范式
    PortSwigger SQL注入LAB11
    数据库即时编译JIT
    [Begin]AI Learn Data Day 0
    深度学习进阶(二十七)现代 LLM 的核心架构设计其二:SwiGLU
  • 热门文章
  • 十款代码表白小特效 一个比一个浪漫 赶紧收藏起来吧!!!
    奉劝各位学弟学妹们,该打造你的技术影响力了!
    五年了,我在 CSDN 的两个一百万。
    Java俄罗斯方块,老程序员花了一个周末,连接中学年代!
    面试官都震惊,你这网络基础可以啊!
    你真的会用百度吗?我不信 — 那些不为人知的搜索引擎语法
    心情不好的时候,用 Python 画棵樱花树送给自己吧
    通宵一晚做出来的一款类似CS的第一人称射击游戏Demo!原来做游戏也不是很难,连憨憨学妹都学会了!
    13 万字 C 语言从入门到精通保姆级教程2021 年版
    10行代码集2000张美女图,Python爬虫120例,再上征途
小工具 小游戏
Copyright © 2022 侵权请联系2656653265@qq.com    京ICP备2022015340号-1

京公网安备 11010502049817号