我在学习Elixir的多进程模型的同时,尝试让Phoeix能够访问Memcached
做的事情 (zuò de
在Phoenix应用中添加访问memcached的进程
确认在Phoenix中启动子进程的部分。
Elixir(以及Erlang)採用了多進程模型,多個進程協同執行一個應用程序。因此,显然以Elixir构建的Phoenix Framework也是基於多進程模型。
实际上,在创建应用程序时会创建以下类型的核心模块。
defmodule PhoenixSample 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, warn: false
children = [
# Start the endpoint when the application starts
supervisor(PhoenixSample.Endpoint, []),
# Start the Ecto repository
supervisor(PhoenixSample.Repo, []),
# Here you could define other workers and supervisors as children
# worker(PhoenixSample.Worker, [arg1, arg2, arg3]),
]
# See http://elixir-lang.org/docs/stable/elixir/Supervisor.html
# for other strategies and supported options
opts = [strategy: :one_for_one, name: PhoenixSample.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
PhoenixSample.Endpoint.config_change(changed, removed)
:ok
end
end
这是在做什么?
children = [
# Start the endpoint when the application starts
supervisor(PhoenixSample.Endpoint, []),
# Start the Ecto repository
supervisor(PhoenixSample.Repo, []),
# Here you could define other workers and supervisors as children
# worker(PhoenixSample.Worker, [arg1, arg2, arg3]),
]
在这一部分中,我们将启动两个进程作为监管者,一个用于访问数据库,另一个用于处理通过HTTP的访问。
Supervisor.start_link(children, opts)
在这个部分,它将当前进程注册为子进程。
什么是监督者?
在Elixir中,我们经常使用一种方法来管理进程,即在一个父进程下挂载多个子进程,并且由父进程统一处理子进程的生命周期。这种方法被广泛应用作为进程管理的一种良好模式。监督者是指监视子进程的状态的父进程,而实际上执行任务处理的进程被称为工作者。
最重要的是,并非所有监督者的子进程都是工作者,有时还会有其他监督者来监视这些子进程。
在上面的代码中,执行对数据库的访问的Repository进程被生成为一个监督者,很可能这个Repository进程的子进程将执行实际的数据库访问任务。
我觉得需要进一步详细讲解Supervisor模块和GenServer模块,但我参考的这篇文章非常易懂,所以只贴链接省略了描述。
新增一个访问memcached的进程。
基于上述情况,我们将在示例应用程序中添加子进程以访问memcached。作为memcached的客户端,我们将使用mcd,并使用poolboy对工作进程进行池化。
修复依赖包
打开mix.exs文件,在deps中添加库。
defp deps do
[{:phoenix, "~> 1.1.4"},
{:mariaex, ">= 0.0.0"},
{:phoenix_ecto, "~> 2.0"},
{:phoenix_html, "~> 2.4"},
{:phoenix_live_reload, "~> 1.0", only: :dev},
{:gettext, "~> 0.9"},
{:cowboy, "~> 1.0"},
{:ecto_fixtures, github: "DockYard/ecto_fixtures"},
{:poolboy, "~> 1.5.1"}, # ADD
{:mcd, github: "EchoTeam/mcd"} #ADD
]
end
我还会在应用程序中添加poolboy。
def application do
[mod: {PhoenixSample, []},
applications: [:phoenix, :phoenix_html, :cowboy, :logger, :gettext,
:phoenix_ecto, :mariaex, :poolboy]]
end
使用Supervisor模块来创建memcached。
我将创建如下所示的模块。(Wǒ de .)
defmodule PhoenixSample.Memcached do
use Supervisor
def start_link do
Supervisor.start_link(__MODULE__, [])
end
def init([]) do
pool_options = [
name: {:local, :memcached_pool},
worker_module: :mcd,
size: 5,
max_overflow: 0,
]
arg = ['localhost', 11211]
children = [
:poolboy.child_spec(:memcached_pool, pool_options, arg)
]
supervise(children, strategy: :one_for_one)
end
def set(key, value) do
:poolboy.transaction :memcached_pool, &(:mcd.set(&1, key, value))
end
def get(key) do
:poolboy.transaction(:memcached_pool, fn worker ->
:mcd.get(worker, key)
|> case do
{:ok, resp} -> resp
_ -> "Boom!"
end
end)
end
def delete(key) do
:poolboy.transaction :memcached_pool, &(:mcd.delete(&1, key))
end
end
这个模块的作用是,在进程启动时创建一个由多个mcd工作进程组成的组,名称为:memcached_pool。
def init([]) do
pool_options = [
name: {:local, :memcached_pool},
worker_module: :mcd,
size: 5,
max_overflow: 0,
]
arg = ['localhost', 11211]
children = [
:poolboy.child_spec(:memcached_pool, pool_options, arg)
]
supervise(children, strategy: :one_for_one)
end
当访问memcached时,根据名称为:memcached_pool的进程进行池化,然后使用该进程进行访问操作。
def set(key, value) do
:poolboy.transaction :memcached_pool, &(:mcd.set(&1, key, value))
end
:mcd.set/3函数的第一个参数是访问memcached的进程pid。
将上述模块作为子进程添加到示例应用程序中。
defmodule PhoenixSample 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, warn: false
children = [
# Start the endpoint when the application starts
supervisor(PhoenixSample.Endpoint, []),
# Start the Ecto repository
supervisor(PhoenixSample.Repo, []),
# Here you could define other workers and supervisors as children
# worker(PhoenixSample.Worker, [arg1, arg2, arg3]),
supervisor(PhoenixSample.Memcached, []), # ADD
]
# See http://elixir-lang.org/docs/stable/elixir/Supervisor.html
# for other strategies and supported options
opts = [strategy: :one_for_one, name: PhoenixSample.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
PhoenixSample.Endpoint.config_change(changed, removed)
:ok
end
end
确认动作
我将使用iex来确认操作。
[vagrant@vagrant-centos7 phoenix_sample]$ iex -S mix phoenix.server
Erlang/OTP 18 [erts-7.2] [source-e6dd627] [64-bit] [smp:2:2] [async-threads:10] [hipe] [kernel-poll:false]
...
[info] Running PhoenixSample.Endpoint with Cowboy using http on port 4000
Interactive Elixir (1.3.0-dev) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> [info] Creating interface #PID<0.451.0> to memcached on 'localhost':11211
[info] Creating interface #PID<0.454.0> to memcached on 'localhost':11211
[info] Creating interface #PID<0.457.0> to memcached on 'localhost':11211
[info] Creating interface #PID<0.460.0> to memcached on 'localhost':11211
[info] Creating interface #PID<0.463.0> to memcached on 'localhost':11211
iex(2)> PhoenixSample.Memcached.set("name", "YMST")
{:ok, "YMST"}
iex(3)> PhoenixSample.Memcached.get("name")
"YMST"
$ memcached-tool localhost dump
Dumping memcache contents
Number of buckets: 1
Number of items : 1
Dumping bucket 2 - 1 total items
add ucO902IZy6w7DAHvEHd/Qw== 0 1458551639 10
?mYMST
$
你可以看到,在服务器启动时会创建5个用于memcached的进程,并且可以通过这些进程访问memcached。
最后
本次使用的示例项目在这里。
https://github.com/ak-ymst/phoenix_sample
当初设定的目标,即与Memcached进行通信,已经实现了。
但是,实际的实现方式使用了各种包装过的模块,所以对于基本部分还不能说已经完全理解,希望能更深入地理解后再写一篇文章。
请参考
《Elixir OTP(GenServer 篇):http://qiita.com/naoya@github/items/ae17a8166e52fc463012》的中文翻译:
使用Phoenix中的Poolboy来进行连接池管理(进程池):http://ymmtmsys.hatenablog.com/entry/2015/09/02/214254
在Phoenix中添加自定义处理:http://qiita.com/yokaigundan/items/d4a049676d1e58d0948e