primus

primus

统一实时通信接口 简化框架切换与开发

Primus作为实时通信框架包装器,为Node.js环境提供统一接口。它支持多种实时框架,便于技术栈切换,具备自动重连、离线检测和消息编解码功能。Primus设计了插件接口,在保持核心库轻量的同时支持扩展。其流兼容接口简洁,为实时Web应用开发提供了灵活性和稳定性。

Primus实时通信WebSocket服务器跨平台Github开源项目

Primus

Version npmCICoverage Status

Primus, the creator god of transformers but now also known as universal wrapper for real-time frameworks. There are a lot of real-time frameworks available for Node.js and they all have different opinions on how real-time should be done. Primus provides a common low level interface to communicate in real-time using various real-time frameworks.

Advantages

  1. Effortless switching between real-time frameworks by changing one single line of code. No more API rewrites needed when your project requirements change, the framework gets abandoned or simply breaks down.
  2. Built-in reconnect, it just works. The reconnect is controlled by a randomised exponential back-off algorithm to reduce server stress.
  3. Offline detection, Primus is smart enough to detect when users drop their internet connection (switching WIFI points/cell towers for example) and reconnects when they are back online.
  4. Automatically encodes and decodes messages using custom parsers. Can be easily switched for binary encoding for example.
  5. A clean, stream-compatible interface for the client and server. You can just stream#pipe data around. In addition to that, the client works on Node.js as well, write once, run it everywhere.
  6. Fixes various of bugs in the supported frameworks and additional stability patches to improve real-time communication.
  7. Comes with an amazing plugin interface to keep the core library as fast and lean as possible while still allowing the server and the client to be extended.
  8. Last but not least, Primus is built with love, passion and dedication to the real-time web.

Installation

Primus is released on npm and can be installed using:

npm install primus --save

Before Starting

If you deploy your application behind a reverse proxy (Nginx, HAProxy, etc.) you might need to add WebSocket specific settings to its configuration files. If you intend to use WebSockets, please ensure that these settings have been added. There are some example configuration files available in the observing/balancerbattle repository.

Table of Contents

Getting started

Primus doesn't ship with real-time frameworks as dependencies, it assumes that you as user add them yourself as a dependency. This is done to keep the module as lightweight as possible. This works because require in will walk through your directories searching for node_module folders that have these matching dependencies.

Primus needs to be "attached" to a HTTP compatible server. These includes the built-in http and https servers but also the spdy module as it has the same API as node servers. Creating a new Primus instance is relatively straightforward:

'use strict'; var Primus = require('primus') , http = require('http'); var server = http.createServer(/* request handler */) , primus = new Primus(server, {/* options */});

The following options can be provided:

NameDescriptionDefault
authorizationAuthorization handlernull
pathnameThe URL namespace that Primus can own/primus
parserMessage encoder for all communicationJSON
transformerThe transformer we should use internallywebsockets
pluginThe plugins that should be applied{}
pingIntervalInterval at which heartbeats are sent30000
globalSet a custom client class / global namePrimus
compressionUse permessage-deflate / HTTP compressionfalse
maxLengthMaximum allowed packet size, in bytes10485760
transportTransformer specific configuration{}
idGeneratorCustom spark id generator functionundefined
originscors List of origins*
methodscors List of accepted HTTP methodsGET,HEAD,PUT,POST,DELETE,OPTIONS
credentialscors Allow sending of credentialstrue
maxAgecors Cache duration of CORS preflight30 days
headerscors Allowed headersfalse
exposedcors Headers exposed to the clientfalse

The options that are prefixed with cors are supplied to our access-control module which handles HTTP Access Control (CORS), so for a more detailed explanation of these options check it out.

The transport option allows you to use any configuration option supported by the underlying real-time framework. Its use is discouraged as these options are framework specific and no longer work if you change transformer. Our advise is to use it only if you know what you are doing and if you need fine-grained control over the real-time framework. Please also keep in mind that some of these options are overriden by Primus.

The pingInterval option specifies the interval at which heartbeats are transmitted. It is possible to completely disable the heartbeats by setting the value of the pingInterval option to false.

The idGenerator option can be used to define a function which will be called to set each spark.id. The generator function should return a unique string each time it is invoked. If idGenerator is not defined, Primus will try to use ids provided by the transformer. If the transformer does not provide ids, Primus will use nanoid to generate Spark ids.

If you don't have a pre-existing server where you want or can attach your Primus server to you can also use the Primus.createServer convenience method. The createServer method will automatically:

  • Setup a HTTP, HTTPS or SPDY server for you on the given port number.
  • Setup your Primus server with the given configuration.
  • Listen on the HTTP, HTTPS, SPDY server.
  • Attach a primus.on('connection') listener.
  • Return the created Primus instance.
Primus.createServer(function connection(spark) { }, { port: 8080, transformer: 'websockets' });

In the above example we automatically create a HTTP server which will listen on port 8080, a primus instance with the websockets transformer and start listening for incoming connections. The supplied function in the Primus.createServer method is optional. You can just listen for incoming connections your self using the returned Primus instance. If you want to listen to a HTTPS or SPDY server, which is recommended, you can directly pass the SPDY and HTTPS certs/keys/pfx files in the options object:

var primus = Primus.createServer({ port: 443, root: '/folder/with/https/cert/files', cert: 'myfilename.cert', key: 'myfilename.cert', ca: 'myfilename.ca', pfx: 'filename.pfx', passphrase: 'my super sweet password' }); primus.on('connection', function (spark) { spark.write('hello connnection'); });

Primus.createServer returns a warning when it starts a HTTP server. The warning advises you to use a HTTPS server and can be disabled setting the option iknowhttpsisbetter to true.

Client library

As most libraries come with their own client-side framework for making the connection we've also created a small wrapper for this. The library can be retrieved using:

primus.library();

Which returns the client-side library as a string (which can then be minified or even have more code added to it). It does not come pre-minified as that is out of the scope of this project. You can store this on a CDN or on your static server. Do whatever you want with it, but remember to regenerate it every time you change Primus server options. This is important because some properties of the client are set using the server configuration. For example if you change the pathname, the client should be regenerated to reflect that change and work correctly. We advise you to regenerate the library every time you redeploy so you always have a client compatible with your back-end. To save the file you can use:

primus.save(__dirname +'/primus.js');

This will store the compiled library in your current directory. If you want to save it asynchronously, you can supply the method with a callback method:

primus.save(__dirname +'/primus.js', function save(err) { });

But to make it easier for you during development we've automatically added an extra route to the supplied HTTP server, this will serve the library for you so you don't have to save it. Please note, that this route isn't optimised for serving static assets and should only be used during development. In your HTML page add:

<script src="/primus/primus.js"></script>

As you can see, it will use the /primus pathname by default. Primus needs to own the whole path/namespace in order to function properly as it will forward all other requests directly in to the transformers so they can work their magic. If you already have a static folder with the name primus you can change the pathname to something different and still make this work. But you would of course need to update the src attribute of the script tag to set the correct location. It's always available at:

<protocol>://<server location>/<pathname>/primus.js

Here <pathname> is the pathname set in server options above. The client is cross domain compatible so you don't have to serve it from the same domain you're running Primus on. But please note, that the real-time framework you're using might be tied to same domain restrictions.

Once you're all set up you can start listening for connections. These connections are announced through the connection event.

primus.on('connection', function (spark) { // spark is the new connection. });

Disconnects are announced using a disconnection event:

primus.on('disconnection', function (spark) { // the spark that disconnected });

The spark argument is the actual real-time socket/connection. Sparks have a really low level interface and only expose a couple properties that are cross engine supported. The interface is modeled towards a Node.js stream compatible interface. So this will include all methods that are available on the stream interface including Spark#pipe.

spark.headers

The spark.headers property contains the headers of either the request that started a handshake with the server or the headers of the actual real-time connection. This depends on the module you are using.

Please note that sending custom headers from the client to the server is impossible as not all transports that these transformers support can add custom headers to a request (JSONP for example). If you need to send custom data, use a query string when connecting

spark.address

The spark.address property contains the ip and port of the connection. If you're running your server behind a reverse proxy it will automatically use the x-forwarded-for header. This way you will always have the address of the connecting client and not the IP address of your proxy.

Please note that the port is probably out of date by the time you're going to read it as it's retrieved from an old request, not the request that is active at the time you access this property.

spark.query

The spark.query contains the query string you used to connect to the server. It's parsed as an object. Please note that this may not be available for all supported transformers.

spark.socket

The spark.socket is set to the underlying socket of the transformer. This is not necessarily a raw Socket and will differ from transformer to transformer.

spark.id

This is a unique id that we use to identify this single connection with. Normally the frameworks refer to this as a sessionid, which is confusing as it's only used for the duration of one single connection. You should not see this as a "session id", and rather expect it to change between disconnects and reconnects.

spark.request

The spark.request gives you access to the HTTP request that was used to initiate the real-time connection with the server. Please note that this request is already answered and closed (in most cases) so do not attempt to write or answer it anyway. But it might be useful to access methods that get added by middleware layers, etc.

spark.write(data)

You can use the spark.write method to send data over the socket. The data is automatically encoded for you using the parser that you've set while creating the Primus server instance. This method always returns true on success and false on failure so back pressure isn't handled.

spark.write({ foo: 'bar' });

spark.end(data, options)

You can use spark.end to close the connection. This method takes two optional arguments. The first, if provided, is the data to send to the client before closing the connection. The second is an options object used to customize the behavior of the method. By default the spark.end method closes the connection in a such way that the client knows it was intentional and it doesn't attempt a reconnection.

spark.end(); // the client doesn't reconnect automatically

You can change this behavior and trigger a client-side reconnection using the reconnect option.

spark.end(undefined, { reconnect: true }); // trigger a client-side reconnection

spark.emits(event, parser)

This method is mostly used internally. It works similarly to the native bind function, returning a function that emits the assigned event every time it's called. If the last argument is a function, it will be used to parse the arguments of the returned function. The parser is optional and always async, its first argument is a callback that follows the usual error first pattern, all successive arguments are the ones to parse. Using the parser you can reduce the arguments down to a single value, remove them completely or prevent the event from being emitted. See emits for detailed usage instructions.

spark.emits('event', function parser(next, structure) { next(undefined, structure.data); });

Please note that the data that is received here isn't decoded yet.

spark.on('data')

The data event is emitted when a message is received from the client. It's automatically decoded by the specified decoder.

spark.on('data', function message(data) { // the message we've received. });

spark.on('end')

The end event is emitted when the client has disconnected.

primus.on('connection', function (spark) { console.log('connection has the following headers', spark.headers); console.log('connection was made from', spark.address); console.log('connection id', spark.id); spark.on('data', function (data) { console.log('received data from the client', data); // // Always close the connection if we didn't receive our secret imaginary // handshake. // if ('foo' !== data.secrethandshake) spark.end(); spark.write({ foo: 'bar' }); spark.write('banana');

编辑推荐精选

音述AI

音述AI

全球首个AI音乐社区

音述AI是全球首个AI音乐社区,致力让每个人都能用音乐表达自我。音述AI提供零门槛AI创作工具,独创GETI法则帮助用户精准定义音乐风格,AI润色功能支持自动优化作品质感。音述AI支持交流讨论、二次创作与价值变现。针对中文用户的语言习惯与文化背景进行专门优化,支持国风融合、C-pop等本土音乐标签,让技术更好地承载人文表达。

QoderWork

QoderWork

阿里Qoder团队推出的桌面端AI智能体

QoderWork 是阿里推出的本地优先桌面 AI 智能体,适配 macOS14+/Windows10+,以自然语言交互实现文件管理、数据分析、AI 视觉生成、浏览器自动化等办公任务,自主拆解执行复杂工作流,数据本地运行零上传,技能市场可无限扩展,是高效的 Agentic 生产力办公助手。

lynote.ai

lynote.ai

一站式搞定所有学习需求

不再被海量信息淹没,开始真正理解知识。Lynote 可摘要 YouTube 视频、PDF、文章等内容。即时创建笔记,检测 AI 内容并下载资料,将您的学习效率提升 10 倍。

AniShort

AniShort

为AI短剧协作而生

专为AI短剧协作而生的AniShort正式发布,深度重构AI短剧全流程生产模式,整合创意策划、制作执行、实时协作、在线审片、资产复用等全链路功能,独创无限画布、双轨并行工业化工作流与Ani智能体助手,集成多款主流AI大模型,破解素材零散、版本混乱、沟通低效等行业痛点,助力3人团队效率提升800%,打造标准化、可追溯的AI短剧量产体系,是AI短剧团队协同创作、提升制作效率的核心工具。

seedancetwo2.0

seedancetwo2.0

能听懂你表达的视频模型

Seedance two是基于seedance2.0的中国大模型,支持图像、视频、音频、文本四种模态输入,表达方式更丰富,生成也更可控。

nano-banana纳米香蕉中文站

nano-banana纳米香蕉中文站

国内直接访问,限时3折

输入简单文字,生成想要的图片,纳米香蕉中文站基于 Google 模型的 AI 图片生成网站,支持文字生图、图生图。官网价格限时3折活动

扣子-AI办公

扣子-AI办公

职场AI,就用扣子

AI办公助手,复杂任务高效处理。办公效率低?扣子空间AI助手支持播客生成、PPT制作、网页开发及报告写作,覆盖科研、商业、舆情等领域的专家Agent 7x24小时响应,生活工作无缝切换,提升50%效率!

堆友

堆友

多风格AI绘画神器

堆友平台由阿里巴巴设计团队创建,作为一款AI驱动的设计工具,专为设计师提供一站式增长服务。功能覆盖海量3D素材、AI绘画、实时渲染以及专业抠图,显著提升设计品质和效率。平台不仅提供工具,还是一个促进创意交流和个人发展的空间,界面友好,适合所有级别的设计师和创意工作者。

图像生成AI工具AI反应堆AI工具箱AI绘画GOAI艺术字堆友相机AI图像热门
码上飞

码上飞

零代码AI应用开发平台

零代码AI应用开发平台,用户只需一句话简单描述需求,AI能自动生成小程序、APP或H5网页应用,无需编写代码。

Vora

Vora

免费创建高清无水印Sora视频

Vora是一个免费创建高清无水印Sora视频的AI工具

下拉加载更多