当前位置: 首页 > 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. 多轴绘图六、…...

微软PowerBI考试 PL300-选择 Power BI 模型框架【附练习数据】

微软PowerBI考试 PL300-选择 Power BI 模型框架 20 多年来&#xff0c;Microsoft 持续对企业商业智能 (BI) 进行大量投资。 Azure Analysis Services (AAS) 和 SQL Server Analysis Services (SSAS) 基于无数企业使用的成熟的 BI 数据建模技术。 同样的技术也是 Power BI 数据…...

java 实现excel文件转pdf | 无水印 | 无限制

文章目录 目录 文章目录 前言 1.项目远程仓库配置 2.pom文件引入相关依赖 3.代码破解 二、Excel转PDF 1.代码实现 2.Aspose.License.xml 授权文件 总结 前言 java处理excel转pdf一直没找到什么好用的免费jar包工具,自己手写的难度,恐怕高级程序员花费一年的事件,也…...

C++ 基础特性深度解析

目录 引言 一、命名空间&#xff08;namespace&#xff09; C 中的命名空间​ 与 C 语言的对比​ 二、缺省参数​ C 中的缺省参数​ 与 C 语言的对比​ 三、引用&#xff08;reference&#xff09;​ C 中的引用​ 与 C 语言的对比​ 四、inline&#xff08;内联函数…...

【android bluetooth 框架分析 04】【bt-framework 层详解 1】【BluetoothProperties介绍】

1. BluetoothProperties介绍 libsysprop/srcs/android/sysprop/BluetoothProperties.sysprop BluetoothProperties.sysprop 是 Android AOSP 中的一种 系统属性定义文件&#xff08;System Property Definition File&#xff09;&#xff0c;用于声明和管理 Bluetooth 模块相…...

工业自动化时代的精准装配革新:迁移科技3D视觉系统如何重塑机器人定位装配

AI3D视觉的工业赋能者 迁移科技成立于2017年&#xff0c;作为行业领先的3D工业相机及视觉系统供应商&#xff0c;累计完成数亿元融资。其核心技术覆盖硬件设计、算法优化及软件集成&#xff0c;通过稳定、易用、高回报的AI3D视觉系统&#xff0c;为汽车、新能源、金属制造等行…...

高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数

高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数 在软件开发中,单例模式(Singleton Pattern)是一种常见的设计模式,确保一个类仅有一个实例,并提供一个全局访问点。在多线程环境下,实现单例模式时需要注意线程安全问题,以防止多个线程同时创建实例,导致…...

Java求职者面试指南:Spring、Spring Boot、MyBatis框架与计算机基础问题解析

Java求职者面试指南&#xff1a;Spring、Spring Boot、MyBatis框架与计算机基础问题解析 一、第一轮提问&#xff08;基础概念问题&#xff09; 1. 请解释Spring框架的核心容器是什么&#xff1f;它在Spring中起到什么作用&#xff1f; Spring框架的核心容器是IoC容器&#…...

scikit-learn机器学习

# 同时添加如下代码, 这样每次环境(kernel)启动的时候只要运行下方代码即可: # Also add the following code, # so that every time the environment (kernel) starts, # just run the following code: import sys sys.path.append(/home/aistudio/external-libraries)机…...

LRU 缓存机制详解与实现(Java版) + 力扣解决

&#x1f4cc; LRU 缓存机制详解与实现&#xff08;Java版&#xff09; 一、&#x1f4d6; 问题背景 在日常开发中&#xff0c;我们经常会使用 缓存&#xff08;Cache&#xff09; 来提升性能。但由于内存有限&#xff0c;缓存不可能无限增长&#xff0c;于是需要策略决定&am…...

[大语言模型]在个人电脑上部署ollama 并进行管理,最后配置AI程序开发助手.

ollama官网: 下载 https://ollama.com/ 安装 查看可以使用的模型 https://ollama.com/search 例如 https://ollama.com/library/deepseek-r1/tags # deepseek-r1:7bollama pull deepseek-r1:7b改token数量为409622 16384 ollama命令说明 ollama serve #&#xff1a…...