让 socket.io 客户端版本落后于服务器版本
Letting socket.io client version lag behind server version
情况
我们正在使用 socket.io 进行 mobile-server 通信。由于我们不能 force-upgrade 用户的设备,如果我们想升级到版本 1 (non-back-compatible),我们必须在服务器上处理这两个版本一段时间。
问题
有哪些选项?
我目前最喜欢的是将旧版本和新版本都包装在一个多路复用器中。它根据 headers 和查询参数检测传入请求的版本,从而知道要调用哪些函数。
另一个(更糟糕的)选项是将新版本包装在一个模块中,该模块可以在必要时将协议的旧版本转换为新版本(并再次转换回来)。这有一个严重的缺点。这将是 time-consuming 和不确定的工作,以确保我已正确确定并处理所有微小的差异。有些差异可能需要一些认真的按摩。
(如果您好奇或者知道这对您有帮助,我们将在 Go 中进行此操作。)
我真的没有立即解决这个问题的方法,但我有一些建议。我想你可以用它来节省很多时间。
- 首先,我在一家使用 socketIo 的初创公司工作
一切
- 我们知道会发生这个问题,所以我们最初的设计是
让一切都可插拔,这意味着我们可以将 socketio 换成
sockjs,它仍然可以工作。
- 其实现方式是定义很少更改的 API 的通用集
在一个系统中。我们称之为 经理 。管理人员可以公开其他开发人员需要使用的 API 而不会弄乱任何东西。它加快了很多。
- 管理器实现在后台发生变化,但 API 仍然相同,因此从事核心工作的工程师可以放心地进行更改。
- 您的代码似乎有很强的依赖性。或者可能不是。我不确定。如果还没有,请尝试遵循此原则。
您似乎可以在服务器上 运行 两个不同版本的 socket.io。由于这两个版本没有唯一的模块文件名,您可能需要从不同的路径加载一个版本。而且,很明显,在加载模块并初始化它们时,您会将它们分配给不同名称的变量。例如:
var io_old = require('old/socket.io');
var io = require('socket.io);
一旦你在服务器上加载了两个版本,我认为有两种不同的方法可以 运行。
1) 每个版本使用不同的端口。旧版本将使用与 [=58] 共享的默认端口 80(无需更改配置) =] 网络服务器。较新的版本将是 运行 在不同的端口(比如端口 3000)上。然后,您会将 socket.io 的每个版本初始化到它自己的端口。然后,您的较新版本客户端将连接到较新版本运行正在打开的端口。
对于端口 80 上的旧 socket.io 服务器 运行ning,您将使用您已经拥有的任何可能挂接到现有 http 服务器的初始化。
对于新的 socket.io 服务器 运行ning 在其他端口上,您可以像这样单独初始化它:
var io_old = require('old/socket.io')(server);
var io = require('socket.io')(3000);
那么在新版客户端中,连接时需要指定3000端口
var socket = io("http://yourdomain.com:3000");
2) 为每个版本使用不同的 HTTP 请求路径。 默认情况下,每个 socket.io 连接都以如下所示的 HTTP 请求开始:http://yourdomain.com/socket.io?EIO=xx&transport=xxx?t=xxx
.但是,该请求的 /socket.io
部分是可配置的,并且 socket.io 的两个单独版本可以各自使用不同的路径名。在服务器上,启动 socket.io 侦听的 .listen()
方法采用可选的选项对象,可以使用自定义路径配置该对象,如 path: "/socket.io-v2"
中一样。同样,客户端中的 .connect()
方法也接受该选项对象。很难找到此选项的 the documentation,因为它实际上是一个 engine.io 选项(socket.io 使用),但是 socket.io 将选项传递给 engine.io .
我自己都没有尝试过,但我研究了 socket.io 连接是如何从客户端和服务器发起的,看起来底层引擎支持这种能力,但我看不出为什么会这样不应该工作。
以下是更改服务器路径的方法:
var io = require('socket.io')(server, {path: "/socket.io.v1"});
然后,在新版本的客户端代码中,您将像这样连接:
var socket = io({path: "/socket.io.v1"});
这将导致向 HTTP URL 发出初始连接请求,如下所示:
http://yourdomain.com/socket.io.v1?EIO=xx&transport=xxx?t=xxx
这将由您的 HTTP 服务器上的不同请求处理程序处理,从而将两个版本分开。
仅供参考,socket.io 连接 URL 中的 EIO=3
查询参数也有可能实际上是一个 engine.io 版本号,也可用于根据该值辨别客户端版本和 "do the right thing"。我没有找到任何关于它如何工作的文档,甚至找不到在 engine.io 或 socket.io 源代码中查看该查询参数的位置,因此另一种可能性需要更多调查。
我们打算将 0.9.x 版本和当前版本作为单独的库保存在服务器上。最终,当客户端池 more-or-less 全部更新时,我们将关闭 0.9.x 版本。
我们管理这两个版本的方法是将 socket.io 服务包装在一个包中,该包将确定将请求传递给哪个包装的 socket.io 版本。这一决定将取决于请求的特征,例如自定义 headers(可以添加到较新的客户端)以及查询参数和其他 headers 由一个版本或另一个版本独占使用。
自从我们使用 Go 以来,目前还没有普遍认可的管理依赖关系的方法,更不用说可以尊重版本差异的方法了。假设回购的 back-compat 分支没有被破坏(确实如此),我们有两个选择。第一个是分叉回购并使 back-compat 版本成为主版本。然后我们将导入它,就好像它与另一个无关一样。第二种选择是使用 gopkg.in 假装单独的分支是单独的 repos。
无论哪种情况,我们都可以像这样导入两个 branches/repos
import (
socketioV0 "github.com/path/to/older/version"
socketioV1 "github.com/path/to/current/version"
)
然后在代码中使用它们的导入名称 socketioV0
和 socketioV1
.
引用它们
情况
我们正在使用 socket.io 进行 mobile-server 通信。由于我们不能 force-upgrade 用户的设备,如果我们想升级到版本 1 (non-back-compatible),我们必须在服务器上处理这两个版本一段时间。
问题
有哪些选项?
我目前最喜欢的是将旧版本和新版本都包装在一个多路复用器中。它根据 headers 和查询参数检测传入请求的版本,从而知道要调用哪些函数。
另一个(更糟糕的)选项是将新版本包装在一个模块中,该模块可以在必要时将协议的旧版本转换为新版本(并再次转换回来)。这有一个严重的缺点。这将是 time-consuming 和不确定的工作,以确保我已正确确定并处理所有微小的差异。有些差异可能需要一些认真的按摩。
(如果您好奇或者知道这对您有帮助,我们将在 Go 中进行此操作。)
我真的没有立即解决这个问题的方法,但我有一些建议。我想你可以用它来节省很多时间。
- 首先,我在一家使用 socketIo 的初创公司工作
一切 - 我们知道会发生这个问题,所以我们最初的设计是
让一切都可插拔,这意味着我们可以将 socketio 换成
sockjs,它仍然可以工作。 - 其实现方式是定义很少更改的 API 的通用集
在一个系统中。我们称之为 经理 。管理人员可以公开其他开发人员需要使用的 API 而不会弄乱任何东西。它加快了很多。 - 管理器实现在后台发生变化,但 API 仍然相同,因此从事核心工作的工程师可以放心地进行更改。
- 您的代码似乎有很强的依赖性。或者可能不是。我不确定。如果还没有,请尝试遵循此原则。
您似乎可以在服务器上 运行 两个不同版本的 socket.io。由于这两个版本没有唯一的模块文件名,您可能需要从不同的路径加载一个版本。而且,很明显,在加载模块并初始化它们时,您会将它们分配给不同名称的变量。例如:
var io_old = require('old/socket.io');
var io = require('socket.io);
一旦你在服务器上加载了两个版本,我认为有两种不同的方法可以 运行。
1) 每个版本使用不同的端口。旧版本将使用与 [=58] 共享的默认端口 80(无需更改配置) =] 网络服务器。较新的版本将是 运行 在不同的端口(比如端口 3000)上。然后,您会将 socket.io 的每个版本初始化到它自己的端口。然后,您的较新版本客户端将连接到较新版本运行正在打开的端口。
对于端口 80 上的旧 socket.io 服务器 运行ning,您将使用您已经拥有的任何可能挂接到现有 http 服务器的初始化。
对于新的 socket.io 服务器 运行ning 在其他端口上,您可以像这样单独初始化它:
var io_old = require('old/socket.io')(server);
var io = require('socket.io')(3000);
那么在新版客户端中,连接时需要指定3000端口
var socket = io("http://yourdomain.com:3000");
2) 为每个版本使用不同的 HTTP 请求路径。 默认情况下,每个 socket.io 连接都以如下所示的 HTTP 请求开始:http://yourdomain.com/socket.io?EIO=xx&transport=xxx?t=xxx
.但是,该请求的 /socket.io
部分是可配置的,并且 socket.io 的两个单独版本可以各自使用不同的路径名。在服务器上,启动 socket.io 侦听的 .listen()
方法采用可选的选项对象,可以使用自定义路径配置该对象,如 path: "/socket.io-v2"
中一样。同样,客户端中的 .connect()
方法也接受该选项对象。很难找到此选项的 the documentation,因为它实际上是一个 engine.io 选项(socket.io 使用),但是 socket.io 将选项传递给 engine.io .
我自己都没有尝试过,但我研究了 socket.io 连接是如何从客户端和服务器发起的,看起来底层引擎支持这种能力,但我看不出为什么会这样不应该工作。
以下是更改服务器路径的方法:
var io = require('socket.io')(server, {path: "/socket.io.v1"});
然后,在新版本的客户端代码中,您将像这样连接:
var socket = io({path: "/socket.io.v1"});
这将导致向 HTTP URL 发出初始连接请求,如下所示:
http://yourdomain.com/socket.io.v1?EIO=xx&transport=xxx?t=xxx
这将由您的 HTTP 服务器上的不同请求处理程序处理,从而将两个版本分开。
仅供参考,socket.io 连接 URL 中的 EIO=3
查询参数也有可能实际上是一个 engine.io 版本号,也可用于根据该值辨别客户端版本和 "do the right thing"。我没有找到任何关于它如何工作的文档,甚至找不到在 engine.io 或 socket.io 源代码中查看该查询参数的位置,因此另一种可能性需要更多调查。
我们打算将 0.9.x 版本和当前版本作为单独的库保存在服务器上。最终,当客户端池 more-or-less 全部更新时,我们将关闭 0.9.x 版本。
我们管理这两个版本的方法是将 socket.io 服务包装在一个包中,该包将确定将请求传递给哪个包装的 socket.io 版本。这一决定将取决于请求的特征,例如自定义 headers(可以添加到较新的客户端)以及查询参数和其他 headers 由一个版本或另一个版本独占使用。
自从我们使用 Go 以来,目前还没有普遍认可的管理依赖关系的方法,更不用说可以尊重版本差异的方法了。假设回购的 back-compat 分支没有被破坏(确实如此),我们有两个选择。第一个是分叉回购并使 back-compat 版本成为主版本。然后我们将导入它,就好像它与另一个无关一样。第二种选择是使用 gopkg.in 假装单独的分支是单独的 repos。
无论哪种情况,我们都可以像这样导入两个 branches/repos
import (
socketioV0 "github.com/path/to/older/version"
socketioV1 "github.com/path/to/current/version"
)
然后在代码中使用它们的导入名称 socketioV0
和 socketioV1
.