构建比HTTP快100倍的Elixir Redis 服务器 - statetrace

21-11-15 banq

运行 Redis 协议比依赖 HTTP 快 100 倍以上。在大约 10 分钟内编写了一个基于 Redis 协议的高性能服务器。该服务器可以轻松处理数千个连接,并且开销最小。一个缺点是,在使用 HTTP 以外的协议进行多节点部署时,负载平衡变得更加困难。

如果您有一个或数千个客户端需要以最快的方式与服务器通信,请考虑使用 Redis 作为您的首选协议,而不是 HTTP。

构建基于 RESP(Redis 协议)的服务器意味着您正在削减与 HTTP 相关的大量开销。除了更精简的协议之外,几乎每种语言都有一个为 Redis 构建的高性能客户端,允许流水线操作。流水线在您发送命令时组合命令,以提高效率。大多数 redis 客户端甚至支持池化以在高并发下工作。

使用这些内置功能,您无需做太多事情就可以以极高的性能方式与服务器通信。

完整示例:

defmodule MyRedisServer.Redis do
  require Logger

  def accept(port) do
    {:ok, socket} = :gen_tcp.listen(port, [:binary, active: false, reuseaddr: true])
    Logger.info("Accepting connections on port #{port}")
    loop_acceptor(socket)
  end

  defp loop_acceptor(socket) do
    {:ok, client} = :gen_tcp.accept(socket)

    {:ok, pid} =
      Task.start(fn ->
        serve(client, %{continuation: nil})
      end)

    :ok = :gen_tcp.controlling_process(client, pid)

    loop_acceptor(socket)
  end

  defp serve(socket, %{continuation: nil}) do
    case :gen_tcp.recv(socket, 0) do
      {:ok, data} ->  handle_parse(socket, Redix.Protocol.parse(data))
      {:error, :closed} -> :ok
    end
  end

  defp serve(socket, %{continuation: fun}) do
    case :gen_tcp.recv(socket, 0) do
      {:ok, data} ->  handle_parse(socket, fun.(data))
      {:error, :closed} -> :ok
    end
  end

  defp handle_parse(socket, {:continuation, fun}) do
    serve(socket, %{continuation: fun})
  end

  defp handle_parse(socket, {:ok, req, left_over}) do
    resp = handle(req)

    :gen_tcp.send(socket, Redix.Protocol.pack(resp))

    case left_over do
      "" -> serve(socket, %{continuation: nil})
      _ -> handle_parse(socket, Redix.Protocol.parse(left_over))
    end
  end

  def handle(data) do
    data
  end
end

有关完整的基准测试,请参阅源代码

 

猜你喜欢