elli WebSocket 实现指南:Handover 机制让双工通信更简单的 30 分钟实战
【免费下载链接】elliSimple, robust and performant Erlang web server项目地址: https://gitcode.com/gh_mirrors/ell/elli
elli 是一个简单、健壮且高性能的 Erlang web server,专为 HTTP API 场景设计,但你同样可以用它轻松实现 WebSocket 服务。本文将带你用 30 分钟掌握 elli 的 Handover 机制:它能把 TCP socket 的控制权直接交给你的业务代码,让你从零搭建一个双工通信服务。无论你是 Erlang 新手还是正在选型 WebSocket 服务器方案,这份 elli WebSocket 实现指南都能帮你快速上手。
为什么用 elli 做 WebSocket 服务?🧐
先说结论:elli 本身不内置 WebSocket 协议解析,但它提供了一条极其优雅的"逃生通道"——Handover。相比 Cowboy 等重型框架,elli 的核心优势在于:
| 特性 | 说明 |
|---|---|
| 🚀 高性能 | 基于 acceptor 池 + 事件驱动,文档称其为 performant Erlang web server |
| 📦 极简 | 源码仅十几个模块,无重型依赖,学习成本低 |
| 🔌 可扩展 | Middleware 机制支持请求预处理与多 handler 链式路由 |
| 🎯 精准控制 | Handover 把连接控制权交给用户,协议实现完全自由 |
更关键的是,elli 的 CHANGELOG.md 明确提到:"Handover" a socket to user code, making it possible to implement WebSockets,也就是说 Handover 机制的设计初衷之一,就是为 WebSocket 这类长连接双工协议提供实现空间。
Handover 机制的核心原理:连接控制权交接 🔄
在了解实战之前,先花 2 分钟搞懂原理。elli 的请求处理流程通常是这样:连接建立 → 解析 HTTP 请求 → 调用回调函数 → 返回响应。而 Handover 机制在中间插入了一个"岔路口":
%% 位于 elli_http.erl 的 handle_request/4 case init(Req) of {ok, standard} -> %% 标准流程:读 body、执行回调、返回响应 ...; {ok, handover} -> %% Handover 流程:直接调用你的 handle/2, %% 由你的代码接管整个 socket! Req1 = Req#req{body = B1}, Response = Mod:handle(Req1, Args), Response end这段逻辑就在 elli_http.erl。当你的回调模块在init/2中返回{ok, handover}时,elli 就不再按 HTTP 语义处理这个连接,而是把Req(其中携带了socket字段)直接交给你的handle/2函数。
💡一句话总结:Handover 就像你把方向盘交给了司机——之后车怎么开(协议怎么聊),完全由你决定。这正是实现 WebSocket 升级握手的理想切入点。
30 分钟实战:搭建基于 Handover 的 WebSocket 服务 🛠️
第一步:获取并编译 elli 源码(约 3 分钟)
首先克隆项目源码:
git clone https://gitcode.com/gh_mirrors/ell/elli cd elli make项目使用 rebar 构建,依赖为空(deps, []),编译非常轻量。编译通过后,你可以先跑一下测试验证环境:
make eunit其中 elli_handover_tests.erl 就是 Handover 路径的集成测试用例,稍后我们会对照它来理解代码。
第二步:从官方示例理解 Handover 触发方式(约 10 分钟)
elli 提供了一个开箱即用的示例模块 elli_example_callback_handover.erl,它展示了 Handover 的完整用法,核心逻辑分为三部分:
1. 在 init/2 中判定哪些请求走 Handover:
init(Req, _Args) -> case elli_request:path(Req) of [<<"hello">>, <<"world">>] -> {ok, handover}; %% 命中路径 → 触发 Handover _ -> ignore %% 其他路径 → 标准流程 end.2. 在 handle/2 中接管连接,直接使用底层 socket 通信:
handle('GET', [<<"hello">>, <<"world">>], Req, _Args) -> Body = <<"Hello World!">>, Size = list_to_binary(integer_to_list(size(Body))), %% 绕过 elli 的响应封装,直接通过 socket 发送 elli_http:send_response(Req, 200, [{"Connection", "close"}, {"Content-Length", Size}], Body), {close, <<>>}; %% 返回 {close, _} 表示处理完毕后关闭连接3. 其他请求照常走普通 HTTP 流程:
handle('GET', [<<"hello">>], Req, _Args) -> Name = elli_request:get_arg(<<"name">>, Req, <<"undefined">>), {ok, [], <<"Hello ", Name/binary>>}.⚠️ 注意{close, <<>>}这个返回值——它告诉 elli"连接已由我处理完毕,请关闭 socket"。如果你的 WebSocket 连接要保持长连接,这里就要返回相应的长连接语义(例如持续循环接收数据),这也是从 Handover 到 WebSocket 的关键一步。
第三步:改造为真正的 WebSocket 双工通信(约 15 分钟)
现在你已经掌握了 Handover 的骨架,要让它"升级"成 WebSocket 服务,只需要做两件事:
- 完成 HTTP 升级握手:在
handle/2中读取请求头,校验Upgrade: websocket与Sec-WebSocket-Key,然后向客户端返回101 Switching Protocols响应; - 进入消息循环:握手成功后,用
gen_tcp:recv/2循环读取客户端帧,解析 WebSocket 帧格式(FIN、opcode、mask、payload length 等),并调用gen_tcp:send/2回写数据。
由于 socket 已经握在你手里,双工通信的实现完全透明。你可以按需实现 ping/pong 心跳、分片消息、二进制帧等功能,不受 elli 框架的任何约束。
启动服务的方式与普通 elli 服务完全一致,只需把 callback 换成我们的模块:
{ok, Pid} = elli:start_link([{callback, elli_example_callback_handover}, {port, 3003}]).参考 elli_handover_tests.erl 中的setup/0,你可以照葫芦画瓢写出自己的启动与集成测试。
进阶:Handover 与 Middleware 的搭配技巧 🧩
如果你的项目需要鉴权、压缩等横切逻辑,可以借助 elli_middleware.erl 实现多 handler 链式处理。Middleware 本身也是一个 elli handler,支持预处理与后处理,例如:
Config = [{mods, [ {elli_example_middleware, []}, %% 前置中间件 {elli_middleware_compress, []}, %% 压缩中间件 {elli_example_callback_handover, []} %% 真正的业务回调(Handover) ]}], elli:start_link([..., {callback, elli_middleware}, {callback_args, Config}]).推荐把鉴权、日志放在前置中间件,让 Handover 回调专注于 WebSocket 协议本身,这样职责清晰、便于测试。
常见问题与避坑指南 🚧
- 为什么我的 handover 请求没有触发?检查
init/2的返回值:必须是{ok, handover}原子元组,且你的模块需要导出init/2、handle/2、handle_event/3三个函数(见 elli_example_callback_handover.erl)。 - WebSocket 长连接如何保持?Handover 后 elli 默认流程已结束,你需要在自己的
handle/2里维护消息循环,注意设置合适的 socket 接收超时,并实现心跳机制防掉线。 - 性能如何保障?elli 每个连接由独立进程处理,天然契合 Erlang 的并发模型。WebSocket 长连接占用的进程资源远低于短连接风暴,配合
min_acceptors参数调节 elli.erl 中的 acceptor 池大小即可。 - 想用现成的 WebSocket 库?社区有基于 Handover 构建的 elli_websocket 扩展,你可以在 CHANGELOG 的 v1.0 说明中找到线索,再结合本文原理自行集成。
总结 ✅
通过本文的 30 分钟实战,你学会了:
- elli 作为Erlang web server的轻量、高性能特性;
- Handover 机制的本质:
init/2返回{ok, handover}后,连接控制权交给用户代码; - 基于 elli_example_callback_handover.erl 改造出 WebSocket 双工通信服务的完整思路;
- 与 Middleware 搭配以及常见坑位的规避方法。
elli 的哲学是"框架只管 HTTP,协议自由发挥"。Handover 机制就像一把万能钥匙,让 WebSocket 这类双工协议在你的 Erlang 服务中轻松落地。现在就 clone 一份源码动手试试吧,30 分钟后你也能拥有自己的 WebSocket 服务!🚀
【免费下载链接】elliSimple, robust and performant Erlang web server项目地址: https://gitcode.com/gh_mirrors/ell/elli
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考