当前位置: 首页 > article >正文

Springboot 接入 WebSocket 实战

Springboot 接入 WebSocket 实战

前言:
WebSocket协议是基于TCP的一种新的网络协议。它实现了浏览器与服务器全双工(full-duplex)通信——允许服务器主动发送信息给客户端。

简单理解:
1,常见开发过程中我们知道 Http协议,客户端请求一次,服务器响应一次,推送数据,不能主动的推送数据,每次请求都要做一个连接,非常消耗性能。

2,websocket 建立一次链接,可以主动向客户端推送数据。

需求说明:
1,项目需要做一个知识助手,远程调用三方接口,那边是websocket 实时推送数据,类似gpt
2,后端需要连接三方服务,调用接口返回数据给前端,做渲染

在这里插入图片描述

功能实现

1,依赖

 		<dependency>
            <groupId>javax.websocket</groupId>
            <artifactId>javax.websocket-api</artifactId>
            <version>1.1</version>
        </dependency>

       <dependency>
            <groupId>com.squareup.okhttp3</groupId>
            <artifactId>okhttp</artifactId>
        </dependency>

2,代码接口实现

controller:

    @GetMapping("/callxxxxModel")
    public String callxxxxModel(@RequestParam("paramOne") String paramOne) {
       return webSocketxxxClientService.callxxxxModel(paramOne);
    }

service:

    /**
     * 
     * @param paramOne
     *            参数1
     */
    String callxxxxModel(String paramOne);

serviceImpl:

@Slf4j
@Service("webSocketxxxClientService")
public class WebSocketxxxClientServiceImpl implements WebSocketxxxClientService{



    @Override
    public String callxxxxModel(String paramOne, Integer executeType) {
        String uri = "ws://10.xx.xx.13:123/sss/xxx/aa/xxx_v2";
        String xappid = "lsjdfljsdxxx09980dsfsd";
        String xappkey = "xxsfdsf12123123";
        long timestamp = System.currentTimeMillis();
        String seqid = "";

        log.info("timestamp=" + timestamp);
        log.info("seqid=" + seqid);

        WebSocketClientRemote client = new WebSocketClientRemote(uri, xappid, xappkey);

        // Prepare message
        Map<String, Object> message = new HashMap<>();
        message.put("uid", xappid);
        message.put("timestamp", timestamp);
        message.put("seqid", seqid);
        message.put("stream", "true");
        // 会话识别码,切换话题可能需要更换
        message.put("session_id", seqid);
        message.put("prov", "xxsdfsdf23424332");
        message.put("param1", paramOne);
        message.put("param2", "xvsdfds23423423xxxxx");

        String jsonMessage = JSON.toJSONString(message);
        client.sendMessage(jsonMessage);
        String responseMessage = client.getResponseMessage();
        client.close();
        return responseMessage;
    }
package com.xx.xx.xx.web;

import com.xx.xx.common.result.ResultCodeEnum;
import com.xx.xx.xxx.exception.CloudException;
import lombok.extern.slf4j.Slf4j;
import okhttp3.*;
import okio.ByteString;

import javax.websocket.ClientEndpoint;
import java.util.concurrent.CountDownLatch;

/**
 * @author nobuyboday
 */
@Slf4j
@ClientEndpoint
public class WebSocketClientRemote {

    private final OkHttpClient client;
    private final WebSocket webSocket;
    // public final CountDownLatch latch = new CountDownLatch(50);
	
	// 记录websocket 返回的信息
    public String responseMessage = "";

    public WebSocketClientRemote(String uri, String xappid, String xappkey) {
        client = new OkHttpClient();

        Request request = new Request.Builder().url(uri)
                // 添加自定义头
                .addHeader("X-App-ID", "xxxfjslfjslj1231321xxxx3")
                // 添加自定义头
                .addHeader("X-App-Key", "0923jhdjflsdjflsdjljxxxxxxflsn").build();

        webSocket = client.newWebSocket(request, new WebSocketListener() {
            @Override
            public void onOpen(WebSocket webSocket, Response response) {
                log.info("已连接到服务器................");
            }

            @Override
            public void onMessage(WebSocket webSocket, String text) {
                // log.info("收到消息: " + text);
                responseMessage += text;
            }

            @Override
            public void onMessage(WebSocket webSocket, ByteString bytes) {
                log.info("收到字节消息: " + bytes.hex());
            }

            @Override
            public void onClosing(WebSocket webSocket, int code, String reason) {
                webSocket.close(1000, null);
                log.info("连接关闭: " + reason);
                // latch.countDown();
            }

            @Override
            public void onFailure(WebSocket webSocket, Throwable t, Response response) {
                log.info("连接失败: " + t.getMessage());
            }
        });
    }

    public void sendMessage(String message) {
        webSocket.send(message);
    }

    public void close() {
        webSocket.close(1000, "关闭连接");
    }

    public void await() throws InterruptedException {
        // latch.await();
    }

    public String getResponseMessage() {
        // 对方数据是以 <#END> 代表推送数据完毕,这个\\u003c#END\\u003e是<#END>编码问题 不用管
        boolean isReturn = responseMessage.endsWith("<#END>") || responseMessage.endsWith("\\u003c#END\\u003e") || responseMessage.contains("#END")
                || responseMessage.contains("\\u003c#END\\u003e");
        if (isReturn) {
//            log.info("最终的responseMessage:================>>>:{}", responseMessage);
            return responseMessage.substring(0, responseMessage.length() - "<#END>".length());
        } else {
            // 循环等待
            try {
                Thread.sleep(3000);
            } catch (Exception e) {
                throw new CloudException(ResultCodeEnum.CALL_GROUP_DCOOS_TYCLOUD_KNOWLEDGE_ASSISTANT_FAIL.getCode(),
                        ResultCodeEnum.CALL_GROUP_DCOOS_TYCLOUD_KNOWLEDGE_ASSISTANT_FAIL.getMessage() + ":" + e);
            }
            return getResponseMessage();
        }
    }

}

遇到的问题:

1,websocket 没有返回值,需要加一个接口获取返回值
2,对方返回的数据 <#END> 结尾,在接收判断是否完毕后,字符集问题 不识别 <#END>
3,推送数据要保证全部推送完毕,要有个循环调用

喜欢我的文章记得点个在看,或者点赞,持续更新中ing…


http://www.kler.cn/a/349889.html

相关文章:

  • 2024收尾工作
  • AndroidCompose Navigation导航精通1-基本页面导航与ViewPager
  • 01学习预热篇(D6_正式踏入JVM深入学习前的铺垫)
  • Linux学习笔记——磁盘管理命令
  • 基于RIP的MGRE VPN综合实验
  • java入门笔记基础语法篇(4)
  • AMBA:AHB的历史(从AHB2到AHB5)
  • 0基础能不能转行做网络安全?
  • linux信号 | 学习信号四步走 | 全解析信号的产生方式
  • Flume面试整理-常见的Sink类型
  • 中小型医院网站:Spring Boot开发策略
  • 安装TDengine数据库3.3版本和TDengine数据库可视化管理工具
  • 杂记9---C++工程目录一键生成脚本分享
  • 2015年-2016年 软件工程程序设计题(算法题)实战_c语言程序设计数据结构程序设计分析
  • Spring Security自定义登录接口处理JSON请求体
  • 开源OpenStack
  • 施磊C++ | 进阶学习笔记 | 5.设计模式
  • H3Linux部署iMC智能管理中心平台PLAT-7.3_E0706实验
  • 基于java SpringBoot和Vue校园食堂网站管理系统设计
  • 51 | 适配器模式:代理、适配器、桥接、装饰,这四个模式有何区别?
  • 【数据结构】之学习路线
  • 【Sceneform-EQR】(手势优化)通过手势事件实现在AR/VR等三维场景中的控制模型旋转、平移与缩放
  • linux_find命令
  • SQL分类中的DCL
  • [LeetCode 题1] 两数之和
  • uni-app中添加自定义相机(微信小程序+app)