Невозможно подключиться к Redis из приложения Phoenix - PullRequest
0 голосов
/ 12 июля 2019

Я хочу подключиться к Redis из моего приложения Phoenix, но не могу этого сделать.

Я использую Phoenix v1.3.0.Я установил пакет Redix.В lib / myapp.ex у меня есть следующий код

defmodule myapp do
  use Application

  # See http://elixir-lang.org/docs/stable/elixir/Application.html
  # for more information on OTP Applications
  def start(_type, _args) do
    import Supervisor.Spec

    # Get all configuration used for Cache module
    pool_size = Application.get_env(:myapp, :redis_pool_size)
    redis_host = Application.get_env(:myapp, :redis_host)
    redis_port = Application.get_env(:myapp, :redis_port)

    # Define workers and child supervisors to be supervised
    children = [
      # Start the Ecto repository
      supervisor(myapp.Repo, []),
      # Start the endpoint when the application starts
      supervisor(myapp.Endpoint, []),
      #Start redis supervisor
      supervisor(myapp.Cache.Supervisor, [
        %{
          pool_size: pool_size,
          host: redis_host,
          port: redis_port
        }
      ])
      # supervisor(Kafka.Endpoint, [])
    ]

    # See http://elixir-lang.org/docs/stable/elixir/Supervisor.html
    # for other strategies and supported options
    opts = [strategy: :one_for_one, name: myapp.Supervisor]
    Supervisor.start_link(children, opts)
  end

  # Tell Phoenix to update the endpoint configuration
  # whenever the application is updated.
  def config_change(changed, _new, removed) do
    myapp.Endpoint.config_change(changed, removed)
    :ok
  end
end

В lib / myapp / cache / cache.ex у меня есть

defmodule MyApp.Cache do
    require Logger

    def setex(segment, key, value, ttl \\ nil) do
      ttl = ttl || get_default_ttl()
      pid = get_pid()
      key = build_key(segment, key)
      command = ["SETEX", key, ttl, value]

      run_command(pid, command)
    end

    def get(segment, key) do
      pid = get_pid()
      key = build_key(segment, key)
      command = ["GET", key]

      run_command(pid, command)
    end

    def flush_all do
      pid = get_pid()
      command = ["FLUSHALL"]

      run_command(pid, command)
    end

    defp get_default_ttl(), do: Application.get_env(:myapp, :redis_ttl)
    defp get_app_name(), do: Application.get_env(:myapp, :app_name)
    defp get_pool_size(), do: Application.get_env(:myapp, :redis_pool_size)

    defp build_key(segment, key), do: "#{get_app_name()}:#{segment}-#{key}"

    defp get_pid, do: :"redix_#{random_index()}"

    defp random_index(), do: rem(System.unique_integer([:positive]), get_pool_size())

    defp run_command(pid, command) do
      Logger.debug("Running command: #{inspect(command)} in Redis")
      Redix.command(pid, command)
    end
end

И в lib / myapp / cache/supervisor.ex, у меня есть,

defmodule MyApp.Cache.Supervisor do
    use Supervisor

    def start_link(opts) do
      Supervisor.start_link(__MODULE__, opts, name: __MODULE__)
    end

    def init(%{
          pool_size: pool_size,
          host: host,
          port: port
        }) do
      children =
        for i <- 0..(pool_size - 1) do
          Supervisor.child_spec(
            {Redix, [host: host, port: port, name: :"redix_#{i}"]},
            id: {Redix, i}
          )
        end

      Supervisor.init(children, strategy: :one_for_one)
    end
end

Теперь, когда я запускаю MyApp.get("1", "2"), я получаю следующую ошибку:

(ArgumentError) ошибка аргумента (stdlib):ets.lookup (: telemetry_handler_table, [: redix,: pipe])

Я проверил в Redis, ключ и значение существуют.Как я могу это исправить?

Спасибо

1 Ответ

1 голос
/ 12 июля 2019

Теперь, когда я запускаю MyApp.get ("1", "2"), я получаю следующую ошибку:

Нет модуля с именем MyApp - вы написали defmodule myapp - и в этом модуле нет функции с именем get().Вместо описания того, что вы сделали, лучше скопировать и вставить команды, которые вы выполняли в iex, и вывод.

Когда вы используете mix для создания нового проекта Phoenix, у вас должен быть файл с именем myapp/lib/myapp/application.ex, которыйопределяет модуль defmodule MyApp.Application, который содержит код, который вы опубликовали для файла lib/myapp.ex.

. Это:

pid = get_pid()

- функция с плохим именем, потому что она не возвращает pid--это возвращает атом.

Также проверьте это:

iex(21)> for _ <- 1..20, do: System.unique_integer([:positive])        
[9412, 9444, 9476, 9508, 9540, 9572, 9604, 9636, 9668, 
 9700, 9732, 9764, 9796, 9828, 9860, 9892, 9924, 9956, 
 9988, 10020]

iex(22)> for _ <- 1..20, do: rem(System.unique_integer([:positive]), 4)
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

Обратите внимание, что в первом списке чисел каждое число на 32 больше, чем предыдущее число.Вам нужна следующая функция:

Enum.random(1..get_pool_size() )

Теперь посмотрите на вывод:

for _ <- 1..20, do: Enum.random(1..4)
[2, 2, 1, 3, 3, 2, 4, 2, 3, 1, 4, 2, 1, 1, 4, 1, 1, 4, 1, 2]

И здесь нет причин использовать индекс 0:

for i <- 0..(pool_size - 1) do

Облегчите себе задачу:

for i <- 1..pool_size do

Вот простой пример проекта phoenix, который подключается к серверу redis, работающему на локальном хосте:

~$ redis-server
50136:C 11 Jul 2019 21:26:30.954 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
50136:C 11 Jul 2019 21:26:30.954 # Redis version=5.0.5, bits=64, commit=00000000, modified=0, pid=50136, just started
50136:C 11 Jul 2019 21:26:30.954 # Warning: no config file specified, using the default config. In order to specify a config file use redis-server /path/to/redis.conf
50136:M 11 Jul 2019 21:26:30.955 * Increased maximum number of open files to 10032 (it was originally set to 256).
                _._                                                  
           _.-``__ ''-._                                             
      _.-``    `.  `_.  ''-._           Redis 5.0.5 (00000000/0) 64 bit
  .-`` .-```.  ```\/    _.,_ ''-._                                   
 (    '      ,       .-`  | `,    )     Running in standalone mode
 |`-._`-...-` __...-.``-._|'` _.-'|     Port: 6379
 |    `-._   `._    /     _.-'    |     PID: 50136
  `-._    `-._  `-./  _.-'    _.-'                                   
 |`-._`-._    `-.__.-'    _.-'_.-'|                                  
 |    `-._`-._        _.-'_.-'    |           http://redis.io        
  `-._    `-._`-.__.-'_.-'    _.-'                                   
 |`-._`-._    `-.__.-'    _.-'_.-'|                                  
 |    `-._`-._        _.-'_.-'    |                                  
  `-._    `-._`-.__.-'_.-'    _.-'                                   
      `-._    `-.__.-'    _.-'                                       
          `-._        _.-'                                           
              `-.__.-'                                               

50136:M 11 Jul 2019 21:26:30.957 # Server initialized
50136:M 11 Jul 2019 21:26:30.957 * Ready to accept connections

foo = your_app

foo / lib / foo / Applicaton.ex:

defmodule Foo.Application do
  # See https://hexdocs.pm/elixir/Application.html
  # for more information on OTP Applications
  @moduledoc false

  use Application

  def start(_type, _args) do
    # List all child processes to be supervised
    children = [
      # Start the Ecto repository
      Foo.Repo,
      # Start the endpoint when the application starts
      FooWeb.Endpoint,

      # Starts a worker by calling: Foo.Worker.start_link(arg)
      # {Foo.Worker, arg},
      {Redix, host: "localhost", name: :redix}
    ]

    # See https://hexdocs.pm/elixir/Supervisor.html
    # for other strategies and supported options
    opts = [strategy: :one_for_one, name: Foo.Supervisor]
    Supervisor.start_link(children, opts)
  end

  # Tell Phoenix to update the endpoint configuration
  # whenever the application is updated.
  def config_change(changed, _new, removed) do
    FooWeb.Endpoint.config_change(changed, removed)
    :ok
  end
end

foo / mix.exs:

defmodule Foo.MixProject do
  use Mix.Project

  def project do
    [
      app: :foo,
      version: "0.1.0",
      elixir: "~> 1.5",
      elixirc_paths: elixirc_paths(Mix.env()),
      compilers: [:phoenix, :gettext] ++ Mix.compilers(),
      start_permanent: Mix.env() == :prod,
      aliases: aliases(),
      deps: deps()
    ]
  end

  # Configuration for the OTP application.
  #
  # Type `mix help compile.app` for more information.
  def application do
    [
      mod: {Foo.Application, []},
      extra_applications: [:logger, :runtime_tools]
    ]
  end

  # Specifies which paths to compile per environment.
  defp elixirc_paths(:test), do: ["lib", "test/support"]
  defp elixirc_paths(_), do: ["lib"]

  # Specifies your project dependencies.
  #
  # Type `mix help deps` for examples and options.
  defp deps do
    [
      {:phoenix, "~> 1.4.0"},
      {:phoenix_pubsub, "~> 1.1"},

      {:phoenix_ecto, "~> 4.0"},
      {:ecto_sql, "~> 3.0"},
      {:postgrex, ">= 0.0.0"},

      {:phoenix_html, "~> 2.11"},
      {:phoenix_live_reload, "~> 1.2", only: :dev},
      {:gettext, "~> 0.11"},
      {:jason, "~> 1.0"},
      {:plug_cowboy, "~> 2.0"},

      {:mox, "~> 0.5.1"},

      {:redix, ">= 0.0.0"},
      {:castore, ">= 0.0.0"}
    ]
  end

  # Aliases are shortcuts or tasks specific to the current project.
  # For example, to create, migrate and run the seeds file at once:
  #
  #     $ mix ecto.setup
  #
  # See the documentation for `Mix` for more info on aliases.
  defp aliases do
    [
      "ecto.setup": ["ecto.create", "ecto.migrate", "run priv/repo/seeds.exs"],
      "ecto.reset": ["ecto.drop", "ecto.setup"],
      test: ["ecto.create --quiet", "ecto.migrate", "test"]
    ]
  end
end

В iex:

~/phoenix_apps/foo$ iex -S mix
Erlang/OTP 20 [erts-9.3] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:10] [hipe] [kernel-poll:false]
Interactive Elixir (1.8.2) - press Ctrl+C to exit (type h() ENTER for help)

iex(1)> Redix.command(:redix, ["SET", "connections", 10])
{:ok, "OK"}

iex(2)> Redix.command(:redix, ["INCR", "connections"])   
{:ok, 11}

iex(3)> 
...