【问题标题】:Elixir stream to all subscribersElixir 流向所有订阅者
【发布时间】:2021-06-23 16:08:37
【问题描述】:

我正在尝试在 Elixir 中实现无线电服务器

一个进程始终在工作并读取文件 (mp3) 并发布到主题“:radio”,目前用于测试目的,当它完成时重新开始

每个连接都订阅主题“:radio”

我不明白如何将块发送到所有订阅的连接,连接在 2 或 3 个块后关闭

defmodule Plugtest do
  import Plug.Conn

  def init(opts), do: opts

  def start() do
    Plug.Adapters.Cowboy.http(Plugtest, [])
    {:ok, _pid} = PubSub.start_link()
    spawn(fn -> stream_from_file("./song.mp3", 128) end)
  end

  def call(conn, _opts) do
    conn = conn
    |> send_chunked(200)
    |> put_resp_content_type("audio/mpeg")

    :ok = PubSub.subscribe(spawn(fn -> send_chunk_to_connection(conn) end), :radio)
#    File.stream!("./song.mp3", [], 128) |> Enum.into(conn) # test purpose only
  end

  defp send_chunk_to_connection(conn) do
    receive do
      {:radio_data, data} ->
        IO.inspect "* #{inspect self()} * [ #{inspect conn.owner} ] [ #{inspect data} ]"
#        Enum.into(data, conn) # not working TODO send chunk to connection
        {:ok, conn} = chunk(conn, data)
        send_chunk_to_connection(conn)
    end
  end

  defp stream_from_file(fpath, bytes) do
    File.stream!(fpath, [], bytes)
    |> Enum.each(fn chunk ->
      PubSub.publish(:radio, {:radio_data, chunk})
    end)
    stream_from_file(fpath, bytes)
  end

end

堆栈跟踪:

[error] Process #PID<0.274.0> raised an exception
        ** (MatchError) no match of right hand side value: {:error, :closed}    
            (plugtest) lib/plugtest.ex:26: Plugtest.send_chunk_to_connection/1

依赖:

  defp deps do
    [{:plug, "~> 1.0"}, {:cowboy, "~> 1.0"}, {:pubsub, "~> 0.0.2"}]
  end

在@maxdec 评论后编辑

defmodule Plugtest do
  import Plug.Conn

  @file_path "./song.mp3"
  @port 4000
  @chunk_size 128

  def init(opts), do: opts

  def start() do
    Plug.Adapters.Cowboy.http Plugtest, [], port: @port
    {:ok, _pid} = PubSub.start_link()
    spawn fn ->
        stream_from_file(@file_path, @chunk_size)
    end
  end

  def call(conn, _opts) do
    conn = conn
    |> send_chunked(200)
    |> put_resp_content_type("audio/mpeg")

    :ok = PubSub.subscribe(spawn(fn -> send_chunk_to_connection(conn) end), :radio)
#    File.stream!("./song.mp3", [], 128) |> Enum.into(conn) # test purpose only
    conn
  end
  defp send_chunk_to_connection(conn) do
    receive do
      {:radio_data, data} ->
        case chunk(conn, data) do
          {:ok, conn} -> send_chunk_to_connection(conn)
          {:error, err} -> IO.puts err # do nothing, as something went wrong (client disconnection or something else...)
        end
    end
  end

  defp stream_from_file(fpath, bytes) do
    File.stream!(fpath, [], bytes)
    |> Enum.each(fn chunk ->
      PubSub.publish(:radio, {:radio_data, chunk})
    end)
    stream_from_file(fpath, bytes)
  end

end

【问题讨论】:

  • 我是否需要发送唯一的标头才能通过 http 对音频进行分块编码?

标签: streaming elixir cowboy


【解决方案1】:

快速浏览后,我认为您应该解决两件事:

  1. PlugTestPlug 所以 call/2 应该返回 conn (虽然这不是你的问题)。它还应该在等待事件时阻塞 (receive):
    def call(conn, _opts) do
      conn = conn
      |> send_chunked(200)
      |> put_resp_content_type("audio/mpeg")

      :ok = PubSub.subscribe(self(), :radio)
      send_chunk_to_connection(conn)
    end
  1. send_chunk_to_connection 你应该这样做:
    defp send_chunk_to_connection(conn) do
      receive do
        {:radio_data, data} ->
          case chunk(conn, data) do
            {:ok, conn} -> send_chunk_to_connection(conn)
            {:error, err} -> IO.puts err; conn # do nothing, as something went wrong (client disconnection or something else...)
          end
      end
    end

【讨论】:

  • 首先,谢谢,它不起作用,我更改了 call/2,在 send_chunk_to_connection/1 处不明白要做什么,附加上面的代码
  • 试过了,“关闭”了,我没有得到 2#send_chunk_to_connection#{:ok, conn},将数据发送到连接的 Enum.into 在哪里? (另外,上面编辑过的代码)
  • @JimWest:试试我的新编辑:订阅当前连接的进程并阻止,只要您继续接收事件。不幸的是,我目前无法测试自己
  • 没关系,谢谢你的帮助,无论如何 ":cowboy_protocol:start_link/4 at #PID exit with reason: {{%Protocol.UndefinedError{description: "", protocol: Enumerable ...”我仍然不明白将数据发送到连接的行在哪里(Enum.into?)
猜你喜欢
  • 2022-09-27
  • 1970-01-01
  • 1970-01-01
  • 2019-06-16
  • 2016-02-20
  • 2020-06-11
  • 2016-05-22
  • 2021-02-23
  • 1970-01-01
相关资源
最近更新 更多