根据websocketd source code,它似乎使用基于进程的服务器(每个连接一个新进程)....我不是 GO 程序员,所以我不确定我是否正确阅读,但README 似乎表示相同的概念。
因此...
问题是它为每个连接的客户端生成新进程
这是无法避免的。该设计意味着新连接与它们的 STDIN 和 STDOUT 本质上是分叉的。无法从新连接中访问流式传输到原始 STDIN 的数据...
...因此不能选择使用单个 streamgenerator 和 websocketd。
我现在唯一想到的就是写 C 程序......
我认为这可能是避免多进程设计的唯一方法。
您不必为此使用 C。您可能可以同样轻松地使用 Ruby 或 node.js(它们可能更容易编写,而您会为方便而付出代价)。
我的问题是有什么办法不开发这个工具?
我不这么认为。
但是,facil.io 可以让创作一个使用其原生 Pub/Sub API 广播数据的 C Web 套接字工具变得相当简单(允许您在未来使用 Redis 轻松扩展)...作者,我有偏见。
编辑
这是一个简短的 Ruby 脚本,它将数据从管道广播到任何连接的 Web 套接字连接(数据由行分隔)。
文件script.rb:
#!/usr/bin/env ruby
require 'iodine'
class Example
def self.call(env)
if env['upgrade.websocket?'.freeze] && env["HTTP_UPGRADE".freeze] =~ /websocket/i.freeze
env['upgrade.websocket'.freeze] = Example.new
return [0,{}, []] # It's possible to set cookies for the response.
end
[404, {"Content-Length" => "12"}, ["Bad Request."] ]
end
def on_open
subscribe channel: :stream, force: :text
end
def on_message data
close # are we expecting any messages?
end
def on_close
# nothing to do
end
def on_shutdown
# server is going away, notify client.
end
end
Iodine::Rack.app = Example
# remove these two lines for automatice, core related, detection
Iodine.processes = 1;
Iodine.threads = 1;
# initialize the Redis engine for each iodine process.
require 'uri'
if ENV["REDIS_URL"]
uri = URI(ENV["REDIS_URL"])
Iodine.default_pubsub = Iodine::PubSub::RedisEngine.new(uri.host, uri.port, 0, uri.password)
else
puts "* No Redis, it's okay, pub/sub will still run on the whole process cluster."
end
# Create the loop that reads from ARGF (the pipe)
# defer threading because we might fork the main server
root_pid = Process.pid
Iodine.run do
puts "Starting to listen to pipe"
if(root_pid == Process.pid)
Thread.new do
ARGF.each_line do |s|
Iodine.publish channel: :stream, message: s
puts "read:", s
end
end
end
end
# start iodine
Iodine.start
您可以通过以下方式从终端使用它:
$ streamgenerator | ruby script.rb
这只是一个肮脏的例子,但它说明了这可能是多么容易。
哦,它需要the iodine gem,这是 fail.io 到 Ruby(也是我的)的端口。
编辑 2
我添加了一些代码,允许您将 Redis 与我提供的示例代码一起使用,并将发布限制为单个进程。
Redis 引擎是 facil.io 的原生引擎(它是 C 语言,带有一个用于碘的 Ruby 桥),您可以使用它来发送命令以及 Pub/Sub。
如果您使用 Redis 在多台机器上进行扩展,我会考虑将脚本拆分为发布者脚本和服务器应用程序。
另外,如果您使用多台机器,则只需要 Redis。如果您使用 Iodine.processes = 8 运行 Iodine,则 pub/sub 引擎仍然可以工作。
Iodine 还有很多其他功能,如果您需要的话,例如静态文件服务等。
您还可以将整个东西打包到一个中间件中,并使其成为现有 Rails/Sintara/Rack 项目的一部分,使用 iodine 作为服务器(用于 Websocket 支持)。
...
至于:
$ ./streamgenerator | pub --channel "redisstream"
$ websocketd --port=8080 sub --channel "redisstream"
听起来这会缓解这个问题,尽管我认为websocketd 仍然会为每个连接打开一个新进程,它比事件“反应器模式”使用更多的资源,例如 nginx 等服务器使用的资源(和碘,乘客,美洲狮和其他一堆)。