case resource_pool:borrow(test_pool) of {error, E} -> io:format("Error while borrow from pool, reason: ~p", [E]); Resource -> try resource:operation(Resource), resource_pool:return(test_pool, Resource) catch _:_ -> resource_pool:invalidate(test_pool, Resource) end, end
If it reaches the limit then following borrow operation will successfully supplies a resource to invoker and then pool will additionally create new resource in Idle container to provide min_idle condition.
Resource pool can check status of managed resources. Options test_on_borrow and test_on_return control how pool tests resources: before providing resource to invoker {test_on_borrow, true} and after a resource was returned to pool {test_on_return, true}. If pool finds that the resource is not alive during test then the resource will be destroyed.
资源在Idle列表中的顺序
Option fifo (first-input-first-output) controls order of extracting a resources from Idle list. Diagrams below illustrate this. Suppose we fill out Idle list in order: was first, is next, then . Resource is active in given moment. If {fifo, true} is set the borrow operation leads to situation below: resource was came first and it becames active now (first input).
defmodule BookStore 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 = [
# Define workers and child supervisors to be supervised
# worker(BookStore.Worker, [arg1, arg2, arg3])
worker(BookStore.Repo, [])
]
BookStore.Router.start
# See http://elixir-lang.org/docs/stable/elixir/Supervisor.html
# for other strategies and supported options
opts = [strategy: :one_for_one, name: BookStore.Supervisor]
Supervisor.start_link(children, opts)
end
end
require 'capistrano/ext/multistage'
set :stages, ["staging", "production"]
set :default_stage, "production"
set :keep_releases, 5
set :application, "My Awesome App"
set :repository, "git@github.com:learnelixir/my-awesome-app.git"
set :scm, :git
set :branch, :master
set :use_sudo, false
set :normalize_asset_timestamps, false
set :deploy_via, :remote_cache
after "deploy:update", "deploy:cleanup"
after "deploy:update", "deploy:build", "deploy:cleanup"
namespace :assets do
task :precompile, roles: :web do
# do nothing
end
end
def is_application_running?(current_path)
pid = capture(%Q{ps ax -o pid= -o command=|
grep "/home/app/www/book_store/current/rel/book_store/.*/[b]eam"|awk '{print $1}'})
return pid != ""
end
namespace :deploy do
task :is_running, roles: :web do
is_running = is_application_running?(current_path)
if is_running
puts "Application is running"
else
puts "Application is NOT running"
end
end
task :build, roles: :web do
run "cd #{current_path} && mix deps.get && MIX_ENV=#{mix_env} mix release"
end
task :restart, roles: :web do
if is_application_running?(current_path)
run "cd #{current_path}/rel/book_store/bin && ./book_store stop"
end
run "cd #{current_path}/rel/book_store/bin && ./book_store start"
end
task :start, roles: :web do
run "cd #{current_path}/rel/book_store/bin && ./book_store start"
end
task :stop, roles: :web do
run "cd #{current_path}/rel/book_store/bin && ./book_store stop"
end
end
步骤7: 创建production.rb
1
vim config/deploy/production.rb
内容如下
1
server "xx.xx.xx.xx", :app, :web, :db, :primary => true
set :user, '<user>'
set :branch, :master
set :mix_env, :prod
set :deploy_to, "/home/<user>/www/book_store"
set :default_environment, {
'PATH' => "$PATH:/home/app/src/elixir/bin" # --> replace by path to your elixir bin folder
}
iex> book = %{book | description: "Programming Elixir: a lot more fun", \
title: "Programming Elixir with fun"}
%BookStore.Books{author: "Dave Thomas",
description: "Programming Elixir: a lot more fun", id: 1,
publisher: "The Pragmatic Bookshelf", title: "Programming Elixir with fun"}
iex> Repo.update(book)
:ok
iex> Repo.get(Books, 1)
%BookStore.Books{author: "Dave Thomas",
description: "Programming Elixir: a lot more fun", id: 1,
publisher: "The Pragmatic Bookshelf", title: "Programming Elixir with fun"}
插入
1
iex> Repo.insert(%Books{author: "Simon St. Laurent, J. David Eisenberg", \
description: "Elixir is an excellent language if you want to \
learn about functional programming, and with this hands-on \
introduction",
publisher: "O'Reilly", title: "Introducing Elixir"})
%BookStore.Books{author: "Simon St. Laurent, J. David Eisenberg",
description: "Elixir is an excellent language if you want to learn about \
functional programming, and with this hands-on introduction", \
id: 18, publisher: "O'Reilly", title: "Introducing Elixir"}
删除
1
iex> introducing_elixir_book = Repo.get(Books, 2)
%BookStore.Books{author: "Simon St. Laurent, J. David Eisenberg",
description: "Elixir is an excellent language if you want to learn \
about functional programming, and with this hands-on introduction",
id: 2, publisher: "O'Reilly", title: "Introducing Elixir"}
iex> Repo.delete(introducing_elixir_book)
:ok
defmodule BookStore.Mixfile do
use Mix.Project
def project do
[app: :book_store,
version: "0.0.1",
elixir: "~> 1.0",
elixirc_paths: ["lib", "web"],
compilers: [:phoenix] ++ Mix.compilers,
deps: deps]
end
# Configuration for the OTP application
#
# Type `mix help compile.app` for more information
def application do
[mod: {BookStore, []},
applications: [:phoenix, :cowboy, :logger, :postgrex, :ecto]]
end
# Specifies your project dependencies
#
# Type `mix help deps` for examples and options
defp deps do
[ {:phoenix, github: "phoenixframework/phoenix"},
{:cowboy, "~> 1.0"},
{:postgrex, "~> 0.6.0"},
{:ecto, "~> 0.2.8"} ]
end
end
defmodule BookStore.Repo do
use Ecto.Repo, adapter: Ecto.Adapters.Postgres
def conf do
parse_url "ecto://postgres:postgres@localhost/book_store"
end
def priv do
app_dir(:book_store, "priv/repo")
end
end
defmodule BookStore 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 = [
# Define workers and child supervisors to be supervised
worker(BookStore.Repo, [])
]
opts = [strategy: :one_for_one, name: BookStore.Supervisor]
Supervisor.start_link(children, opts)
end
end
编译
1
mix compile
创建模型
创建文件web/models/books.ex, 内容如下:
1
defmodule BookStore.Books do
use Ecto.Model
schema "books" do
field :title, :string
field :description, :string
field :author, :string
field :publisher, :string
end
end
defmodule BookStore.Queries do
import Ecto.Query
def books_query do
query = from book in BookStore.Books,
select: book
BookStore.Repo.all(query)
end
end
配置路由
打开文件web/router.ex, 修改为如下:
1
defmodule BookStore.Router do
use Phoenix.Router
scope "/" do
# Use the default browser stack.
pipe_through :browser
#get "/", BookStore.PageController, :index, as: :pages
get "/", BookStore.BookController, :index, as: :books
end
# Other scopes may use custom stacks.
# scope "/api" do
# pipe_through :api
# end
end
创建控制器
创建文件web/controllers/book_controller.ex, 内容如下:
1
defmodule BookStore.BookController do
use Phoenix.Controller
plug :action
def index(conn, _params) do
books = BookStore.Queries.books_query
render conn, "index", books: books
end
end
创建书单视图
创建文件web/views/book_view.ex, 内容如下:
1
defmodule BookStore.BookView do
use BookStore.Views
end
Quote function is like a function which is used to put an expression between a quote so that it can be used later on.
Next let’s try to define some variables and use those in quote body:
1
iex> a = 1
1
iex> b = 2
2
iex> Code.eval_quoted(quote do: a + b)
** (CompileError) nofile:1: undefined function a/0
The eval_quoted function call will give you an error on undefined function a. This happens because when Code.eval_quoted is called, it does not know any a value, because the a here is not the same variable that we defined outside ealier. In order to refer a variable defined outside quote, unquote function needs to be used
Unquote
So here, how it should be written if a variable is referred to outside of the scope of quote:
1
iex> a = 1
1
iex> b = 2
b
iex> quote do: unquote(a) + unquote(b)
{:+, [context: Elixir, import: Kernel], [1, 2]}
As you can see, the value of a and b are now evaluated correctly before Elixir construct the abstract syntax tree and these values are actually computed at compiled time and not runtime. Now, let say, we define a function:
1
iex> a = 1
1
iex> b = 2
b
iex> fun = fn -> quote do
unquote(a) + unquote(b)
end
end
{:+, [context: Elixir, import: Kernel], [1, 2]}
Now we try to change a value and call the function again to see if the presentation will change with the new a value:
As you can see, although a’s value is change but the funtioncal representing a + b is still reflecting the original value of a and b. The way that we use quote and unquote in Elixir can be very creative and dynamic, for instance, we can define like following to play with the real function definition at runtime.
1
iex> num1 = 5
iex> num2 = 2
iex> perform = fn fun -> Code.eval_quoted(quote do: unquote(fun)(unquote(num1), unquote(num2))) end
iex> perform.(:rem) # calculate remaining of 5 and 2
{1, []}
iex> perform.(:div) # calculate division result of 5 and 2
{2, []}
defmodule MyList do
def filter([], _func) do
[]
end
def filter([head|tail], func) do
if func.(head) do
[head | filter(tail, func)]
else
filter(tail, func)
end
end
end
## 奇数
MyList.filter([1,2,3,4,5,6], fn num -> rem(num,2) == 1 end)
defmodule MyProject.Mixfile do
use Mix.Project
def project do
[app: :my_project,
version: "0.0.1",
deps: deps]
end
# Configuration for the OTP application
def application do
[]
end
# Returns the list of dependencies in the format:
# {:foobar, git: "https://github.com/elixir-lang/foobar.git", tag: "0.1"}
#
# To specify particular versions, regardless of the tag, do:
# {:barbat, "~> 0.1", github: "elixir-lang/barbat"}
defp deps do
[]
end
end
defmodule A.Mixfile do
use Mix.Project
def project do
[app: :a,
deps_path: "../../deps",
lockfile: "../../mix.lock",
deps: deps]
end
defp deps do
[{ :b, in_umbrella: true }]
end
end
root@0b85dcd174f2:~/ejabberd/elixir# mix new umbrella_project --umbrella
* creating .gitignore
* creating README.md
* creating mix.exs
* creating apps
* creating config
* creating config/config.exs
Your umbrella project was created successfully.
Inside your project, you will find an apps/ directory
where you can create and host many apps:
cd umbrella_project
cd apps
mix new my_app
Commands like `mix compile` and `mix test` when executed
in the umbrella project root will automatically run
for each application in the apps/ directory.
defmodule Stacker.Server do
use GenServer.Behaviour
def init(stack) do
{ :ok, stack }
end
def handle_call(:pop, _from, [h|stack]) do
{ :reply, h, stack }
end
def handle_cast({ :push, new }, stack) do
{ :noreply, [new|stack] }
end
end
# Let's start the server using Erlang's :gen_server module.
# It expects 3 arguments: the server module, the initial
# stack and some options (if desired):
iex> { :ok, pid } = :gen_server.start_link(Stacker.Server, [], [])
{:ok,<...>}
# Now let's push something onto the stack
iex> :gen_server.cast(pid, { :push, 13 })
:ok
# Now let's get it out from the stack
# Notice we are using *call* instead of *cast*
iex> :gen_server.call(pid, :pop)
13
defmodule Stacker.Supervisor do
use Supervisor.Behaviour
# A convenience to start the supervisor
def start_link(stack) do
:supervisor.start_link(__MODULE__, stack)
end
# The callback invoked when the supervisor starts
def init(stack) do
children = [ worker(Stacker.Server, [stack]) ]
supervise children, strategy: :one_for_one
end
end
defmodule Stacker.Server do
use GenServer.Behaviour
def start_link(stack) do
:gen_server.start_link({ :local, :stacker }, __MODULE__, stack, [])
end
def init(stack) do
{ :ok, stack }
end
def handle_call(:pop, _from, [h|stack]) do
{ :reply, h, stack }
end
def handle_cast({ :push, new }, stack) do
{ :noreply, [new|stack] }
end
end
# Now we will start the supervisor with a
# default stack containing :hello
iex> Stacker.Supervisor.start_link([:hello])
{:ok,<...>}
# And we will access the server by name since
# we registered it
iex> :gen_server.call(:stacker, :pop)
:hello
defmodule Mix.Tasks.Mytasks do
@shortdoc "任务集合模块"
@moduledoc """
用于构建和部署的任务集合
"""
defmodule Build do
use Mix.Task
@shortdoc "构建任务"
@moduledoc """
构建一个软件组件模块
"""
def run(_) do
IO.puts "运行子任务Build"
end
end
defmodule Deploy do
use Mix.Task
@shortdoc "部署一个软件组件"
@moduledoc """
把一个软件组件部署到服务器
"""
def run(_) do
IO.puts "运行子任务Deploy"
end
end
end