weblogic 建立websocket连接报404_基于 Serverless 与 Websocket 的聊天工具实现

传统业务实现 Websocket 并不难,然而函数计算基本上都是事件驱动,不支持长链接操作。如果将函数计算与 API 网关结合,是否可以有 Websocket 的实现方案呢?

API 网关触发器实现 Websocket

WebSocket 协议是基于 TCP 的一种新的网络协议。它实现了浏览器与服务器全双工 (full-duplex) 通信,即允许服务器主动发送信息给客户端。WebSocket 在服务端有数据推送需求时,可以主动发送数据至客户端。而原有 HTTP 协议的服务端对于需推送的数据,仅能通过轮询或 long poll 的方式来让客户端获得。

由于云函数是无状态且以触发式运行,即在有事件到来时才会被触发。因此,为了实现 WebSocket,云函数 SCF 与 API 网关相结合,通过 API 网关承接及保持与客户端的连接。您可以认为云函数与 API 网关一起实现了服务端。当客户端有消息发出时,会先传递给 API 网关,再由 API 网关触发云函数执行。当服务端云函数要向客户端发送消息时,会先由云函数将消息 POST 到 API 网关的反向推送链接,再由 API 网关向客户端完成消息的推送。

具体的实现架构如下:

38cb314207e6162889c04b339a0cabb9.png

对于 WebSocket 的整个生命周期,主要由以下几个事件组成:

  • 连接建立:客户端向服务端请求建立连接并完成连接建立;
  • 数据上行:客户端通过已经建立的连接向服务端发送数据;
  • 数据下行:服务端通过已经建立的连接向客户端发送数据;
  • 客户端断开:客户端要求断开已经建立的连接;
  • 服务端断开:服务端要求断开已经建立的连接。

对于 WebSocket 整个生命周期的事件,云函数和 API 网关的处理过程如下:

  • 连接建立:客户端与 API 网关建立 WebSocket 连接,API 网关将连接建立事件发送给 SCF;
  • 数据上行:客户端通过 WebSocket 发送数据,API 网关将数据转发送给 SCF;
  • 数据下行:SCF 通过向 API 网关指定的推送地址发送请求,API 网关收到后会将数据通过 WebSocket 发送给客户端;
  • 客户端断开:客户端请求断开连接,API 网关将连接断开事件发送给 SCF;
  • 服务端断开:SCF 通过向 API 网关指定的推送地址发送断开请求,API 网关收到后断开 WebSocket 连接。

因此,云函数与 API 网关之间的交互,需要由 3 类云函数来承载:

  • 注册函数:在客户端发起和 API 网关之间建立 WebSocket 连接时触发该函数,通知 SCF WebSocket 连接的 secConnectionID。通常会在该函数记录 secConnectionID 到持久存储中,用于后续数据的反向推送;
  • 清理函数:在客户端主动发起 WebSocket 连接中断请求时触发该函数,通知 SCF 准备断开连接的 secConnectionID。通常会在该函数清理持久存储中记录的该 secConnectionID;
  • 传输函数:在客户端通过 WebSocket 连接发送数据时触发该函数,告知 SCF 连接的 secConnectionID 以及发送的数据。通常会在该函数处理业务数据。例如,是否将数据推送给持久存储中的其他 secConnectionID。

Websocket 功能实现

根据腾讯云官网提供的该功能的整体架构图:

cdbfcf6ea6d8b7950bc5c37702ed2896.png

这里我们可以使用对象存储 COS 作为持久化的方案,当用户建立链接存储 ConnectionId 到 COS 中,当用户断开连接删除该链接 ID。

其中注册函数:

# -*- coding: utf8 -*-import osfrom qcloud_cos_v5 import CosConfigfrom qcloud_cos_v5 import CosS3Clientbucket = os.environ.get('bucket')region = os.environ.get('region')secret_id = os.environ.get('secret_id')secret_key = os.environ.get('secret_key')cosClient = CosS3Client(CosConfig(Region=region, SecretId=secret_id, SecretKey=secret_key))def main_handler(event, context):    print("event is %s" % event)    connectionID = event['websocket']['secConnectionID']    retmsg = {}    retmsg['errNo'] = 0    retmsg['errMsg'] = "ok"    retmsg['websocket'] = {        "action": "connecting",        "secConnectionID": connectionID    }    cosClient.put_object(        Bucket=bucket,        Body='websocket'.encode("utf-8"),        Key=str(connectionID),        EnableMD5=False    )    return retmsg

传输函数:

# -*- coding: utf8 -*-import osimport jsonimport requestsfrom qcloud_cos_v5 import CosConfigfrom qcloud_cos_v5 import CosS3Clientbucket = os.environ.get('bucket')region = os.environ.get('region')secret_id = os.environ.get('secret_id')secret_key = os.environ.get('secret_key')cosClient = CosS3Client(CosConfig(Region=region, SecretId=secret_id, SecretKey=secret_key))sendbackHost = os.environ.get("url")def Get_ConnectionID_List():    response = cosClient.list_objects(        Bucket=bucket,    )    return [eve['Key'] for eve in response['Contents']]def send(connectionID, data):    retmsg = {}    retmsg['websocket'] = {}    retmsg['websocket']['action'] = "data send"    retmsg['websocket']['secConnectionID'] = connectionID    retmsg['websocket']['dataType'] = 'text'    retmsg['websocket']['data'] = data    requests.post(sendbackHost, json=retmsg)def main_handler(event, context):    print("event is %s" % event)    connectionID_List = Get_ConnectionID_List()    connectionID = event['websocket']['secConnectionID']    count = len(connectionID_List)    data = event['websocket']['data'] + "(===Online people:" + str(count) + "===)"    for ID in connectionID_List:        if ID != connectionID:            send(ID, data)    return "send success"

清理函数:

# -*- coding: utf8 -*-import osimport requestsfrom qcloud_cos_v5 import CosConfigfrom qcloud_cos_v5 import CosS3Clientbucket = os.environ.get('bucket')region = os.environ.get('region')secret_id = os.environ.get('secret_id')secret_key = os.environ.get('secret_key')cosClient = CosS3Client(CosConfig(Region=region, SecretId=secret_id, SecretKey=secret_key))sendbackHost = os.environ.get("url")def main_handler(event, context):    print("event is %s" % event)    connectionID = event['websocket']['secConnectionID']    retmsg = {}    retmsg['websocket'] = {}    retmsg['websocket']['action'] = "closing"    retmsg['websocket']['secConnectionID'] = connectionID    requests.post(sendbackHost, json=retmsg)    cosClient.delete_object(        Bucket=bucket,        Key=str(connectionID),    )    return event

Yaml 文件如下:

Conf:  component: "serverless-global"  inputs:    region: ap-guangzhou    bucket: chat-cos-1256773370    secret_id:     secret_key: myBucket:  component: '@serverless/tencent-cos'  inputs:    bucket: ${Conf.bucket}    region: ${Conf.region}restApi:  component: '@serverless/tencent-apigateway'  inputs:    region: ${Conf.region}    protocols:      - http      - https    serviceName: ChatDemo    environment: release    endpoints:      - path: /        method: GET        protocol: WEBSOCKET        serviceTimeout: 800        function:          transportFunctionName: ChatTrans          registerFunctionName: ChatReg          cleanupFunctionName: ChatCleanChatReg:  component: "@serverless/tencent-scf"  inputs:    name: ChatReg    codeUri: ./code    handler: reg.main_handler    runtime: Python3.6    region:  ${Conf.region}    environment:      variables:        region: ${Conf.region}        bucket: ${Conf.bucket}        secret_id: ${Conf.secret_id}        secret_key: ${Conf.secret_key}        url: http://set-gwm9thyc.cb-guangzhou.apigateway.tencentyun.com/api-etj7lhtwChatTrans:  component: "@serverless/tencent-scf"  inputs:    name: ChatTrans    codeUri: ./code    handler: trans.main_handler    runtime: Python3.6    region:  ${Conf.region}    environment:      variables:        region: ${Conf.region}        bucket: ${Conf.bucket}        secret_id: ${Conf.secret_id}        secret_key: ${Conf.secret_key}        url: http://set-gwm9thyc.cb-guangzhou.apigateway.tencentyun.com/api-etj7lhtwChatClean:  component: "@serverless/tencent-scf"  inputs:    name: ChatClean    codeUri: ./code    handler: clean.main_handler    runtime: Python3.6    region:  ${Conf.region}    environment:      variables:        region: ${Conf.region}        bucket: ${Conf.bucket}        secret_id: ${Conf.secret_id}        secret_key: ${Conf.secret_key}        url: http://set-gwm9thyc.cb-guangzhou.apigateway.tencentyun.com/api-etj7lhtw

注意,这里需要先部署 API 网关。当部署完成,获得回推地址,将回推地址以 url 的形式写入到对应函数的环境变量中:

b2d231e10045b0a68c2a888409c57efc.png

理论上应该是可以通过 ${restApi.url[0].internalDomain} 自动获得到 url 的,但是我并没有成功获得到这个 url,只能先部署 API 网关,获得到这个地址之后,再重新部署。

部署完成之后,我们可以编写 HTML 代码,实现可视化的 Websocket Client,其核心的 JavaScript 代码为:

window.onload = function () {    var conn;    var msg = document.getElementById("msg");    var log = document.getElementById("log");    function appendLog(item) {        var doScroll = log.scrollTop === log.scrollHeight - log.clientHeight;        log.appendChild(item);        if (doScroll) {            log.scrollTop = log.scrollHeight - log.clientHeight;        }    }    document.getElementById("form").onsubmit = function () {        if (!conn) {            return false;        }        if (!msg.value) {            return false;        }        conn.send(msg.value);        //msg.value = "";        var item = document.createElement("div");        item.innerText = "发送↑:";        appendLog(item);        var item = document.createElement("div");        item.innerText = msg.value;        appendLog(item);        return false;    };    if (window["WebSocket"]) {        //替换为websocket连接地址        conn = new WebSocket("ws://service-01era6ni-1256773370.gz.apigw.tencentcs.com/release/");        conn.onclose = function (evt) {            var item = document.createElement("div");            item.innerHTML = "Connection closed.";            appendLog(item);        };        conn.onmessage = function (evt) {            var item = document.createElement("div");            item.innerText = "接收↓:";            appendLog(item);            var messages = evt.data.split('');            for (var i = 0; i < messages.length; i++) {                var item = document.createElement("div");                item.innerText = messages[i];                appendLog(item);            }        };    } else {        var item = document.createElement("div");        item.innerHTML = "Your browser does not support WebSockets.";        appendLog(item);    }};

完成之后,我们打开两个页面,进行测试:

b2f1edfc33c1b965a67a1a43833d78a3.png

总结

通过云函数 + API 网关进行 Websocket 的实践,绝对不仅仅是一个聊天工具这么简单,它可以用在很多方面,例如通过 Websocket 进行实时日志系统的制作等。

单独的函数计算,仅仅是一个计算平台,只有和周边的 BaaS 结合,才能展示出 Serverless 架构的价值和真正的能力。这也是为什么很多人说 Serverless=FaaS+BaaS 的一个原因。

期待更多小伙伴,可以通过 Serverless 架构,创造出更多有趣的应用。

我们诚邀您来体验最便捷的 Serverless 开发和部署方式。在试用期内,相关联的产品及服务均提供免费资源和专业的技术支持,帮助您的业务快速、便捷地实现 Serverless!

Serverless 极速部署,只需三步

Serverless Framework 是构建和运维 Serverless 应用的框架。简单三步,即可通过 Serverless Framework 快速实现服务部署。

1. 安装 Serverless

macOS/Linux 系统:推荐使用二进制安装

$ curl -o- -L https://slss.io/install | bash

Windows 系统:可通过 npm 安装

$ npm install -g serverless

2. 创建云上应用

在空文件夹下输入 serverless 命令

$ serverless

访问命令行中输出的链接,即可访问成功部署后的应用。

3. 查看部署信息

进入到部署成功的文件夹,运行如下命令,查看部署状态和资源信息:

$ sls info

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/335971.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

java的默认值规则_Java 8:默认方法解析规则

java的默认值规则随着Java 8中默认方法的引入&#xff0c;一个类现在可以从多个位置&#xff08;例如另一个类或接口&#xff09;继承相同的方法。 在这种情况下&#xff0c;可以使用以下规则来确定选择哪种方法&#xff1a; 类或超类方法声明始终优先于默认方法 否则&#x…

【Android OpenGL ES 开发 (五)】纹理相关(二)

纹理放大和缩小的过滤参数 1.使用线性插值效果最佳 2.通过修改 float maxscale 4.0f //放大 float minscale 0.5f //缩小 vertices[0].mPosition[0]0.5f * maxscale; //x vertices[0].mPosition[1]0.5f * maxscale; //y 实现模糊效果 1.利用显卡的并行计算的强大功能对…

python与sqlite3_sqlite3与python2.5,pysqlite和apsw有什么区别

我想知道python2.5,pysqlite和apsw的sqlite3之间的区别&#xff1f;当我尝试使用python2.5在windows vista上安装pysqlite时,我有一个颠簸的运行,请参阅以下内容&#xff1a;>从http://sqlite.org/download.html下载sqlite并将它们解压缩到windows / system32文件夹并将sqli…

monolith_将Java EE Monolith雕刻成微服务

monolith在介绍了为什么微服务应该由事件驱动的简介博客之后&#xff0c;我想采取一些其他步骤&#xff0c;并在有关博客的同时准备我即将进行的一系列演讲&#xff08;在jBCNconf和Red Hat Summit上与您见面&#xff09; 。旧金山 &#xff09;。 在Twitter christianposta上关…

【H.264/AVC视频编解码技术】第四章【SPS序列参数集】

1.H264码流中重要的组成部分,保存针对整个视频序列的参数,丢失SPS的码流通常无法正常解码。 2.SPS信息的保存位置: 封装格式: FLV======会保存在 Tag中的Video Tag Data 部分,会有AVC VIDEO PACKET结构。SPS就包含在其中。 MP4===== H264裸码流: 保存…

spring内容协商管理_Spring框架中的内容协商

spring内容协商管理1.简介 使用BeanNameViewResolver完成的工作就是&#xff0c;我们刚刚在Spring上下文中创建了多个bean视图以生成预期的输出。 Spring很快引入了内容协商策略 &#xff0c;该策略可以使用传统的RESTful ResponseBody方法和HTTP消息转换器&#xff0c;以JSON或…

二叉树专题

二叉树 &#xff08;一&#xff09;二叉树的三种遍历方式&#xff1a; 前序遍历 &#xff1a; 1 2 4 5 3 6 7 &#xff1b; 中序遍历 &#xff1a; 4 2 5 1 6 3 7 &#xff1b; 后序遍历 &#xff1a; 4 5 2 6 7 3 1 &#xff1b; 本质是在递归序的基础上…

echart中拆线点的偏移_Real BIM | Rhino+Grasshopper在双曲异形玻璃幕墙中的应用

转载请联系并注明来源你好&#xff0c;我以自己所做的项目为例&#xff0c;介绍一下我的认知里&#xff0c;BIM技术对于真实项目的作用。案例是一个异形、双曲面的玻璃屋盖幕墙系统。如效果图所示&#xff0c;玻璃屋盖呈波浪状&#xff0c;塔楼装饰条与屋盖装饰条需要无缝连接。…

【安卓开发】Android初级开发(okhttp3发送带header与带参数的GET请求)

1.首先需要先加入相应依赖 dependencies{implementation com.squareup.okhttp3:okhttp:3.13.1 implementation com.squareup.okio:okio:2.2.2} 2.加入互联网权限 <!-- 互联网 --><uses-permission android:name"android.permission.INTERNET" /> <!…

java 文件保存在内存_如何掌握Java内存(并保存程序)

java 文件保存在内存通过AppDynamics解决应用程序问题的速度提高了10倍–以最小的开销在代码级深度监视生产应用程序。 开始免费试用&#xff01; 您花费了无数小时来解决Java应用程序中的错误并在需要的地方获得其性能。 在测试过程中&#xff0c;您注意到应用程序随着时间的…

sql 返回日期的年月部分_公示|2020年11月部分志愿活动名单公示

2020年11月部分志愿活动名单公示2020年11月18日人文与法学学院院楼协助分发教职工运动会服装志愿活动2020年11月19日人文与法学学院院楼“收彩旗”志愿活动2020年11月20日人文与法学学院组织观看2020年全国科学道德和学风建设宣讲教育报告会直播志愿活动(此活动不录入i志愿)202…

【安卓开发 】Android初级开发(三)动画

逐帧动画 方法一&#xff0c;在xml中设置 1.先将图片加入drawable 2.在drawable中新建xml,设置每一帧的图片和时间 <?xml version"1.0" encoding"utf-8"?> <animation-list xmlns:android"http://schemas.android.com/apk/res/android&…

javafx swing_Swing应用程序中的JavaFX 8 DatePicker

javafx swing1.概述 本文显示了一个使用JavaFX 8 DatePicker控件的Java SE 8 Swing应用程序的示例。 DatePicker控件允许用户以文本形式输入日期或从日历弹出窗口中选择日期。 本示例使用其中带有FX控件的Swing JFrame 。 为了将FX内容嵌入Swing应用程序中&#xff0c; javafx…

cup过高是什么意思_做青和焙火有什么关系?

武夷岩茶制作工艺复杂&#xff0c;环环相扣&#xff0c;每一步工艺对下一步工艺都有很大的影响&#xff0c;经过一系列的生化变化&#xff0c;最终呈现一杯好茶在我们面前&#xff0c;那我们最关心的做青和焙火两个问题&#xff0c;有什么影响呢&#xff1f;它们之间既有因果关…

【安卓开发 】Android初级开发(四)ListView

ListView的实现步骤 1.单独一行的布局可以如下 <?xml version"1.0" encoding"utf-8"?> <LinearLayout xmlns:android"http://schemas.android.com/apk/res/android"android:orientation"horizontal"android:layout_width&…

platform_SpringOne Platform 2016回顾

platform我最近结束了在拉斯维加斯参加SpringOne Platform会议的总结。 这是我第一次参加SpringOne。 这是聆听演讲并与软件开发领域的一些顶级专家进行对话的一种体验。 如果您没有参加SpringOne&#xff0c;那么您肯定会想要阅读这篇文章。 我们将讨论这四个主题&#xff0c…

不能装载文档控件。请在检查浏览器的选项中检查浏览器的安全设置_【网络安全宣传周】网络安全小黑板 | 如何正确设置浏览器...

李夏是一个公司的职员&#xff0c;一天晚上加班赶制文档&#xff0c;由于要向客户汇报产品情况&#xff0c;需要获取大量网上信息&#xff0c;然而在制作中却发现浏览器的网页打不开了。第二天原计划向客户展示的材料未能完整汇总&#xff0c;客户见面对接效果也打了折扣。在当…

【安卓开发 】Android初级开发(五)自定义View

1.自定义View的构造函数调用的场景 package com.sina.myapplication;import android.content.Context; import android.util.AttributeSet; import android.view.View;import androidx.annotation.Nullable;public class Textview extends View {//在new Textview类的时候调用…

kafka分布式_带有Kafka和ZeroMQ的分布式类星体演员

kafka分布式因此&#xff0c;您已经有了使用actor的精美设计&#xff0c;选择了JVM和Quasar在该主题上的强大而忠实的观点。 所有明智的决定&#xff0c;但是在集群上进行分配时您有什么选择呢&#xff1f; 星系 Galaxy是一个非常酷的选择&#xff1a;快速的内存中数据网格&am…

vs 不能自动 析构函数_深入理解C++虚函数的override、overload与hide以及虚析构函数...

今天主要讲的是虚函数的override与overload的区别。首先我们来看一段代码&#xff1a;示例代码#include <stdio.h>#include <string>#include <iostream>#include <complex>using namespace std;class Father{public: Father(); ~Father(); virtual vo…