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

深入理解 Cowboy WebSocket:使用 Erlang/OTP 构建高效的即时通讯(IM)应用

深入理解 Cowboy WebSocket:使用 Erlang/OTP 构建高效的即时通讯(IM)应用

引言

实时通信技术在现代 Web 应用中扮演着核心角色,而 WebSocket 作为其中的关键技术,已成为即时通讯(IM)系统不可或缺的一部分。Cowboy,这个基于 Erlang/OTP 的轻量级 HTTP 服务器框架,以其强大且用户友好的 WebSocket 功能,为开发者提供了构建高效 IM 应用的利器。本文将深入分析如何利用 Cowboy WebSocket 来打造高性能的即时通讯解决方案。

Cowboy 高效的理由

构建在现代Web应用基础上的Cowboy,利用Erlang/OTP框架的强大功能,提供了一系列的高效特性,这些特性使其成为开发高性能即时通讯(IM)应用的理想选择:

  1. 轻量级架构
    Cowboy建立在Ranch之上,采用每连接一个进程的模型,这不仅简化了并发处理,还降低了内存占用,因为进程可以在处理多个请求时被重用。

  2. 高效的二进制处理
    与字符串相比,二进制数据处理在性能上更为高效和节省资源。Cowboy充分利用Erlang的二进制模式,优化了数据传输和处理。

  3. 智能连接管理
    Cowboy默认配置了足够大的最大活动连接数,有效防止了大量进程处理繁重任务时对系统资源和内存的过度消耗。对于短连接请求,通过设置{max_connections, infinity},可以极大提升性能。

  4. HTTP/2的透明支持
    HTTP/2作为一种高效的Web服务协议,Cowboy为其提供了透明支持,包括保持连接打开、并发请求处理以及通过头部压缩减少请求大小等特性。

  5. WebSocket的完全控制
    Cowboy的Websocket处理程序接口允许开发者完全控制Websocket连接,包括自定义协议实现和消息处理。

  6. 自动超时和连接关闭
    通过设置超时,Cowboy能够自动关闭空闲连接,避免不必要的资源占用。同时,Cowboy在回调返回后使连接进程进入休眠状态,进一步减少了内存使用。

  7. 长轮询和服务器发送事件支持
    Cowboy提供了接口支持长轮询和服务器发送事件,有助于实现高效的数据传输和实时通信。

  8. RESTful API简化实现
    Cowboy提供的REST处理程序接口简化了在HTTP协议上REST API的实现,使开发者可以更专注于业务逻辑。

  9. 内存优化
    通过在回调返回后使连接进程进入休眠状态,Cowboy显著降低了内存占用,同时在CPU使用或延迟上可能有所增加,但这对于大量并发连接的服务器来说是一个可接受的权衡。

  10. 动态超时设置
    Cowboy允许开发者根据客户端网络状况动态设置WebSocket的idle timeout值,提供了更灵活的连接管理。

  11. Websocket协议的广泛支持
    Cowboy支持Websocket协议的所有标准,包括通过Autobahn测试套件的验证,证明了其高性能和符合标准的实现。

  12. 压缩扩展
    Cowboy的Websocket实现包括permessage-deflatex-webkit-deflate-frame压缩扩展,进一步减少了传输数据的大小。

通过这些高效的特性,Cowboy WebSocket 成为了构建高性能、低延迟的即时通讯应用的强大工具。开发者可以利用这些特性,构建出既快速又可靠的实时通信系统。

Websocket Handler 架构

IMBoy 的 websocket_handler.erl 模块通过实现 cowboy_websocket 行为来管理 WebSocket 连接。以下是关键组件的概览:

  • init/2:初始化请求处理。
  • websocket_init/1:WebSocket 连接建立后的初始化操作。
  • websocket_handle/2:处理 WebSocket 接收到的消息。
  • websocket_info/2:处理从其他进程发送到 WebSocket 进程的消息。
  • terminate/3:关闭 WebSocket 连接时的资源清理。

websocket_handler.erl 代码解析

以下是对 websocket_handler.erl 代码片段的解析:

1. 模块定义与行为引入

-module(websocket_handler).
-behavior(cowboy_websocket).

定义了名为 websocket_handler 的模块,并引入了名为 cowboy_websocket 的 behavior。

2. 导出函数

-export([init/2]).
-export([websocket_init/1]).
-export([websocket_handle/2]).
-export([websocket_info/2]).
-export([terminate/3]).

这些函数分别对应 WebSocket 生命周期的不同阶段。

3. WebSocket 初始化握手 (init/2):

在此阶段,我们从请求中提取关键信息(如设备 ID、版本号等),并根据这些信息配置 WebSocket。

3.1 客户端连接频率控制

首先,检查客户端设备 ID 的连接频率。配置文件中设定了每秒 2 次、每分钟 20 次的限制。

case throttle:check(throttle_ws, DID) of{limit_exceeded, _, _} ->imboy_log:warning("DeviceID ~p exceeded api limit", [DID]),Req = cowboy_req:reply(429, Req0),{ok, Req, State0};_ ->% ... 代码省略
end.
3.2 WebSocket 子协议升级

频率检查通过后,检查 sec-websocket-protocol 请求头,确保其为非空列表。IMBoy 采用列表中的第一个元素(例如 “text”),并设置响应头。

check_subprotocols([H | _Tail], Req0) ->Req = cowboy_req:set_resp_header(<<"sec-websocket-protocol">>, H, Req0),{cowboy_websocket, Req}.
3.3 校验 Authorization

子协议检查通过后,校验 authorization 请求头中的 JWT 令牌。验证成功后,将当前用户 UID 写入状态参数 State,供后续使用。

auth_after(Uid, Req, State, Opt) ->Timeout = idle_timeout(Uid),{cowboy_websocket, Req, State#{current_uid => Uid}, Opt#{idle_timeout := Timeout}}.
3.4 动态设置 WebSocket 的 idle timeout 值

设想根据客户端网络状况动态计算 idle timeout 值(该功能尚未实现,但值得期待)。

% 设置用户 WebSocket 超时时间,默认为 60 秒
% Cowboy 默认在 128 秒后关闭空闲连接,此处设置为 60000
idle_timeout(_Uid) ->60000.

4. 连接初始化 (websocket_init/1):

一旦 WebSocket 连接建立,可以执行一些初始化操作,例如记录用户上线、获取离线消息等。

5. 消息处理 (websocket_handle/2):

在此处理客户端发送的各种消息。例如,对于 ping 消息,回复 pong;对于文本消息,根据消息类型调用相应的处理函数。

5.1 客户端消息确认方法
  • 消息格式为 CLIENT_ACK,type,msgid,did,例如前缀 "CLIENT_ACK," 后跟消息类型、消息唯一 ID 和设备 ID。
  • 检查缓存系统中是否有相关消息的计时器引用,如果有,取消计时器并删除缓存。
  • 根据消息类型清理离线消息。

相关代码如下:

% 客户端确认消息
% 格式:CLIENT_ACK,type,msgid,did
websocket_handle({text, <<"CLIENT_ACK,", Tail/binary>>}, State) ->CurrentUid = maps:get(current_uid, State),try binary:split(Tail, <<",">>, [global]) of[Type, MsgId, DID] ->Key = {CurrentUid, DID, MsgId},% 缓存设置在 message_ds:send_next/5 中case imboy_cache:get(Key) ofundefined ->ok;{ok, TimerRef} ->erlang:cancel_timer(TimerRef),imboy_cache:flush(Key)end,% ... 根据消息类型处理
end.
5.2 处理 WebSocket 消息

根据接收到的文本消息类型,调用不同的逻辑处理函数。

websocket_handle({text, Msg}, State) ->% ... 解码消息、获取当前用户 UID% 根据消息类型分发处理逻辑case cowboy_bstr:to_lower(Type) of<<"c2c">> ->  % 单聊消息websocket_logic:c2c(MsgId, CurrentUid, Data);% ... 其他消息类型处理end;
% ... 其他处理分支

6. 信息处理 (websocket_info/2):

处理从 Erlang 系统发送到 WebSocket 进程的消息,例如超时消息或关闭连接请求。

  • 处理超时消息
websocket_info({timeout, _Ref, Msg}, State) ->{reply, {text, Msg}, State, hibernate};

当超时发生时,回复文本消息,并保持挂起状态以节省资源。

  • 服务端主动关闭连接处理
websocket_info({close, CloseCode, Reason}, State) ->{reply, {close, CloseCode, Reason}, State};
websocket_info(stop, State) ->{stop, State};

7. 连接终止 (terminate/3):

在连接终止时,根据关闭原因执行清理操作,如记录用户下线。

terminate(Reason, _Req, State) ->% ... 执行清理操作
end;

WebSocket vs AMQP vs MQTT

在选择适合 IM 应用的协议时,需考虑以下因素:

  • 实时性:WebSocket 提供最低延迟和最高实时性。
  • 复杂性:AMQP 提供丰富消息模式,但配置和实现较复杂。
  • 轻量级:MQTT 适合资源受限环境,但全双工通信受限。

结论

Cowboy WebSocket 提供了高效、简洁的方法来实现实时 Web 通信,特别适合需要快速交互的 IM 应用。通过深入理解其实现原理和生命周期管理,开发者可以构建高性能的实时通信系统。

希望通过本文的分析和代码示例,能帮助不同经验水平的开发者更好地理解和使用 Cowboy WebSocket,从而在项目中实现高效、稳定的实时通信功能。

欢迎关注 IMBoy 开源项目 https://gitee.com/imboy-pub。

相关文章:

深入理解 Cowboy WebSocket:使用 Erlang/OTP 构建高效的即时通讯(IM)应用

深入理解 Cowboy WebSocket&#xff1a;使用 Erlang/OTP 构建高效的即时通讯(IM)应用 引言 实时通信技术在现代 Web 应用中扮演着核心角色&#xff0c;而 WebSocket 作为其中的关键技术&#xff0c;已成为即时通讯(IM)系统不可或缺的一部分。Cowboy&#xff0c;这个基于 Erla…...

算法的几种常见形式

算法&#xff08;Algorithm&#xff09; 算法&#xff08;Algorithm&#xff09;是指解决问题或完成任务的一系列明确的步骤或规则。在计算机科学中&#xff0c;算法是程序的核心部分&#xff0c;它定义了如何执行特定的任务或解决特定的问题。算法可以用多种方式来表示和实现…...

SpringBoot新手快速入门系列教程二:MySql5.7.44的免安装版本下载和配置,以及简单的Mysql生存指令指南。

我的教程都是亲自测试可行才发布的&#xff0c;如果有任何问题欢迎留言或者来群里我每天都会解答。 我们要如何选择MySql 目前主流的Mysql有5.0、8.0、9.0 主要区别 MySQL 5.0 发布年份&#xff1a;2005年特性&#xff1a; 基础事务支持存储过程、触发器、视图基础存储引擎…...

Elasticsearch 更新指定字段

Elasticsearch 更新指定字段 准备条件查询数据更新指定字段更新子级字段 准备条件 以下查询操作都基于索引crm_clue来操作&#xff0c;索引已经建过了&#xff0c;本文主要讲Elasticsearch更新指定字段语句&#xff0c;下面开始写更新语句执行更新啦&#xff01; 查询数据 查…...

Koa.js、Egg.js与Express.js:探析三大Node.js框架的异同

在Node.js的世界里&#xff0c;选择合适的框架对于构建高效、可维护的后端服务至关重要。Express.js、Koa.js 和 Egg.js 是三个备受欢迎的框架&#xff0c;它们各有特色&#xff0c;适用于不同的开发场景。本文旨在深入探讨这三个框架的区别&#xff0c;并通过代码示例帮助开发…...

【MYSQL】如何解决 bin log 与 redo log 的一致性问题

该问题问的其实就是redo log 的两阶段提交 为什么说redo log 具有崩溃恢复的能力 MySQL Server 层拥有的 bin log 只能用于归档&#xff0c;不足以实现崩溃恢复&#xff08;crash-safe&#xff09;&#xff0c;需要借助 InnoDB 引擎的 redo log 才能拥有崩溃恢复的能力。所谓崩…...

翻译语音识别在线的软件,分享4款实用的软件!

在全球化日益加速的今天&#xff0c;语言沟通已成为人们生活中不可或缺的一部分。无论是商务洽谈、学术交流还是日常交流&#xff0c;翻译语音识别技术都扮演着举足轻重的角色。今天&#xff0c;我们就来揭秘一下&#xff0c;那些能让你在语言沟通中如虎添翼的翻译语音识别软件…...

Qt 的Q_PROPERTY关键字

Qt 的Q_PROPERTY关键字 1. Q_PROPERTY 的由来2. 实现原理3. Q_PROPERTY 的特点4. Q_PROPERTY 的属性5. 应用说明示例代码示例代码连接信号和槽的多种方式处理信号和槽的注意事项 QT的元对象系统1. 元对象系统的由来2. 实现原理3. 元对象系统的特点4. 元对象系统的属性5. 应用说…...

github 下载提速的几种方法

1. 代理下载&#xff08;无需注册&#xff09; //toolwa.com/github/ //d.serctl.com/2. 转入 Gitee 加速 将项目镜像到 Gitee 中下载加速 3. 使用 Watt Toolkit 加速 Watt Toolkit //steampp.net/选择合适的版本下载 选择 github&#xff0c;一键加速 4.CDN 加速 (修改…...

【Oracle】实验三 Oracle数据库的创建和管理

【实验目的】 掌握Oracle数据库的创建方法使用DBCA创建数据库在数据库中装入SCOTT用户及其表 【实验内容】 使用DBCA创建数据库&#xff0c;名为MYDB&#xff0c;找到其初始化文件(文本型和服务器型文件都要找到)&#xff0c;查看各类默认位置并记录下来(包括物理文件所在目…...

Linux rpm和ssh损坏修复

背景介绍 我遇到的问题可能和你的不一样。但是如果遇到错误一样也可以按此方案尝试修复。 我是想在Linux上安装Oracle&#xff0c;因为必须在离线环境下安装。就在网上搜一篇文章linux离线安装oracle&#xff0c;然后安装教程走&#xff0c;进行到安装oracle依赖包的时候执行了…...

仕考网:公务员考试面试时间一般多长?

公务员考试主要分为笔试与面试两个阶段&#xff0c;其中面试是笔试通过的下一关&#xff0c;面试的具体安排通常由相关考试机构或招录单位负责发布并通知考生。 公务员面试的持续时间一般在30分钟至1小时之间&#xff0c;具体时长可能因地区和招录单位的不同而有所变化。常见的…...

C语言作业5(学生管理系统C语言)

成学生管理系统 1> 使用菜单完成 2> 有学生的信息录入功能&#xff1a;输入学生个数&#xff0c;并将学生的姓名、分数录入 3> 查看学生信息&#xff1a;输出所有学生姓名以及对应的分数 4> 求出学习最好的学生信息&#xff1a;求最大值 5> 按姓名将所有学…...

OS Copilot:新手测评体验

文章目录 前言一、OS Copilot&#xff08;阿里云操作系统智能助手&#xff09;简介二、测评体验总结OS Copilot 产品体验评测OS Copilot 产品功能反馈 前言 本文简单分享一下自己使用OS Copilot测评体验。 一、OS Copilot&#xff08;阿里云操作系统智能助手&#xff09;简介 …...

PS 2024【最新】中文白嫖版!,安装教程,图文步骤

文章目录 软件介绍软件下载安装步骤 软件介绍 Photoshop&#xff0c;简称“PS” Adobe Photoshop&#xff0c;简称“PS”&#xff0c;是由Adobe Systems开发和发行的图像处理软件。Photoshop主要处理以像素所构成的数字图像。使用其众多的编修与绘图工具&#xff0c;可以有效地…...

bind方法的使用

在JavaScript或TypeScript中&#xff0c;this.data.setEventListener(this.onAddEvent.bind(this)); 和 this.data.setEventListener(this.onAddEvent); 之间的主要区别在于this关键字的绑定方式。 不使用.bind(this) 当你直接传递函数引用 this.onAddEvent给 setEventListene…...

MySQL数据库基本操作-DDL和DML

1. DDL解释 DDL(Data Definition Language)&#xff0c;数据定义语言&#xff0c;该语言部分包括以下内容&#xff1a; 对数据库的常用操作对表结构的常用操作修改表结构 2. 对数据库的常用操作 功能SQL查看所有的数据库show databases&#xff1b;查看有印象的数据库show d…...

iOS 应用内存超过多少会收到系统内存警告 ?

iOS 应用内存超过多少会收到系统内存警告 &#xff1f; 在 iOS 应用中&#xff0c;系统内存警告的触发是由 iOS 操作系统动态决定的&#xff0c;并不是一个固定的阈值。系统会根据当前设备的可用内存、正在运行的其他应用程序的内存需求以及当前应用程序的内存占用情况来判断是…...

【分布式系统】Filebeat+Kafka+ELK 的服务部署

目录 一.实验准备 二.配置部署 Filebeat 三.配置Logstash 四.验证 一.实验准备 结合之前的博客中的实验 主机名ip地址主要软件es01192.168.80.101ElasticSearches02192.168.80.102ElasticSearches03192.168.80.103ElasticSearch、Kibananginx01192.168.80.104nginx、Logs…...

Qt Qwt 图表库详解及使用

文章目录 Qt Qwt 图表库详解及使用一、Qwt 概述二、安装 Qwt1. 下载和编译 Qwt2. 在项目中使用 Qwt三、Qwt 的基本使用1. 创建一个简单的折线图2. 添加图例和自定义样式四、Qwt 的交互功能1. 启用缩放和平移2. 启用数据点选择五、Qwt 的高级特性1. 实时数据更新2. 多轴绘图六、…...

Leetcode 3576. Transform Array to All Equal Elements

Leetcode 3576. Transform Array to All Equal Elements 1. 解题思路2. 代码实现 题目链接&#xff1a;3576. Transform Array to All Equal Elements 1. 解题思路 这一题思路上就是分别考察一下是否能将其转化为全1或者全-1数组即可。 至于每一种情况是否可以达到&#xf…...

Java 加密常用的各种算法及其选择

在数字化时代&#xff0c;数据安全至关重要&#xff0c;Java 作为广泛应用的编程语言&#xff0c;提供了丰富的加密算法来保障数据的保密性、完整性和真实性。了解这些常用加密算法及其适用场景&#xff0c;有助于开发者在不同的业务需求中做出正确的选择。​ 一、对称加密算法…...

拉力测试cuda pytorch 把 4070显卡拉满

import torch import timedef stress_test_gpu(matrix_size16384, duration300):"""对GPU进行压力测试&#xff0c;通过持续的矩阵乘法来最大化GPU利用率参数:matrix_size: 矩阵维度大小&#xff0c;增大可提高计算复杂度duration: 测试持续时间&#xff08;秒&…...

Swagger和OpenApi的前世今生

Swagger与OpenAPI的关系演进是API标准化进程中的重要篇章&#xff0c;二者共同塑造了现代RESTful API的开发范式。 本期就扒一扒其技术演进的关键节点与核心逻辑&#xff1a; &#x1f504; 一、起源与初创期&#xff1a;Swagger的诞生&#xff08;2010-2014&#xff09; 核心…...

iview框架主题色的应用

1.下载 less要使用3.0.0以下的版本 npm install less2.7.3 npm install less-loader4.0.52./src/config/theme.js文件 module.exports {yellow: {theme-color: #FDCE04},blue: {theme-color: #547CE7} }在sass中使用theme配置的颜色主题&#xff0c;无需引入&#xff0c;直接可…...

基于鸿蒙(HarmonyOS5)的打车小程序

1. 开发环境准备 安装DevEco Studio (鸿蒙官方IDE)配置HarmonyOS SDK申请开发者账号和必要的API密钥 2. 项目结构设计 ├── entry │ ├── src │ │ ├── main │ │ │ ├── ets │ │ │ │ ├── pages │ │ │ │ │ ├── H…...

【HarmonyOS 5】鸿蒙中Stage模型与FA模型详解

一、前言 在HarmonyOS 5的应用开发模型中&#xff0c;featureAbility是旧版FA模型&#xff08;Feature Ability&#xff09;的用法&#xff0c;Stage模型已采用全新的应用架构&#xff0c;推荐使用组件化的上下文获取方式&#xff0c;而非依赖featureAbility。 FA大概是API7之…...

Yolo11改进策略:Block改进|FCM,特征互补映射模块|AAAI 2025|即插即用

1 论文信息 FBRT-YOLO&#xff08;Faster and Better for Real-Time Aerial Image Detection&#xff09;是由北京理工大学团队提出的专用于航拍图像实时目标检测的创新框架&#xff0c;发表于AAAI 2025。论文针对航拍场景中小目标检测的核心难题展开研究&#xff0c;重点解决…...

前端打包工具简单介绍

前端打包工具简单介绍 一、Webpack 架构与插件机制 1. Webpack 架构核心组成 Entry&#xff08;入口&#xff09; 指定应用的起点文件&#xff0c;比如 src/index.js。 Module&#xff08;模块&#xff09; Webpack 把项目当作模块图&#xff0c;模块可以是 JS、CSS、图片等…...

Spring Boot 与 Kafka 的深度集成实践(二)

3. 生产者实现 3.1 生产者配置 在 Spring Boot 项目中&#xff0c;配置 Kafka 生产者主要是配置生产者工厂&#xff08;ProducerFactory&#xff09;和 KafkaTemplate 。生产者工厂负责创建 Kafka 生产者实例&#xff0c;而 KafkaTemplate 则是用于发送消息的核心组件&#x…...